Version in base suite: 5.0.32-7etch8 Version in overlay suite: 5.0.32-7etch10 Base version: mysql-dfsg-5.0_5.0.32-7etch8 Target version: mysql-dfsg-5.0_5.0.32-7etch11 Base file: /org/ftp.debian.org/ftp/pool/main/m/mysql-dfsg-5.0/mysql-dfsg-5.0_5.0.32-7etch8.dsc Target file: /org/ftp.debian.org/queue/o-p-u-new/mysql-dfsg-5.0_5.0.32-7etch11.dsc debian/patches/95_SECURITY_CVE-2009-2446.dpatch | 95 debian/patches/98_SECURITY_CVE-2008-3963.dpatch | 6771 +++++++++++++++++ debian/patches/99_SECURITY_CVE-2008-4456.dpatch | 37 mysql-dfsg-5.0-5.0.32/Docs/Makefile | 18 mysql-dfsg-5.0-5.0.32/cmd-line-utils/Makefile | 18 mysql-dfsg-5.0-5.0.32/cmd-line-utils/libedit/Makefile | 18 mysql-dfsg-5.0-5.0.32/cmd-line-utils/readline/Makefile | 18 mysql-dfsg-5.0-5.0.32/debian/changelog | 27 mysql-dfsg-5.0-5.0.32/debian/patches/00list | 3 9 files changed, 6969 insertions(+), 36 deletions(-) diff -u mysql-dfsg-5.0-5.0.32/cmd-line-utils/libedit/Makefile mysql-dfsg-5.0-5.0.32/cmd-line-utils/libedit/Makefile --- mysql-dfsg-5.0-5.0.32/cmd-line-utils/libedit/Makefile +++ mysql-dfsg-5.0-5.0.32/cmd-line-utils/libedit/Makefile @@ -103,11 +103,11 @@ ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 +ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 ALLOCA = AMDEP_FALSE = # AMDEP_TRUE = -AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run tar +AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run tar AR = ar ARFLAGS = cru AS = gcc -c @@ -119,9 +119,9 @@ ASSEMBLER_sparc64_TRUE = # ASSEMBLER_x86_FALSE = ASSEMBLER_x86_TRUE = # -AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoconf -AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoheader -AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 +AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoconf +AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoheader +AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 AVAILABLE_LANGUAGES = czech danish dutch english estonian french german greek hungarian italian japanese korean norwegian norwegian-ny polish portuguese romanian russian serbian slovak spanish swedish ukrainian AWK = gawk CC = gcc @@ -138,7 +138,7 @@ COMPILATION_COMMENT = Debian etch distribution COMPILE_PSTACK_FALSE = COMPILE_PSTACK_TRUE = # -CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch6' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' +CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch9' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' CP = /bin/cp CPP = gcc -E CPPFLAGS = @@ -196,7 +196,7 @@ LTLIBOBJS = MACHINE_TYPE = x86_64 MAKEINDEX = /usr/bin/makeindex -MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run makeinfo +MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run makeinfo MAKE_BINARY_DISTRIBUTION_OPTIONS = --with-ndbcluster MAKE_SHELL = /bin/sh MV = /bin/mv @@ -205,7 +205,7 @@ MYSQLD_USER = mysql MYSQL_BASE_VERSION = 5.0 MYSQL_NO_DASH_VERSION = 5.0.32 -MYSQL_SERVER_SUFFIX = -Debian_7etch6 +MYSQL_SERVER_SUFFIX = -Debian_7etch9 MYSQL_TCP_PORT = 3306 MYSQL_TCP_PORT_DEFAULT = 3306 MYSQL_UNIX_ADDR = /var/run/mysqld/mysqld.sock @@ -304,7 +304,7 @@ innodb_includes = -I$(top_builddir)/innobase/include -I$(top_srcdir)/innobase/include innodb_libs = $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/dict/libdict.a $(top_builddir)/innobase/que/libque.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/row/librow.a $(top_builddir)/innobase/pars/libpars.a $(top_builddir)/innobase/btr/libbtr.a $(top_builddir)/innobase/trx/libtrx.a $(top_builddir)/innobase/read/libread.a $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/buf/libbuf.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/eval/libeval.a $(top_builddir)/innobase/log/liblog.a $(top_builddir)/innobase/fsp/libfsp.a $(top_builddir)/innobase/fut/libfut.a $(top_builddir)/innobase/fil/libfil.a $(top_builddir)/innobase/lock/liblock.a $(top_builddir)/innobase/mtr/libmtr.a $(top_builddir)/innobase/page/libpage.a $(top_builddir)/innobase/rem/librem.a $(top_builddir)/innobase/thr/libthr.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/data/libdata.a $(top_builddir)/innobase/mach/libmach.a $(top_builddir)/innobase/ha/libha.a $(top_builddir)/innobase/dyn/libdyn.a $(top_builddir)/innobase/mem/libmem.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/ut/libut.a $(top_builddir)/innobase/os/libos.a $(top_builddir)/innobase/ut/libut.a innodb_system_libs = -lrt -install_sh = /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/install-sh +install_sh = /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/install-sh libdir = ${exec_prefix}/lib libexecdir = /usr/sbin libmysqld_dirs = libmysqld diff -u mysql-dfsg-5.0-5.0.32/cmd-line-utils/readline/Makefile mysql-dfsg-5.0-5.0.32/cmd-line-utils/readline/Makefile --- mysql-dfsg-5.0-5.0.32/cmd-line-utils/readline/Makefile +++ mysql-dfsg-5.0-5.0.32/cmd-line-utils/readline/Makefile @@ -109,11 +109,11 @@ ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 +ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 ALLOCA = AMDEP_FALSE = # AMDEP_TRUE = -AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run tar +AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run tar AR = ar ARFLAGS = cru AS = gcc -c @@ -125,9 +125,9 @@ ASSEMBLER_sparc64_TRUE = # ASSEMBLER_x86_FALSE = ASSEMBLER_x86_TRUE = # -AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoconf -AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoheader -AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 +AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoconf +AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoheader +AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 AVAILABLE_LANGUAGES = czech danish dutch english estonian french german greek hungarian italian japanese korean norwegian norwegian-ny polish portuguese romanian russian serbian slovak spanish swedish ukrainian AWK = gawk CC = gcc @@ -144,7 +144,7 @@ COMPILATION_COMMENT = Debian etch distribution COMPILE_PSTACK_FALSE = COMPILE_PSTACK_TRUE = # -CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch6' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' +CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch9' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' CP = /bin/cp CPP = gcc -E CPPFLAGS = @@ -202,7 +202,7 @@ LTLIBOBJS = MACHINE_TYPE = x86_64 MAKEINDEX = /usr/bin/makeindex -MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run makeinfo +MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run makeinfo MAKE_BINARY_DISTRIBUTION_OPTIONS = --with-ndbcluster MAKE_SHELL = /bin/sh MV = /bin/mv @@ -211,7 +211,7 @@ MYSQLD_USER = mysql MYSQL_BASE_VERSION = 5.0 MYSQL_NO_DASH_VERSION = 5.0.32 -MYSQL_SERVER_SUFFIX = -Debian_7etch6 +MYSQL_SERVER_SUFFIX = -Debian_7etch9 MYSQL_TCP_PORT = 3306 MYSQL_TCP_PORT_DEFAULT = 3306 MYSQL_UNIX_ADDR = /var/run/mysqld/mysqld.sock @@ -310,7 +310,7 @@ innodb_includes = -I$(top_builddir)/innobase/include -I$(top_srcdir)/innobase/include innodb_libs = $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/dict/libdict.a $(top_builddir)/innobase/que/libque.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/row/librow.a $(top_builddir)/innobase/pars/libpars.a $(top_builddir)/innobase/btr/libbtr.a $(top_builddir)/innobase/trx/libtrx.a $(top_builddir)/innobase/read/libread.a $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/buf/libbuf.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/eval/libeval.a $(top_builddir)/innobase/log/liblog.a $(top_builddir)/innobase/fsp/libfsp.a $(top_builddir)/innobase/fut/libfut.a $(top_builddir)/innobase/fil/libfil.a $(top_builddir)/innobase/lock/liblock.a $(top_builddir)/innobase/mtr/libmtr.a $(top_builddir)/innobase/page/libpage.a $(top_builddir)/innobase/rem/librem.a $(top_builddir)/innobase/thr/libthr.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/data/libdata.a $(top_builddir)/innobase/mach/libmach.a $(top_builddir)/innobase/ha/libha.a $(top_builddir)/innobase/dyn/libdyn.a $(top_builddir)/innobase/mem/libmem.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/ut/libut.a $(top_builddir)/innobase/os/libos.a $(top_builddir)/innobase/ut/libut.a innodb_system_libs = -lrt -install_sh = /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/install-sh +install_sh = /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/install-sh libdir = ${exec_prefix}/lib libexecdir = /usr/sbin libmysqld_dirs = libmysqld diff -u mysql-dfsg-5.0-5.0.32/cmd-line-utils/Makefile mysql-dfsg-5.0-5.0.32/cmd-line-utils/Makefile --- mysql-dfsg-5.0-5.0.32/cmd-line-utils/Makefile +++ mysql-dfsg-5.0-5.0.32/cmd-line-utils/Makefile @@ -91,11 +91,11 @@ ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 +ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 ALLOCA = AMDEP_FALSE = # AMDEP_TRUE = -AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run tar +AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run tar AR = ar ARFLAGS = cru AS = gcc -c @@ -107,9 +107,9 @@ ASSEMBLER_sparc64_TRUE = # ASSEMBLER_x86_FALSE = ASSEMBLER_x86_TRUE = # -AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoconf -AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoheader -AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 +AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoconf +AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoheader +AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 AVAILABLE_LANGUAGES = czech danish dutch english estonian french german greek hungarian italian japanese korean norwegian norwegian-ny polish portuguese romanian russian serbian slovak spanish swedish ukrainian AWK = gawk CC = gcc @@ -126,7 +126,7 @@ COMPILATION_COMMENT = Debian etch distribution COMPILE_PSTACK_FALSE = COMPILE_PSTACK_TRUE = # -CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch6' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' +CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch9' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' CP = /bin/cp CPP = gcc -E CPPFLAGS = @@ -184,7 +184,7 @@ LTLIBOBJS = MACHINE_TYPE = x86_64 MAKEINDEX = /usr/bin/makeindex -MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run makeinfo +MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run makeinfo MAKE_BINARY_DISTRIBUTION_OPTIONS = --with-ndbcluster MAKE_SHELL = /bin/sh MV = /bin/mv @@ -193,7 +193,7 @@ MYSQLD_USER = mysql MYSQL_BASE_VERSION = 5.0 MYSQL_NO_DASH_VERSION = 5.0.32 -MYSQL_SERVER_SUFFIX = -Debian_7etch6 +MYSQL_SERVER_SUFFIX = -Debian_7etch9 MYSQL_TCP_PORT = 3306 MYSQL_TCP_PORT_DEFAULT = 3306 MYSQL_UNIX_ADDR = /var/run/mysqld/mysqld.sock @@ -292,7 +292,7 @@ innodb_includes = -I$(top_builddir)/innobase/include -I$(top_srcdir)/innobase/include innodb_libs = $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/dict/libdict.a $(top_builddir)/innobase/que/libque.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/row/librow.a $(top_builddir)/innobase/pars/libpars.a $(top_builddir)/innobase/btr/libbtr.a $(top_builddir)/innobase/trx/libtrx.a $(top_builddir)/innobase/read/libread.a $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/buf/libbuf.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/eval/libeval.a $(top_builddir)/innobase/log/liblog.a $(top_builddir)/innobase/fsp/libfsp.a $(top_builddir)/innobase/fut/libfut.a $(top_builddir)/innobase/fil/libfil.a $(top_builddir)/innobase/lock/liblock.a $(top_builddir)/innobase/mtr/libmtr.a $(top_builddir)/innobase/page/libpage.a $(top_builddir)/innobase/rem/librem.a $(top_builddir)/innobase/thr/libthr.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/data/libdata.a $(top_builddir)/innobase/mach/libmach.a $(top_builddir)/innobase/ha/libha.a $(top_builddir)/innobase/dyn/libdyn.a $(top_builddir)/innobase/mem/libmem.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/ut/libut.a $(top_builddir)/innobase/os/libos.a $(top_builddir)/innobase/ut/libut.a innodb_system_libs = -lrt -install_sh = /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/install-sh +install_sh = /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/install-sh libdir = ${exec_prefix}/lib libexecdir = /usr/sbin libmysqld_dirs = libmysqld diff -u mysql-dfsg-5.0-5.0.32/debian/changelog mysql-dfsg-5.0-5.0.32/debian/changelog --- mysql-dfsg-5.0-5.0.32/debian/changelog +++ mysql-dfsg-5.0-5.0.32/debian/changelog @@ -1,3 +1,30 @@ +mysql-dfsg-5.0 (5.0.32-7etch11) oldstable-security; urgency=high + + * Non-maintainer upload by the security team. + * Backport upstream fix for CVE-2009-2446: Multiple format string + vulnerabilities in the dispatch_command function in + libmysqld/sql_parse.cc allow remote authenticated users to cause a + denial of service (daemon crash) and possibly have unspecified other + impact via format string specifiers in a database name in a + (1) COM_CREATE_DB or (2) COM_DROP_DB request. Closes: #536726 + Complete debdiff for 5.0.32-7etch11 generously contributed by + Christian Hammers + + -- Sebastien Delafond Thu, 27 Aug 2009 21:08:27 +0200 + +mysql-dfsg-5.0 (5.0.32-7etch10) oldstable-security; urgency=high + + * Non-maintainer upload by the security team. + * Backport upstream fix for CVE-2008-3963, which allowed an + authenticated attacker to crash the MySQL server by supplying an + empty binary string literal (e.g. SELECT b'') in a statement. + * Apply third-party fix for CVE-2008-4456, in which XML/HTML special + characters were not escaped in output from the mysql client's --xml + and --html output modes, potentially facilitating script privilege + escalation or cross-site scripting attacks. + + -- Devin Carraway Mon, 23 Feb 2009 07:37:44 +0000 + mysql-dfsg-5.0 (5.0.32-7etch8) stable-security; urgency=high * Non-maintainer upload by the security team. diff -u mysql-dfsg-5.0-5.0.32/debian/patches/00list mysql-dfsg-5.0-5.0.32/debian/patches/00list --- mysql-dfsg-5.0-5.0.32/debian/patches/00list +++ mysql-dfsg-5.0-5.0.32/debian/patches/00list @@ -30,2 +30,5 @@ +95_SECURITY_CVE-2009-2446.dpatch 96_SECURITY_CVE-2008-0226+0227.dpatch 97_SECURITY_CVE-2008-4098.dpatch +98_SECURITY_CVE-2008-3963.dpatch +99_SECURITY_CVE-2008-4456.dpatch diff -u mysql-dfsg-5.0-5.0.32/Docs/Makefile mysql-dfsg-5.0-5.0.32/Docs/Makefile --- mysql-dfsg-5.0-5.0.32/Docs/Makefile +++ mysql-dfsg-5.0-5.0.32/Docs/Makefile @@ -56,11 +56,11 @@ build_triplet = x86_64-pc-linux-gnu host_triplet = x86_64-pc-linux-gnu target_triplet = x86_64-pc-linux-gnu -ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 +ACLOCAL = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run aclocal-1.9 ALLOCA = AMDEP_FALSE = # AMDEP_TRUE = -AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run tar +AMTAR = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run tar AR = ar AS = gcc -c ASSEMBLER_FALSE = @@ -71,9 +71,9 @@ ASSEMBLER_sparc64_TRUE = # ASSEMBLER_x86_FALSE = ASSEMBLER_x86_TRUE = # -AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoconf -AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run autoheader -AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 +AUTOCONF = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoconf +AUTOHEADER = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run autoheader +AUTOMAKE = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run automake-1.9 AVAILABLE_LANGUAGES = czech danish dutch english estonian french german greek hungarian italian japanese korean norwegian norwegian-ny polish portuguese romanian russian serbian slovak spanish swedish ukrainian AVAILABLE_LANGUAGES_ERRORS = @AVAILABLE_LANGUAGES_ERRORS@ AWK = gawk @@ -94,7 +94,7 @@ COMPILATION_COMMENT = Debian etch distribution COMPILE_PSTACK_FALSE = COMPILE_PSTACK_TRUE = # -CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch6' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' +CONF_COMMAND = ./configure '--build=x86_64-linux-gnu' '--host=x86_64-linux-gnu' '--prefix=/usr' '--exec-prefix=/usr' '--libexecdir=/usr/sbin' '--datadir=/usr/share' '--localstatedir=/var/lib/mysql' '--includedir=/usr/include' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--with-server-suffix=-Debian_7etch9' '--with-comment=Debian etch distribution' '--enable-shared' '--enable-static' '--enable-thread-safe-client' '--enable-local-infile' '--with-big-tables' '--with-raid' '--with-unix-socket-path=/var/run/mysqld/mysqld.sock' '--with-mysqld-user=mysql' '--with-libwrap' '--with-vio' '--without-openssl' '--with-yassl' '--without-docs' '--with-bench' '--without-readline' '--with-extra-charsets=all' '--with-innodb' '--with-isam' '--with-archive-storage-engine' '--with-csv-storage-engine' '--with-federated-storage-engine' '--without-embedded-server' '--with-ndbcluster' '--with-ndb-shm' '--without-ndb-sci' '--without-ndb-test' '--with-embedded-server' '--with-embedded-privilege-control' '--with-ndb-docs' 'CC=gcc' 'CFLAGS=-DBIG_JOINS=1 -O2' 'CXXFLAGS=-DBIG_JOINS=1 -felide-constructors -fno-rtti -O2' 'CXX=g++' 'build_alias=x86_64-linux-gnu' 'host_alias=x86_64-linux-gnu' CP = /bin/cp CPP = gcc -E CPPFLAGS = @@ -147,7 +147,7 @@ MAINT = @MAINT@ MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ -MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/missing --run makeinfo +MAKEINFO = ${SHELL} /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/missing --run makeinfo MAKE_BINARY_DISTRIBUTION_OPTIONS = --with-ndbcluster MAKE_SHELL = /bin/sh MT_INCLUDES = @MT_INCLUDES@ @@ -158,7 +158,7 @@ MYSQLD_USER = mysql MYSQL_BASE_VERSION = 5.0 MYSQL_NO_DASH_VERSION = 5.0.32 -MYSQL_SERVER_SUFFIX = -Debian_7etch6 +MYSQL_SERVER_SUFFIX = -Debian_7etch9 MYSQL_TCP_PORT = 3306 MYSQL_TCP_PORT_DEFAULT = 3306 MYSQL_UNIX_ADDR = /var/run/mysqld/mysqld.sock @@ -235,7 +235,7 @@ innodb_includes = -I$(top_builddir)/innobase/include -I$(top_srcdir)/innobase/include innodb_libs = $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/dict/libdict.a $(top_builddir)/innobase/que/libque.a $(top_builddir)/innobase/srv/libsrv.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/row/librow.a $(top_builddir)/innobase/pars/libpars.a $(top_builddir)/innobase/btr/libbtr.a $(top_builddir)/innobase/trx/libtrx.a $(top_builddir)/innobase/read/libread.a $(top_builddir)/innobase/usr/libusr.a $(top_builddir)/innobase/buf/libbuf.a $(top_builddir)/innobase/ibuf/libibuf.a $(top_builddir)/innobase/eval/libeval.a $(top_builddir)/innobase/log/liblog.a $(top_builddir)/innobase/fsp/libfsp.a $(top_builddir)/innobase/fut/libfut.a $(top_builddir)/innobase/fil/libfil.a $(top_builddir)/innobase/lock/liblock.a $(top_builddir)/innobase/mtr/libmtr.a $(top_builddir)/innobase/page/libpage.a $(top_builddir)/innobase/rem/librem.a $(top_builddir)/innobase/thr/libthr.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/data/libdata.a $(top_builddir)/innobase/mach/libmach.a $(top_builddir)/innobase/ha/libha.a $(top_builddir)/innobase/dyn/libdyn.a $(top_builddir)/innobase/mem/libmem.a $(top_builddir)/innobase/sync/libsync.a $(top_builddir)/innobase/ut/libut.a $(top_builddir)/innobase/os/libos.a $(top_builddir)/innobase/ut/libut.a innodb_system_libs = -lrt -install_sh = /home/aqua/tinker/debian/security/mysql/mysql-dfsg-5.0-5.0.32/install-sh +install_sh = /home/aqua/tinker/debian/security/mysql/three/mysql-dfsg-5.0-5.0.32/install-sh isam_libs = @isam_libs@ libdir = ${exec_prefix}/lib libexecdir = /usr/sbin only in patch2: unchanged: --- mysql-dfsg-5.0-5.0.32.orig/debian/patches/95_SECURITY_CVE-2009-2446.dpatch +++ mysql-dfsg-5.0-5.0.32/debian/patches/95_SECURITY_CVE-2009-2446.dpatch @@ -0,0 +1,95 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 99-unnamed.dpatch by Christian Hammers +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Patch provided by Sergei Golubchik of MySQL. + +@DPATCH@ + +# From: Date: July 1 2009 2:09pm +# Subject: bzr commit into mysql-5.0-bugteam branch (staale.smedseng:2789) Bug#45790 +# List-Archive: http://lists.mysql.com/commits/77649 +# +# #At file:///export/home/tmp/ss156133/z/45790-50/ based on revid:staale.smedseng@stripped +# +# 2789 Staale Smedseng 2009-07-01 +# Bug #45790 Potential DoS vector: Writing of user input to log +# without proper formatting +# +# The problem is that a suitably crafted database identifier +# supplied to COM_CREATE_DB or COM_DROP_DB can cause a SIGSEGV, +# and thereby a denial of service. The database name is printed +# to the log without using a format string, so potential +# attackers can control the behavior of my_b_vprintf() by +# supplying their own format string. A CREATE or DROP privilege +# would be required. +# +# This patch supplies a format string to the printing of the +# database name. A test case is added to mysql_client_test. +# @ sql/sql_parse.cc +# Added format strings. +# @ tests/mysql_client_test.c +# Added new test case. +# +# modified: +# sql/sql_parse.cc +# tests/mysql_client_test.c +--- old/sql/sql_parse.cc 2009-07-22 00:28:28.000000000 +0200 ++++ new/sql/sql_parse.cc 2009-07-22 00:29:32.000000000 +0200 +@@ -1935,7 +1935,7 @@ + } + if (check_access(thd,CREATE_ACL,db,0,1,0,is_schema_db(db))) + break; +- mysql_log.write(thd,command,packet); ++ mysql_log.write(thd, command, "%s", db); + bzero(&create_info, sizeof(create_info)); + mysql_create_db(thd, (lower_case_table_names == 2 ? alias : db), + &create_info, 0); +@@ -1960,7 +1960,7 @@ + ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0)); + break; + } +- mysql_log.write(thd,command,db); ++ mysql_log.write(thd, command, "%s", db); + mysql_rm_db(thd, db, 0, 0); + break; + } +--- old/tests/mysql_client_test.c 2009-05-05 09:07:11 +0000 ++++ new/tests/mysql_client_test.c 2009-07-01 12:09:44 +0000 +@@ -12063,6 +12063,27 @@ static void test_bug6081() + } + + ++/* ++ Verify that bogus database names are handled properly with ++ COM_CREATE_DB and COM_DROP_DB, i.e., cannot cause SIGSEGV through ++ the use of printf specifiers in the database name. ++*/ ++static void test_bug45790() ++{ ++ const char* bogus_db = "%s%s%s%s%s%s%s"; ++ int rc; ++ ++ myheader("test_bug45790"); ++ rc= simple_command(mysql, COM_CREATE_DB, bogus_db, ++ (ulong)strlen(bogus_db), 0); ++ myquery(rc); ++ ++ rc= simple_command(mysql, COM_DROP_DB, bogus_db, ++ (ulong)strlen(bogus_db), 0); ++ myquery(rc); ++} ++ ++ + static void test_bug6096() + { + MYSQL_STMT *stmt; +@@ -16829,6 +16850,7 @@ static struct my_tests_st my_tests[]= { + { "test_bug6059", test_bug6059 }, + { "test_bug6046", test_bug6046 }, + { "test_bug6081", test_bug6081 }, ++ { "test_bug45790",test_bug45790 }, + { "test_bug6096", test_bug6096 }, + { "test_datetime_ranges", test_datetime_ranges }, + { "test_bug4172", test_bug4172 }, + only in patch2: unchanged: --- mysql-dfsg-5.0-5.0.32.orig/debian/patches/98_SECURITY_CVE-2008-3963.dpatch +++ mysql-dfsg-5.0-5.0.32/debian/patches/98_SECURITY_CVE-2008-3963.dpatch @@ -0,0 +1,6771 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 98_SECURITY_CVE-2008-3963.dpatch by Devin Carraway +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Backport of upstream fix to CVE-2008-3963, correcting a denial of +## service (crash) vulnerability in mysqld's interpretation of empty +## binary literals. + +@DPATCH@ +diff -aruN mysql-dfsg-5.0-5.0.32.orig/mysql-test/r/varbinary.result mysql-dfsg-5.0-5.0.32.CVE-2008-3963/mysql-test/r/varbinary.result +--- mysql-dfsg-5.0-5.0.32.orig/mysql-test/r/varbinary.result 2006-12-20 11:30:57.000000000 +0000 ++++ mysql-dfsg-5.0-5.0.32.CVE-2008-3963/mysql-test/r/varbinary.result 2009-02-23 06:58:03.000000000 +0000 +@@ -78,3 +78,34 @@ + select length(a) from t1; + length(a) + 6 ++select 0b01000001; ++0b01000001 ++A ++select 0x41; ++0x41 ++A ++select b'01000001'; ++b'01000001' ++A ++select x'41', 0+x'41'; ++x'41' 0+x'41' ++A 65 ++select N'abc', length(N'abc'); ++abc length(N'abc') ++abc 3 ++select N'', length(N''); ++ length(N'') ++ 0 ++select '', length(''); ++ length('') ++ 0 ++select b'', 0+b''; ++b'' 0+b'' ++ 0 ++select x'', 0+x''; ++x'' 0+x'' ++ 0 ++select 0x; ++ERROR 42S22: Unknown column '0x' in 'field list' ++select 0b; ++ERROR 42S22: Unknown column '0b' in 'field list' +diff -aruN mysql-dfsg-5.0-5.0.32.orig/mysql-test/t/varbinary.test mysql-dfsg-5.0-5.0.32.CVE-2008-3963/mysql-test/t/varbinary.test +--- mysql-dfsg-5.0-5.0.32.orig/mysql-test/t/varbinary.test 2006-12-20 11:30:57.000000000 +0000 ++++ mysql-dfsg-5.0-5.0.32.CVE-2008-3963/mysql-test/t/varbinary.test 2009-02-23 06:58:03.000000000 +0000 +@@ -80,3 +80,31 @@ + alter table t1 modify a varchar(255); + select length(a) from t1; + ++# ++# Bug#35658 (An empty binary value leads to mysqld crash) ++# ++ ++select 0b01000001; ++ ++select 0x41; ++ ++select b'01000001'; ++ ++select x'41', 0+x'41'; ++ ++select N'abc', length(N'abc'); ++ ++select N'', length(N''); ++ ++select '', length(''); ++ ++select b'', 0+b''; ++ ++select x'', 0+x''; ++ ++--error ER_BAD_FIELD_ERROR ++select 0x; ++ ++--error ER_BAD_FIELD_ERROR ++select 0b; ++ +diff -aruN mysql-dfsg-5.0-5.0.32.orig/mysql-test/t/varbinary.test.orig mysql-dfsg-5.0-5.0.32.CVE-2008-3963/mysql-test/t/varbinary.test.orig +--- mysql-dfsg-5.0-5.0.32.orig/mysql-test/t/varbinary.test.orig 1970-01-01 00:00:00.000000000 +0000 ++++ mysql-dfsg-5.0-5.0.32.CVE-2008-3963/mysql-test/t/varbinary.test.orig 2006-12-20 11:30:57.000000000 +0000 +@@ -0,0 +1,82 @@ ++# Initialise ++--disable_warnings ++drop table if exists t1; ++--enable_warnings ++ ++# ++# varbinary as string and number ++# ++ ++select 0x41,0x41+0,0x41 | 0x7fffffffffffffff | 0,0xffffffffffffffff | 0 ; ++select 0x31+1,concat(0x31)+1,-0xf; ++select x'31',X'ffff'+0; ++ ++# ++# Test of hex constants in WHERE: ++# ++ ++create table t1 (ID int(8) unsigned zerofill not null auto_increment,UNIQ bigint(21) unsigned zerofill not null,primary key (ID),unique (UNIQ) ); ++insert into t1 set UNIQ=0x38afba1d73e6a18a; ++insert into t1 set UNIQ=123; ++explain extended select * from t1 where UNIQ=0x38afba1d73e6a18a; ++drop table t1; ++ ++# ++# Test error conditions ++# ++--error 1064 ++select x'hello'; ++--error 1054 ++select 0xfg; ++ ++# ++# Test likely error conditions ++# ++create table t1 select 1 as x, 2 as xx; ++select x,xx from t1; ++drop table t1; ++ ++# End of 4.1 tests ++ ++# ++# Bug #19371 VARBINARY() have trailing zeros after upgrade from 4.1 ++# ++ ++# Test with a saved table from 4.1 ++copy_file std_data/bug19371.frm $MYSQLTEST_VARDIR/master-data/test/t1.frm; ++chmod 0777 $MYSQLTEST_VARDIR/master-data/test/t1.frm; ++copy_file std_data/bug19371.MYD $MYSQLTEST_VARDIR/master-data/test/t1.MYD; ++chmod 0777 $MYSQLTEST_VARDIR/master-data/test/t1.MYD; ++copy_file std_data/bug19371.MYI $MYSQLTEST_VARDIR/master-data/test/t1.MYI; ++chmod 0777 $MYSQLTEST_VARDIR/master-data/test/t1.MYI; ++ ++# Everything _looks_ fine ++show create table t1; ++ ++# But the length of the varbinary columns are too long ++select length(a), length(b) from t1; ++ ++# Run CHECK TABLE, it should indicate table need a REPAIR TABLE ++CHECK TABLE t1 FOR UPGRADE; ++ ++# Run REPAIR TABLE to alter the table and repair ++# the varbinary fields ++REPAIR TABLE t1; ++ ++# Now check it's back to normal ++show create table t1; ++select length(a), length(b) from t1; ++insert into t1 values("ccc", "ddd"); ++select length(a), length(b) from t1; ++select hex(a), hex(b) from t1; ++select concat("'", a, "'"), concat("'", b, "'") from t1; ++ ++drop table t1; ++ ++# Check that the fix does not affect table created with current version ++create table t1(a varbinary(255)); ++insert into t1 values("aaa "); ++select length(a) from t1; ++alter table t1 modify a varchar(255); ++select length(a) from t1; ++ +diff -aruN mysql-dfsg-5.0-5.0.32.orig/sql/item.cc mysql-dfsg-5.0-5.0.32.CVE-2008-3963/sql/item.cc +--- mysql-dfsg-5.0-5.0.32.orig/sql/item.cc 2006-12-20 11:13:59.000000000 +0000 ++++ mysql-dfsg-5.0-5.0.32.CVE-2008-3963/sql/item.cc 2009-02-23 06:58:03.000000000 +0000 +@@ -4610,21 +4610,28 @@ + if (!ptr) + return; + str_value.set(ptr, max_length, &my_charset_bin); +- ptr+= max_length - 1; +- ptr[1]= 0; // Set end null for string +- for (; end >= str; end--) ++ ++ if (max_length > 0) + { +- if (power == 256) ++ ptr+= max_length - 1; ++ ptr[1]= 0; // Set end null for string ++ for (; end >= str; end--) + { +- power= 1; +- *ptr--= bits; +- bits= 0; ++ if (power == 256) ++ { ++ power= 1; ++ *ptr--= bits; ++ bits= 0; ++ } ++ if (*end == '1') ++ bits|= power; ++ power<<= 1; + } +- if (*end == '1') +- bits|= power; +- power<<= 1; ++ *ptr= (char) bits; + } +- *ptr= (char) bits; ++ else ++ ptr[0]= 0; ++ + collation.set(&my_charset_bin, DERIVATION_COERCIBLE); + fixed= 1; + } +diff -aruN mysql-dfsg-5.0-5.0.32.orig/sql/item.cc.orig mysql-dfsg-5.0-5.0.32.CVE-2008-3963/sql/item.cc.orig +--- mysql-dfsg-5.0-5.0.32.orig/sql/item.cc.orig 1970-01-01 00:00:00.000000000 +0000 ++++ mysql-dfsg-5.0-5.0.32.CVE-2008-3963/sql/item.cc.orig 2006-12-20 11:13:59.000000000 +0000 +@@ -0,0 +1,6556 @@ ++/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ ++ ++ ++#ifdef USE_PRAGMA_IMPLEMENTATION ++#pragma implementation // gcc: Class implementation ++#endif ++#include "mysql_priv.h" ++#include ++#include "my_dir.h" ++#include "sp_rcontext.h" ++#include "sp_head.h" ++#include "sql_trigger.h" ++#include "sql_select.h" ++ ++static void mark_as_dependent(THD *thd, ++ SELECT_LEX *last, SELECT_LEX *current, ++ Item_ident *item); ++ ++const String my_null_string("NULL", 4, default_charset_info); ++ ++/****************************************************************************/ ++ ++/* Hybrid_type_traits {_real} */ ++ ++void Hybrid_type_traits::fix_length_and_dec(Item *item, Item *arg) const ++{ ++ item->decimals= NOT_FIXED_DEC; ++ item->max_length= item->float_length(arg->decimals); ++} ++ ++static const Hybrid_type_traits real_traits_instance; ++ ++const Hybrid_type_traits *Hybrid_type_traits::instance() ++{ ++ return &real_traits_instance; ++} ++ ++ ++my_decimal * ++Hybrid_type_traits::val_decimal(Hybrid_type *val, my_decimal *to) const ++{ ++ double2my_decimal(E_DEC_FATAL_ERROR, val->real, val->dec_buf); ++ return val->dec_buf; ++} ++ ++ ++String * ++Hybrid_type_traits::val_str(Hybrid_type *val, String *to, uint8 decimals) const ++{ ++ to->set(val->real, decimals, &my_charset_bin); ++ return to; ++} ++ ++/* Hybrid_type_traits_decimal */ ++static const Hybrid_type_traits_decimal decimal_traits_instance; ++ ++const Hybrid_type_traits_decimal *Hybrid_type_traits_decimal::instance() ++{ ++ return &decimal_traits_instance; ++} ++ ++ ++void ++Hybrid_type_traits_decimal::fix_length_and_dec(Item *item, Item *arg) const ++{ ++ item->decimals= arg->decimals; ++ item->max_length= min(arg->max_length + DECIMAL_LONGLONG_DIGITS, ++ DECIMAL_MAX_STR_LENGTH); ++} ++ ++ ++void Hybrid_type_traits_decimal::set_zero(Hybrid_type *val) const ++{ ++ my_decimal_set_zero(&val->dec_buf[0]); ++ val->used_dec_buf_no= 0; ++} ++ ++ ++void Hybrid_type_traits_decimal::add(Hybrid_type *val, Field *f) const ++{ ++ my_decimal_add(E_DEC_FATAL_ERROR, ++ &val->dec_buf[val->used_dec_buf_no ^ 1], ++ &val->dec_buf[val->used_dec_buf_no], ++ f->val_decimal(&val->dec_buf[2])); ++ val->used_dec_buf_no^= 1; ++} ++ ++ ++void Hybrid_type_traits_decimal::div(Hybrid_type *val, ulonglong u) const ++{ ++ int2my_decimal(E_DEC_FATAL_ERROR, u, TRUE, &val->dec_buf[2]); ++ /* XXX: what is '4' for scale? */ ++ my_decimal_div(E_DEC_FATAL_ERROR, ++ &val->dec_buf[val->used_dec_buf_no ^ 1], ++ &val->dec_buf[val->used_dec_buf_no], ++ &val->dec_buf[2], 4); ++ val->used_dec_buf_no^= 1; ++} ++ ++ ++longlong ++Hybrid_type_traits_decimal::val_int(Hybrid_type *val, bool unsigned_flag) const ++{ ++ longlong result; ++ my_decimal2int(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], ++ unsigned_flag, &result); ++ return result; ++} ++ ++ ++double ++Hybrid_type_traits_decimal::val_real(Hybrid_type *val) const ++{ ++ my_decimal2double(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], ++ &val->real); ++ return val->real; ++} ++ ++ ++String * ++Hybrid_type_traits_decimal::val_str(Hybrid_type *val, String *to, ++ uint8 decimals) const ++{ ++ my_decimal_round(E_DEC_FATAL_ERROR, &val->dec_buf[val->used_dec_buf_no], ++ decimals, FALSE, &val->dec_buf[2]); ++ my_decimal2string(E_DEC_FATAL_ERROR, &val->dec_buf[2], 0, 0, 0, to); ++ return to; ++} ++ ++/* Hybrid_type_traits_integer */ ++static const Hybrid_type_traits_integer integer_traits_instance; ++ ++const Hybrid_type_traits_integer *Hybrid_type_traits_integer::instance() ++{ ++ return &integer_traits_instance; ++} ++ ++void ++Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const ++{ ++ item->decimals= 0; ++ item->max_length= 21; ++ item->unsigned_flag= 0; ++} ++ ++/***************************************************************************** ++** Item functions ++*****************************************************************************/ ++ ++/* Init all special items */ ++ ++void item_init(void) ++{ ++ item_user_lock_init(); ++} ++ ++ ++/* ++TODO: make this functions class dependent ++*/ ++ ++bool Item::val_bool() ++{ ++ switch(result_type()) { ++ case INT_RESULT: ++ return val_int() != 0; ++ case DECIMAL_RESULT: ++ { ++ my_decimal decimal_value; ++ my_decimal *val= val_decimal(&decimal_value); ++ if (val) ++ return !my_decimal_is_zero(val); ++ return 0; ++ } ++ case REAL_RESULT: ++ case STRING_RESULT: ++ return val_real() != 0.0; ++ case ROW_RESULT: ++ default: ++ DBUG_ASSERT(0); ++ return 0; // Wrong (but safe) ++ } ++} ++ ++ ++String *Item::val_string_from_real(String *str) ++{ ++ double nr= val_real(); ++ if (null_value) ++ return 0; /* purecov: inspected */ ++ str->set(nr,decimals, &my_charset_bin); ++ return str; ++} ++ ++ ++String *Item::val_string_from_int(String *str) ++{ ++ longlong nr= val_int(); ++ if (null_value) ++ return 0; ++ if (unsigned_flag) ++ str->set((ulonglong) nr, &my_charset_bin); ++ else ++ str->set(nr, &my_charset_bin); ++ return str; ++} ++ ++ ++String *Item::val_string_from_decimal(String *str) ++{ ++ my_decimal dec_buf, *dec= val_decimal(&dec_buf); ++ if (null_value) ++ return 0; ++ my_decimal_round(E_DEC_FATAL_ERROR, dec, decimals, FALSE, &dec_buf); ++ my_decimal2string(E_DEC_FATAL_ERROR, &dec_buf, 0, 0, 0, str); ++ return str; ++} ++ ++ ++my_decimal *Item::val_decimal_from_real(my_decimal *decimal_value) ++{ ++ double nr= val_real(); ++ if (null_value) ++ return 0; ++ double2my_decimal(E_DEC_FATAL_ERROR, nr, decimal_value); ++ return (decimal_value); ++} ++ ++ ++my_decimal *Item::val_decimal_from_int(my_decimal *decimal_value) ++{ ++ longlong nr= val_int(); ++ if (null_value) ++ return 0; ++ int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value); ++ return decimal_value; ++} ++ ++ ++my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) ++{ ++ String *res; ++ char *end_ptr; ++ if (!(res= val_str(&str_value))) ++ return 0; // NULL or EOM ++ ++ end_ptr= (char*) res->ptr()+ res->length(); ++ if (str2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_BAD_NUM, ++ res->ptr(), res->length(), res->charset(), ++ decimal_value) & E_DEC_BAD_NUM) ++ { ++ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ++ ER_TRUNCATED_WRONG_VALUE, ++ ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", ++ str_value.c_ptr()); ++ } ++ return decimal_value; ++} ++ ++ ++my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value) ++{ ++ DBUG_ASSERT(fixed == 1); ++ TIME ltime; ++ if (get_date(<ime, TIME_FUZZY_DATE)) ++ { ++ my_decimal_set_zero(decimal_value); ++ return 0; ++ } ++ return date2my_decimal(<ime, decimal_value); ++} ++ ++ ++my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value) ++{ ++ DBUG_ASSERT(fixed == 1); ++ TIME ltime; ++ if (get_time(<ime)) ++ { ++ my_decimal_set_zero(decimal_value); ++ return 0; ++ } ++ return date2my_decimal(<ime, decimal_value); ++} ++ ++ ++double Item::val_real_from_decimal() ++{ ++ /* Note that fix_fields may not be called for Item_avg_field items */ ++ double result; ++ my_decimal value_buff, *dec_val= val_decimal(&value_buff); ++ if (null_value) ++ return 0.0; ++ my_decimal2double(E_DEC_FATAL_ERROR, dec_val, &result); ++ return result; ++} ++ ++ ++longlong Item::val_int_from_decimal() ++{ ++ /* Note that fix_fields may not be called for Item_avg_field items */ ++ longlong result; ++ my_decimal value, *dec_val= val_decimal(&value); ++ if (null_value) ++ return 0; ++ my_decimal2int(E_DEC_FATAL_ERROR, dec_val, unsigned_flag, &result); ++ return result; ++} ++ ++int Item::save_time_in_field(Field *field) ++{ ++ TIME ltime; ++ if (get_time(<ime)) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ return field->store_time(<ime, MYSQL_TIMESTAMP_TIME); ++} ++ ++ ++int Item::save_date_in_field(Field *field) ++{ ++ TIME ltime; ++ if (get_date(<ime, TIME_FUZZY_DATE)) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ return field->store_time(<ime, MYSQL_TIMESTAMP_DATETIME); ++} ++ ++ ++Item::Item(): ++ rsize(0), name(0), orig_name(0), name_length(0), fixed(0), ++ is_autogenerated_name(TRUE), ++ collation(&my_charset_bin, DERIVATION_COERCIBLE) ++{ ++ marker= 0; ++ maybe_null=null_value=with_sum_func=unsigned_flag=0; ++ decimals= 0; max_length= 0; ++ with_subselect= 0; ++ cmp_context= (Item_result)-1; ++ ++ /* Put item in free list so that we can free all items at end */ ++ THD *thd= current_thd; ++ next= thd->free_list; ++ thd->free_list= this; ++ /* ++ Item constructor can be called during execution other then SQL_COM ++ command => we should check thd->lex->current_select on zero (thd->lex ++ can be uninitialised) ++ */ ++ if (thd->lex->current_select) ++ { ++ enum_parsing_place place= ++ thd->lex->current_select->parsing_place; ++ if (place == SELECT_LIST || ++ place == IN_HAVING) ++ thd->lex->current_select->select_n_having_items++; ++ } ++} ++ ++/* ++ Constructor used by Item_field, Item_*_ref & aggregate (sum) functions. ++ Used for duplicating lists in processing queries with temporary ++ tables ++*/ ++Item::Item(THD *thd, Item *item): ++ rsize(0), ++ str_value(item->str_value), ++ name(item->name), ++ orig_name(item->orig_name), ++ max_length(item->max_length), ++ marker(item->marker), ++ decimals(item->decimals), ++ maybe_null(item->maybe_null), ++ null_value(item->null_value), ++ unsigned_flag(item->unsigned_flag), ++ with_sum_func(item->with_sum_func), ++ fixed(item->fixed), ++ collation(item->collation), ++ cmp_context(item->cmp_context) ++{ ++ next= thd->free_list; // Put in free list ++ thd->free_list= this; ++} ++ ++ ++uint Item::decimal_precision() const ++{ ++ Item_result restype= result_type(); ++ ++ if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) ++ return min(my_decimal_length_to_precision(max_length, decimals, unsigned_flag), ++ DECIMAL_MAX_PRECISION); ++ return min(max_length, DECIMAL_MAX_PRECISION); ++} ++ ++ ++void Item::print_item_w_name(String *str) ++{ ++ print(str); ++ if (name) ++ { ++ THD *thd= current_thd; ++ str->append(STRING_WITH_LEN(" AS ")); ++ append_identifier(thd, str, name, (uint) strlen(name)); ++ } ++} ++ ++ ++void Item::cleanup() ++{ ++ DBUG_ENTER("Item::cleanup"); ++ fixed=0; ++ marker= 0; ++ if (orig_name) ++ name= orig_name; ++ DBUG_VOID_RETURN; ++} ++ ++ ++/* ++ cleanup() item if it is 'fixed' ++ ++ SYNOPSIS ++ cleanup_processor() ++ arg - a dummy parameter, is not used here ++*/ ++ ++bool Item::cleanup_processor(byte *arg) ++{ ++ if (fixed) ++ cleanup(); ++ return FALSE; ++} ++ ++ ++/* ++ rename item (used for views, cleanup() return original name) ++ ++ SYNOPSIS ++ Item::rename() ++ new_name new name of item; ++*/ ++ ++void Item::rename(char *new_name) ++{ ++ /* ++ we can compare pointers to names here, because if name was not changed, ++ pointer will be same ++ */ ++ if (!orig_name && new_name != name) ++ orig_name= name; ++ name= new_name; ++} ++ ++ ++/* ++ Traverse item tree possibly transforming it (replacing items). ++ ++ SYNOPSIS ++ Item::transform() ++ transformer functor that performs transformation of a subtree ++ arg opaque argument passed to the functor ++ ++ DESCRIPTION ++ This function is designed to ease transformation of Item trees. ++ ++ Re-execution note: every such transformation is registered for ++ rollback by THD::change_item_tree() and is rolled back at the end ++ of execution by THD::rollback_item_tree_changes(). ++ ++ Therefore: ++ ++ - this function can not be used at prepared statement prepare ++ (in particular, in fix_fields!), as only permanent ++ transformation of Item trees are allowed at prepare. ++ ++ - the transformer function shall allocate new Items in execution ++ memory root (thd->mem_root) and not anywhere else: allocated ++ items will be gone in the end of execution. ++ ++ If you don't need to transform an item tree, but only traverse ++ it, please use Item::walk() instead. ++ ++ ++ RETURN VALUE ++ Returns pointer to the new subtree root. THD::change_item_tree() ++ should be called for it if transformation took place, i.e. if a ++ pointer to newly allocated item is returned. ++*/ ++ ++Item* Item::transform(Item_transformer transformer, byte *arg) ++{ ++ DBUG_ASSERT(!current_thd->is_stmt_prepare()); ++ ++ return (this->*transformer)(arg); ++} ++ ++ ++Item_ident::Item_ident(Name_resolution_context *context_arg, ++ const char *db_name_arg,const char *table_name_arg, ++ const char *field_name_arg) ++ :orig_db_name(db_name_arg), orig_table_name(table_name_arg), ++ orig_field_name(field_name_arg), context(context_arg), ++ db_name(db_name_arg), table_name(table_name_arg), ++ field_name(field_name_arg), ++ alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), ++ cached_table(0), depended_from(0) ++{ ++ name = (char*) field_name_arg; ++} ++ ++ ++/* Constructor used by Item_field & Item_*_ref (see Item comment) */ ++ ++Item_ident::Item_ident(THD *thd, Item_ident *item) ++ :Item(thd, item), ++ orig_db_name(item->orig_db_name), ++ orig_table_name(item->orig_table_name), ++ orig_field_name(item->orig_field_name), ++ context(item->context), ++ db_name(item->db_name), ++ table_name(item->table_name), ++ field_name(item->field_name), ++ alias_name_used(item->alias_name_used), ++ cached_field_index(item->cached_field_index), ++ cached_table(item->cached_table), ++ depended_from(item->depended_from) ++{} ++ ++void Item_ident::cleanup() ++{ ++ DBUG_ENTER("Item_ident::cleanup"); ++#ifdef CANT_BE_USED_AS_MEMORY_IS_FREED ++ db_name ? db_name : "(null)", ++ orig_db_name ? orig_db_name : "(null)", ++ table_name ? table_name : "(null)", ++ orig_table_name ? orig_table_name : "(null)", ++ field_name ? field_name : "(null)", ++ orig_field_name ? orig_field_name : "(null)")); ++#endif ++ Item::cleanup(); ++ db_name= orig_db_name; ++ table_name= orig_table_name; ++ field_name= orig_field_name; ++ depended_from= 0; ++ DBUG_VOID_RETURN; ++} ++ ++bool Item_ident::remove_dependence_processor(byte * arg) ++{ ++ DBUG_ENTER("Item_ident::remove_dependence_processor"); ++ if (depended_from == (st_select_lex *) arg) ++ depended_from= 0; ++ DBUG_RETURN(0); ++} ++ ++ ++/* ++ Store the pointer to this item field into a list if not already there. ++ ++ SYNOPSIS ++ Item_field::collect_item_field_processor() ++ arg pointer to a List ++ ++ DESCRIPTION ++ The method is used by Item::walk to collect all unique Item_field objects ++ from a tree of Items into a set of items represented as a list. ++ ++ IMPLEMENTATION ++ Item_cond::walk() and Item_func::walk() stop the evaluation of the ++ processor function for its arguments once the processor returns ++ true.Therefore in order to force this method being called for all item ++ arguments in a condition the method must return false. ++ ++ RETURN ++ FALSE to force the evaluation of collect_item_field_processor ++ for the subsequent items. ++*/ ++ ++bool Item_field::collect_item_field_processor(byte *arg) ++{ ++ DBUG_ENTER("Item_field::collect_item_field_processor"); ++ DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname")); ++ List *item_list= (List*) arg; ++ List_iterator item_list_it(*item_list); ++ Item_field *curr_item; ++ while ((curr_item= item_list_it++)) ++ { ++ if (curr_item->eq(this, 1)) ++ DBUG_RETURN(FALSE); /* Already in the set. */ ++ } ++ item_list->push_back(this); ++ DBUG_RETURN(FALSE); ++} ++ ++ ++/* ++ Check if an Item_field references some field from a list of fields. ++ ++ SYNOPSIS ++ Item_field::find_item_in_field_list_processor ++ arg Field being compared, arg must be of type Field ++ ++ DESCRIPTION ++ Check whether the Item_field represented by 'this' references any ++ of the fields in the keyparts passed via 'arg'. Used with the ++ method Item::walk() to test whether any keypart in a sequence of ++ keyparts is referenced in an expression. ++ ++ RETURN ++ TRUE if 'this' references the field 'arg' ++ FALSE otherwise ++*/ ++ ++bool Item_field::find_item_in_field_list_processor(byte *arg) ++{ ++ KEY_PART_INFO *first_non_group_part= *((KEY_PART_INFO **) arg); ++ KEY_PART_INFO *last_part= *(((KEY_PART_INFO **) arg) + 1); ++ KEY_PART_INFO *cur_part; ++ ++ for (cur_part= first_non_group_part; cur_part != last_part; cur_part++) ++ { ++ if (field->eq(cur_part->field)) ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++ ++bool Item::check_cols(uint c) ++{ ++ if (c != 1) ++ { ++ my_error(ER_OPERAND_COLUMNS, MYF(0), c); ++ return 1; ++ } ++ return 0; ++} ++ ++ ++void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) ++{ ++ if (!length) ++ { ++ /* Empty string, used by AS or internal function like last_insert_id() */ ++ name= (char*) str; ++ name_length= 0; ++ return; ++ } ++ if (cs->ctype) ++ { ++ uint orig_len= length; ++ /* ++ This will probably need a better implementation in the future: ++ a function in CHARSET_INFO structure. ++ */ ++ while (length && !my_isgraph(cs,*str)) ++ { // Fix problem with yacc ++ length--; ++ str++; ++ } ++ if (orig_len != length && !is_autogenerated_name) ++ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ++ ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), ++ str + length - orig_len); ++ ++ } ++ if (!my_charset_same(cs, system_charset_info)) ++ { ++ uint32 res_length; ++ name= sql_strmake_with_convert(str, name_length= length, cs, ++ MAX_ALIAS_NAME, system_charset_info, ++ &res_length); ++ } ++ else ++ name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME))); ++} ++ ++ ++/* ++ This function is called when: ++ - Comparing items in the WHERE clause (when doing where optimization) ++ - When trying to find an ORDER BY/GROUP BY item in the SELECT part ++*/ ++ ++bool Item::eq(const Item *item, bool binary_cmp) const ++{ ++ /* ++ Note, that this is never TRUE if item is a Item_param: ++ for all basic constants we have special checks, and Item_param's ++ type() can be only among basic constant types. ++ */ ++ return type() == item->type() && name && item->name && ++ !my_strcasecmp(system_charset_info,name,item->name); ++} ++ ++ ++Item *Item::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ Item_func_conv_charset *conv= new Item_func_conv_charset(this, tocs, 1); ++ return conv->safe ? conv : NULL; ++} ++ ++ ++/* ++ Created mostly for mysql_prepare_table(). Important ++ when a string ENUM/SET column is described with a numeric default value: ++ ++ CREATE TABLE t1(a SET('a') DEFAULT 1); ++ ++ We cannot use generic Item::safe_charset_converter(), because ++ the latter returns a non-fixed Item, so val_str() crashes afterwards. ++ Override Item_num method, to return a fixed item. ++*/ ++Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ Item_string *conv; ++ char buf[64]; ++ String *s, tmp(buf, sizeof(buf), &my_charset_bin); ++ s= val_str(&tmp); ++ if ((conv= new Item_string(s->ptr(), s->length(), s->charset()))) ++ { ++ conv->str_value.copy(); ++ conv->str_value.mark_as_const(); ++ } ++ return conv; ++} ++ ++ ++Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ Item_string *conv; ++ char buf[64]; ++ String *s, tmp(buf, sizeof(buf), &my_charset_bin); ++ s= val_str(&tmp); ++ if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(), ++ s->charset()))) ++ { ++ conv->str_value.copy(); ++ conv->str_value.mark_as_const(); ++ } ++ return conv; ++} ++ ++ ++Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ Item_string *conv; ++ uint conv_errors; ++ String tmp, cstr, *ostr= val_str(&tmp); ++ cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); ++ if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(), ++ cstr.charset(), ++ collation.derivation))) ++ { ++ /* ++ Safe conversion is not possible (or EOM). ++ We could not convert a string into the requested character set ++ without data loss. The target charset does not cover all the ++ characters from the string. Operation cannot be done correctly. ++ */ ++ return NULL; ++ } ++ conv->str_value.copy(); ++ /* Ensure that no one is going to change the result string */ ++ conv->str_value.mark_as_const(); ++ return conv; ++} ++ ++ ++Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ if (const_item()) ++ { ++ uint cnv_errors; ++ String *ostr= val_str(&cnvstr); ++ cnvitem->str_value.copy(ostr->ptr(), ostr->length(), ++ ostr->charset(), tocs, &cnv_errors); ++ if (cnv_errors) ++ return NULL; ++ cnvitem->str_value.mark_as_const(); ++ cnvitem->max_length= cnvitem->str_value.numchars() * tocs->mbmaxlen; ++ return cnvitem; ++ } ++ return NULL; ++} ++ ++ ++Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ Item_string *conv; ++ uint conv_errors; ++ String tmp, cstr, *ostr= val_str(&tmp); ++ cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); ++ if (conv_errors || ++ !(conv= new Item_static_string_func(func_name, ++ cstr.ptr(), cstr.length(), ++ cstr.charset(), ++ collation.derivation))) ++ { ++ /* ++ Safe conversion is not possible (or EOM). ++ We could not convert a string into the requested character set ++ without data loss. The target charset does not cover all the ++ characters from the string. Operation cannot be done correctly. ++ */ ++ return NULL; ++ } ++ conv->str_value.copy(); ++ /* Ensure that no one is going to change the result string */ ++ conv->str_value.mark_as_const(); ++ return conv; ++} ++ ++ ++bool Item_string::eq(const Item *item, bool binary_cmp) const ++{ ++ if (type() == item->type() && item->basic_const_item()) ++ { ++ if (binary_cmp) ++ return !stringcmp(&str_value, &item->str_value); ++ return (collation.collation == item->collation.collation && ++ !sortcmp(&str_value, &item->str_value, collation.collation)); ++ } ++ return 0; ++} ++ ++ ++/* ++ Get the value of the function as a TIME structure. ++ As a extra convenience the time structure is reset on error! ++ */ ++ ++bool Item::get_date(TIME *ltime,uint fuzzydate) ++{ ++ char buff[40]; ++ String tmp(buff,sizeof(buff), &my_charset_bin),*res; ++ if (!(res=val_str(&tmp)) || ++ str_to_datetime_with_warn(res->ptr(), res->length(), ++ ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR) ++ { ++ bzero((char*) ltime,sizeof(*ltime)); ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ Get time of first argument. ++ As a extra convenience the time structure is reset on error! ++ */ ++ ++bool Item::get_time(TIME *ltime) ++{ ++ char buff[40]; ++ String tmp(buff,sizeof(buff),&my_charset_bin),*res; ++ if (!(res=val_str(&tmp)) || ++ str_to_time_with_warn(res->ptr(), res->length(), ltime)) ++ { ++ bzero((char*) ltime,sizeof(*ltime)); ++ return 1; ++ } ++ return 0; ++} ++ ++CHARSET_INFO *Item::default_charset() ++{ ++ return current_thd->variables.collation_connection; ++} ++ ++ ++int Item::save_in_field_no_warnings(Field *field, bool no_conversions) ++{ ++ int res; ++ THD *thd= field->table->in_use; ++ enum_check_fields tmp= thd->count_cuted_fields; ++ thd->count_cuted_fields= CHECK_FIELD_IGNORE; ++ res= save_in_field(field, no_conversions); ++ thd->count_cuted_fields= tmp; ++ return res; ++} ++ ++ ++/***************************************************************************** ++ Item_sp_variable methods ++*****************************************************************************/ ++ ++Item_sp_variable::Item_sp_variable(char *sp_var_name_str, ++ uint sp_var_name_length) ++ :m_thd(0) ++#ifndef DBUG_OFF ++ , m_sp(0) ++#endif ++{ ++ m_name.str= sp_var_name_str; ++ m_name.length= sp_var_name_length; ++} ++ ++ ++bool Item_sp_variable::fix_fields(THD *thd, Item **) ++{ ++ Item *it; ++ ++ m_thd= thd; /* NOTE: this must be set before any this_xxx() */ ++ it= this_item(); ++ ++ DBUG_ASSERT(it->fixed); ++ ++ max_length= it->max_length; ++ decimals= it->decimals; ++ unsigned_flag= it->unsigned_flag; ++ fixed= 1; ++ collation.set(it->collation.collation, it->collation.derivation); ++ ++ return FALSE; ++} ++ ++ ++double Item_sp_variable::val_real() ++{ ++ DBUG_ASSERT(fixed); ++ Item *it= this_item(); ++ double ret= it->val_real(); ++ null_value= it->null_value; ++ return ret; ++} ++ ++ ++longlong Item_sp_variable::val_int() ++{ ++ DBUG_ASSERT(fixed); ++ Item *it= this_item(); ++ longlong ret= it->val_int(); ++ null_value= it->null_value; ++ return ret; ++} ++ ++ ++String *Item_sp_variable::val_str(String *sp) ++{ ++ DBUG_ASSERT(fixed); ++ Item *it= this_item(); ++ String *res= it->val_str(sp); ++ ++ null_value= it->null_value; ++ ++ if (!res) ++ return NULL; ++ ++ /* ++ This way we mark returned value of val_str as const, ++ so that various functions (e.g. CONCAT) won't try to ++ modify the value of the Item. Analogous mechanism is ++ implemented for Item_param. ++ Without this trick Item_splocal could be changed as a ++ side-effect of expression computation. Here is an example ++ of what happens without it: suppose x is varchar local ++ variable in a SP with initial value 'ab' Then ++ select concat(x,'c'); ++ would change x's value to 'abc', as Item_func_concat::val_str() ++ would use x's internal buffer to compute the result. ++ This is intended behaviour of Item_func_concat. Comments to ++ Item_param class contain some more details on the topic. ++ */ ++ ++ if (res != &str_value) ++ str_value.set(res->ptr(), res->length(), res->charset()); ++ else ++ res->mark_as_const(); ++ ++ return &str_value; ++} ++ ++ ++my_decimal *Item_sp_variable::val_decimal(my_decimal *decimal_value) ++{ ++ DBUG_ASSERT(fixed); ++ Item *it= this_item(); ++ my_decimal *val= it->val_decimal(decimal_value); ++ null_value= it->null_value; ++ return val; ++} ++ ++ ++bool Item_sp_variable::is_null() ++{ ++ return this_item()->is_null(); ++} ++ ++ ++/***************************************************************************** ++ Item_splocal methods ++*****************************************************************************/ ++ ++Item_splocal::Item_splocal(const LEX_STRING &sp_var_name, ++ uint sp_var_idx, ++ enum_field_types sp_var_type, ++ uint pos_in_q) ++ :Item_sp_variable(sp_var_name.str, sp_var_name.length), ++ m_var_idx(sp_var_idx), pos_in_query(pos_in_q) ++{ ++ maybe_null= TRUE; ++ ++ m_type= sp_map_item_type(sp_var_type); ++ m_result_type= sp_map_result_type(sp_var_type); ++} ++ ++ ++Item * ++Item_splocal::this_item() ++{ ++ DBUG_ASSERT(m_sp == m_thd->spcont->sp); ++ ++ return m_thd->spcont->get_item(m_var_idx); ++} ++ ++const Item * ++Item_splocal::this_item() const ++{ ++ DBUG_ASSERT(m_sp == m_thd->spcont->sp); ++ ++ return m_thd->spcont->get_item(m_var_idx); ++} ++ ++ ++Item ** ++Item_splocal::this_item_addr(THD *thd, Item **) ++{ ++ DBUG_ASSERT(m_sp == thd->spcont->sp); ++ ++ return thd->spcont->get_item_addr(m_var_idx); ++} ++ ++ ++void Item_splocal::print(String *str) ++{ ++ str->reserve(m_name.length+8); ++ str->append(m_name.str, m_name.length); ++ str->append('@'); ++ str->qs_append(m_var_idx); ++} ++ ++ ++bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) ++{ ++ return ctx->set_variable(thd, get_var_idx(), it); ++} ++ ++ ++/***************************************************************************** ++ Item_case_expr methods ++*****************************************************************************/ ++ ++Item_case_expr::Item_case_expr(int case_expr_id) ++ :Item_sp_variable((char *) STRING_WITH_LEN("case_expr")), ++ m_case_expr_id(case_expr_id) ++{ ++} ++ ++ ++Item * ++Item_case_expr::this_item() ++{ ++ DBUG_ASSERT(m_sp == m_thd->spcont->sp); ++ ++ return m_thd->spcont->get_case_expr(m_case_expr_id); ++} ++ ++ ++ ++const Item * ++Item_case_expr::this_item() const ++{ ++ DBUG_ASSERT(m_sp == m_thd->spcont->sp); ++ ++ return m_thd->spcont->get_case_expr(m_case_expr_id); ++} ++ ++ ++Item ** ++Item_case_expr::this_item_addr(THD *thd, Item **) ++{ ++ DBUG_ASSERT(m_sp == thd->spcont->sp); ++ ++ return thd->spcont->get_case_expr_addr(m_case_expr_id); ++} ++ ++ ++void Item_case_expr::print(String *str) ++{ ++ VOID(str->append(STRING_WITH_LEN("case_expr@"))); ++ str->qs_append(m_case_expr_id); ++} ++ ++ ++/***************************************************************************** ++ Item_name_const methods ++*****************************************************************************/ ++ ++double Item_name_const::val_real() ++{ ++ DBUG_ASSERT(fixed); ++ double ret= value_item->val_real(); ++ null_value= value_item->null_value; ++ return ret; ++} ++ ++ ++longlong Item_name_const::val_int() ++{ ++ DBUG_ASSERT(fixed); ++ longlong ret= value_item->val_int(); ++ null_value= value_item->null_value; ++ return ret; ++} ++ ++ ++String *Item_name_const::val_str(String *sp) ++{ ++ DBUG_ASSERT(fixed); ++ String *ret= value_item->val_str(sp); ++ null_value= value_item->null_value; ++ return ret; ++} ++ ++ ++my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value) ++{ ++ DBUG_ASSERT(fixed); ++ my_decimal *val= value_item->val_decimal(decimal_value); ++ null_value= value_item->null_value; ++ return val; ++} ++ ++ ++bool Item_name_const::is_null() ++{ ++ return value_item->is_null(); ++} ++ ++Item::Type Item_name_const::type() const ++{ ++ return value_item->type(); ++} ++ ++ ++bool Item_name_const::fix_fields(THD *thd, Item **ref) ++{ ++ char buf[128]; ++ String *item_name; ++ String s(buf, sizeof(buf), &my_charset_bin); ++ s.length(0); ++ ++ if (value_item->fix_fields(thd, &value_item) || ++ name_item->fix_fields(thd, &name_item)) ++ return TRUE; ++ if (!(value_item->const_item() && name_item->const_item())) ++ return TRUE; ++ ++ if (!(item_name= name_item->val_str(&s))) ++ return TRUE; /* Can't have a NULL name */ ++ ++ set_name(item_name->ptr(), (uint) item_name->length(), system_charset_info); ++ max_length= value_item->max_length; ++ decimals= value_item->decimals; ++ fixed= 1; ++ return FALSE; ++} ++ ++ ++void Item_name_const::print(String *str) ++{ ++ str->append(STRING_WITH_LEN("NAME_CONST(")); ++ name_item->print(str); ++ str->append(','); ++ value_item->print(str); ++ str->append(')'); ++} ++ ++ ++/* ++ need a special class to adjust printing : references to aggregate functions ++ must not be printed as refs because the aggregate functions that are added to ++ the front of select list are not printed as well. ++*/ ++class Item_aggregate_ref : public Item_ref ++{ ++public: ++ Item_aggregate_ref(Name_resolution_context *context_arg, Item **item, ++ const char *table_name_arg, const char *field_name_arg) ++ :Item_ref(context_arg, item, table_name_arg, field_name_arg) {} ++ ++ void print (String *str) ++ { ++ if (ref) ++ (*ref)->print(str); ++ else ++ Item_ident::print(str); ++ } ++}; ++ ++ ++/* ++ Move SUM items out from item tree and replace with reference ++ ++ SYNOPSIS ++ split_sum_func2() ++ thd Thread handler ++ ref_pointer_array Pointer to array of reference fields ++ fields All fields in select ++ ref Pointer to item ++ skip_registered <=> function be must skipped for registered SUM items ++ ++ NOTES ++ This is from split_sum_func2() for items that should be split ++ ++ All found SUM items are added FIRST in the fields list and ++ we replace the item with a reference. ++ ++ thd->fatal_error() may be called if we are out of memory ++*/ ++ ++ ++void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, ++ List &fields, Item **ref, ++ bool skip_registered) ++{ ++ /* An item of type Item_sum is registered <=> ref_by != 0 */ ++ if (type() == SUM_FUNC_ITEM && skip_registered && ++ ((Item_sum *) this)->ref_by) ++ return; ++ if (type() != SUM_FUNC_ITEM && with_sum_func) ++ { ++ /* Will split complicated items and ignore simple ones */ ++ split_sum_func(thd, ref_pointer_array, fields); ++ } ++ else if ((type() == SUM_FUNC_ITEM || (used_tables() & ~PARAM_TABLE_BIT)) && ++ type() != SUBSELECT_ITEM && ++ (type() != REF_ITEM || ++ ((Item_ref*)this)->ref_type() == Item_ref::VIEW_REF)) ++ { ++ /* ++ Replace item with a reference so that we can easily calculate ++ it (in case of sum functions) or copy it (in case of fields) ++ ++ The test above is to ensure we don't do a reference for things ++ that are constants (PARAM_TABLE_BIT is in effect a constant) ++ or already referenced (for example an item in HAVING) ++ Exception is Item_direct_view_ref which we need to convert to ++ Item_ref to allow fields from view being stored in tmp table. ++ */ ++ uint el= fields.elements; ++ Item *new_item, *real_itm= real_item(); ++ ++ ref_pointer_array[el]= real_itm; ++ if (!(new_item= new Item_aggregate_ref(&thd->lex->current_select->context, ++ ref_pointer_array + el, 0, name))) ++ return; // fatal_error is set ++ fields.push_front(real_itm); ++ thd->change_item_tree(ref, new_item); ++ } ++} ++ ++ ++/* ++ Aggregate two collations together taking ++ into account their coercibility (aka derivation): ++ ++ 0 == DERIVATION_EXPLICIT - an explicitly written COLLATE clause ++ 1 == DERIVATION_NONE - a mix of two different collations ++ 2 == DERIVATION_IMPLICIT - a column ++ 3 == DERIVATION_COERCIBLE - a string constant ++ ++ The most important rules are: ++ ++ 1. If collations are the same: ++ chose this collation, and the strongest derivation. ++ ++ 2. If collations are different: ++ - Character sets may differ, but only if conversion without ++ data loss is possible. The caller provides flags whether ++ character set conversion attempts should be done. If no ++ flags are substituted, then the character sets must be the same. ++ Currently processed flags are: ++ MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset ++ MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value ++ - two EXPLICIT collations produce an error, e.g. this is wrong: ++ CONCAT(expr1 collate latin1_swedish_ci, expr2 collate latin1_german_ci) ++ - the side with smaller derivation value wins, ++ i.e. a column is stronger than a string constant, ++ an explicit COLLATE clause is stronger than a column. ++ - if derivations are the same, we have DERIVATION_NONE, ++ we'll wait for an explicit COLLATE clause which possibly can ++ come from another argument later: for example, this is valid, ++ but we don't know yet when collecting the first two arguments: ++ CONCAT(latin1_swedish_ci_column, ++ latin1_german1_ci_column, ++ expr COLLATE latin1_german2_ci) ++*/ ++bool DTCollation::aggregate(DTCollation &dt, uint flags) ++{ ++ if (!my_charset_same(collation, dt.collation)) ++ { ++ /* ++ We do allow to use binary strings (like BLOBS) ++ together with character strings. ++ Binaries have more precedence than a character ++ string of the same derivation. ++ */ ++ if (collation == &my_charset_bin) ++ { ++ if (derivation <= dt.derivation) ++ ; // Do nothing ++ else ++ { ++ set(dt); ++ } ++ } ++ else if (dt.collation == &my_charset_bin) ++ { ++ if (dt.derivation <= derivation) ++ { ++ set(dt); ++ } ++ else ++ ; // Do nothing ++ } ++ else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) && ++ collation->state & MY_CS_UNICODE && ++ (derivation < dt.derivation || ++ (derivation == dt.derivation && ++ !(dt.collation->state & MY_CS_UNICODE)))) ++ { ++ // Do nothing ++ } ++ else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) && ++ dt.collation->state & MY_CS_UNICODE && ++ (dt.derivation < derivation || ++ (dt.derivation == derivation && ++ !(collation->state & MY_CS_UNICODE)))) ++ { ++ set(dt); ++ } ++ else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) && ++ derivation < dt.derivation && ++ dt.derivation >= DERIVATION_SYSCONST) ++ { ++ // Do nothing; ++ } ++ else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) && ++ dt.derivation < derivation && ++ derivation >= DERIVATION_SYSCONST) ++ { ++ set(dt); ++ } ++ else ++ { ++ // Cannot apply conversion ++ set(0, DERIVATION_NONE); ++ return 1; ++ } ++ } ++ else if (derivation < dt.derivation) ++ { ++ // Do nothing ++ } ++ else if (dt.derivation < derivation) ++ { ++ set(dt); ++ } ++ else ++ { ++ if (collation == dt.collation) ++ { ++ // Do nothing ++ } ++ else ++ { ++ if (derivation == DERIVATION_EXPLICIT) ++ { ++ set(0, DERIVATION_NONE); ++ return 1; ++ } ++ if (collation->state & MY_CS_BINSORT) ++ return 0; ++ if (dt.collation->state & MY_CS_BINSORT) ++ { ++ set(dt); ++ return 0; ++ } ++ CHARSET_INFO *bin= get_charset_by_csname(collation->csname, ++ MY_CS_BINSORT,MYF(0)); ++ set(bin, DERIVATION_NONE); ++ } ++ } ++ return 0; ++} ++ ++/******************************/ ++static ++void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) ++{ ++ my_error(ER_CANT_AGGREGATE_2COLLATIONS,MYF(0), ++ c1.collation->name,c1.derivation_name(), ++ c2.collation->name,c2.derivation_name(), ++ fname); ++} ++ ++ ++static ++void my_coll_agg_error(DTCollation &c1, DTCollation &c2, DTCollation &c3, ++ const char *fname) ++{ ++ my_error(ER_CANT_AGGREGATE_3COLLATIONS,MYF(0), ++ c1.collation->name,c1.derivation_name(), ++ c2.collation->name,c2.derivation_name(), ++ c3.collation->name,c3.derivation_name(), ++ fname); ++} ++ ++ ++static ++void my_coll_agg_error(Item** args, uint count, const char *fname, ++ int item_sep) ++{ ++ if (count == 2) ++ my_coll_agg_error(args[0]->collation, args[item_sep]->collation, fname); ++ else if (count == 3) ++ my_coll_agg_error(args[0]->collation, args[item_sep]->collation, ++ args[2*item_sep]->collation, fname); ++ else ++ my_error(ER_CANT_AGGREGATE_NCOLLATIONS,MYF(0),fname); ++} ++ ++ ++bool agg_item_collations(DTCollation &c, const char *fname, ++ Item **av, uint count, uint flags, int item_sep) ++{ ++ uint i; ++ Item **arg; ++ c.set(av[0]->collation); ++ for (i= 1, arg= &av[item_sep]; i < count; i++, arg++) ++ { ++ if (c.aggregate((*arg)->collation, flags)) ++ { ++ my_coll_agg_error(av, count, fname, item_sep); ++ return TRUE; ++ } ++ } ++ if ((flags & MY_COLL_DISALLOW_NONE) && ++ c.derivation == DERIVATION_NONE) ++ { ++ my_coll_agg_error(av, count, fname, item_sep); ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++ ++bool agg_item_collations_for_comparison(DTCollation &c, const char *fname, ++ Item **av, uint count, uint flags) ++{ ++ return (agg_item_collations(c, fname, av, count, ++ flags | MY_COLL_DISALLOW_NONE, 1)); ++} ++ ++ ++/* ++ Collect arguments' character sets together. ++ We allow to apply automatic character set conversion in some cases. ++ The conditions when conversion is possible are: ++ - arguments A and B have different charsets ++ - A wins according to coercibility rules ++ (i.e. a column is stronger than a string constant, ++ an explicit COLLATE clause is stronger than a column) ++ - character set of A is either superset for character set of B, ++ or B is a string constant which can be converted into the ++ character set of A without data loss. ++ ++ If all of the above is true, then it's possible to convert ++ B into the character set of A, and then compare according ++ to the collation of A. ++ ++ For functions with more than two arguments: ++ ++ collect(A,B,C) ::= collect(collect(A,B),C) ++ ++ Since this function calls THD::change_item_tree() on the passed Item ** ++ pointers, it is necessary to pass the original Item **'s, not copies. ++ Otherwise their values will not be properly restored (see BUG#20769). ++ If the items are not consecutive (eg. args[2] and args[5]), use the ++ item_sep argument, ie. ++ ++ agg_item_charsets(coll, fname, &args[2], 2, flags, 3) ++ ++*/ ++ ++bool agg_item_charsets(DTCollation &coll, const char *fname, ++ Item **args, uint nargs, uint flags, int item_sep) ++{ ++ Item **arg, *safe_args[2]; ++ if (agg_item_collations(coll, fname, args, nargs, flags, item_sep)) ++ return TRUE; ++ ++ /* ++ For better error reporting: save the first and the second argument. ++ We need this only if the the number of args is 3 or 2: ++ - for a longer argument list, "Illegal mix of collations" ++ doesn't display each argument's characteristics. ++ - if nargs is 1, then this error cannot happen. ++ */ ++ if (nargs >=2 && nargs <= 3) ++ { ++ safe_args[0]= args[0]; ++ safe_args[1]= args[item_sep]; ++ } ++ ++ THD *thd= current_thd; ++ Query_arena *arena, backup; ++ bool res= FALSE; ++ uint i; ++ /* ++ In case we're in statement prepare, create conversion item ++ in its memory: it will be reused on each execute. ++ */ ++ arena= thd->is_stmt_prepare() ? thd->activate_stmt_arena_if_needed(&backup) ++ : NULL; ++ ++ for (i= 0, arg= args; i < nargs; i++, arg+= item_sep) ++ { ++ Item* conv; ++ uint32 dummy_offset; ++ if (!String::needs_conversion(0, coll.collation, ++ (*arg)->collation.collation, ++ &dummy_offset)) ++ continue; ++ ++ if (!(conv= (*arg)->safe_charset_converter(coll.collation))) ++ { ++ if (nargs >=2 && nargs <= 3) ++ { ++ /* restore the original arguments for better error message */ ++ args[0]= safe_args[0]; ++ args[item_sep]= safe_args[1]; ++ } ++ my_coll_agg_error(args, nargs, fname, item_sep); ++ res= TRUE; ++ break; // we cannot return here, we need to restore "arena". ++ } ++ if ((*arg)->type() == Item::FIELD_ITEM) ++ ((Item_field *)(*arg))->no_const_subst= 1; ++ /* ++ If in statement prepare, then we create a converter for two ++ constant items, do it once and then reuse it. ++ If we're in execution of a prepared statement, arena is NULL, ++ and the conv was created in runtime memory. This can be ++ the case only if the argument is a parameter marker ('?'), ++ because for all true constants the charset converter has already ++ been created in prepare. In this case register the change for ++ rollback. ++ */ ++ if (arena) ++ *arg= conv; ++ else ++ thd->change_item_tree(arg, conv); ++ /* ++ We do not check conv->fixed, because Item_func_conv_charset which can ++ be return by safe_charset_converter can't be fixed at creation ++ */ ++ conv->fix_fields(thd, arg); ++ } ++ if (arena) ++ thd->restore_active_arena(arena, &backup); ++ return res; ++} ++ ++ ++void Item_ident_for_show::make_field(Send_field *tmp_field) ++{ ++ tmp_field->table_name= tmp_field->org_table_name= table_name; ++ tmp_field->db_name= db_name; ++ tmp_field->col_name= tmp_field->org_col_name= field->field_name; ++ tmp_field->charsetnr= field->charset()->number; ++ tmp_field->length=field->field_length; ++ tmp_field->type=field->type(); ++ tmp_field->flags= field->table->maybe_null ? ++ (field->flags & ~NOT_NULL_FLAG) : field->flags; ++ tmp_field->decimals= 0; ++} ++ ++/**********************************************/ ++ ++Item_field::Item_field(Field *f) ++ :Item_ident(0, NullS, *f->table_name, f->field_name), ++ item_equal(0), no_const_subst(0), ++ have_privileges(0), any_privileges(0) ++{ ++ set_field(f); ++ /* ++ field_name and talbe_name should not point to garbage ++ if this item is to be reused ++ */ ++ orig_table_name= orig_field_name= ""; ++} ++ ++Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, ++ Field *f) ++ :Item_ident(context_arg, f->table->s->db, *f->table_name, f->field_name), ++ item_equal(0), no_const_subst(0), ++ have_privileges(0), any_privileges(0) ++{ ++ /* ++ We always need to provide Item_field with a fully qualified field ++ name to avoid ambiguity when executing prepared statements like ++ SELECT * from d1.t1, d2.t1; (assuming d1.t1 and d2.t1 have columns ++ with same names). ++ This is because prepared statements never deal with wildcards in ++ select list ('*') and always fix fields using fully specified path ++ (i.e. db.table.column). ++ No check for OOM: if db_name is NULL, we'll just get ++ "Field not found" error. ++ We need to copy db_name, table_name and field_name because they must ++ be allocated in the statement memory, not in table memory (the table ++ structure can go away and pop up again between subsequent executions ++ of a prepared statement). ++ */ ++ if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) ++ { ++ if (db_name) ++ orig_db_name= thd->strdup(db_name); ++ orig_table_name= thd->strdup(table_name); ++ orig_field_name= thd->strdup(field_name); ++ /* ++ We don't restore 'name' in cleanup because it's not changed ++ during execution. Still we need it to point to persistent ++ memory if this item is to be reused. ++ */ ++ name= (char*) orig_field_name; ++ } ++ set_field(f); ++} ++ ++ ++Item_field::Item_field(Name_resolution_context *context_arg, ++ const char *db_arg,const char *table_name_arg, ++ const char *field_name_arg) ++ :Item_ident(context_arg, db_arg,table_name_arg,field_name_arg), ++ field(0), result_field(0), item_equal(0), no_const_subst(0), ++ have_privileges(0), any_privileges(0) ++{ ++ collation.set(DERIVATION_IMPLICIT); ++} ++ ++// Constructor need to process subselect with temporary tables (see Item) ++Item_field::Item_field(THD *thd, Item_field *item) ++ :Item_ident(thd, item), ++ field(item->field), ++ result_field(item->result_field), ++ item_equal(item->item_equal), ++ no_const_subst(item->no_const_subst), ++ have_privileges(item->have_privileges), ++ any_privileges(item->any_privileges) ++{ ++ collation.set(DERIVATION_IMPLICIT); ++} ++ ++void Item_field::set_field(Field *field_par) ++{ ++ field=result_field=field_par; // for easy coding with fields ++ maybe_null=field->maybe_null(); ++ decimals= field->decimals(); ++ max_length= field_par->max_length(); ++ table_name= *field_par->table_name; ++ field_name= field_par->field_name; ++ db_name= field_par->table->s->db; ++ alias_name_used= field_par->table->alias_name_used; ++ unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); ++ collation.set(field_par->charset(), field_par->derivation()); ++ fixed= 1; ++} ++ ++ ++/* ++ Reset this item to point to a field from the new temporary table. ++ This is used when we create a new temporary table for each execution ++ of prepared statement. ++*/ ++ ++void Item_field::reset_field(Field *f) ++{ ++ set_field(f); ++ /* 'name' is pointing at field->field_name of old field */ ++ name= (char*) f->field_name; ++} ++ ++const char *Item_ident::full_name() const ++{ ++ char *tmp; ++ if (!table_name || !field_name) ++ return field_name ? field_name : name ? name : "tmp_field"; ++ if (db_name && db_name[0]) ++ { ++ tmp=(char*) sql_alloc((uint) strlen(db_name)+(uint) strlen(table_name)+ ++ (uint) strlen(field_name)+3); ++ strxmov(tmp,db_name,".",table_name,".",field_name,NullS); ++ } ++ else ++ { ++ if (table_name[0]) ++ { ++ tmp= (char*) sql_alloc((uint) strlen(table_name) + ++ (uint) strlen(field_name) + 2); ++ strxmov(tmp, table_name, ".", field_name, NullS); ++ } ++ else ++ tmp= (char*) field_name; ++ } ++ return tmp; ++} ++ ++void Item_ident::print(String *str) ++{ ++ THD *thd= current_thd; ++ char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; ++ const char *d_name= db_name, *t_name= table_name; ++ if (lower_case_table_names== 1 || ++ (lower_case_table_names == 2 && !alias_name_used)) ++ { ++ if (table_name && table_name[0]) ++ { ++ strmov(t_name_buff, table_name); ++ my_casedn_str(files_charset_info, t_name_buff); ++ t_name= t_name_buff; ++ } ++ if (db_name && db_name[0]) ++ { ++ strmov(d_name_buff, db_name); ++ my_casedn_str(files_charset_info, d_name_buff); ++ d_name= d_name_buff; ++ } ++ } ++ ++ if (!table_name || !field_name) ++ { ++ const char *nm= field_name ? field_name : name ? name : "tmp_field"; ++ append_identifier(thd, str, nm, (uint) strlen(nm)); ++ return; ++ } ++ if (db_name && db_name[0] && !alias_name_used) ++ { ++ if (!(cached_table && cached_table->belong_to_view && ++ cached_table->belong_to_view->compact_view_format)) ++ { ++ append_identifier(thd, str, d_name, (uint)strlen(d_name)); ++ str->append('.'); ++ } ++ append_identifier(thd, str, t_name, (uint)strlen(t_name)); ++ str->append('.'); ++ append_identifier(thd, str, field_name, (uint)strlen(field_name)); ++ } ++ else ++ { ++ if (table_name[0]) ++ { ++ append_identifier(thd, str, t_name, (uint) strlen(t_name)); ++ str->append('.'); ++ append_identifier(thd, str, field_name, (uint) strlen(field_name)); ++ } ++ else ++ append_identifier(thd, str, field_name, (uint) strlen(field_name)); ++ } ++} ++ ++/* ARGSUSED */ ++String *Item_field::val_str(String *str) ++{ ++ DBUG_ASSERT(fixed == 1); ++ if ((null_value=field->is_null())) ++ return 0; ++ str->set_charset(str_value.charset()); ++ return field->val_str(str,&str_value); ++} ++ ++ ++double Item_field::val_real() ++{ ++ DBUG_ASSERT(fixed == 1); ++ if ((null_value=field->is_null())) ++ return 0.0; ++ return field->val_real(); ++} ++ ++ ++longlong Item_field::val_int() ++{ ++ DBUG_ASSERT(fixed == 1); ++ if ((null_value=field->is_null())) ++ return 0; ++ return field->val_int(); ++} ++ ++ ++my_decimal *Item_field::val_decimal(my_decimal *decimal_value) ++{ ++ if ((null_value= field->is_null())) ++ return 0; ++ return field->val_decimal(decimal_value); ++} ++ ++ ++String *Item_field::str_result(String *str) ++{ ++ if ((null_value=result_field->is_null())) ++ return 0; ++ str->set_charset(str_value.charset()); ++ return result_field->val_str(str,&str_value); ++} ++ ++bool Item_field::get_date(TIME *ltime,uint fuzzydate) ++{ ++ if ((null_value=field->is_null()) || field->get_date(ltime,fuzzydate)) ++ { ++ bzero((char*) ltime,sizeof(*ltime)); ++ return 1; ++ } ++ return 0; ++} ++ ++bool Item_field::get_date_result(TIME *ltime,uint fuzzydate) ++{ ++ if ((null_value=result_field->is_null()) || ++ result_field->get_date(ltime,fuzzydate)) ++ { ++ bzero((char*) ltime,sizeof(*ltime)); ++ return 1; ++ } ++ return 0; ++} ++ ++bool Item_field::get_time(TIME *ltime) ++{ ++ if ((null_value=field->is_null()) || field->get_time(ltime)) ++ { ++ bzero((char*) ltime,sizeof(*ltime)); ++ return 1; ++ } ++ return 0; ++} ++ ++double Item_field::val_result() ++{ ++ if ((null_value=result_field->is_null())) ++ return 0.0; ++ return result_field->val_real(); ++} ++ ++longlong Item_field::val_int_result() ++{ ++ if ((null_value=result_field->is_null())) ++ return 0; ++ return result_field->val_int(); ++} ++ ++ ++my_decimal *Item_field::val_decimal_result(my_decimal *decimal_value) ++{ ++ if ((null_value= result_field->is_null())) ++ return 0; ++ return result_field->val_decimal(decimal_value); ++} ++ ++ ++bool Item_field::val_bool_result() ++{ ++ if ((null_value= result_field->is_null())) ++ return FALSE; ++ switch (result_field->result_type()) { ++ case INT_RESULT: ++ return result_field->val_int() != 0; ++ case DECIMAL_RESULT: ++ { ++ my_decimal decimal_value; ++ my_decimal *val= result_field->val_decimal(&decimal_value); ++ if (val) ++ return !my_decimal_is_zero(val); ++ return 0; ++ } ++ case REAL_RESULT: ++ case STRING_RESULT: ++ return result_field->val_real() != 0.0; ++ case ROW_RESULT: ++ default: ++ DBUG_ASSERT(0); ++ return 0; // Shut up compiler ++ } ++} ++ ++ ++bool Item_field::eq(const Item *item, bool binary_cmp) const ++{ ++ if (item->type() != FIELD_ITEM) ++ return 0; ++ ++ Item_field *item_field= (Item_field*) item; ++ if (item_field->field && field) ++ return item_field->field == field; ++ /* ++ We may come here when we are trying to find a function in a GROUP BY ++ clause from the select list. ++ In this case the '100 % correct' way to do this would be to first ++ run fix_fields() on the GROUP BY item and then retry this function, but ++ I think it's better to relax the checking a bit as we will in ++ most cases do the correct thing by just checking the field name. ++ (In cases where we would choose wrong we would have to generate a ++ ER_NON_UNIQ_ERROR). ++ */ ++ return (!my_strcasecmp(system_charset_info, item_field->name, ++ field_name) && ++ (!item_field->table_name || !table_name || ++ (!my_strcasecmp(table_alias_charset, item_field->table_name, ++ table_name) && ++ (!item_field->db_name || !db_name || ++ (item_field->db_name && !strcmp(item_field->db_name, ++ db_name)))))); ++} ++ ++ ++table_map Item_field::used_tables() const ++{ ++ if (field->table->const_table) ++ return 0; // const item ++ return (depended_from ? OUTER_REF_TABLE_BIT : field->table->map); ++} ++ ++ ++Item *Item_field::get_tmp_table_item(THD *thd) ++{ ++ Item_field *new_item= new Item_field(thd, this); ++ if (new_item) ++ new_item->field= new_item->result_field; ++ return new_item; ++} ++ ++ ++/* ++ Create an item from a string we KNOW points to a valid longlong ++ end \0 terminated number string. ++ This is always 'signed'. Unsigned values are created with Item_uint() ++*/ ++ ++Item_int::Item_int(const char *str_arg, uint length) ++{ ++ char *end_ptr= (char*) str_arg + length; ++ int error; ++ value= my_strtoll10(str_arg, &end_ptr, &error); ++ max_length= (uint) (end_ptr - str_arg); ++ name= (char*) str_arg; ++ fixed= 1; ++} ++ ++ ++my_decimal *Item_int::val_decimal(my_decimal *decimal_value) ++{ ++ int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_value); ++ return decimal_value; ++} ++ ++String *Item_int::val_str(String *str) ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ str->set(value, &my_charset_bin); ++ return str; ++} ++ ++void Item_int::print(String *str) ++{ ++ // my_charset_bin is good enough for numbers ++ str_value.set(value, &my_charset_bin); ++ str->append(str_value); ++} ++ ++ ++Item_uint::Item_uint(const char *str_arg, uint length): ++ Item_int(str_arg, length) ++{ ++ unsigned_flag= 1; ++} ++ ++ ++Item_uint::Item_uint(const char *str_arg, longlong i, uint length): ++ Item_int(str_arg, i, length) ++{ ++ unsigned_flag= 1; ++} ++ ++ ++String *Item_uint::val_str(String *str) ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ str->set((ulonglong) value, &my_charset_bin); ++ return str; ++} ++ ++ ++void Item_uint::print(String *str) ++{ ++ // latin1 is good enough for numbers ++ str_value.set((ulonglong) value, default_charset()); ++ str->append(str_value); ++} ++ ++ ++Item_decimal::Item_decimal(const char *str_arg, uint length, ++ CHARSET_INFO *charset) ++{ ++ str2my_decimal(E_DEC_FATAL_ERROR, str_arg, length, charset, &decimal_value); ++ name= (char*) str_arg; ++ decimals= (uint8) decimal_value.frac; ++ fixed= 1; ++ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, ++ decimals, unsigned_flag); ++} ++ ++Item_decimal::Item_decimal(longlong val, bool unsig) ++{ ++ int2my_decimal(E_DEC_FATAL_ERROR, val, unsig, &decimal_value); ++ decimals= (uint8) decimal_value.frac; ++ fixed= 1; ++ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, ++ decimals, unsigned_flag); ++} ++ ++ ++Item_decimal::Item_decimal(double val, int precision, int scale) ++{ ++ double2my_decimal(E_DEC_FATAL_ERROR, val, &decimal_value); ++ decimals= (uint8) decimal_value.frac; ++ fixed= 1; ++ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, ++ decimals, unsigned_flag); ++} ++ ++ ++Item_decimal::Item_decimal(const char *str, const my_decimal *val_arg, ++ uint decimal_par, uint length) ++{ ++ my_decimal2decimal(val_arg, &decimal_value); ++ name= (char*) str; ++ decimals= (uint8) decimal_par; ++ max_length= length; ++ fixed= 1; ++} ++ ++ ++Item_decimal::Item_decimal(my_decimal *value_par) ++{ ++ my_decimal2decimal(value_par, &decimal_value); ++ decimals= (uint8) decimal_value.frac; ++ fixed= 1; ++ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, ++ decimals, unsigned_flag); ++} ++ ++ ++Item_decimal::Item_decimal(const char *bin, int precision, int scale) ++{ ++ binary2my_decimal(E_DEC_FATAL_ERROR, bin, ++ &decimal_value, precision, scale); ++ decimals= (uint8) decimal_value.frac; ++ fixed= 1; ++ max_length= my_decimal_precision_to_length(precision, decimals, ++ unsigned_flag); ++} ++ ++ ++longlong Item_decimal::val_int() ++{ ++ longlong result; ++ my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &result); ++ return result; ++} ++ ++double Item_decimal::val_real() ++{ ++ double result; ++ my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); ++ return result; ++} ++ ++String *Item_decimal::val_str(String *result) ++{ ++ result->set_charset(&my_charset_bin); ++ my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, result); ++ return result; ++} ++ ++void Item_decimal::print(String *str) ++{ ++ my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, &str_value); ++ str->append(str_value); ++} ++ ++ ++bool Item_decimal::eq(const Item *item, bool binary_cmp) const ++{ ++ if (type() == item->type() && item->basic_const_item()) ++ { ++ /* ++ We need to cast off const to call val_decimal(). This should ++ be OK for a basic constant. Additionally, we can pass 0 as ++ a true decimal constant will return its internal decimal ++ storage and ignore the argument. ++ */ ++ Item *arg= (Item*) item; ++ my_decimal *value= arg->val_decimal(0); ++ return !my_decimal_cmp(&decimal_value, value); ++ } ++ return 0; ++} ++ ++ ++void Item_decimal::set_decimal_value(my_decimal *value_par) ++{ ++ my_decimal2decimal(value_par, &decimal_value); ++ decimals= (uint8) decimal_value.frac; ++ unsigned_flag= !decimal_value.sign(); ++ max_length= my_decimal_precision_to_length(decimal_value.intg + decimals, ++ decimals, unsigned_flag); ++} ++ ++ ++String *Item_float::val_str(String *str) ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ str->set(value,decimals,&my_charset_bin); ++ return str; ++} ++ ++ ++my_decimal *Item_float::val_decimal(my_decimal *decimal_value) ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_value); ++ return (decimal_value); ++} ++ ++ ++void Item_string::print(String *str) ++{ ++ str->append('_'); ++ str->append(collation.collation->csname); ++ str->append('\''); ++ str_value.print(str); ++ str->append('\''); ++} ++ ++ ++double Item_string::val_real() ++{ ++ DBUG_ASSERT(fixed == 1); ++ int error; ++ char *end, *org_end; ++ double tmp; ++ CHARSET_INFO *cs= str_value.charset(); ++ ++ org_end= (char*) str_value.ptr() + str_value.length(); ++ tmp= my_strntod(cs, (char*) str_value.ptr(), str_value.length(), &end, ++ &error); ++ if (error || (end != org_end && !check_if_only_end_space(cs, end, org_end))) ++ { ++ /* ++ We can use str_value.ptr() here as Item_string is gurantee to put an ++ end \0 here. ++ */ ++ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ++ ER_TRUNCATED_WRONG_VALUE, ++ ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", ++ str_value.ptr()); ++ } ++ return tmp; ++} ++ ++ ++longlong Item_string::val_int() ++{ ++ DBUG_ASSERT(fixed == 1); ++ int err; ++ longlong tmp; ++ char *end= (char*) str_value.ptr()+ str_value.length(); ++ char *org_end= end; ++ CHARSET_INFO *cs= str_value.charset(); ++ ++ tmp= (*(cs->cset->strtoll10))(cs, str_value.ptr(), &end, &err); ++ /* ++ TODO: Give error if we wanted a signed integer and we got an unsigned ++ one ++ */ ++ if (err > 0 || ++ (end != org_end && !check_if_only_end_space(cs, end, org_end))) ++ { ++ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ++ ER_TRUNCATED_WRONG_VALUE, ++ ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", ++ str_value.ptr()); ++ } ++ return tmp; ++} ++ ++ ++my_decimal *Item_string::val_decimal(my_decimal *decimal_value) ++{ ++ return val_decimal_from_string(decimal_value); ++} ++ ++ ++bool Item_null::eq(const Item *item, bool binary_cmp) const ++{ return item->type() == type(); } ++ ++ ++double Item_null::val_real() ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ null_value=1; ++ return 0.0; ++} ++longlong Item_null::val_int() ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ null_value=1; ++ return 0; ++} ++/* ARGSUSED */ ++String *Item_null::val_str(String *str) ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ null_value=1; ++ return 0; ++} ++ ++my_decimal *Item_null::val_decimal(my_decimal *decimal_value) ++{ ++ return 0; ++} ++ ++ ++Item *Item_null::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ collation.set(tocs); ++ return this; ++} ++ ++/*********************** Item_param related ******************************/ ++ ++/* ++ Default function of Item_param::set_param_func, so in case ++ of malformed packet the server won't SIGSEGV ++*/ ++ ++static void ++default_set_param_func(Item_param *param, ++ uchar **pos __attribute__((unused)), ++ ulong len __attribute__((unused))) ++{ ++ param->set_null(); ++} ++ ++ ++Item_param::Item_param(unsigned pos_in_query_arg) : ++ state(NO_VALUE), ++ item_result_type(STRING_RESULT), ++ /* Don't pretend to be a literal unless value for this item is set. */ ++ item_type(PARAM_ITEM), ++ param_type(MYSQL_TYPE_VARCHAR), ++ pos_in_query(pos_in_query_arg), ++ set_param_func(default_set_param_func) ++{ ++ name= (char*) "?"; ++ /* ++ Since we can't say whenever this item can be NULL or cannot be NULL ++ before mysql_stmt_execute(), so we assuming that it can be NULL until ++ value is set. ++ */ ++ maybe_null= 1; ++ cnvitem= new Item_string("", 0, &my_charset_bin, DERIVATION_COERCIBLE); ++ cnvstr.set(cnvbuf, sizeof(cnvbuf), &my_charset_bin); ++} ++ ++ ++void Item_param::set_null() ++{ ++ DBUG_ENTER("Item_param::set_null"); ++ /* These are cleared after each execution by reset() method */ ++ null_value= 1; ++ /* ++ Because of NULL and string values we need to set max_length for each new ++ placeholder value: user can submit NULL for any placeholder type, and ++ string length can be different in each execution. ++ */ ++ max_length= 0; ++ decimals= 0; ++ state= NULL_VALUE; ++ item_type= Item::NULL_ITEM; ++ DBUG_VOID_RETURN; ++} ++ ++void Item_param::set_int(longlong i, uint32 max_length_arg) ++{ ++ DBUG_ENTER("Item_param::set_int"); ++ value.integer= (longlong) i; ++ state= INT_VALUE; ++ max_length= max_length_arg; ++ decimals= 0; ++ maybe_null= 0; ++ DBUG_VOID_RETURN; ++} ++ ++void Item_param::set_double(double d) ++{ ++ DBUG_ENTER("Item_param::set_double"); ++ value.real= d; ++ state= REAL_VALUE; ++ max_length= DBL_DIG + 8; ++ decimals= NOT_FIXED_DEC; ++ maybe_null= 0; ++ DBUG_VOID_RETURN; ++} ++ ++ ++/* ++ Set decimal parameter value from string. ++ ++ SYNOPSIS ++ set_decimal() ++ str - character string ++ length - string length ++ ++ NOTE ++ as we use character strings to send decimal values in ++ binary protocol, we use str2my_decimal to convert it to ++ internal decimal value. ++*/ ++ ++void Item_param::set_decimal(const char *str, ulong length) ++{ ++ char *end; ++ DBUG_ENTER("Item_param::set_decimal"); ++ ++ end= (char*) str+length; ++ str2my_decimal(E_DEC_FATAL_ERROR, str, &decimal_value, &end); ++ state= DECIMAL_VALUE; ++ decimals= decimal_value.frac; ++ max_length= my_decimal_precision_to_length(decimal_value.precision(), ++ decimals, unsigned_flag); ++ maybe_null= 0; ++ DBUG_VOID_RETURN; ++} ++ ++ ++/* ++ Set parameter value from TIME value. ++ ++ SYNOPSIS ++ set_time() ++ tm - datetime value to set (time_type is ignored) ++ type - type of datetime value ++ max_length_arg - max length of datetime value as string ++ ++ NOTE ++ If we value to be stored is not normalized, zero value will be stored ++ instead and proper warning will be produced. This function relies on ++ the fact that even wrong value sent over binary protocol fits into ++ MAX_DATE_STRING_REP_LENGTH buffer. ++*/ ++void Item_param::set_time(TIME *tm, timestamp_type type, uint32 max_length_arg) ++{ ++ DBUG_ENTER("Item_param::set_time"); ++ ++ value.time= *tm; ++ value.time.time_type= type; ++ ++ if (value.time.year > 9999 || value.time.month > 12 || ++ value.time.day > 31 || ++ type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23 || ++ value.time.minute > 59 || value.time.second > 59) ++ { ++ char buff[MAX_DATE_STRING_REP_LENGTH]; ++ uint length= my_TIME_to_str(&value.time, buff); ++ make_truncated_value_warning(current_thd, buff, length, type, 0); ++ set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); ++ } ++ ++ state= TIME_VALUE; ++ maybe_null= 0; ++ max_length= max_length_arg; ++ decimals= 0; ++ DBUG_VOID_RETURN; ++} ++ ++ ++bool Item_param::set_str(const char *str, ulong length) ++{ ++ DBUG_ENTER("Item_param::set_str"); ++ /* ++ Assign string with no conversion: data is converted only after it's ++ been written to the binary log. ++ */ ++ uint dummy_errors; ++ if (str_value.copy(str, length, &my_charset_bin, &my_charset_bin, ++ &dummy_errors)) ++ DBUG_RETURN(TRUE); ++ state= STRING_VALUE; ++ max_length= length; ++ maybe_null= 0; ++ /* max_length and decimals are set after charset conversion */ ++ /* sic: str may be not null-terminated, don't add DBUG_PRINT here */ ++ DBUG_RETURN(FALSE); ++} ++ ++ ++bool Item_param::set_longdata(const char *str, ulong length) ++{ ++ DBUG_ENTER("Item_param::set_longdata"); ++ ++ /* ++ If client character set is multibyte, end of long data packet ++ may hit at the middle of a multibyte character. Additionally, ++ if binary log is open we must write long data value to the ++ binary log in character set of client. This is why we can't ++ convert long data to connection character set as it comes ++ (here), and first have to concatenate all pieces together, ++ write query to the binary log and only then perform conversion. ++ */ ++ if (str_value.append(str, length, &my_charset_bin)) ++ DBUG_RETURN(TRUE); ++ state= LONG_DATA_VALUE; ++ maybe_null= 0; ++ ++ DBUG_RETURN(FALSE); ++} ++ ++ ++/* ++ Set parameter value from user variable value. ++ ++ SYNOPSIS ++ set_from_user_var ++ thd Current thread ++ entry User variable structure (NULL means use NULL value) ++ ++ RETURN ++ 0 OK ++ 1 Out of memory ++*/ ++ ++bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) ++{ ++ DBUG_ENTER("Item_param::set_from_user_var"); ++ if (entry && entry->value) ++ { ++ item_result_type= entry->type; ++ switch (entry->type) { ++ case REAL_RESULT: ++ set_double(*(double*)entry->value); ++ item_type= Item::REAL_ITEM; ++ item_result_type= REAL_RESULT; ++ break; ++ case INT_RESULT: ++ set_int(*(longlong*)entry->value, 21); ++ item_type= Item::INT_ITEM; ++ item_result_type= INT_RESULT; ++ break; ++ case STRING_RESULT: ++ { ++ CHARSET_INFO *fromcs= entry->collation.collation; ++ CHARSET_INFO *tocs= thd->variables.collation_connection; ++ uint32 dummy_offset; ++ ++ value.cs_info.character_set_of_placeholder= fromcs; ++ /* ++ Setup source and destination character sets so that they ++ are different only if conversion is necessary: this will ++ make later checks easier. ++ */ ++ value.cs_info.final_character_set_of_str_value= ++ String::needs_conversion(0, fromcs, tocs, &dummy_offset) ? ++ tocs : fromcs; ++ /* ++ Exact value of max_length is not known unless data is converted to ++ charset of connection, so we have to set it later. ++ */ ++ item_type= Item::STRING_ITEM; ++ item_result_type= STRING_RESULT; ++ ++ if (set_str((const char *)entry->value, entry->length)) ++ DBUG_RETURN(1); ++ break; ++ } ++ case DECIMAL_RESULT: ++ { ++ const my_decimal *ent_value= (const my_decimal *)entry->value; ++ my_decimal2decimal(ent_value, &decimal_value); ++ state= DECIMAL_VALUE; ++ decimals= ent_value->frac; ++ max_length= my_decimal_precision_to_length(ent_value->precision(), ++ decimals, unsigned_flag); ++ break; ++ } ++ default: ++ DBUG_ASSERT(0); ++ set_null(); ++ } ++ } ++ else ++ set_null(); ++ ++ DBUG_RETURN(0); ++} ++ ++/* ++ Resets parameter after execution. ++ ++ SYNOPSIS ++ Item_param::reset() ++ ++ NOTES ++ We clear null_value here instead of setting it in set_* methods, ++ because we want more easily handle case for long data. ++*/ ++ ++void Item_param::reset() ++{ ++ DBUG_ENTER("Item_param::reset"); ++ /* Shrink string buffer if it's bigger than max possible CHAR column */ ++ if (str_value.alloced_length() > MAX_CHAR_WIDTH) ++ str_value.free(); ++ else ++ str_value.length(0); ++ str_value_ptr.length(0); ++ /* ++ We must prevent all charset conversions until data has been written ++ to the binary log. ++ */ ++ str_value.set_charset(&my_charset_bin); ++ collation.set(&my_charset_bin, DERIVATION_COERCIBLE); ++ state= NO_VALUE; ++ maybe_null= 1; ++ null_value= 0; ++ /* ++ Don't reset item_type to PARAM_ITEM: it's only needed to guard ++ us from item optimizations at prepare stage, when item doesn't yet ++ contain a literal of some kind. ++ In all other cases when this object is accessed its value is ++ set (this assumption is guarded by 'state' and ++ DBUG_ASSERTS(state != NO_VALUE) in all Item_param::get_* ++ methods). ++ */ ++ DBUG_VOID_RETURN; ++} ++ ++ ++int Item_param::save_in_field(Field *field, bool no_conversions) ++{ ++ field->set_notnull(); ++ ++ switch (state) { ++ case INT_VALUE: ++ return field->store(value.integer, unsigned_flag); ++ case REAL_VALUE: ++ return field->store(value.real); ++ case DECIMAL_VALUE: ++ return field->store_decimal(&decimal_value); ++ case TIME_VALUE: ++ field->store_time(&value.time, value.time.time_type); ++ return 0; ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ return field->store(str_value.ptr(), str_value.length(), ++ str_value.charset()); ++ case NULL_VALUE: ++ return set_field_to_null_with_conversions(field, no_conversions); ++ case NO_VALUE: ++ default: ++ DBUG_ASSERT(0); ++ } ++ return 1; ++} ++ ++ ++bool Item_param::get_time(TIME *res) ++{ ++ if (state == TIME_VALUE) ++ { ++ *res= value.time; ++ return 0; ++ } ++ /* ++ If parameter value isn't supplied assertion will fire in val_str() ++ which is called from Item::get_time(). ++ */ ++ return Item::get_time(res); ++} ++ ++ ++bool Item_param::get_date(TIME *res, uint fuzzydate) ++{ ++ if (state == TIME_VALUE) ++ { ++ *res= value.time; ++ return 0; ++ } ++ return Item::get_date(res, fuzzydate); ++} ++ ++ ++double Item_param::val_real() ++{ ++ switch (state) { ++ case REAL_VALUE: ++ return value.real; ++ case INT_VALUE: ++ return (double) value.integer; ++ case DECIMAL_VALUE: ++ { ++ double result; ++ my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &result); ++ return result; ++ } ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ { ++ int dummy_err; ++ char *end_not_used; ++ return my_strntod(str_value.charset(), (char*) str_value.ptr(), ++ str_value.length(), &end_not_used, &dummy_err); ++ } ++ case TIME_VALUE: ++ /* ++ This works for example when user says SELECT ?+0.0 and supplies ++ time value for the placeholder. ++ */ ++ return ulonglong2double(TIME_to_ulonglong(&value.time)); ++ case NULL_VALUE: ++ return 0.0; ++ default: ++ DBUG_ASSERT(0); ++ } ++ return 0.0; ++} ++ ++ ++longlong Item_param::val_int() ++{ ++ switch (state) { ++ case REAL_VALUE: ++ return (longlong) rint(value.real); ++ case INT_VALUE: ++ return value.integer; ++ case DECIMAL_VALUE: ++ { ++ longlong i; ++ my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &i); ++ return i; ++ } ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ { ++ int dummy_err; ++ return my_strntoll(str_value.charset(), str_value.ptr(), ++ str_value.length(), 10, (char**) 0, &dummy_err); ++ } ++ case TIME_VALUE: ++ return (longlong) TIME_to_ulonglong(&value.time); ++ case NULL_VALUE: ++ return 0; ++ default: ++ DBUG_ASSERT(0); ++ } ++ return 0; ++} ++ ++ ++my_decimal *Item_param::val_decimal(my_decimal *dec) ++{ ++ switch (state) { ++ case DECIMAL_VALUE: ++ return &decimal_value; ++ case REAL_VALUE: ++ double2my_decimal(E_DEC_FATAL_ERROR, value.real, dec); ++ return dec; ++ case INT_VALUE: ++ int2my_decimal(E_DEC_FATAL_ERROR, value.integer, unsigned_flag, dec); ++ return dec; ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ string2my_decimal(E_DEC_FATAL_ERROR, &str_value, dec); ++ return dec; ++ case TIME_VALUE: ++ { ++ longlong i= (longlong) TIME_to_ulonglong(&value.time); ++ int2my_decimal(E_DEC_FATAL_ERROR, i, 0, dec); ++ return dec; ++ } ++ case NULL_VALUE: ++ return 0; ++ default: ++ DBUG_ASSERT(0); ++ } ++ return 0; ++} ++ ++ ++String *Item_param::val_str(String* str) ++{ ++ switch (state) { ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ return &str_value_ptr; ++ case REAL_VALUE: ++ str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); ++ return str; ++ case INT_VALUE: ++ str->set(value.integer, &my_charset_bin); ++ return str; ++ case DECIMAL_VALUE: ++ if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, ++ 0, 0, 0, str) <= 1) ++ return str; ++ return NULL; ++ case TIME_VALUE: ++ { ++ if (str->reserve(MAX_DATE_STRING_REP_LENGTH)) ++ break; ++ str->length((uint) my_TIME_to_str(&value.time, (char*) str->ptr())); ++ str->set_charset(&my_charset_bin); ++ return str; ++ } ++ case NULL_VALUE: ++ return NULL; ++ default: ++ DBUG_ASSERT(0); ++ } ++ return str; ++} ++ ++/* ++ Return Param item values in string format, for generating the dynamic ++ query used in update/binary logs ++ TODO: change interface and implementation to fill log data in place ++ and avoid one more memcpy/alloc between str and log string. ++*/ ++ ++const String *Item_param::query_val_str(String* str) const ++{ ++ switch (state) { ++ case INT_VALUE: ++ str->set(value.integer, &my_charset_bin); ++ break; ++ case REAL_VALUE: ++ str->set(value.real, NOT_FIXED_DEC, &my_charset_bin); ++ break; ++ case DECIMAL_VALUE: ++ if (my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, ++ 0, 0, 0, str) > 1) ++ return &my_null_string; ++ break; ++ case TIME_VALUE: ++ { ++ char *buf, *ptr; ++ str->length(0); ++ /* ++ TODO: in case of error we need to notify replication ++ that binary log contains wrong statement ++ */ ++ if (str->reserve(MAX_DATE_STRING_REP_LENGTH+3)) ++ break; ++ ++ /* Create date string inplace */ ++ buf= str->c_ptr_quick(); ++ ptr= buf; ++ *ptr++= '\''; ++ ptr+= (uint) my_TIME_to_str(&value.time, ptr); ++ *ptr++= '\''; ++ str->length((uint32) (ptr - buf)); ++ break; ++ } ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ { ++ str->length(0); ++ append_query_string(value.cs_info.character_set_client, &str_value, str); ++ break; ++ } ++ case NULL_VALUE: ++ return &my_null_string; ++ default: ++ DBUG_ASSERT(0); ++ } ++ return str; ++} ++ ++ ++/* ++ Convert string from client character set to the character set of ++ connection. ++*/ ++ ++bool Item_param::convert_str_value(THD *thd) ++{ ++ bool rc= FALSE; ++ if (state == STRING_VALUE || state == LONG_DATA_VALUE) ++ { ++ /* ++ Check is so simple because all charsets were set up properly ++ in setup_one_conversion_function, where typecode of ++ placeholder was also taken into account: the variables are different ++ here only if conversion is really necessary. ++ */ ++ if (value.cs_info.final_character_set_of_str_value != ++ value.cs_info.character_set_of_placeholder) ++ { ++ rc= thd->convert_string(&str_value, ++ value.cs_info.character_set_of_placeholder, ++ value.cs_info.final_character_set_of_str_value); ++ } ++ else ++ str_value.set_charset(value.cs_info.final_character_set_of_str_value); ++ /* Here str_value is guaranteed to be in final_character_set_of_str_value */ ++ ++ max_length= str_value.length(); ++ decimals= 0; ++ /* ++ str_value_ptr is returned from val_str(). It must be not alloced ++ to prevent it's modification by val_str() invoker. ++ */ ++ str_value_ptr.set(str_value.ptr(), str_value.length(), ++ str_value.charset()); ++ /* Synchronize item charset with value charset */ ++ collation.set(str_value.charset(), DERIVATION_COERCIBLE); ++ } ++ return rc; ++} ++ ++ ++bool Item_param::basic_const_item() const ++{ ++ if (state == NO_VALUE || state == TIME_VALUE) ++ return FALSE; ++ return TRUE; ++} ++ ++ ++Item * ++Item_param::new_item() ++{ ++ /* see comments in the header file */ ++ switch (state) { ++ case NULL_VALUE: ++ return new Item_null(name); ++ case INT_VALUE: ++ return (unsigned_flag ? ++ new Item_uint(name, value.integer, max_length) : ++ new Item_int(name, value.integer, max_length)); ++ case REAL_VALUE: ++ return new Item_float(name, value.real, decimals, max_length); ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ return new Item_string(name, str_value.c_ptr_quick(), str_value.length(), ++ str_value.charset()); ++ case TIME_VALUE: ++ break; ++ case NO_VALUE: ++ default: ++ DBUG_ASSERT(0); ++ }; ++ return 0; ++} ++ ++ ++bool ++Item_param::eq(const Item *arg, bool binary_cmp) const ++{ ++ Item *item; ++ if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type()) ++ return FALSE; ++ /* ++ We need to cast off const to call val_int(). This should be OK for ++ a basic constant. ++ */ ++ item= (Item*) arg; ++ ++ switch (state) { ++ case NULL_VALUE: ++ return TRUE; ++ case INT_VALUE: ++ return value.integer == item->val_int() && ++ unsigned_flag == item->unsigned_flag; ++ case REAL_VALUE: ++ return value.real == item->val_real(); ++ case STRING_VALUE: ++ case LONG_DATA_VALUE: ++ if (binary_cmp) ++ return !stringcmp(&str_value, &item->str_value); ++ return !sortcmp(&str_value, &item->str_value, collation.collation); ++ default: ++ break; ++ } ++ return FALSE; ++} ++ ++/* End of Item_param related */ ++ ++void Item_param::print(String *str) ++{ ++ if (state == NO_VALUE) ++ { ++ str->append('?'); ++ } ++ else ++ { ++ char buffer[STRING_BUFFER_USUAL_SIZE]; ++ String tmp(buffer, sizeof(buffer), &my_charset_bin); ++ const String *res; ++ res= query_val_str(&tmp); ++ str->append(*res); ++ } ++} ++ ++ ++/**************************************************************************** ++ Item_copy_string ++****************************************************************************/ ++ ++void Item_copy_string::copy() ++{ ++ String *res=item->val_str(&str_value); ++ if (res && res != &str_value) ++ str_value.copy(*res); ++ null_value=item->null_value; ++} ++ ++/* ARGSUSED */ ++String *Item_copy_string::val_str(String *str) ++{ ++ // Item_copy_string is used without fix_fields call ++ if (null_value) ++ return (String*) 0; ++ return &str_value; ++} ++ ++ ++my_decimal *Item_copy_string::val_decimal(my_decimal *decimal_value) ++{ ++ // Item_copy_string is used without fix_fields call ++ if (null_value) ++ return 0; ++ string2my_decimal(E_DEC_FATAL_ERROR, &str_value, decimal_value); ++ return (decimal_value); ++} ++ ++ ++ ++int Item_copy_string::save_in_field(Field *field, bool no_conversions) ++{ ++ if (null_value) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ return field->store(str_value.ptr(),str_value.length(), ++ collation.collation); ++} ++ ++/* ++ Functions to convert item to field (for send_fields) ++*/ ++ ++/* ARGSUSED */ ++bool Item::fix_fields(THD *thd, Item **ref) ++{ ++ ++ // We do not check fields which are fixed during construction ++ DBUG_ASSERT(fixed == 0 || basic_const_item()); ++ fixed= 1; ++ return FALSE; ++} ++ ++double Item_ref_null_helper::val_real() ++{ ++ DBUG_ASSERT(fixed == 1); ++ double tmp= (*ref)->val_result(); ++ owner->was_null|= null_value= (*ref)->null_value; ++ return tmp; ++} ++ ++ ++longlong Item_ref_null_helper::val_int() ++{ ++ DBUG_ASSERT(fixed == 1); ++ longlong tmp= (*ref)->val_int_result(); ++ owner->was_null|= null_value= (*ref)->null_value; ++ return tmp; ++} ++ ++ ++my_decimal *Item_ref_null_helper::val_decimal(my_decimal *decimal_value) ++{ ++ DBUG_ASSERT(fixed == 1); ++ my_decimal *val= (*ref)->val_decimal_result(decimal_value); ++ owner->was_null|= null_value= (*ref)->null_value; ++ return val; ++} ++ ++ ++bool Item_ref_null_helper::val_bool() ++{ ++ DBUG_ASSERT(fixed == 1); ++ bool val= (*ref)->val_bool_result(); ++ owner->was_null|= null_value= (*ref)->null_value; ++ return val; ++} ++ ++ ++String* Item_ref_null_helper::val_str(String* s) ++{ ++ DBUG_ASSERT(fixed == 1); ++ String* tmp= (*ref)->str_result(s); ++ owner->was_null|= null_value= (*ref)->null_value; ++ return tmp; ++} ++ ++ ++bool Item_ref_null_helper::get_date(TIME *ltime, uint fuzzydate) ++{ ++ return (owner->was_null|= null_value= (*ref)->get_date(ltime, fuzzydate)); ++} ++ ++ ++/* ++ Mark item and SELECT_LEXs as dependent if item was resolved in outer SELECT ++ ++ SYNOPSIS ++ mark_as_dependent() ++ thd - thread handler ++ last - select from which current item depend ++ current - current select ++ resolved_item - item which was resolved in outer SELECT(for warning) ++ mark_item - item which should be marked (can be differ in case of ++ substitution) ++*/ ++ ++static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, ++ Item_ident *resolved_item, ++ Item_ident *mark_item) ++{ ++ const char *db_name= (resolved_item->db_name ? ++ resolved_item->db_name : ""); ++ const char *table_name= (resolved_item->table_name ? ++ resolved_item->table_name : ""); ++ /* store pointer on SELECT_LEX from which item is dependent */ ++ if (mark_item) ++ mark_item->depended_from= last; ++ current->mark_as_dependent(last); ++ if (thd->lex->describe & DESCRIBE_EXTENDED) ++ { ++ char warn_buff[MYSQL_ERRMSG_SIZE]; ++ sprintf(warn_buff, ER(ER_WARN_FIELD_RESOLVED), ++ db_name, (db_name[0] ? "." : ""), ++ table_name, (table_name [0] ? "." : ""), ++ resolved_item->field_name, ++ current->select_number, last->select_number); ++ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ++ ER_WARN_FIELD_RESOLVED, warn_buff); ++ } ++} ++ ++ ++/* ++ Mark range of selects and resolved identifier (field/reference) item as ++ dependent ++ ++ SYNOPSIS ++ mark_select_range_as_dependent() ++ thd - thread handler ++ last_select - select where resolved_item was resolved ++ current_sel - current select (select where resolved_item was placed) ++ found_field - field which was found during resolving ++ found_item - Item which was found during resolving (if resolved ++ identifier belongs to VIEW) ++ resolved_item - Identifier which was resolved ++ ++ NOTE: ++ We have to mark all items between current_sel (including) and ++ last_select (excluding) as dependend (select before last_select should ++ be marked with actual table mask used by resolved item, all other with ++ OUTER_REF_TABLE_BIT) and also write dependence information to Item of ++ resolved identifier. ++*/ ++ ++void mark_select_range_as_dependent(THD *thd, ++ SELECT_LEX *last_select, ++ SELECT_LEX *current_sel, ++ Field *found_field, Item *found_item, ++ Item_ident *resolved_item) ++{ ++ /* ++ Go from current SELECT to SELECT where field was resolved (it ++ have to be reachable from current SELECT, because it was already ++ done once when we resolved this field and cached result of ++ resolving) ++ */ ++ SELECT_LEX *previous_select= current_sel; ++ for (; previous_select->outer_select() != last_select; ++ previous_select= previous_select->outer_select()) ++ { ++ Item_subselect *prev_subselect_item= ++ previous_select->master_unit()->item; ++ prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; ++ prev_subselect_item->const_item_cache= 0; ++ } ++ { ++ Item_subselect *prev_subselect_item= ++ previous_select->master_unit()->item; ++ Item_ident *dependent= resolved_item; ++ if (found_field == view_ref_found) ++ { ++ Item::Type type= found_item->type(); ++ prev_subselect_item->used_tables_cache|= ++ found_item->used_tables(); ++ dependent= ((type == Item::REF_ITEM || type == Item::FIELD_ITEM) ? ++ (Item_ident*) found_item : ++ 0); ++ } ++ else ++ prev_subselect_item->used_tables_cache|= ++ found_field->table->map; ++ prev_subselect_item->const_item_cache= 0; ++ mark_as_dependent(thd, last_select, current_sel, resolved_item, ++ dependent); ++ } ++} ++ ++ ++/* ++ Search a GROUP BY clause for a field with a certain name. ++ ++ SYNOPSIS ++ find_field_in_group_list() ++ find_item the item being searched for ++ group_list GROUP BY clause ++ ++ DESCRIPTION ++ Search the GROUP BY list for a column named as find_item. When searching ++ preference is given to columns that are qualified with the same table (and ++ database) name as the one being searched for. ++ ++ RETURN ++ - the found item on success ++ - NULL if find_item is not in group_list ++*/ ++ ++static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) ++{ ++ const char *db_name; ++ const char *table_name; ++ const char *field_name; ++ ORDER *found_group= NULL; ++ int found_match_degree= 0; ++ Item_ident *cur_field; ++ int cur_match_degree= 0; ++ char name_buff[NAME_LEN+1]; ++ ++ if (find_item->type() == Item::FIELD_ITEM || ++ find_item->type() == Item::REF_ITEM) ++ { ++ db_name= ((Item_ident*) find_item)->db_name; ++ table_name= ((Item_ident*) find_item)->table_name; ++ field_name= ((Item_ident*) find_item)->field_name; ++ } ++ else ++ return NULL; ++ ++ if (db_name && lower_case_table_names) ++ { ++ /* Convert database to lower case for comparison */ ++ strmake(name_buff, db_name, sizeof(name_buff)-1); ++ my_casedn_str(files_charset_info, name_buff); ++ db_name= name_buff; ++ } ++ ++ DBUG_ASSERT(field_name != 0); ++ ++ for (ORDER *cur_group= group_list ; cur_group ; cur_group= cur_group->next) ++ { ++ if ((*(cur_group->item))->real_item()->type() == Item::FIELD_ITEM) ++ { ++ cur_field= (Item_ident*) *cur_group->item; ++ cur_match_degree= 0; ++ ++ DBUG_ASSERT(cur_field->field_name != 0); ++ ++ if (!my_strcasecmp(system_charset_info, ++ cur_field->field_name, field_name)) ++ ++cur_match_degree; ++ else ++ continue; ++ ++ if (cur_field->table_name && table_name) ++ { ++ /* If field_name is qualified by a table name. */ ++ if (strcmp(cur_field->table_name, table_name)) ++ /* Same field names, different tables. */ ++ return NULL; ++ ++ ++cur_match_degree; ++ if (cur_field->db_name && db_name) ++ { ++ /* If field_name is also qualified by a database name. */ ++ if (strcmp(cur_field->db_name, db_name)) ++ /* Same field names, different databases. */ ++ return NULL; ++ ++cur_match_degree; ++ } ++ } ++ ++ if (cur_match_degree > found_match_degree) ++ { ++ found_match_degree= cur_match_degree; ++ found_group= cur_group; ++ } ++ else if (found_group && (cur_match_degree == found_match_degree) && ++ ! (*(found_group->item))->eq(cur_field, 0)) ++ { ++ /* ++ If the current resolve candidate matches equally well as the current ++ best match, they must reference the same column, otherwise the field ++ is ambiguous. ++ */ ++ my_error(ER_NON_UNIQ_ERROR, MYF(0), ++ find_item->full_name(), current_thd->where); ++ return NULL; ++ } ++ } ++ } ++ ++ if (found_group) ++ return found_group->item; ++ else ++ return NULL; ++} ++ ++ ++/* ++ Resolve a column reference in a sub-select. ++ ++ SYNOPSIS ++ resolve_ref_in_select_and_group() ++ thd current thread ++ ref column reference being resolved ++ select the sub-select that ref is resolved against ++ ++ DESCRIPTION ++ Resolve a column reference (usually inside a HAVING clause) against the ++ SELECT and GROUP BY clauses of the query described by 'select'. The name ++ resolution algorithm searches both the SELECT and GROUP BY clauses, and in ++ case of a name conflict prefers GROUP BY column names over SELECT names. If ++ both clauses contain different fields with the same names, a warning is ++ issued that name of 'ref' is ambiguous. We extend ANSI SQL in that when no ++ GROUP BY column is found, then a HAVING name is resolved as a possibly ++ derived SELECT column. This extension is allowed only if the ++ MODE_ONLY_FULL_GROUP_BY sql mode isn't enabled. ++ ++ NOTES ++ The resolution procedure is: ++ - Search for a column or derived column named col_ref_i [in table T_j] ++ in the SELECT clause of Q. ++ - Search for a column named col_ref_i [in table T_j] ++ in the GROUP BY clause of Q. ++ - If found different columns with the same name in GROUP BY and SELECT ++ - issue a warning and return the GROUP BY column, ++ - otherwise ++ - if the MODE_ONLY_FULL_GROUP_BY mode is enabled return error ++ - else return the found SELECT column. ++ ++ ++ RETURN ++ NULL - there was an error, and the error was already reported ++ not_found_item - the item was not resolved, no error was reported ++ resolved item - if the item was resolved ++*/ ++ ++static Item** ++resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) ++{ ++ Item **group_by_ref= NULL; ++ Item **select_ref= NULL; ++ ORDER *group_list= (ORDER*) select->group_list.first; ++ bool ambiguous_fields= FALSE; ++ uint counter; ++ bool not_used; ++ ++ /* ++ Search for a column or derived column named as 'ref' in the SELECT ++ clause of the current select. ++ */ ++ if (!(select_ref= find_item_in_list(ref, *(select->get_item_list()), ++ &counter, REPORT_EXCEPT_NOT_FOUND, ++ ¬_used))) ++ return NULL; /* Some error occurred. */ ++ ++ /* If this is a non-aggregated field inside HAVING, search in GROUP BY. */ ++ if (select->having_fix_field && !ref->with_sum_func && group_list) ++ { ++ group_by_ref= find_field_in_group_list(ref, group_list); ++ ++ /* Check if the fields found in SELECT and GROUP BY are the same field. */ ++ if (group_by_ref && (select_ref != not_found_item) && ++ !((*group_by_ref)->eq(*select_ref, 0))) ++ { ++ ambiguous_fields= TRUE; ++ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, ++ ER(ER_NON_UNIQ_ERROR), ref->full_name(), ++ current_thd->where); ++ ++ } ++ } ++ ++ if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && ++ select_ref != not_found_item && !group_by_ref) ++ { ++ /* ++ Report the error if fields was found only in the SELECT item list and ++ the strict mode is enabled. ++ */ ++ my_error(ER_NON_GROUPING_FIELD_USED, MYF(0), ++ ref->name, "HAVING"); ++ return NULL; ++ } ++ if (select_ref != not_found_item || group_by_ref) ++ { ++ if (select_ref != not_found_item && !ambiguous_fields) ++ { ++ DBUG_ASSERT(*select_ref != 0); ++ if (!select->ref_pointer_array[counter]) ++ { ++ my_error(ER_ILLEGAL_REFERENCE, MYF(0), ++ ref->name, "forward reference in item list"); ++ return NULL; ++ } ++ DBUG_ASSERT((*select_ref)->fixed); ++ return (select->ref_pointer_array + counter); ++ } ++ if (group_by_ref) ++ return group_by_ref; ++ DBUG_ASSERT(FALSE); ++ return NULL; /* So there is no compiler warning. */ ++ } ++ ++ return (Item**) not_found_item; ++} ++ ++ ++/* ++ Resolve the name of an outer select column reference. ++ ++ SYNOPSIS ++ Item_field::fix_outer_field() ++ thd [in] current thread ++ from_field [in/out] found field reference or (Field*)not_found_field ++ reference [in/out] view column if this item was resolved to a view column ++ ++ DESCRIPTION ++ The method resolves the column reference represented by 'this' as a column ++ present in outer selects that contain current select. ++ ++ NOTES ++ This is the inner loop of Item_field::fix_fields: ++ ++ for each outer query Q_k beginning from the inner-most one ++ { ++ search for a column or derived column named col_ref_i ++ [in table T_j] in the FROM clause of Q_k; ++ ++ if such a column is not found ++ Search for a column or derived column named col_ref_i ++ [in table T_j] in the SELECT and GROUP clauses of Q_k. ++ } ++ ++ IMPLEMENTATION ++ In prepared statements, because of cache, find_field_in_tables() ++ can resolve fields even if they don't belong to current context. ++ In this case this method only finds appropriate context and marks ++ current select as dependent. The found reference of field should be ++ provided in 'from_field'. ++ ++ RETURN ++ 1 - column succefully resolved and fix_fields() should continue. ++ 0 - column fully fixed and fix_fields() should return FALSE ++ -1 - error occured ++*/ ++int ++Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) ++{ ++ enum_parsing_place place= NO_MATTER; ++ bool field_found= (*from_field != not_found_field); ++ bool upward_lookup= FALSE; ++ ++ /* ++ If there are outer contexts (outer selects, but current select is ++ not derived table or view) try to resolve this reference in the ++ outer contexts. ++ ++ We treat each subselect as a separate namespace, so that different ++ subselects may contain columns with the same names. The subselects ++ are searched starting from the innermost. ++ */ ++ Name_resolution_context *last_checked_context= context; ++ Item **ref= (Item **) not_found_item; ++ Name_resolution_context *outer_context= context->outer_context; ++ for (; ++ outer_context; ++ outer_context= outer_context->outer_context) ++ { ++ SELECT_LEX *select= outer_context->select_lex; ++ Item_subselect *prev_subselect_item= ++ last_checked_context->select_lex->master_unit()->item; ++ last_checked_context= outer_context; ++ upward_lookup= TRUE; ++ ++ place= prev_subselect_item->parsing_place; ++ /* ++ If outer_field is set, field was already found by first call ++ to find_field_in_tables(). Only need to find appropriate context. ++ */ ++ if (field_found && outer_context->select_lex != ++ cached_table->select_lex) ++ continue; ++ /* ++ In case of a view, find_field_in_tables() writes the pointer to ++ the found view field into '*reference', in other words, it ++ substitutes this Item_field with the found expression. ++ */ ++ if (field_found || (*from_field= find_field_in_tables(thd, this, ++ outer_context-> ++ first_name_resolution_table, ++ outer_context-> ++ last_name_resolution_table, ++ reference, ++ IGNORE_EXCEPT_NON_UNIQUE, ++ TRUE, TRUE)) != ++ not_found_field) ++ { ++ if (*from_field) ++ { ++ if (*from_field != view_ref_found) ++ { ++ prev_subselect_item->used_tables_cache|= (*from_field)->table->map; ++ prev_subselect_item->const_item_cache= 0; ++ if (thd->lex->in_sum_func && ++ thd->lex->in_sum_func->nest_level == ++ thd->lex->current_select->nest_level) ++ { ++ Item::Type type= (*reference)->type(); ++ set_if_bigger(thd->lex->in_sum_func->max_arg_level, ++ select->nest_level); ++ set_field(*from_field); ++ fixed= 1; ++ mark_as_dependent(thd, last_checked_context->select_lex, ++ context->select_lex, this, ++ ((type == REF_ITEM || type == FIELD_ITEM) ? ++ (Item_ident*) (*reference) : 0)); ++ return 0; ++ } ++ } ++ else ++ { ++ Item::Type type= (*reference)->type(); ++ prev_subselect_item->used_tables_cache|= ++ (*reference)->used_tables(); ++ prev_subselect_item->const_item_cache&= ++ (*reference)->const_item(); ++ mark_as_dependent(thd, last_checked_context->select_lex, ++ context->select_lex, this, ++ ((type == REF_ITEM || type == FIELD_ITEM) ? ++ (Item_ident*) (*reference) : ++ 0)); ++ /* ++ A reference to a view field had been found and we ++ substituted it instead of this Item (find_field_in_tables ++ does it by assigning the new value to *reference), so now ++ we can return from this function. ++ */ ++ return 0; ++ } ++ } ++ break; ++ } ++ ++ /* Search in SELECT and GROUP lists of the outer select. */ ++ if (outer_context->resolve_in_select_list) ++ { ++ if (!(ref= resolve_ref_in_select_and_group(thd, this, select))) ++ return -1; /* Some error occurred (e.g. ambiguous names). */ ++ if (ref != not_found_item) ++ { ++ DBUG_ASSERT(*ref && (*ref)->fixed); ++ prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); ++ prev_subselect_item->const_item_cache&= (*ref)->const_item(); ++ break; ++ } ++ } ++ ++ /* ++ Reference is not found in this select => this subquery depend on ++ outer select (or we just trying to find wrong identifier, in this ++ case it does not matter which used tables bits we set) ++ */ ++ prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; ++ prev_subselect_item->const_item_cache= 0; ++ } ++ ++ DBUG_ASSERT(ref != 0); ++ if (!*from_field) ++ return -1; ++ if (ref == not_found_item && *from_field == not_found_field) ++ { ++ if (upward_lookup) ++ { ++ // We can't say exactly what absent table or field ++ my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); ++ } ++ else ++ { ++ /* Call find_field_in_tables only to report the error */ ++ find_field_in_tables(thd, this, ++ context->first_name_resolution_table, ++ context->last_name_resolution_table, ++ reference, REPORT_ALL_ERRORS, ++ !any_privileges && ++ TRUE, TRUE); ++ } ++ return -1; ++ } ++ else if (ref != not_found_item) ++ { ++ Item *save; ++ Item_ref *rf; ++ ++ /* Should have been checked in resolve_ref_in_select_and_group(). */ ++ DBUG_ASSERT(*ref && (*ref)->fixed); ++ /* ++ Here, a subset of actions performed by Item_ref::set_properties ++ is not enough. So we pass ptr to NULL into Item_[direct]_ref ++ constructor, so no initialization is performed, and call ++ fix_fields() below. ++ */ ++ save= *ref; ++ *ref= NULL; // Don't call set_properties() ++ rf= (place == IN_HAVING ? ++ new Item_ref(context, ref, (char*) table_name, ++ (char*) field_name) : ++ new Item_direct_ref(context, ref, (char*) table_name, ++ (char*) field_name)); ++ *ref= save; ++ if (!rf) ++ return -1; ++ thd->change_item_tree(reference, rf); ++ /* ++ rf is Item_ref => never substitute other items (in this case) ++ during fix_fields() => we can use rf after fix_fields() ++ */ ++ DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() ++ if (rf->fix_fields(thd, reference) || rf->check_cols(1)) ++ return -1; ++ ++ mark_as_dependent(thd, last_checked_context->select_lex, ++ context->select_lex, this, ++ rf); ++ return 0; ++ } ++ else ++ { ++ mark_as_dependent(thd, last_checked_context->select_lex, ++ context->select_lex, ++ this, this); ++ if (last_checked_context->select_lex->having_fix_field) ++ { ++ Item_ref *rf; ++ rf= new Item_ref(context, ++ (cached_table->db[0] ? cached_table->db : 0), ++ (char*) cached_table->alias, (char*) field_name); ++ if (!rf) ++ return -1; ++ thd->change_item_tree(reference, rf); ++ /* ++ rf is Item_ref => never substitute other items (in this case) ++ during fix_fields() => we can use rf after fix_fields() ++ */ ++ DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() ++ if (rf->fix_fields(thd, reference) || rf->check_cols(1)) ++ return -1; ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++ ++/* ++ Resolve the name of a column reference. ++ ++ SYNOPSIS ++ Item_field::fix_fields() ++ thd [in] current thread ++ reference [in/out] view column if this item was resolved to a view column ++ ++ DESCRIPTION ++ The method resolves the column reference represented by 'this' as a column ++ present in one of: FROM clause, SELECT clause, GROUP BY clause of a query ++ Q, or in outer queries that contain Q. ++ ++ NOTES ++ The name resolution algorithm used is (where [T_j] is an optional table ++ name that qualifies the column name): ++ ++ resolve_column_reference([T_j].col_ref_i) ++ { ++ search for a column or derived column named col_ref_i ++ [in table T_j] in the FROM clause of Q; ++ ++ if such a column is NOT found AND // Lookup in outer queries. ++ there are outer queries ++ { ++ for each outer query Q_k beginning from the inner-most one ++ { ++ search for a column or derived column named col_ref_i ++ [in table T_j] in the FROM clause of Q_k; ++ ++ if such a column is not found ++ Search for a column or derived column named col_ref_i ++ [in table T_j] in the SELECT and GROUP clauses of Q_k. ++ } ++ } ++ } ++ ++ Notice that compared to Item_ref::fix_fields, here we first search the FROM ++ clause, and then we search the SELECT and GROUP BY clauses. ++ ++ RETURN ++ TRUE if error ++ FALSE on success ++*/ ++ ++bool Item_field::fix_fields(THD *thd, Item **reference) ++{ ++ DBUG_ASSERT(fixed == 0); ++ if (!field) // If field is not checked ++ { ++ Field *from_field= (Field *)not_found_field; ++ bool outer_fixed= false; ++ /* ++ In case of view, find_field_in_tables() write pointer to view field ++ expression to 'reference', i.e. it substitute that expression instead ++ of this Item_field ++ */ ++ if ((from_field= find_field_in_tables(thd, this, ++ context->first_name_resolution_table, ++ context->last_name_resolution_table, ++ reference, ++ IGNORE_EXCEPT_NON_UNIQUE, ++ !any_privileges, ++ TRUE)) == ++ not_found_field) ++ { ++ int ret; ++ /* Look up in current select's item_list to find aliased fields */ ++ if (thd->lex->current_select->is_item_list_lookup) ++ { ++ uint counter; ++ bool not_used; ++ Item** res= find_item_in_list(this, thd->lex->current_select->item_list, ++ &counter, REPORT_EXCEPT_NOT_FOUND, ++ ¬_used); ++ if (res != (Item **)not_found_item) ++ { ++ if ((*res)->type() == Item::FIELD_ITEM) ++ { ++ /* ++ It's an Item_field referencing another Item_field in the select ++ list. ++ use the field from the Item_field in the select list and leave ++ the Item_field instance in place. ++ */ ++ set_field((*((Item_field**)res))->field); ++ return 0; ++ } ++ else ++ { ++ /* ++ It's not an Item_field in the select list so we must make a new ++ Item_ref to point to the Item in the select list and replace the ++ Item_field created by the parser with the new Item_ref. ++ */ ++ Item_ref *rf= new Item_ref(context, db_name,table_name,field_name); ++ if (!rf) ++ return 1; ++ thd->change_item_tree(reference, rf); ++ /* ++ Because Item_ref never substitutes itself with other items ++ in Item_ref::fix_fields(), we can safely use the original ++ pointer to it even after fix_fields() ++ */ ++ return rf->fix_fields(thd, reference) || rf->check_cols(1); ++ } ++ } ++ } ++ if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) ++ goto error; ++ else if (!ret) ++ return FALSE; ++ outer_fixed= TRUE; ++ } ++ else if (!from_field) ++ goto error; ++ ++ /* ++ if it is not expression from merged VIEW we will set this field. ++ ++ We can leave expression substituted from view for next PS/SP rexecution ++ (i.e. do not register this substitution for reverting on cleupup() ++ (register_item_tree_changing())), because this subtree will be ++ fix_field'ed during setup_tables()->setup_underlying() (i.e. before ++ all other expressions of query, and references on tables which do ++ not present in query will not make problems. ++ ++ Also we suppose that view can't be changed during PS/SP life. ++ */ ++ if (from_field == view_ref_found) ++ return FALSE; ++ ++ if (!outer_fixed && cached_table && cached_table->select_lex && ++ context->select_lex && ++ cached_table->select_lex != context->select_lex) ++ { ++ int ret; ++ if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) ++ goto error; ++ else if (!ret) ++ return FALSE; ++ } ++ ++ set_field(from_field); ++ if (thd->lex->in_sum_func && ++ thd->lex->in_sum_func->nest_level == ++ thd->lex->current_select->nest_level) ++ set_if_bigger(thd->lex->in_sum_func->max_arg_level, ++ thd->lex->current_select->nest_level); ++ } ++ else if (thd->set_query_id && field->query_id != thd->query_id) ++ { ++ /* We only come here in unions */ ++ TABLE *table=field->table; ++ field->query_id=thd->query_id; ++ table->used_fields++; ++ table->used_keys.intersect(field->part_of_key); ++ } ++#ifndef NO_EMBEDDED_ACCESS_CHECKS ++ if (any_privileges) ++ { ++ char *db, *tab; ++ if (cached_table->view) ++ { ++ db= cached_table->view_db.str; ++ tab= cached_table->view_name.str; ++ } ++ else ++ { ++ db= cached_table->db; ++ tab= cached_table->table_name; ++ } ++ if (!(have_privileges= (get_column_grant(thd, &field->table->grant, ++ db, tab, field_name) & ++ VIEW_ANY_ACL))) ++ { ++ my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), ++ "ANY", thd->security_ctx->priv_user, ++ thd->security_ctx->host_or_ip, field_name, tab); ++ goto error; ++ } ++ } ++#endif ++ fixed= 1; ++ return FALSE; ++ ++error: ++ context->process_error(thd); ++ return TRUE; ++} ++ ++ ++Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ no_const_subst= 1; ++ return Item::safe_charset_converter(tocs); ++} ++ ++ ++void Item_field::cleanup() ++{ ++ DBUG_ENTER("Item_field::cleanup"); ++ Item_ident::cleanup(); ++ /* ++ Even if this object was created by direct link to field in setup_wild() ++ it will be linked correctly next time by name of field and table alias. ++ I.e. we can drop 'field'. ++ */ ++ field= result_field= 0; ++ null_value= FALSE; ++ DBUG_VOID_RETURN; ++} ++ ++/* ++ Find a field among specified multiple equalities ++ ++ SYNOPSIS ++ find_item_equal() ++ cond_equal reference to list of multiple equalities where ++ the field (this object) is to be looked for ++ ++ DESCRIPTION ++ The function first searches the field among multiple equalities ++ of the current level (in the cond_equal->current_level list). ++ If it fails, it continues searching in upper levels accessed ++ through a pointer cond_equal->upper_levels. ++ The search terminates as soon as a multiple equality containing ++ the field is found. ++ ++ RETURN VALUES ++ First Item_equal containing the field, if success ++ 0, otherwise ++*/ ++Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal) ++{ ++ Item_equal *item= 0; ++ while (cond_equal) ++ { ++ List_iterator_fast li(cond_equal->current_level); ++ while ((item= li++)) ++ { ++ if (item->contains(field)) ++ return item; ++ } ++ /* ++ The field is not found in any of the multiple equalities ++ of the current level. Look for it in upper levels ++ */ ++ cond_equal= cond_equal->upper_levels; ++ } ++ return 0; ++} ++ ++ ++/* ++ Check whether a field can be substituted by an equal item ++ ++ SYNOPSIS ++ equal_fields_propagator() ++ arg - *arg != NULL <-> the field is in the context where ++ substitution for an equal item is valid ++ ++ DESCRIPTION ++ The function checks whether a substitution of the field ++ occurrence for an equal item is valid. ++ ++ NOTES ++ The following statement is not always true: ++ x=y => F(x)=F(x/y). ++ This means substitution of an item for an equal item not always ++ yields an equavalent condition. ++ Here's an example: ++ 'a'='a ' ++ (LENGTH('a')=1) != (LENGTH('a ')=2) ++ Such a substitution is surely valid if either the substituted ++ field is not of a STRING type or if it is an argument of ++ a comparison predicate. ++ ++ RETURN ++ TRUE substitution is valid ++ FALSE otherwise ++*/ ++ ++bool Item_field::subst_argument_checker(byte **arg) ++{ ++ return (result_type() != STRING_RESULT) || (*arg); ++} ++ ++ ++/* ++ Set a pointer to the multiple equality the field reference belongs to ++ (if any) ++ ++ SYNOPSIS ++ equal_fields_propagator() ++ arg - reference to list of multiple equalities where ++ the field (this object) is to be looked for ++ ++ DESCRIPTION ++ The function looks for a multiple equality containing the field item ++ among those referenced by arg. ++ In the case such equality exists the function does the following. ++ If the found multiple equality contains a constant, then the field ++ reference is substituted for this constant, otherwise it sets a pointer ++ to the multiple equality in the field item. ++ ++ NOTES ++ This function is supposed to be called as a callback parameter in calls ++ of the compile method. ++ ++ RETURN VALUES ++ pointer to the replacing constant item, if the field item was substituted ++ pointer to the field item, otherwise. ++*/ ++ ++Item *Item_field::equal_fields_propagator(byte *arg) ++{ ++ if (no_const_subst) ++ return this; ++ item_equal= find_item_equal((COND_EQUAL *) arg); ++ Item *item= 0; ++ if (item_equal) ++ item= item_equal->get_const(); ++ /* ++ Disable const propagation for items used in different comparison contexts. ++ This must be done because, for example, Item_hex_string->val_int() is not ++ the same as (Item_hex_string->val_str() in BINARY column)->val_int(). ++ We cannot simply disable the replacement in a particular context ( ++ e.g. = AND = ) since ++ Items don't know the context they are in and there are functions like ++ IF (, 'yes', 'no'). ++ The same problem occurs when comparing a DATE/TIME field with a ++ DATE/TIME represented as an int and as a string. ++ */ ++ if (!item || ++ (cmp_context != (Item_result)-1 && item->cmp_context != cmp_context)) ++ item= this; ++ return item; ++} ++ ++ ++/* ++ Mark the item to not be part of substitution if it's not a binary item ++ See comments in Arg_comparator::set_compare_func() for details ++*/ ++ ++bool Item_field::set_no_const_sub(byte *arg) ++{ ++ if (field->charset() != &my_charset_bin) ++ no_const_subst=1; ++ return FALSE; ++} ++ ++ ++/* ++ Replace an Item_field for an equal Item_field that evaluated earlier ++ (if any) ++ ++ SYNOPSIS ++ replace_equal_field_() ++ arg - a dummy parameter, is not used here ++ ++ DESCRIPTION ++ The function returns a pointer to an item that is taken from ++ the very beginning of the item_equal list which the Item_field ++ object refers to (belongs to). ++ If the Item_field object does not refer any Item_equal object ++ 'this' is returned ++ ++ NOTES ++ This function is supposed to be called as a callback parameter in calls ++ of the thransformer method. ++ ++ RETURN VALUES ++ pointer to a replacement Item_field if there is a better equal item; ++ this - otherwise. ++*/ ++ ++Item *Item_field::replace_equal_field(byte *arg) ++{ ++ if (item_equal) ++ { ++ Item_field *subst= item_equal->get_first(); ++ if (subst && !field->eq(subst->field)) ++ return subst; ++ } ++ return this; ++} ++ ++ ++void Item::init_make_field(Send_field *tmp_field, ++ enum enum_field_types field_type) ++{ ++ char *empty_name= (char*) ""; ++ tmp_field->db_name= empty_name; ++ tmp_field->org_table_name= empty_name; ++ tmp_field->org_col_name= empty_name; ++ tmp_field->table_name= empty_name; ++ tmp_field->col_name= name; ++ tmp_field->charsetnr= collation.collation->number; ++ tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) | ++ (my_binary_compare(collation.collation) ? ++ BINARY_FLAG : 0); ++ tmp_field->type=field_type; ++ tmp_field->length=max_length; ++ tmp_field->decimals=decimals; ++ if (unsigned_flag) ++ tmp_field->flags |= UNSIGNED_FLAG; ++} ++ ++void Item::make_field(Send_field *tmp_field) ++{ ++ init_make_field(tmp_field, field_type()); ++} ++ ++ ++void Item_empty_string::make_field(Send_field *tmp_field) ++{ ++ enum_field_types type= FIELD_TYPE_VAR_STRING; ++ if (max_length >= 16777216) ++ type= FIELD_TYPE_LONG_BLOB; ++ else if (max_length >= 65536) ++ type= FIELD_TYPE_MEDIUM_BLOB; ++ init_make_field(tmp_field, type); ++} ++ ++ ++enum_field_types Item::field_type() const ++{ ++ switch (result_type()) { ++ case STRING_RESULT: return MYSQL_TYPE_VARCHAR; ++ case INT_RESULT: return FIELD_TYPE_LONGLONG; ++ case DECIMAL_RESULT: return FIELD_TYPE_NEWDECIMAL; ++ case REAL_RESULT: return FIELD_TYPE_DOUBLE; ++ case ROW_RESULT: ++ default: ++ DBUG_ASSERT(0); ++ return MYSQL_TYPE_VARCHAR; ++ } ++} ++ ++ ++/* ++ Create a field to hold a string value from an item ++ ++ SYNOPSIS ++ make_string_field() ++ table Table for which the field is created ++ ++ IMPLEMENTATION ++ If max_length > CONVERT_IF_BIGGER_TO_BLOB create a blob ++ If max_length > 0 create a varchar ++ If max_length == 0 create a CHAR(0) ++*/ ++ ++ ++Field *Item::make_string_field(TABLE *table) ++{ ++ DBUG_ASSERT(collation.collation); ++ if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) ++ return new Field_blob(max_length, maybe_null, name, table, ++ collation.collation); ++ /* Item_type_holder holds the exact type, do not change it */ ++ if (max_length > 0 && ++ (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING)) ++ return new Field_varstring(max_length, maybe_null, name, table, ++ collation.collation); ++ return new Field_string(max_length, maybe_null, name, table, ++ collation.collation); ++} ++ ++ ++/* ++ Create a field based on field_type of argument ++ ++ For now, this is only used to create a field for ++ IFNULL(x,something) ++ ++ RETURN ++ 0 error ++ # Created field ++*/ ++ ++Field *Item::tmp_table_field_from_field_type(TABLE *table) ++{ ++ /* ++ The field functions defines a field to be not null if null_ptr is not 0 ++ */ ++ uchar *null_ptr= maybe_null ? (uchar*) "" : 0; ++ ++ switch (field_type()) { ++ case MYSQL_TYPE_DECIMAL: ++ case MYSQL_TYPE_NEWDECIMAL: ++ return new Field_new_decimal((char*) 0, max_length, null_ptr, 0, ++ Field::NONE, name, table, decimals, 0, ++ unsigned_flag); ++ case MYSQL_TYPE_TINY: ++ return new Field_tiny((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table, 0, unsigned_flag); ++ case MYSQL_TYPE_SHORT: ++ return new Field_short((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table, 0, unsigned_flag); ++ case MYSQL_TYPE_LONG: ++ return new Field_long((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table, 0, unsigned_flag); ++#ifdef HAVE_LONG_LONG ++ case MYSQL_TYPE_LONGLONG: ++ return new Field_longlong((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table, 0, unsigned_flag); ++#endif ++ case MYSQL_TYPE_FLOAT: ++ return new Field_float((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table, decimals, 0, unsigned_flag); ++ case MYSQL_TYPE_DOUBLE: ++ return new Field_double((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table, decimals, 0, unsigned_flag); ++ case MYSQL_TYPE_NULL: ++ return new Field_null((char*) 0, max_length, Field::NONE, ++ name, table, &my_charset_bin); ++ case MYSQL_TYPE_INT24: ++ return new Field_medium((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table, 0, unsigned_flag); ++ case MYSQL_TYPE_NEWDATE: ++ case MYSQL_TYPE_DATE: ++ return new Field_date(maybe_null, name, table, &my_charset_bin); ++ case MYSQL_TYPE_TIME: ++ return new Field_time(maybe_null, name, table, &my_charset_bin); ++ case MYSQL_TYPE_TIMESTAMP: ++ return new Field_timestamp(maybe_null, name, table, &my_charset_bin); ++ case MYSQL_TYPE_DATETIME: ++ return new Field_datetime(maybe_null, name, table, &my_charset_bin); ++ case MYSQL_TYPE_YEAR: ++ return new Field_year((char*) 0, max_length, null_ptr, 0, Field::NONE, ++ name, table); ++ case MYSQL_TYPE_BIT: ++ return new Field_bit_as_char(NULL, max_length, null_ptr, 0, ++ Field::NONE, name, table); ++ default: ++ /* This case should never be chosen */ ++ DBUG_ASSERT(0); ++ /* If something goes awfully wrong, it's better to get a string than die */ ++ case MYSQL_TYPE_ENUM: ++ case MYSQL_TYPE_SET: ++ case MYSQL_TYPE_STRING: ++ case MYSQL_TYPE_VAR_STRING: ++ case MYSQL_TYPE_VARCHAR: ++ return make_string_field(table); ++ case MYSQL_TYPE_TINY_BLOB: ++ case MYSQL_TYPE_MEDIUM_BLOB: ++ case MYSQL_TYPE_LONG_BLOB: ++ case MYSQL_TYPE_BLOB: ++ case MYSQL_TYPE_GEOMETRY: ++ if (this->type() == Item::TYPE_HOLDER) ++ return new Field_blob(max_length, maybe_null, name, table, ++ collation.collation, 1); ++ else ++ return new Field_blob(max_length, maybe_null, name, table, ++ collation.collation); ++ break; // Blob handled outside of case ++ } ++} ++ ++ ++/* ARGSUSED */ ++void Item_field::make_field(Send_field *tmp_field) ++{ ++ field->make_field(tmp_field); ++ DBUG_ASSERT(tmp_field->table_name != 0); ++ if (name) ++ tmp_field->col_name=name; // Use user supplied name ++ if (table_name) ++ tmp_field->table_name= table_name; ++ if (db_name) ++ tmp_field->db_name= db_name; ++} ++ ++ ++/* ++ Set a field:s value from a item ++*/ ++ ++void Item_field::save_org_in_field(Field *to) ++{ ++ if (field->is_null()) ++ { ++ null_value=1; ++ set_field_to_null_with_conversions(to, 1); ++ } ++ else ++ { ++ to->set_notnull(); ++ field_conv(to,field); ++ null_value=0; ++ } ++} ++ ++int Item_field::save_in_field(Field *to, bool no_conversions) ++{ ++ if (result_field->is_null()) ++ { ++ null_value=1; ++ return set_field_to_null_with_conversions(to, no_conversions); ++ } ++ else ++ { ++ to->set_notnull(); ++ field_conv(to,result_field); ++ null_value=0; ++ } ++ return 0; ++} ++ ++ ++/* ++ Store null in field ++ ++ SYNOPSIS ++ save_in_field() ++ field Field where we want to store NULL ++ ++ DESCRIPTION ++ This is used on INSERT. ++ Allow NULL to be inserted in timestamp and auto_increment values ++ ++ RETURN VALUES ++ 0 ok ++ 1 Field doesn't support NULL values and can't handle 'field = NULL' ++*/ ++ ++int Item_null::save_in_field(Field *field, bool no_conversions) ++{ ++ return set_field_to_null_with_conversions(field, no_conversions); ++} ++ ++ ++/* ++ Store null in field ++ ++ SYNOPSIS ++ save_safe_in_field() ++ field Field where we want to store NULL ++ ++ RETURN VALUES ++ 0 OK ++ 1 Field doesn't support NULL values ++*/ ++ ++int Item_null::save_safe_in_field(Field *field) ++{ ++ return set_field_to_null(field); ++} ++ ++ ++int Item::save_in_field(Field *field, bool no_conversions) ++{ ++ int error; ++ if (result_type() == STRING_RESULT || ++ result_type() == REAL_RESULT && ++ field->result_type() == STRING_RESULT) ++ { ++ String *result; ++ CHARSET_INFO *cs= collation.collation; ++ char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns ++ str_value.set_quick(buff, sizeof(buff), cs); ++ result=val_str(&str_value); ++ if (null_value) ++ { ++ str_value.set_quick(0, 0, cs); ++ return set_field_to_null_with_conversions(field, no_conversions); ++ } ++ ++ /* NOTE: If null_value == FALSE, "result" must be not NULL. */ ++ ++ field->set_notnull(); ++ error=field->store(result->ptr(),result->length(),cs); ++ str_value.set_quick(0, 0, cs); ++ } ++ else if (result_type() == REAL_RESULT) ++ { ++ double nr= val_real(); ++ if (null_value) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ error=field->store(nr); ++ } ++ else if (result_type() == DECIMAL_RESULT) ++ { ++ my_decimal decimal_value; ++ my_decimal *value= val_decimal(&decimal_value); ++ if (null_value) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ error=field->store_decimal(value); ++ } ++ else ++ { ++ longlong nr=val_int(); ++ if (null_value) ++ return set_field_to_null_with_conversions(field, no_conversions); ++ field->set_notnull(); ++ error=field->store(nr, unsigned_flag); ++ } ++ return error; ++} ++ ++ ++int Item_string::save_in_field(Field *field, bool no_conversions) ++{ ++ String *result; ++ result=val_str(&str_value); ++ if (null_value) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ return field->store(result->ptr(),result->length(),collation.collation); ++} ++ ++ ++int Item_uint::save_in_field(Field *field, bool no_conversions) ++{ ++ /* Item_int::save_in_field handles both signed and unsigned. */ ++ return Item_int::save_in_field(field, no_conversions); ++} ++ ++ ++int Item_int::save_in_field(Field *field, bool no_conversions) ++{ ++ longlong nr=val_int(); ++ if (null_value) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ return field->store(nr, unsigned_flag); ++} ++ ++ ++int Item_decimal::save_in_field(Field *field, bool no_conversions) ++{ ++ field->set_notnull(); ++ return field->store_decimal(&decimal_value); ++} ++ ++ ++bool Item_int::eq(const Item *arg, bool binary_cmp) const ++{ ++ /* No need to check for null value as basic constant can't be NULL */ ++ if (arg->basic_const_item() && arg->type() == type()) ++ { ++ /* ++ We need to cast off const to call val_int(). This should be OK for ++ a basic constant. ++ */ ++ Item *item= (Item*) arg; ++ return item->val_int() == value && item->unsigned_flag == unsigned_flag; ++ } ++ return FALSE; ++} ++ ++ ++Item *Item_int_with_ref::new_item() ++{ ++ DBUG_ASSERT(ref->const_item()); ++ /* ++ We need to evaluate the constant to make sure it works with ++ parameter markers. ++ */ ++ return (ref->unsigned_flag ? ++ new Item_uint(ref->name, ref->val_int(), ref->max_length) : ++ new Item_int(ref->name, ref->val_int(), ref->max_length)); ++} ++ ++ ++Item_num *Item_uint::neg() ++{ ++ Item_decimal *item= new Item_decimal(value, 1); ++ return item->neg(); ++} ++ ++ ++static uint nr_of_decimals(const char *str, const char *end) ++{ ++ const char *decimal_point; ++ ++ /* Find position for '.' */ ++ for (;;) ++ { ++ if (str == end) ++ return 0; ++ if (*str == 'e' || *str == 'E') ++ return NOT_FIXED_DEC; ++ if (*str++ == '.') ++ break; ++ } ++ decimal_point= str; ++ for (; my_isdigit(system_charset_info, *str) ; str++) ++ ; ++ if (*str == 'e' || *str == 'E') ++ return NOT_FIXED_DEC; ++ return (uint) (str - decimal_point); ++} ++ ++ ++/* ++ This function is only called during parsing. We will signal an error if ++ value is not a true double value (overflow) ++*/ ++ ++Item_float::Item_float(const char *str_arg, uint length) ++{ ++ int error; ++ char *end_not_used; ++ value= my_strntod(&my_charset_bin, (char*) str_arg, length, &end_not_used, ++ &error); ++ if (error) ++ { ++ /* ++ Note that we depend on that str_arg is null terminated, which is true ++ when we are in the parser ++ */ ++ DBUG_ASSERT(str_arg[length] == 0); ++ my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", (char*) str_arg); ++ } ++ presentation= name=(char*) str_arg; ++ decimals=(uint8) nr_of_decimals(str_arg, str_arg+length); ++ max_length=length; ++ fixed= 1; ++} ++ ++ ++int Item_float::save_in_field(Field *field, bool no_conversions) ++{ ++ double nr= val_real(); ++ if (null_value) ++ return set_field_to_null(field); ++ field->set_notnull(); ++ return field->store(nr); ++} ++ ++ ++void Item_float::print(String *str) ++{ ++ if (presentation) ++ { ++ str->append(presentation); ++ return; ++ } ++ char buffer[20]; ++ String num(buffer, sizeof(buffer), &my_charset_bin); ++ num.set(value, decimals, &my_charset_bin); ++ str->append(num); ++} ++ ++ ++/* ++ hex item ++ In string context this is a binary string. ++ In number context this is a longlong value. ++*/ ++ ++bool Item_float::eq(const Item *arg, bool binary_cmp) const ++{ ++ if (arg->basic_const_item() && arg->type() == type()) ++ { ++ /* ++ We need to cast off const to call val_int(). This should be OK for ++ a basic constant. ++ */ ++ Item *item= (Item*) arg; ++ return item->val_real() == value; ++ } ++ return FALSE; ++} ++ ++ ++inline uint char_val(char X) ++{ ++ return (uint) (X >= '0' && X <= '9' ? X-'0' : ++ X >= 'A' && X <= 'Z' ? X-'A'+10 : ++ X-'a'+10); ++} ++ ++ ++Item_hex_string::Item_hex_string(const char *str, uint str_length) ++{ ++ name=(char*) str-2; // Lex makes this start with 0x ++ max_length=(str_length+1)/2; ++ char *ptr=(char*) sql_alloc(max_length+1); ++ if (!ptr) ++ return; ++ str_value.set(ptr,max_length,&my_charset_bin); ++ char *end=ptr+max_length; ++ if (max_length*2 != str_length) ++ *ptr++=char_val(*str++); // Not even, assume 0 prefix ++ while (ptr != end) ++ { ++ *ptr++= (char) (char_val(str[0])*16+char_val(str[1])); ++ str+=2; ++ } ++ *ptr=0; // Keep purify happy ++ collation.set(&my_charset_bin, DERIVATION_COERCIBLE); ++ fixed= 1; ++ unsigned_flag= 1; ++} ++ ++longlong Item_hex_string::val_int() ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ char *end=(char*) str_value.ptr()+str_value.length(), ++ *ptr=end-min(str_value.length(),sizeof(longlong)); ++ ++ ulonglong value=0; ++ for (; ptr != end ; ptr++) ++ value=(value << 8)+ (ulonglong) (uchar) *ptr; ++ return (longlong) value; ++} ++ ++ ++my_decimal *Item_hex_string::val_decimal(my_decimal *decimal_value) ++{ ++ // following assert is redundant, because fixed=1 assigned in constructor ++ DBUG_ASSERT(fixed == 1); ++ ulonglong value= (ulonglong)val_int(); ++ int2my_decimal(E_DEC_FATAL_ERROR, value, TRUE, decimal_value); ++ return (decimal_value); ++} ++ ++ ++int Item_hex_string::save_in_field(Field *field, bool no_conversions) ++{ ++ int error; ++ field->set_notnull(); ++ if (field->result_type() == STRING_RESULT) ++ { ++ error=field->store(str_value.ptr(),str_value.length(),collation.collation); ++ } ++ else ++ { ++ longlong nr=val_int(); ++ error=field->store(nr, TRUE); // Assume hex numbers are unsigned ++ } ++ return error; ++} ++ ++ ++bool Item_hex_string::eq(const Item *arg, bool binary_cmp) const ++{ ++ if (arg->basic_const_item() && arg->type() == type()) ++ { ++ if (binary_cmp) ++ return !stringcmp(&str_value, &arg->str_value); ++ return !sortcmp(&str_value, &arg->str_value, collation.collation); ++ } ++ return FALSE; ++} ++ ++ ++Item *Item_hex_string::safe_charset_converter(CHARSET_INFO *tocs) ++{ ++ Item_string *conv; ++ String tmp, *str= val_str(&tmp); ++ ++ if (!(conv= new Item_string(str->ptr(), str->length(), tocs))) ++ return NULL; ++ conv->str_value.copy(); ++ conv->str_value.mark_as_const(); ++ return conv; ++} ++ ++ ++/* ++ bin item. ++ In string context this is a binary string. ++ In number context this is a longlong value. ++*/ ++ ++Item_bin_string::Item_bin_string(const char *str, uint str_length) ++{ ++ const char *end= str + str_length - 1; ++ uchar bits= 0; ++ uint power= 1; ++ ++ name= (char*) str - 2; ++ max_length= (str_length + 7) >> 3; ++ char *ptr= (char*) sql_alloc(max_length + 1); ++ if (!ptr) ++ return; ++ str_value.set(ptr, max_length, &my_charset_bin); ++ ptr+= max_length - 1; ++ ptr[1]= 0; // Set end null for string ++ for (; end >= str; end--) ++ { ++ if (power == 256) ++ { ++ power= 1; ++ *ptr--= bits; ++ bits= 0; ++ } ++ if (*end == '1') ++ bits|= power; ++ power<<= 1; ++ } ++ *ptr= (char) bits; ++ collation.set(&my_charset_bin, DERIVATION_COERCIBLE); ++ fixed= 1; ++} ++ ++ ++/* ++ Pack data in buffer for sending ++*/ ++ ++bool Item_null::send(Protocol *protocol, String *packet) ++{ ++ return protocol->store_null(); ++} ++ ++/* ++ This is only called from items that is not of type item_field ++*/ ++ ++bool Item::send(Protocol *protocol, String *buffer) ++{ ++ bool result; ++ enum_field_types type; ++ LINT_INIT(result); // Will be set if null_value == 0 ++ ++ switch ((type=field_type())) { ++ default: ++ case MYSQL_TYPE_NULL: ++ case MYSQL_TYPE_DECIMAL: ++ case MYSQL_TYPE_ENUM: ++ case MYSQL_TYPE_SET: ++ case MYSQL_TYPE_TINY_BLOB: ++ case MYSQL_TYPE_MEDIUM_BLOB: ++ case MYSQL_TYPE_LONG_BLOB: ++ case MYSQL_TYPE_BLOB: ++ case MYSQL_TYPE_GEOMETRY: ++ case MYSQL_TYPE_STRING: ++ case MYSQL_TYPE_VAR_STRING: ++ case MYSQL_TYPE_VARCHAR: ++ case MYSQL_TYPE_BIT: ++ case MYSQL_TYPE_NEWDECIMAL: ++ { ++ String *res; ++ if ((res=val_str(buffer))) ++ result= protocol->store(res->ptr(),res->length(),res->charset()); ++ break; ++ } ++ case MYSQL_TYPE_TINY: ++ { ++ longlong nr; ++ nr= val_int(); ++ if (!null_value) ++ result= protocol->store_tiny(nr); ++ break; ++ } ++ case MYSQL_TYPE_SHORT: ++ case MYSQL_TYPE_YEAR: ++ { ++ longlong nr; ++ nr= val_int(); ++ if (!null_value) ++ result= protocol->store_short(nr); ++ break; ++ } ++ case MYSQL_TYPE_INT24: ++ case MYSQL_TYPE_LONG: ++ { ++ longlong nr; ++ nr= val_int(); ++ if (!null_value) ++ result= protocol->store_long(nr); ++ break; ++ } ++ case MYSQL_TYPE_LONGLONG: ++ { ++ longlong nr; ++ nr= val_int(); ++ if (!null_value) ++ result= protocol->store_longlong(nr, unsigned_flag); ++ break; ++ } ++ case MYSQL_TYPE_FLOAT: ++ { ++ float nr; ++ nr= (float) val_real(); ++ if (!null_value) ++ result= protocol->store(nr, decimals, buffer); ++ break; ++ } ++ case MYSQL_TYPE_DOUBLE: ++ { ++ double nr= val_real(); ++ if (!null_value) ++ result= protocol->store(nr, decimals, buffer); ++ break; ++ } ++ case MYSQL_TYPE_DATETIME: ++ case MYSQL_TYPE_DATE: ++ case MYSQL_TYPE_TIMESTAMP: ++ { ++ TIME tm; ++ get_date(&tm, TIME_FUZZY_DATE); ++ if (!null_value) ++ { ++ if (type == MYSQL_TYPE_DATE) ++ return protocol->store_date(&tm); ++ else ++ result= protocol->store(&tm); ++ } ++ break; ++ } ++ case MYSQL_TYPE_TIME: ++ { ++ TIME tm; ++ get_time(&tm); ++ if (!null_value) ++ result= protocol->store_time(&tm); ++ break; ++ } ++ } ++ if (null_value) ++ result= protocol->store_null(); ++ return result; ++} ++ ++ ++bool Item_field::send(Protocol *protocol, String *buffer) ++{ ++ return protocol->store(result_field); ++} ++ ++ ++void Item_field::update_null_value() ++{ ++ /* ++ need to set no_errors to prevent warnings about type conversion ++ popping up. ++ */ ++ THD *thd= field->table->in_use; ++ int no_errors; ++ ++ no_errors= thd->no_errors; ++ thd->no_errors= 1; ++ Item::update_null_value(); ++ thd->no_errors= no_errors; ++} ++ ++ ++Item_ref::Item_ref(Name_resolution_context *context_arg, ++ Item **item, const char *table_name_arg, ++ const char *field_name_arg) ++ :Item_ident(context_arg, NullS, table_name_arg, field_name_arg), ++ result_field(0), ref(item) ++{ ++ /* ++ This constructor used to create some internals references over fixed items ++ */ ++ DBUG_ASSERT(ref != 0); ++ if (*ref && (*ref)->fixed) ++ set_properties(); ++} ++ ++ ++/* ++ Resolve the name of a reference to a column reference. ++ ++ SYNOPSIS ++ Item_ref::fix_fields() ++ thd [in] current thread ++ reference [in/out] view column if this item was resolved to a view column ++ ++ DESCRIPTION ++ The method resolves the column reference represented by 'this' as a column ++ present in one of: GROUP BY clause, SELECT clause, outer queries. It is ++ used typically for columns in the HAVING clause which are not under ++ aggregate functions. ++ ++ NOTES ++ The name resolution algorithm used is (where [T_j] is an optional table ++ name that qualifies the column name): ++ ++ resolve_extended([T_j].col_ref_i) ++ { ++ Search for a column or derived column named col_ref_i [in table T_j] ++ in the SELECT and GROUP clauses of Q. ++ ++ if such a column is NOT found AND // Lookup in outer queries. ++ there are outer queries ++ { ++ for each outer query Q_k beginning from the inner-most one ++ { ++ Search for a column or derived column named col_ref_i ++ [in table T_j] in the SELECT and GROUP clauses of Q_k. ++ ++ if such a column is not found AND ++ - Q_k is not a group query AND ++ - Q_k is not inside an aggregate function ++ OR ++ - Q_(k-1) is not in a HAVING or SELECT clause of Q_k ++ { ++ search for a column or derived column named col_ref_i ++ [in table T_j] in the FROM clause of Q_k; ++ } ++ } ++ } ++ } ++ ++ This procedure treats GROUP BY and SELECT clauses as one namespace for ++ column references in HAVING. Notice that compared to ++ Item_field::fix_fields, here we first search the SELECT and GROUP BY ++ clauses, and then we search the FROM clause. ++ ++ POSTCONDITION ++ Item_ref::ref is 0 or points to a valid item ++ ++ RETURN ++ TRUE if error ++ FALSE on success ++*/ ++ ++bool Item_ref::fix_fields(THD *thd, Item **reference) ++{ ++ enum_parsing_place place= NO_MATTER; ++ DBUG_ASSERT(fixed == 0); ++ SELECT_LEX *current_sel= thd->lex->current_select; ++ ++ if (!ref || ref == not_found_item) ++ { ++ if (!(ref= resolve_ref_in_select_and_group(thd, this, ++ context->select_lex))) ++ goto error; /* Some error occurred (e.g. ambiguous names). */ ++ ++ if (ref == not_found_item) /* This reference was not resolved. */ ++ { ++ Name_resolution_context *last_checked_context= context; ++ Name_resolution_context *outer_context= context->outer_context; ++ Field *from_field; ++ ref= 0; ++ ++ if (!outer_context) ++ { ++ /* The current reference cannot be resolved in this query. */ ++ my_error(ER_BAD_FIELD_ERROR,MYF(0), ++ this->full_name(), current_thd->where); ++ goto error; ++ } ++ ++ /* ++ If there is an outer context (select), and it is not a derived table ++ (which do not support the use of outer fields for now), try to ++ resolve this reference in the outer select(s). ++ ++ We treat each subselect as a separate namespace, so that different ++ subselects may contain columns with the same names. The subselects are ++ searched starting from the innermost. ++ */ ++ from_field= (Field*) not_found_field; ++ ++ do ++ { ++ SELECT_LEX *select= outer_context->select_lex; ++ Item_subselect *prev_subselect_item= ++ last_checked_context->select_lex->master_unit()->item; ++ last_checked_context= outer_context; ++ ++ /* Search in the SELECT and GROUP lists of the outer select. */ ++ if (outer_context->resolve_in_select_list) ++ { ++ if (!(ref= resolve_ref_in_select_and_group(thd, this, select))) ++ goto error; /* Some error occurred (e.g. ambiguous names). */ ++ if (ref != not_found_item) ++ { ++ DBUG_ASSERT(*ref && (*ref)->fixed); ++ prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); ++ prev_subselect_item->const_item_cache&= (*ref)->const_item(); ++ break; ++ } ++ /* ++ Set ref to 0 to ensure that we get an error in case we replaced ++ this item with another item and still use this item in some ++ other place of the parse tree. ++ */ ++ ref= 0; ++ } ++ ++ place= prev_subselect_item->parsing_place; ++ /* ++ Check table fields only if the subquery is used somewhere out of ++ HAVING or the outer SELECT does not use grouping (i.e. tables are ++ accessible). ++ TODO: ++ Here we could first find the field anyway, and then test this ++ condition, so that we can give a better error message - ++ ER_WRONG_FIELD_WITH_GROUP, instead of the less informative ++ ER_BAD_FIELD_ERROR which we produce now. ++ */ ++ if ((place != IN_HAVING || ++ (!select->with_sum_func && ++ select->group_list.elements == 0))) ++ { ++ /* ++ In case of view, find_field_in_tables() write pointer to view ++ field expression to 'reference', i.e. it substitute that ++ expression instead of this Item_ref ++ */ ++ from_field= find_field_in_tables(thd, this, ++ outer_context-> ++ first_name_resolution_table, ++ outer_context-> ++ last_name_resolution_table, ++ reference, ++ IGNORE_EXCEPT_NON_UNIQUE, ++ TRUE, TRUE); ++ if (! from_field) ++ goto error; ++ if (from_field == view_ref_found) ++ { ++ Item::Type type= (*reference)->type(); ++ prev_subselect_item->used_tables_cache|= ++ (*reference)->used_tables(); ++ prev_subselect_item->const_item_cache&= ++ (*reference)->const_item(); ++ DBUG_ASSERT((*reference)->type() == REF_ITEM); ++ mark_as_dependent(thd, last_checked_context->select_lex, ++ context->select_lex, this, ++ ((type == REF_ITEM || type == FIELD_ITEM) ? ++ (Item_ident*) (*reference) : ++ 0)); ++ /* ++ view reference found, we substituted it instead of this ++ Item, so can quit ++ */ ++ return FALSE; ++ } ++ if (from_field != not_found_field) ++ { ++ if (cached_table && cached_table->select_lex && ++ outer_context->select_lex && ++ cached_table->select_lex != outer_context->select_lex) ++ { ++ /* ++ Due to cache, find_field_in_tables() can return field which ++ doesn't belong to provided outer_context. In this case we have ++ to find proper field context in order to fix field correcly. ++ */ ++ do ++ { ++ outer_context= outer_context->outer_context; ++ select= outer_context->select_lex; ++ prev_subselect_item= ++ last_checked_context->select_lex->master_unit()->item; ++ last_checked_context= outer_context; ++ } while (outer_context && outer_context->select_lex && ++ cached_table->select_lex != outer_context->select_lex); ++ } ++ prev_subselect_item->used_tables_cache|= from_field->table->map; ++ prev_subselect_item->const_item_cache= 0; ++ break; ++ } ++ } ++ DBUG_ASSERT(from_field == not_found_field); ++ ++ /* Reference is not found => depend on outer (or just error). */ ++ prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; ++ prev_subselect_item->const_item_cache= 0; ++ ++ outer_context= outer_context->outer_context; ++ } while (outer_context); ++ ++ DBUG_ASSERT(from_field != 0 && from_field != view_ref_found); ++ if (from_field != not_found_field) ++ { ++ Item_field* fld; ++ if (!(fld= new Item_field(from_field))) ++ goto error; ++ thd->change_item_tree(reference, fld); ++ mark_as_dependent(thd, last_checked_context->select_lex, ++ thd->lex->current_select, this, fld); ++ return FALSE; ++ } ++ if (ref == 0) ++ { ++ /* The item was not a table field and not a reference */ ++ my_error(ER_BAD_FIELD_ERROR, MYF(0), ++ this->full_name(), current_thd->where); ++ goto error; ++ } ++ /* Should be checked in resolve_ref_in_select_and_group(). */ ++ DBUG_ASSERT(*ref && (*ref)->fixed); ++ mark_as_dependent(thd, last_checked_context->select_lex, ++ context->select_lex, this, this); ++ } ++ } ++ ++ DBUG_ASSERT(*ref); ++ /* ++ Check if this is an incorrect reference in a group function or forward ++ reference. Do not issue an error if this is an unnamed reference inside an ++ aggregate function. ++ */ ++ if (((*ref)->with_sum_func && name && ++ !(current_sel->linkage != GLOBAL_OPTIONS_TYPE && ++ current_sel->having_fix_field)) || ++ !(*ref)->fixed) ++ { ++ my_error(ER_ILLEGAL_REFERENCE, MYF(0), ++ name, ((*ref)->with_sum_func? ++ "reference to group function": ++ "forward reference in item list")); ++ goto error; ++ } ++ ++ set_properties(); ++ ++ if ((*ref)->check_cols(1)) ++ goto error; ++ return FALSE; ++ ++error: ++ context->process_error(thd); ++ return TRUE; ++} ++ ++ ++void Item_ref::set_properties() ++{ ++ max_length= (*ref)->max_length; ++ maybe_null= (*ref)->maybe_null; ++ decimals= (*ref)->decimals; ++ collation.set((*ref)->collation); ++ /* ++ We have to remember if we refer to a sum function, to ensure that ++ split_sum_func() doesn't try to change the reference. ++ */ ++ with_sum_func= (*ref)->with_sum_func; ++ unsigned_flag= (*ref)->unsigned_flag; ++ if ((*ref)->type() == FIELD_ITEM) ++ alias_name_used= ((Item_ident *) (*ref))->alias_name_used; ++ else ++ alias_name_used= TRUE; // it is not field, so it is was resolved by alias ++ fixed= 1; ++} ++ ++ ++void Item_ref::cleanup() ++{ ++ DBUG_ENTER("Item_ref::cleanup"); ++ Item_ident::cleanup(); ++ result_field= 0; ++ DBUG_VOID_RETURN; ++} ++ ++ ++void Item_ref::print(String *str) ++{ ++ if (ref) ++ { ++ if ((*ref)->type() != Item::CACHE_ITEM && ref_type() != VIEW_REF && ++ name && alias_name_used) ++ { ++ THD *thd= current_thd; ++ append_identifier(thd, str, name, (uint) strlen(name)); ++ } ++ else ++ (*ref)->print(str); ++ } ++ else ++ Item_ident::print(str); ++} ++ ++ ++bool Item_ref::send(Protocol *prot, String *tmp) ++{ ++ if (result_field) ++ return prot->store(result_field); ++ return (*ref)->send(prot, tmp); ++} ++ ++ ++double Item_ref::val_result() ++{ ++ if (result_field) ++ { ++ if ((null_value= result_field->is_null())) ++ return 0.0; ++ return result_field->val_real(); ++ } ++ return val_real(); ++} ++ ++ ++longlong Item_ref::val_int_result() ++{ ++ if (result_field) ++ { ++ if ((null_value= result_field->is_null())) ++ return 0; ++ return result_field->val_int(); ++ } ++ return val_int(); ++} ++ ++ ++String *Item_ref::str_result(String* str) ++{ ++ if (result_field) ++ { ++ if ((null_value= result_field->is_null())) ++ return 0; ++ str->set_charset(str_value.charset()); ++ return result_field->val_str(str, &str_value); ++ } ++ return val_str(str); ++} ++ ++ ++my_decimal *Item_ref::val_decimal_result(my_decimal *decimal_value) ++{ ++ if (result_field) ++ { ++ if ((null_value= result_field->is_null())) ++ return 0; ++ return result_field->val_decimal(decimal_value); ++ } ++ return val_decimal(decimal_value); ++} ++ ++ ++bool Item_ref::val_bool_result() ++{ ++ if (result_field) ++ { ++ if ((null_value= result_field->is_null())) ++ return 0; ++ switch (result_field->result_type()) { ++ case INT_RESULT: ++ return result_field->val_int() != 0; ++ case DECIMAL_RESULT: ++ { ++ my_decimal decimal_value; ++ my_decimal *val= result_field->val_decimal(&decimal_value); ++ if (val) ++ return !my_decimal_is_zero(val); ++ return 0; ++ } ++ case REAL_RESULT: ++ case STRING_RESULT: ++ return result_field->val_real() != 0.0; ++ case ROW_RESULT: ++ default: ++ DBUG_ASSERT(0); ++ } ++ } ++ return val_bool(); ++} ++ ++ ++double Item_ref::val_real() ++{ ++ DBUG_ASSERT(fixed); ++ double tmp=(*ref)->val_result(); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++longlong Item_ref::val_int() ++{ ++ DBUG_ASSERT(fixed); ++ longlong tmp=(*ref)->val_int_result(); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++bool Item_ref::val_bool() ++{ ++ DBUG_ASSERT(fixed); ++ bool tmp= (*ref)->val_bool_result(); ++ null_value= (*ref)->null_value; ++ return tmp; ++} ++ ++ ++String *Item_ref::val_str(String* tmp) ++{ ++ DBUG_ASSERT(fixed); ++ tmp=(*ref)->str_result(tmp); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++bool Item_ref::is_null() ++{ ++ DBUG_ASSERT(fixed); ++ return (*ref)->is_null(); ++} ++ ++ ++bool Item_ref::get_date(TIME *ltime,uint fuzzydate) ++{ ++ return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); ++} ++ ++ ++my_decimal *Item_ref::val_decimal(my_decimal *decimal_value) ++{ ++ my_decimal *val= (*ref)->val_decimal_result(decimal_value); ++ null_value= (*ref)->null_value; ++ return val; ++} ++ ++int Item_ref::save_in_field(Field *to, bool no_conversions) ++{ ++ int res; ++ if (result_field) ++ { ++ if (result_field->is_null()) ++ { ++ null_value= 1; ++ return set_field_to_null_with_conversions(to, no_conversions); ++ } ++ to->set_notnull(); ++ field_conv(to, result_field); ++ null_value= 0; ++ return 0; ++ } ++ res= (*ref)->save_in_field(to, no_conversions); ++ null_value= (*ref)->null_value; ++ return res; ++} ++ ++ ++void Item_ref::save_org_in_field(Field *field) ++{ ++ (*ref)->save_org_in_field(field); ++} ++ ++ ++void Item_ref::make_field(Send_field *field) ++{ ++ (*ref)->make_field(field); ++ /* Non-zero in case of a view */ ++ if (name) ++ field->col_name= name; ++ if (table_name) ++ field->table_name= table_name; ++ if (db_name) ++ field->db_name= db_name; ++} ++ ++ ++void Item_ref_null_helper::print(String *str) ++{ ++ str->append(STRING_WITH_LEN("(")); ++ if (ref) ++ (*ref)->print(str); ++ else ++ str->append('?'); ++ str->append(')'); ++} ++ ++ ++double Item_direct_ref::val_real() ++{ ++ double tmp=(*ref)->val_real(); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++longlong Item_direct_ref::val_int() ++{ ++ longlong tmp=(*ref)->val_int(); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++String *Item_direct_ref::val_str(String* tmp) ++{ ++ tmp=(*ref)->val_str(tmp); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++my_decimal *Item_direct_ref::val_decimal(my_decimal *decimal_value) ++{ ++ my_decimal *tmp= (*ref)->val_decimal(decimal_value); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++bool Item_direct_ref::val_bool() ++{ ++ bool tmp= (*ref)->val_bool(); ++ null_value=(*ref)->null_value; ++ return tmp; ++} ++ ++ ++bool Item_direct_ref::is_null() ++{ ++ return (*ref)->is_null(); ++} ++ ++ ++bool Item_direct_ref::get_date(TIME *ltime,uint fuzzydate) ++{ ++ return (null_value=(*ref)->get_date(ltime,fuzzydate)); ++} ++ ++ ++/* ++ Prepare referenced view viewld then call usual Item_direct_ref::fix_fields ++ ++ SYNOPSIS ++ Item_direct_view_ref::fix_fields() ++ thd thread handler ++ reference reference on reference where this item stored ++ ++ RETURN ++ FALSE OK ++ TRUE Error ++*/ ++ ++bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference) ++{ ++ /* view fild reference must be defined */ ++ DBUG_ASSERT(*ref); ++ /* (*ref)->check_cols() will be made in Item_direct_ref::fix_fields */ ++ if (!(*ref)->fixed && ++ ((*ref)->fix_fields(thd, ref))) ++ return TRUE; ++ return Item_direct_ref::fix_fields(thd, reference); ++} ++ ++/* ++ Compare two view column references for equality. ++ ++ SYNOPSIS ++ Item_direct_view_ref::eq() ++ item item to compare with ++ binary_cmp make binary comparison ++ ++ DESCRIPTION ++ A view column reference is considered equal to another column ++ reference if the second one is a view column and if both column ++ references resolve to the same item. It is assumed that both ++ items are of the same type. ++ ++ RETURN ++ TRUE Referenced item is equal to given item ++ FALSE otherwise ++*/ ++ ++ ++bool Item_direct_view_ref::eq(const Item *item, bool binary_cmp) const ++{ ++ if (item->type() == REF_ITEM) ++ { ++ Item_ref *item_ref= (Item_ref*) item; ++ if (item_ref->ref_type() == VIEW_REF) ++ { ++ Item *item_ref_ref= *(item_ref->ref); ++ DBUG_ASSERT((*ref)->real_item()->type() == ++ item_ref_ref->real_item()->type()); ++ return ((*ref)->real_item() == item_ref_ref->real_item()); ++ } ++ } ++ return FALSE; ++} ++ ++bool Item_default_value::eq(const Item *item, bool binary_cmp) const ++{ ++ return item->type() == DEFAULT_VALUE_ITEM && ++ ((Item_default_value *)item)->arg->eq(arg, binary_cmp); ++} ++ ++ ++bool Item_default_value::fix_fields(THD *thd, Item **items) ++{ ++ Item *real_arg; ++ Item_field *field_arg; ++ Field *def_field; ++ DBUG_ASSERT(fixed == 0); ++ ++ if (!arg) ++ { ++ fixed= 1; ++ return FALSE; ++ } ++ if (!arg->fixed && arg->fix_fields(thd, &arg)) ++ goto error; ++ ++ ++ real_arg= arg->real_item(); ++ if (real_arg->type() != FIELD_ITEM) ++ { ++ my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), arg->name); ++ goto error; ++ } ++ ++ field_arg= (Item_field *)real_arg; ++ if (field_arg->field->flags & NO_DEFAULT_VALUE_FLAG) ++ { ++ my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), field_arg->field->field_name); ++ goto error; ++ } ++ if (!(def_field= (Field*) sql_alloc(field_arg->field->size_of()))) ++ goto error; ++ memcpy(def_field, field_arg->field, field_arg->field->size_of()); ++ def_field->move_field(def_field->table->s->default_values - ++ def_field->table->record[0]); ++ set_field(def_field); ++ return FALSE; ++ ++error: ++ context->process_error(thd); ++ return TRUE; ++} ++ ++ ++void Item_default_value::print(String *str) ++{ ++ if (!arg) ++ { ++ str->append(STRING_WITH_LEN("default")); ++ return; ++ } ++ str->append(STRING_WITH_LEN("default(")); ++ arg->print(str); ++ str->append(')'); ++} ++ ++ ++int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) ++{ ++ if (!arg) ++ { ++ if (field_arg->flags & NO_DEFAULT_VALUE_FLAG) ++ { ++ if (context->error_processor == &view_error_processor) ++ { ++ TABLE_LIST *view= cached_table->top_table(); ++ push_warning_printf(field_arg->table->in_use, ++ MYSQL_ERROR::WARN_LEVEL_WARN, ++ ER_NO_DEFAULT_FOR_VIEW_FIELD, ++ ER(ER_NO_DEFAULT_FOR_VIEW_FIELD), ++ view->view_db.str, ++ view->view_name.str); ++ } ++ else ++ { ++ push_warning_printf(field_arg->table->in_use, ++ MYSQL_ERROR::WARN_LEVEL_WARN, ++ ER_NO_DEFAULT_FOR_FIELD, ++ ER(ER_NO_DEFAULT_FOR_FIELD), ++ field_arg->field_name); ++ } ++ field_arg->set_default(); ++ return 1; ++ } ++ field_arg->set_default(); ++ return 0; ++ } ++ return Item_field::save_in_field(field_arg, no_conversions); ++} ++ ++ ++/* ++ This method like the walk method traverses the item tree, but at the ++ same time it can replace some nodes in the tree ++*/ ++ ++Item *Item_default_value::transform(Item_transformer transformer, byte *args) ++{ ++ DBUG_ASSERT(!current_thd->is_stmt_prepare()); ++ ++ Item *new_item= arg->transform(transformer, args); ++ if (!new_item) ++ return 0; ++ ++ /* ++ THD::change_item_tree() should be called only if the tree was ++ really transformed, i.e. when a new item has been created. ++ Otherwise we'll be allocating a lot of unnecessary memory for ++ change records at each execution. ++ */ ++ if (arg != new_item) ++ current_thd->change_item_tree(&arg, new_item); ++ return (this->*transformer)(args); ++} ++ ++ ++bool Item_insert_value::eq(const Item *item, bool binary_cmp) const ++{ ++ return item->type() == INSERT_VALUE_ITEM && ++ ((Item_default_value *)item)->arg->eq(arg, binary_cmp); ++} ++ ++ ++bool Item_insert_value::fix_fields(THD *thd, Item **items) ++{ ++ DBUG_ASSERT(fixed == 0); ++ /* We should only check that arg is in first table */ ++ if (!arg->fixed) ++ { ++ bool res; ++ st_table_list *orig_next_table= context->last_name_resolution_table; ++ context->last_name_resolution_table= context->first_name_resolution_table; ++ res= arg->fix_fields(thd, &arg); ++ context->last_name_resolution_table= orig_next_table; ++ if (res) ++ return TRUE; ++ } ++ ++ if (arg->type() == REF_ITEM) ++ { ++ Item_ref *ref= (Item_ref *)arg; ++ if (ref->ref[0]->type() != FIELD_ITEM) ++ { ++ my_error(ER_BAD_FIELD_ERROR, MYF(0), "", "VALUES() function"); ++ return TRUE; ++ } ++ arg= ref->ref[0]; ++ } ++ /* ++ According to our SQL grammar, VALUES() function can reference ++ only to a column. ++ */ ++ DBUG_ASSERT(arg->type() == FIELD_ITEM); ++ ++ Item_field *field_arg= (Item_field *)arg; ++ ++ if (field_arg->field->table->insert_values) ++ { ++ Field *def_field= (Field*) sql_alloc(field_arg->field->size_of()); ++ if (!def_field) ++ return TRUE; ++ memcpy(def_field, field_arg->field, field_arg->field->size_of()); ++ def_field->move_field(def_field->table->insert_values - ++ def_field->table->record[0]); ++ set_field(def_field); ++ } ++ else ++ { ++ Field *tmp_field= field_arg->field; ++ /* charset doesn't matter here, it's to avoid sigsegv only */ ++ set_field(new Field_null(0, 0, Field::NONE, tmp_field->field_name, ++ tmp_field->table, &my_charset_bin)); ++ } ++ return FALSE; ++} ++ ++void Item_insert_value::print(String *str) ++{ ++ str->append(STRING_WITH_LEN("values(")); ++ arg->print(str); ++ str->append(')'); ++} ++ ++ ++/* ++ Find index of Field object which will be appropriate for item ++ representing field of row being changed in trigger. ++ ++ SYNOPSIS ++ setup_field() ++ thd - current thread context ++ table - table of trigger (and where we looking for fields) ++ table_grant_info - GRANT_INFO of the subject table ++ ++ NOTE ++ This function does almost the same as fix_fields() for Item_field ++ but is invoked right after trigger definition parsing. Since at ++ this stage we can't say exactly what Field object (corresponding ++ to TABLE::record[0] or TABLE::record[1]) should be bound to this ++ Item, we only find out index of the Field and then select concrete ++ Field object in fix_fields() (by that time Table_trigger_list::old_field/ ++ new_field should point to proper array of Fields). ++ It also binds Item_trigger_field to Table_triggers_list object for ++ table of trigger which uses this item. ++*/ ++ ++void Item_trigger_field::setup_field(THD *thd, TABLE *table, ++ GRANT_INFO *table_grant_info) ++{ ++ /* ++ There is no sense in marking fields used by trigger with current value ++ of THD::query_id since it is completely unrelated to the THD::query_id ++ value for statements which will invoke trigger. So instead we use ++ Table_triggers_list::mark_fields_used() method which is called during ++ execution of these statements. ++ */ ++ bool save_set_query_id= thd->set_query_id; ++ thd->set_query_id= 0; ++ /* ++ Try to find field by its name and if it will be found ++ set field_idx properly. ++ */ ++ (void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name), ++ 0, &field_idx); ++ thd->set_query_id= save_set_query_id; ++ triggers= table->triggers; ++ table_grants= table_grant_info; ++} ++ ++ ++bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const ++{ ++ return item->type() == TRIGGER_FIELD_ITEM && ++ row_version == ((Item_trigger_field *)item)->row_version && ++ !my_strcasecmp(system_charset_info, field_name, ++ ((Item_trigger_field *)item)->field_name); ++} ++ ++ ++void Item_trigger_field::set_required_privilege(bool rw) ++{ ++ /* ++ Require SELECT and UPDATE privilege if this field will be read and ++ set, and only UPDATE privilege for setting the field. ++ */ ++ want_privilege= (rw ? SELECT_ACL | UPDATE_ACL : UPDATE_ACL); ++} ++ ++ ++bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it) ++{ ++ Item *item= sp_prepare_func_item(thd, it); ++ ++ return (!item || (!fixed && fix_fields(thd, 0)) || ++ (item->save_in_field(field, 0) < 0)); ++} ++ ++ ++bool Item_trigger_field::fix_fields(THD *thd, Item **items) ++{ ++ /* ++ Since trigger is object tightly associated with TABLE object most ++ of its set up can be performed during trigger loading i.e. trigger ++ parsing! So we have little to do in fix_fields. :) ++ */ ++ ++ DBUG_ASSERT(fixed == 0); ++ ++ /* Set field. */ ++ ++ if (field_idx != (uint)-1) ++ { ++#ifndef NO_EMBEDDED_ACCESS_CHECKS ++ /* ++ Check access privileges for the subject table. We check privileges only ++ in runtime. ++ */ ++ ++ if (table_grants) ++ { ++ table_grants->want_privilege= want_privilege; ++ ++ if (check_grant_column(thd, table_grants, triggers->table->s->db, ++ triggers->table->s->table_name, field_name, ++ strlen(field_name), thd->security_ctx)) ++ return TRUE; ++ } ++#endif // NO_EMBEDDED_ACCESS_CHECKS ++ ++ field= (row_version == OLD_ROW) ? triggers->old_field[field_idx] : ++ triggers->new_field[field_idx]; ++ set_field(field); ++ fixed= 1; ++ return FALSE; ++ } ++ ++ my_error(ER_BAD_FIELD_ERROR, MYF(0), field_name, ++ (row_version == NEW_ROW) ? "NEW" : "OLD"); ++ return TRUE; ++} ++ ++ ++void Item_trigger_field::print(String *str) ++{ ++ str->append((row_version == NEW_ROW) ? "NEW" : "OLD", 3); ++ str->append('.'); ++ str->append(field_name); ++} ++ ++ ++void Item_trigger_field::cleanup() ++{ ++ want_privilege= original_privilege; ++ /* ++ Since special nature of Item_trigger_field we should not do most of ++ things from Item_field::cleanup() or Item_ident::cleanup() here. ++ */ ++ Item::cleanup(); ++} ++ ++ ++/* ++ If item is a const function, calculate it and return a const item ++ The original item is freed if not returned ++*/ ++ ++Item_result item_cmp_type(Item_result a,Item_result b) ++{ ++ if (a == STRING_RESULT && b == STRING_RESULT) ++ return STRING_RESULT; ++ if (a == INT_RESULT && b == INT_RESULT) ++ return INT_RESULT; ++ else if (a == ROW_RESULT || b == ROW_RESULT) ++ return ROW_RESULT; ++ if ((a == INT_RESULT || a == DECIMAL_RESULT) && ++ (b == INT_RESULT || b == DECIMAL_RESULT)) ++ return DECIMAL_RESULT; ++ return REAL_RESULT; ++} ++ ++ ++void resolve_const_item(THD *thd, Item **ref, Item *comp_item) ++{ ++ Item *item= *ref; ++ Item *new_item= NULL; ++ if (item->basic_const_item()) ++ return; // Can't be better ++ Item_result res_type=item_cmp_type(comp_item->result_type(), ++ item->result_type()); ++ char *name=item->name; // Alloced by sql_alloc ++ ++ switch (res_type) { ++ case STRING_RESULT: ++ { ++ char buff[MAX_FIELD_WIDTH]; ++ String tmp(buff,sizeof(buff),&my_charset_bin),*result; ++ result=item->val_str(&tmp); ++ if (item->null_value) ++ new_item= new Item_null(name); ++ else ++ { ++ uint length= result->length(); ++ char *tmp_str= sql_strmake(result->ptr(), length); ++ new_item= new Item_string(name, tmp_str, length, result->charset()); ++ } ++ break; ++ } ++ case INT_RESULT: ++ { ++ longlong result=item->val_int(); ++ uint length=item->max_length; ++ bool null_value=item->null_value; ++ new_item= (null_value ? (Item*) new Item_null(name) : ++ (Item*) new Item_int(name, result, length)); ++ break; ++ } ++ case ROW_RESULT: ++ if (item->type() == Item::ROW_ITEM && comp_item->type() == Item::ROW_ITEM) ++ { ++ /* ++ Substitute constants only in Item_rows. Don't affect other Items ++ with ROW_RESULT (eg Item_singlerow_subselect). ++ ++ For such Items more optimal is to detect if it is constant and replace ++ it with Item_row. This would optimize queries like this: ++ SELECT * FROM t1 WHERE (a,b) = (SELECT a,b FROM t2 LIMIT 1); ++ */ ++ Item_row *item_row= (Item_row*) item; ++ Item_row *comp_item_row= (Item_row*) comp_item; ++ uint col; ++ new_item= 0; ++ /* ++ If item and comp_item are both Item_rows and have same number of cols ++ then process items in Item_row one by one. ++ We can't ignore NULL values here as this item may be used with <=>, in ++ which case NULL's are significant. ++ */ ++ DBUG_ASSERT(item->result_type() == comp_item->result_type()); ++ DBUG_ASSERT(item_row->cols() == comp_item_row->cols()); ++ col= item_row->cols(); ++ while (col-- > 0) ++ resolve_const_item(thd, item_row->addr(col), comp_item_row->el(col)); ++ break; ++ } ++ /* Fallthrough */ ++ case REAL_RESULT: ++ { // It must REAL_RESULT ++ double result= item->val_real(); ++ uint length=item->max_length,decimals=item->decimals; ++ bool null_value=item->null_value; ++ new_item= (null_value ? (Item*) new Item_null(name) : (Item*) ++ new Item_float(name, result, decimals, length)); ++ break; ++ } ++ case DECIMAL_RESULT: ++ { ++ my_decimal decimal_value; ++ my_decimal *result= item->val_decimal(&decimal_value); ++ uint length= item->max_length, decimals= item->decimals; ++ bool null_value= item->null_value; ++ new_item= (null_value ? ++ (Item*) new Item_null(name) : ++ (Item*) new Item_decimal(name, result, length, decimals)); ++ break; ++ } ++ default: ++ DBUG_ASSERT(0); ++ } ++ if (new_item) ++ thd->change_item_tree(ref, new_item); ++} ++ ++/* ++ Return true if the value stored in the field is equal to the const item ++ We need to use this on the range optimizer because in some cases ++ we can't store the value in the field without some precision/character loss. ++*/ ++ ++bool field_is_equal_to_item(Field *field,Item *item) ++{ ++ ++ Item_result res_type=item_cmp_type(field->result_type(), ++ item->result_type()); ++ if (res_type == STRING_RESULT) ++ { ++ char item_buff[MAX_FIELD_WIDTH]; ++ char field_buff[MAX_FIELD_WIDTH]; ++ String item_tmp(item_buff,sizeof(item_buff),&my_charset_bin),*item_result; ++ String field_tmp(field_buff,sizeof(field_buff),&my_charset_bin); ++ item_result=item->val_str(&item_tmp); ++ if (item->null_value) ++ return 1; // This must be true ++ field->val_str(&field_tmp); ++ return !stringcmp(&field_tmp,item_result); ++ } ++ if (res_type == INT_RESULT) ++ return 1; // Both where of type int ++ if (res_type == DECIMAL_RESULT) ++ { ++ my_decimal item_buf, *item_val, ++ field_buf, *field_val; ++ item_val= item->val_decimal(&item_buf); ++ if (item->null_value) ++ return 1; // This must be true ++ field_val= field->val_decimal(&field_buf); ++ return !my_decimal_cmp(item_val, field_val); ++ } ++ double result= item->val_real(); ++ if (item->null_value) ++ return 1; ++ return result == field->val_real(); ++} ++ ++Item_cache* Item_cache::get_cache(Item_result type) ++{ ++ switch (type) { ++ case INT_RESULT: ++ return new Item_cache_int(); ++ case REAL_RESULT: ++ return new Item_cache_real(); ++ case DECIMAL_RESULT: ++ return new Item_cache_decimal(); ++ case STRING_RESULT: ++ return new Item_cache_str(); ++ case ROW_RESULT: ++ return new Item_cache_row(); ++ default: ++ // should never be in real life ++ DBUG_ASSERT(0); ++ return 0; ++ } ++} ++ ++ ++void Item_cache::print(String *str) ++{ ++ str->append(STRING_WITH_LEN("(")); ++ if (example) ++ example->print(str); ++ else ++ Item::print(str); ++ str->append(')'); ++} ++ ++ ++void Item_cache_int::store(Item *item) ++{ ++ value= item->val_int_result(); ++ null_value= item->null_value; ++ unsigned_flag= item->unsigned_flag; ++} ++ ++ ++String *Item_cache_int::val_str(String *str) ++{ ++ DBUG_ASSERT(fixed == 1); ++ str->set(value, default_charset()); ++ return str; ++} ++ ++ ++my_decimal *Item_cache_int::val_decimal(my_decimal *decimal_val) ++{ ++ DBUG_ASSERT(fixed == 1); ++ int2my_decimal(E_DEC_FATAL_ERROR, value, unsigned_flag, decimal_val); ++ return decimal_val; ++} ++ ++ ++void Item_cache_real::store(Item *item) ++{ ++ value= item->val_result(); ++ null_value= item->null_value; ++} ++ ++ ++longlong Item_cache_real::val_int() ++{ ++ DBUG_ASSERT(fixed == 1); ++ return (longlong) rint(value); ++} ++ ++ ++String* Item_cache_real::val_str(String *str) ++{ ++ DBUG_ASSERT(fixed == 1); ++ str->set(value, decimals, default_charset()); ++ return str; ++} ++ ++ ++my_decimal *Item_cache_real::val_decimal(my_decimal *decimal_val) ++{ ++ DBUG_ASSERT(fixed == 1); ++ double2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); ++ return decimal_val; ++} ++ ++ ++void Item_cache_decimal::store(Item *item) ++{ ++ my_decimal *val= item->val_decimal_result(&decimal_value); ++ if (!(null_value= item->null_value) && val != &decimal_value) ++ my_decimal2decimal(val, &decimal_value); ++} ++ ++double Item_cache_decimal::val_real() ++{ ++ DBUG_ASSERT(fixed); ++ double res; ++ my_decimal2double(E_DEC_FATAL_ERROR, &decimal_value, &res); ++ return res; ++} ++ ++longlong Item_cache_decimal::val_int() ++{ ++ DBUG_ASSERT(fixed); ++ longlong res; ++ my_decimal2int(E_DEC_FATAL_ERROR, &decimal_value, unsigned_flag, &res); ++ return res; ++} ++ ++String* Item_cache_decimal::val_str(String *str) ++{ ++ DBUG_ASSERT(fixed); ++ my_decimal_round(E_DEC_FATAL_ERROR, &decimal_value, decimals, FALSE, ++ &decimal_value); ++ my_decimal2string(E_DEC_FATAL_ERROR, &decimal_value, 0, 0, 0, str); ++ return str; ++} ++ ++my_decimal *Item_cache_decimal::val_decimal(my_decimal *val) ++{ ++ DBUG_ASSERT(fixed); ++ return &decimal_value; ++} ++ ++ ++void Item_cache_str::store(Item *item) ++{ ++ value_buff.set(buffer, sizeof(buffer), item->collation.collation); ++ value= item->str_result(&value_buff); ++ if ((null_value= item->null_value)) ++ value= 0; ++ else if (value != &value_buff) ++ { ++ /* ++ We copy string value to avoid changing value if 'item' is table field ++ in queries like following (where t1.c is varchar): ++ select a, ++ (select a,b,c from t1 where t1.a=t2.a) = ROW(a,2,'a'), ++ (select c from t1 where a=t2.a) ++ from t2; ++ */ ++ value_buff.copy(*value); ++ value= &value_buff; ++ } ++} ++ ++double Item_cache_str::val_real() ++{ ++ DBUG_ASSERT(fixed == 1); ++ int err_not_used; ++ char *end_not_used; ++ if (value) ++ return my_strntod(value->charset(), (char*) value->ptr(), ++ value->length(), &end_not_used, &err_not_used); ++ return (double) 0; ++} ++ ++ ++longlong Item_cache_str::val_int() ++{ ++ DBUG_ASSERT(fixed == 1); ++ int err; ++ if (value) ++ return my_strntoll(value->charset(), value->ptr(), ++ value->length(), 10, (char**) 0, &err); ++ else ++ return (longlong)0; ++} ++ ++my_decimal *Item_cache_str::val_decimal(my_decimal *decimal_val) ++{ ++ DBUG_ASSERT(fixed == 1); ++ if (value) ++ string2my_decimal(E_DEC_FATAL_ERROR, value, decimal_val); ++ else ++ decimal_val= 0; ++ return decimal_val; ++} ++ ++ ++bool Item_cache_row::allocate(uint num) ++{ ++ item_count= num; ++ THD *thd= current_thd; ++ return (!(values= ++ (Item_cache **) thd->calloc(sizeof(Item_cache *)*item_count))); ++} ++ ++ ++bool Item_cache_row::setup(Item * item) ++{ ++ example= item; ++ if (!values && allocate(item->cols())) ++ return 1; ++ for (uint i= 0; i < item_count; i++) ++ { ++ Item *el= item->el(i); ++ Item_cache *tmp; ++ if (!(tmp= values[i]= Item_cache::get_cache(el->result_type()))) ++ return 1; ++ tmp->setup(el); ++ } ++ return 0; ++} ++ ++ ++void Item_cache_row::store(Item * item) ++{ ++ null_value= 0; ++ item->bring_value(); ++ for (uint i= 0; i < item_count; i++) ++ { ++ values[i]->store(item->el(i)); ++ null_value|= values[i]->null_value; ++ } ++} ++ ++ ++void Item_cache_row::illegal_method_call(const char *method) ++{ ++ DBUG_ENTER("Item_cache_row::illegal_method_call"); ++ DBUG_PRINT("error", ("!!! %s method was called for row item", method)); ++ DBUG_ASSERT(0); ++ my_error(ER_OPERAND_COLUMNS, MYF(0), 1); ++ DBUG_VOID_RETURN; ++} ++ ++ ++bool Item_cache_row::check_cols(uint c) ++{ ++ if (c != item_count) ++ { ++ my_error(ER_OPERAND_COLUMNS, MYF(0), c); ++ return 1; ++ } ++ return 0; ++} ++ ++ ++bool Item_cache_row::null_inside() ++{ ++ for (uint i= 0; i < item_count; i++) ++ { ++ if (values[i]->cols() > 1) ++ { ++ if (values[i]->null_inside()) ++ return 1; ++ } ++ else ++ { ++ values[i]->update_null_value(); ++ if (values[i]->null_value) ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++ ++void Item_cache_row::bring_value() ++{ ++ for (uint i= 0; i < item_count; i++) ++ values[i]->bring_value(); ++ return; ++} ++ ++ ++Item_type_holder::Item_type_holder(THD *thd, Item *item) ++ :Item(thd, item), enum_set_typelib(0), fld_type(get_real_type(item)) ++{ ++ DBUG_ASSERT(item->fixed); ++ ++ max_length= display_length(item); ++ maybe_null= item->maybe_null; ++ collation.set(item->collation); ++ get_full_info(item); ++ /* fix variable decimals which always is NOT_FIXED_DEC */ ++ if (Field::result_merge_type(fld_type) == INT_RESULT) ++ decimals= 0; ++ prev_decimal_int_part= item->decimal_int_part(); ++} ++ ++ ++/* ++ Return expression type of Item_type_holder ++ ++ SYNOPSIS ++ Item_type_holder::result_type() ++ ++ RETURN ++ Item_result (type of internal MySQL expression result) ++*/ ++ ++Item_result Item_type_holder::result_type() const ++{ ++ return Field::result_merge_type(fld_type); ++} ++ ++ ++/* ++ Find real field type of item ++ ++ SYNOPSIS ++ Item_type_holder::get_real_type() ++ ++ RETURN ++ type of field which should be created to store item value ++*/ ++ ++enum_field_types Item_type_holder::get_real_type(Item *item) ++{ ++ switch(item->type()) ++ { ++ case FIELD_ITEM: ++ { ++ /* ++ Item_fields::field_type ask Field_type() but sometimes field return ++ a different type, like for enum/set, so we need to ask real type. ++ */ ++ Field *field= ((Item_field *) item)->field; ++ enum_field_types type= field->real_type(); ++ /* work around about varchar type field detection */ ++ if (type == MYSQL_TYPE_STRING && field->type() == MYSQL_TYPE_VAR_STRING) ++ return MYSQL_TYPE_VAR_STRING; ++ return type; ++ } ++ case SUM_FUNC_ITEM: ++ { ++ /* ++ Argument of aggregate function sometimes should be asked about field ++ type ++ */ ++ Item_sum *item_sum= (Item_sum *) item; ++ if (item_sum->keep_field_type()) ++ return get_real_type(item_sum->args[0]); ++ break; ++ } ++ case FUNC_ITEM: ++ if (((Item_func *) item)->functype() == Item_func::GUSERVAR_FUNC) ++ { ++ /* ++ There are work around of problem with changing variable type on the ++ fly and variable always report "string" as field type to get ++ acceptable information for client in send_field, so we make field ++ type from expression type. ++ */ ++ switch (item->result_type()) { ++ case STRING_RESULT: ++ return MYSQL_TYPE_VAR_STRING; ++ case INT_RESULT: ++ return MYSQL_TYPE_LONGLONG; ++ case REAL_RESULT: ++ return MYSQL_TYPE_DOUBLE; ++ case DECIMAL_RESULT: ++ return MYSQL_TYPE_NEWDECIMAL; ++ case ROW_RESULT: ++ default: ++ DBUG_ASSERT(0); ++ return MYSQL_TYPE_VAR_STRING; ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ return item->field_type(); ++} ++ ++/* ++ Find field type which can carry current Item_type_holder type and ++ type of given Item. ++ ++ SYNOPSIS ++ Item_type_holder::join_types() ++ thd thread handler ++ item given item to join its parameters with this item ones ++ ++ RETURN ++ TRUE error - types are incompatible ++ FALSE OK ++*/ ++ ++bool Item_type_holder::join_types(THD *thd, Item *item) ++{ ++ uint max_length_orig= max_length; ++ uint decimals_orig= decimals; ++ DBUG_ENTER("Item_type_holder::join_types"); ++ DBUG_PRINT("info:", ("was type %d len %d, dec %d name %s", ++ fld_type, max_length, decimals, ++ (name ? name : ""))); ++ DBUG_PRINT("info:", ("in type %d len %d, dec %d", ++ get_real_type(item), ++ item->max_length, item->decimals)); ++ fld_type= Field::field_type_merge(fld_type, get_real_type(item)); ++ { ++ int item_decimals= item->decimals; ++ /* fix variable decimals which always is NOT_FIXED_DEC */ ++ if (Field::result_merge_type(fld_type) == INT_RESULT) ++ item_decimals= 0; ++ decimals= max(decimals, item_decimals); ++ } ++ if (Field::result_merge_type(fld_type) == DECIMAL_RESULT) ++ { ++ decimals= min(max(decimals, item->decimals), DECIMAL_MAX_SCALE); ++ int precision= min(max(prev_decimal_int_part, item->decimal_int_part()) ++ + decimals, DECIMAL_MAX_PRECISION); ++ unsigned_flag&= item->unsigned_flag; ++ max_length= my_decimal_precision_to_length(precision, decimals, ++ unsigned_flag); ++ } ++ ++ switch (Field::result_merge_type(fld_type)) ++ { ++ case STRING_RESULT: ++ { ++ const char *old_cs, *old_derivation; ++ uint32 old_max_chars= max_length / collation.collation->mbmaxlen; ++ old_cs= collation.collation->name; ++ old_derivation= collation.derivation_name(); ++ if (collation.aggregate(item->collation, MY_COLL_ALLOW_CONV)) ++ { ++ my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), ++ old_cs, old_derivation, ++ item->collation.collation->name, ++ item->collation.derivation_name(), ++ "UNION"); ++ DBUG_RETURN(TRUE); ++ } ++ /* ++ To figure out max_length, we have to take into account possible ++ expansion of the size of the values because of character set ++ conversions. ++ */ ++ max_length= max(old_max_chars * collation.collation->mbmaxlen, ++ display_length(item) / item->collation.collation->mbmaxlen * ++ collation.collation->mbmaxlen); ++ break; ++ } ++ case REAL_RESULT: ++ { ++ if (decimals != NOT_FIXED_DEC) ++ { ++ int delta1= max_length_orig - decimals_orig; ++ int delta2= item->max_length - item->decimals; ++ if (fld_type == MYSQL_TYPE_DECIMAL) ++ max_length= max(delta1, delta2) + decimals; ++ else ++ max_length= min(max(delta1, delta2) + decimals, ++ (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7); ++ } ++ else ++ max_length= (fld_type == MYSQL_TYPE_FLOAT) ? FLT_DIG+6 : DBL_DIG+7; ++ break; ++ } ++ default: ++ max_length= max(max_length, display_length(item)); ++ }; ++ maybe_null|= item->maybe_null; ++ get_full_info(item); ++ ++ /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */ ++ prev_decimal_int_part= decimal_int_part(); ++ DBUG_PRINT("info", ("become type: %d len: %u dec: %u", ++ (int) fld_type, max_length, (uint) decimals)); ++ DBUG_RETURN(FALSE); ++} ++ ++/* ++ Calculate lenth for merging result for given Item type ++ ++ SYNOPSIS ++ Item_type_holder::real_length() ++ item Item for lrngth detection ++ ++ RETURN ++ length ++*/ ++ ++uint32 Item_type_holder::display_length(Item *item) ++{ ++ if (item->type() == Item::FIELD_ITEM) ++ return ((Item_field *)item)->max_disp_length(); ++ ++ switch (item->field_type()) ++ { ++ case MYSQL_TYPE_DECIMAL: ++ case MYSQL_TYPE_TIMESTAMP: ++ case MYSQL_TYPE_DATE: ++ case MYSQL_TYPE_TIME: ++ case MYSQL_TYPE_DATETIME: ++ case MYSQL_TYPE_YEAR: ++ case MYSQL_TYPE_NEWDATE: ++ case MYSQL_TYPE_VARCHAR: ++ case MYSQL_TYPE_BIT: ++ case MYSQL_TYPE_NEWDECIMAL: ++ case MYSQL_TYPE_ENUM: ++ case MYSQL_TYPE_SET: ++ case MYSQL_TYPE_TINY_BLOB: ++ case MYSQL_TYPE_MEDIUM_BLOB: ++ case MYSQL_TYPE_LONG_BLOB: ++ case MYSQL_TYPE_BLOB: ++ case MYSQL_TYPE_VAR_STRING: ++ case MYSQL_TYPE_STRING: ++ case MYSQL_TYPE_GEOMETRY: ++ return item->max_length; ++ case MYSQL_TYPE_TINY: ++ return 4; ++ case MYSQL_TYPE_SHORT: ++ return 6; ++ case MYSQL_TYPE_LONG: ++ return 11; ++ case MYSQL_TYPE_FLOAT: ++ return 25; ++ case MYSQL_TYPE_DOUBLE: ++ return 53; ++ case MYSQL_TYPE_NULL: ++ return 0; ++ case MYSQL_TYPE_LONGLONG: ++ return 20; ++ case MYSQL_TYPE_INT24: ++ return 8; ++ default: ++ DBUG_ASSERT(0); // we should never go there ++ return 0; ++ } ++} ++ ++ ++/* ++ Make temporary table field according collected information about type ++ of UNION result ++ ++ SYNOPSIS ++ Item_type_holder::make_field_by_type() ++ table temporary table for which we create fields ++ ++ RETURN ++ created field ++*/ ++ ++Field *Item_type_holder::make_field_by_type(TABLE *table) ++{ ++ /* ++ The field functions defines a field to be not null if null_ptr is not 0 ++ */ ++ uchar *null_ptr= maybe_null ? (uchar*) "" : 0; ++ switch (fld_type) ++ { ++ case MYSQL_TYPE_ENUM: ++ DBUG_ASSERT(enum_set_typelib); ++ return new Field_enum((char *) 0, max_length, null_ptr, 0, ++ Field::NONE, name, ++ table, get_enum_pack_length(enum_set_typelib->count), ++ enum_set_typelib, collation.collation); ++ case MYSQL_TYPE_SET: ++ DBUG_ASSERT(enum_set_typelib); ++ return new Field_set((char *) 0, max_length, null_ptr, 0, ++ Field::NONE, name, ++ table, get_set_pack_length(enum_set_typelib->count), ++ enum_set_typelib, collation.collation); ++ default: ++ break; ++ } ++ return tmp_table_field_from_field_type(table); ++} ++ ++ ++/* ++ Get full information from Item about enum/set fields to be able to create ++ them later ++ ++ SYNOPSIS ++ Item_type_holder::get_full_info ++ item Item for information collection ++*/ ++void Item_type_holder::get_full_info(Item *item) ++{ ++ if (fld_type == MYSQL_TYPE_ENUM || ++ fld_type == MYSQL_TYPE_SET) ++ { ++ if (item->type() == Item::SUM_FUNC_ITEM && ++ (((Item_sum*)item)->sum_func() == Item_sum::MAX_FUNC || ++ ((Item_sum*)item)->sum_func() == Item_sum::MIN_FUNC)) ++ item = ((Item_sum*)item)->args[0]; ++ /* ++ We can have enum/set type after merging only if we have one enum|set ++ field (or MIN|MAX(enum|set field)) and number of NULL fields ++ */ ++ DBUG_ASSERT((enum_set_typelib && ++ get_real_type(item) == MYSQL_TYPE_NULL) || ++ (!enum_set_typelib && ++ item->type() == Item::FIELD_ITEM && ++ (get_real_type(item) == MYSQL_TYPE_ENUM || ++ get_real_type(item) == MYSQL_TYPE_SET) && ++ ((Field_enum*)((Item_field *) item)->field)->typelib)); ++ if (!enum_set_typelib) ++ { ++ enum_set_typelib= ((Field_enum*)((Item_field *) item)->field)->typelib; ++ } ++ } ++} ++ ++ ++double Item_type_holder::val_real() ++{ ++ DBUG_ASSERT(0); // should never be called ++ return 0.0; ++} ++ ++ ++longlong Item_type_holder::val_int() ++{ ++ DBUG_ASSERT(0); // should never be called ++ return 0; ++} ++ ++my_decimal *Item_type_holder::val_decimal(my_decimal *) ++{ ++ DBUG_ASSERT(0); // should never be called ++ return 0; ++} ++ ++String *Item_type_holder::val_str(String*) ++{ ++ DBUG_ASSERT(0); // should never be called ++ return 0; ++} ++ ++void Item_result_field::cleanup() ++{ ++ DBUG_ENTER("Item_result_field::cleanup()"); ++ Item::cleanup(); ++ result_field= 0; ++ DBUG_VOID_RETURN; ++} ++ ++/* ++ Dummy error processor used by default by Name_resolution_context ++ ++ SYNOPSIS ++ dummy_error_processor() ++ ++ NOTE ++ do nothing ++*/ ++ ++void dummy_error_processor(THD *thd, void *data) ++{} ++ ++/* ++ Wrapper of hide_view_error call for Name_resolution_context error processor ++ ++ SYNOPSIS ++ view_error_processor() ++ ++ NOTE ++ hide view underlying tables details in error messages ++*/ ++ ++void view_error_processor(THD *thd, void *data) ++{ ++ ((TABLE_LIST *)data)->hide_view_error(thd); ++} ++ ++/***************************************************************************** ++** Instantiate templates ++*****************************************************************************/ ++ ++#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION ++template class List; ++template class List_iterator; ++template class List_iterator_fast; ++template class List_iterator_fast; ++template class List; ++#endif only in patch2: unchanged: --- mysql-dfsg-5.0-5.0.32.orig/debian/patches/99_SECURITY_CVE-2008-4456.dpatch +++ mysql-dfsg-5.0-5.0.32/debian/patches/99_SECURITY_CVE-2008-4456.dpatch @@ -0,0 +1,37 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 99_SECURITY_CVE-2008-4456.dpatch by Devin Carraway +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Third-party fix for CVE-2008-4456, addressing missing entity encoding +## of special characters in HTML output, potentially enabling script +## injection or other HTML tampering. + +@DPATCH@ +diff -aruN mysql-dfsg-5.0-5.0.32.orig/client/mysql.cc mysql-dfsg-5.0-5.0.32.CVE-2008-4456/client/mysql.cc +--- mysql-dfsg-5.0-5.0.32.orig/client/mysql.cc 2006-12-20 11:14:28.000000000 +0000 ++++ mysql-dfsg-5.0-5.0.32.CVE-2008-4456/client/mysql.cc 2009-02-23 07:27:19.000000000 +0000 +@@ -2499,9 +2499,12 @@ + { + while((field = mysql_fetch_field(result))) + { +- tee_fprintf(PAGER, "%s", (field->name ? +- (field->name[0] ? field->name : +- "   ") : "NULL")); ++ tee_fputs("", PAGER); ++ if (field->name && field->name[0]) ++ xmlencode_print(field->name, field->name_length); ++ else ++ tee_fputs(field->name ? "   " : "NULL", PAGER); ++ tee_fputs("", PAGER); + } + (void) tee_fputs("", PAGER); + } +@@ -2512,7 +2515,7 @@ + for (uint i=0; i < mysql_num_fields(result); i++) + { + (void) tee_fputs("", PAGER); +- safe_put_field(cur[i],lengths[i]); ++ xmlencode_print(cur[i], lengths[i]); + (void) tee_fputs("", PAGER); + } + (void) tee_fputs("", PAGER);