diff --git a/.github/workflows/jdbc_unit_tests.yml b/.github/workflows/jdbc_unit_tests.yml new file mode 100644 index 00000000000..ad4d590b255 --- /dev/null +++ b/.github/workflows/jdbc_unit_tests.yml @@ -0,0 +1,155 @@ +name: JDBC Unit Tests +on: + push: + branches: + - BABEL_1_X_DEV__PG_13_4 + pull_request: + branches: + - BABEL_1_X_DEV__PG_13_4 + +env: + ANTLR4_VERSION: 4.9.3 + +jobs: + extension-tests: + name: Build and test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Requirements + run: | + sudo apt-get update && sudo apt install -y --no-install-recommends \ + build-essential flex libxml2-dev libxml2-utils \ + libxslt-dev libssl-dev \ + libreadline-dev zlib1g-dev libldap2-dev libpam0g-dev gettext \ + uuid uuid-dev cmake lld apt-utils \ + libossp-uuid-dev gnulib bison \ + xsltproc icu-devtools libicu66 libicu-dev gawk curl \ + openjdk-8-jre openssl g++ \ + libssl-dev python-dev libpq-dev \ + pkg-config unzip libutfcpp-dev gnupg + - name: Set up JDK 8 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '8' + check-latest: true + - name: Copy ANTLR jar file + run: | + cd contrib/babelfishpg_tsql/antlr/thirdparty/antlr/ + sudo cp antlr-${ANTLR4_VERSION}-complete.jar /usr/local/lib + - name: Compile ANTLR + run: | + cd .. + wget http://www.antlr.org/download/antlr4-cpp-runtime-${ANTLR4_VERSION}-source.zip + unzip -d antlr4 antlr4-cpp-runtime-${ANTLR4_VERSION}-source.zip + cd antlr4 + mkdir build && cd build + cmake .. -D ANTLR_JAR_LOCATION=/usr/local/lib/antlr-${ANTLR4_VERSION}-complete.jar -DCMAKE_INSTALL_PREFIX=/usr/local -DWITH_DEMO=True + make -j 4 + sudo make install + # cp /usr/local/lib/libantlr4-runtime.so.${ANTLR4_VERSION} ~/postgres/lib/ + - name: Build, and binary installation + run: | + # CFLAGS="${CFLAGS:--Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic}" + ./configure CFLAGS="-ggdb" \ + --prefix=$HOME/postgres/ \ + --enable-debug \ + --with-ldap \ + --with-libxml \ + --with-pam \ + --with-uuid=ossp \ + --enable-nls \ + --with-libxslt \ + --with-icu \ + --with-extra-version=" Babelfish for PostgreSQL" + # ./configure --prefix=$HOME/postgres/ --with-python PYTHON=/usr/bin/python2.7 --enable-debug CFLAGS="-ggdb" --with-libxml --with-uuid=ossp --with-icu + make clean && make DESTDIR=~/postgres/ -j 4 2>error.txt + # make check + sudo make install + - name: Build antlr + run: | + export ANTLR4_JAVA_BIN=/usr/bin/java + export ANTLR4_RUNTIME_LIBRARIES=/usr/include/antlr4-runtime + export ANTLR_EXECUTABLE=/usr/local/lib/antlr-${ANTLR4_VERSION}-complete.jar + export ANTLR_RUNTIME=../antlr4 + #PG_SRC=~/work/postgresql_modified_for_babelfish + export PG_SRC=/home/runner/work/postgresql_modified_for_babelfish/postgresql_modified_for_babelfish/ + export PG_CONFIG=~/postgres/bin/pg_config + cmake=$(which cmake) + + # Copy runtime in Postgres lib + sudo cp /usr/local/lib/libantlr4-runtime.so.${ANTLR4_VERSION} ~/postgres/lib + + cd ${PG_SRC}/contrib/babelfishpg_tsql/antlr + cmake -Wno-dev . + - name: Compile and Install Extensions + run: | + export ANTLR4_JAVA_BIN=/usr/bin/java + export ANTLR4_RUNTIME_LIBRARIES=/usr/include/antlr4-runtime + export ANTLR_EXECUTABLE=/usr/local/lib/antlr-${ANTLR4_VERSION}-complete.jar + export ANTLR_RUNTIME=../antlr4 + export PG_SRC=/home/runner/work/postgresql_modified_for_babelfish/postgresql_modified_for_babelfish/ + export PG_CONFIG=~/postgres/bin/pg_config + cmake=$(which cmake) + + cd $PG_SRC/contrib/ && make && sudo make install + - name: Install extensions + run: | + cd ~ + curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - + curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list + sudo apt-get install mssql-tools unixodbc-dev + export PATH=/opt/mssql-tools/bin:$PATH + ~/postgres/bin/initdb -D ~/postgres/data/ + ~/postgres/bin/pg_ctl -D ~/postgres/data/ -l logfile start + cd postgres/data + sudo sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/g" postgresql.conf + sudo sed -i "s/#shared_preload_libraries = ''/shared_preload_libraries = 'babelfishpg_tds'/g" postgresql.conf + ipaddress=$(ifconfig eth0 | grep 'inet ' | cut -d: -f2 | awk '{ print $2}') + sudo echo "host all all $ipaddress/32 trust" >> pg_hba.conf + ~/postgres/bin/pg_ctl -D ~/postgres/data/ -l logfile restart + sudo ~/postgres/bin/psql -d postgres -U runner -c "CREATE USER jdbc_user WITH SUPERUSER CREATEDB CREATEROLE PASSWORD '12345678' INHERIT;" + sudo ~/postgres/bin/psql -d postgres -U runner -c "DROP DATABASE IF EXISTS jdbc_testdb;" + sudo ~/postgres/bin/psql -d postgres -U runner -c "CREATE DATABASE jdbc_testdb OWNER jdbc_user;" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "set allow_system_table_mods = on;" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "CREATE EXTENSION IF NOT EXISTS "babelfishpg_tds" CASCADE;" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "GRANT ALL ON SCHEMA sys to jdbc_user;" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "ALTER USER jdbc_user CREATEDB;" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "ALTER SYSTEM SET babelfishpg_tsql.database_name = 'jdbc_testdb';" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "ALTER SYSTEM SET babelfishpg_tds.set_db_session_property = true;" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "SELECT pg_reload_conf();" + sudo ~/postgres/bin/psql -d jdbc_testdb -U runner -c "CALL sys.initialize_babelfish('jdbc_user');" + sqlcmd -S localhost -U jdbc_user -P 12345678 -Q "SELECT @@version GO" + - name: Run JDBC test framework + timeout-minutes: 15 + run: | + cd contrib/test/JDBC/ + mvn test + - name: Upload log + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: postgres-log + path: ~/postgres/data/logfile + # The test summary files contain paths with ':' characters, which is not allowed with the upload-artifact actions + - name: Rename test summary files + if: ${{ failure() }} + run: | + cd contrib/test/JDBC/Info + timestamp=`ls -Art | tail -n 1` + cd $timestamp + mv $timestamp.diff ../output-diff.diff + mv "$timestamp"_runSummary.log ../run-summary.log + - name: Upload run summary + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: run-summary.log + path: contrib/test/JDBC/Info/run-summary.log + - name: Upload output diff + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: output-diff.diff + path: contrib/test/JDBC/Info/output-diff.diff diff --git a/INSTALLING b/INSTALLING new file mode 100644 index 00000000000..0a00a635bfe --- /dev/null +++ b/INSTALLING @@ -0,0 +1,112 @@ +# Babelfish distribution compilation and installation + +### NOTE: these directions assume you are running from the directory that you +### uncompressed the Babelfish source code into. +### By default that would be babel_1.0.0__pg_13.4 + +export MAX_JOBS=2 # Or any other value suitable for your CPU capacity + +export PREFIX=/opt/target/babelfishpg +export ANTLR4_VERSION=4.9.3 + +export PG_CONFIG=${PREFIX}/bin/pg_config +export PG_SRC=$(pwd) + +export ANTLR4_RUNTIME_INCLUDE_DIR=/usr/local/include/antlr4-runtime/ + +export ANTLR4_JAVA_BIN=/usr/bin/java + + +## Prerequisites + +apt update + +apt install -y --no-install-recommends \ + build-essential flex libxml2-dev libxml2-utils\ + libxslt-dev libssl-dev \ + libreadline-dev zlib1g-dev libldap2-dev libpam0g-dev gettext \ + uuid uuid-dev cmake lld apt-utils pkg-config libossp-uuid-dev gnulib bison git + +apt install -y --no-install-recommends \ + xsltproc icu-devtools libicu66 libicu-dev gawk curl + +apt install -y openjdk-8-jre openssl \ + libssl-dev python-dev libpq-dev \ + pkgconf unzip libutfcpp-dev gnupg + +## Antlr compilation + +Compiling and installing antlr runtime + +ANTLR_EXECUTABLE=/usr/local/lib/antlr-${ANTLR4_VERSION}-complete.jar + +curl https://www.antlr.org/download/antlr-${ANTLR4_VERSION}-complete.jar \ + --output ${ANTLR_EXECUTABLE} && chmod +x ${ANTLR_EXECUTABLE} + +mkdir -p antlr_runtime + +curl https://www.antlr.org/download/antlr4-cpp-runtime-${ANTLR4_VERSION}-source.zip \ + --output /opt/antlr4-cpp-runtime-${ANTLR4_VERSION}-source.zip && \ + unzip -d antlr_runtime /opt/antlr4-cpp-runtime-${ANTLR4_VERSION}-source.zip + +cd antlr_runtime +mkdir build && cd build && \ + cmake .. -D ANTLR_JAR_LOCATION=${ANTLR_EXECUTABLE} \ + -DCMAKE_INSTALL_PREFIX=/usr/local -DWITH_DEMO=True && \ + make && make install + +## Compiling Babelfish + + +./configure CFLAGS="${CFLAGS:--Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic}" \ + --prefix=${PREFIX} \ + --enable-debug \ + --with-ldap \ + --with-libxml \ + --with-pam \ + --with-uuid=ossp \ + --enable-nls \ + --with-libxslt \ + --with-icu \ + --with-extra-version=" Babelfish for PostgreSQL" + +# Compilation + +make clean && make DESTDIR=${PREFIX} -j ${MAX_JOBS} + +#world-bin + +# Install core + +make install + +# Compile ANTLR shared object + +cp /usr/local/lib/libantlr4-runtime.so.${ANTLR4_VERSION} ${PREFIX}/lib + +cmake=$(which cmake) ANTLR4_RUNTIME_LIBRARIES=/usr/include/antlr4-runtime +cd contrib/babelfishpg_tsql/antlr +cmake . + +# Build all the extensions +cd ../.. && make && make install && cd .. + + + +## Starting Babelfish + +### For starting and initiating Babelfish, follow up the instructions at +### https://babelfishpg.org/docs/installation/compiling-babelfish-from-source/#additional-installation-steps). + + +## Clients + +### SQL Server Tooling dependencies +# Reference: https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-setup-tools?view=sql-server-ver15#ubuntu +# mssql-cli on arm currently isn't supported https://github.com/dbcli/mssql-cli/issues/152#issuecomment-446311124 +# For arm platform, you may find useful: pip install --upgrade mssql-cli +curl -L https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ +curl -L https://packages.microsoft.com/config/ubuntu/20.04/prod.list | tee /etc/apt/sources.list.d/msprod.list && \ +apt update && ACCEPT_EULA=Y apt install -y mssql-tools unixodbc-dev + +PATH="${PREFIX}/bin:/opt/mssql-tools/bin/:${PATH}" diff --git a/contrib/Makefile b/contrib/Makefile index 1846d415b6f..13939a4fc3c 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -9,6 +9,10 @@ SUBDIRS = \ amcheck \ auth_delay \ auto_explain \ + babelfishpg_common \ + babelfishpg_money \ + babelfishpg_tds \ + babelfishpg_tsql \ bloom \ btree_gin \ btree_gist \ @@ -50,6 +54,8 @@ SUBDIRS = \ unaccent \ vacuumlo +SUBDIRS += babelfishpg_tsql + ifeq ($(with_openssl),yes) SUBDIRS += sslinfo else @@ -89,6 +95,14 @@ endif # Missing: # start-scripts \ (does not have a makefile) - $(recurse) $(recurse_always) + + +# all: $(SUBDIRS) +# $(SUBDIRS): +# $(MAKE) -C $@ + +# .PHONY: all $(SUBDIRS) + + diff --git a/contrib/babelfishpg_common/Makefile b/contrib/babelfishpg_common/Makefile new file mode 100644 index 00000000000..f83338af8ee --- /dev/null +++ b/contrib/babelfishpg_common/Makefile @@ -0,0 +1,72 @@ +include Version.config + +EXTENSION = babelfishpg_common +EXTVERSION = $(BBFPGCMN_MAJOR_VERSION).$(BBFPGCMN_MINOR_VERSION).$(BBFPGCMN_MICRO_VERSION) + +MODULEPATH = $$libdir/$(EXTENSION)-$(BBFPGCMN_MAJOR_VERSION) +MODULE_big = $(EXTENSION) + +PG_CFLAGS += -g + +ifdef PREV_EXTVERSION +DATA = sql/$(EXTENSION)--$(PREV_EXTVERSION).sql +endif + +DATA_built = \ + $(EXTENSION).control \ + sql/$(EXTENSION)--$(EXTVERSION).sql + +OBJS = src/babelfishpg_common.o +OBJS += src/varchar.o +OBJS += src/bit.o +OBJS += src/instr.o +OBJS += src/typecode.o +OBJS += src/numeric.o +OBJS += src/varbinary.o +OBJS += src/uniqueidentifier.o +OBJS += src/datetime.o +OBJS += src/datetime2.o +OBJS += src/smalldatetime.o +OBJS += src/datetimeoffset.o +OBJS += src/sqlvariant.o +OBJS += src/coerce.o + + +ifdef USE_PGXS +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/babelfishpg_common +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + + + +MODULEPATH = $$libdir/$(EXTENSION)-$(BBFPGCMN_MAJOR_VERSION) + +ifdef PREV_EXTVERSION +DATA = sql/$(EXTENSION)--$(PREV_EXTVERSION).sql +endif + +DATA_built = \ + $(EXTENSION).control \ + sql/$(EXTENSION)--$(EXTVERSION).sql $(UPGRADES) + +#include ../Makefile.common + +$(EXTENSION).control: $(EXTENSION).control.in + cat $< \ + | sed -e 's|@EXTVERSION@|$(EXTVERSION)|g' \ + | sed -e 's|@EXTENSION@|$(EXTENSION)|g' \ + | sed -e 's|@MODULEPATH@|$(MODULEPATH)|g' \ + > $@ + +sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).in + cpp $< | sed 's/^# /-- /g' > $@ + + +CFLAGS = `$(PG_CONFIG) --includedir-server` + +$(recurse) diff --git a/contrib/babelfishpg_common/Version.config b/contrib/babelfishpg_common/Version.config new file mode 100644 index 00000000000..5ff14992d26 --- /dev/null +++ b/contrib/babelfishpg_common/Version.config @@ -0,0 +1,4 @@ +BBFPGCMN_MAJOR_VERSION=1 +BBFPGCMN_MINOR_VERSION=0 +BBFPGCMN_MICRO_VERSION=0 + diff --git a/contrib/babelfishpg_common/babelfishpg_common.control.in b/contrib/babelfishpg_common/babelfishpg_common.control.in new file mode 100644 index 00000000000..02ae695bb58 --- /dev/null +++ b/contrib/babelfishpg_common/babelfishpg_common.control.in @@ -0,0 +1,7 @@ +# TSQL Datatype extension +comment = 'Transact SQL Datatype Support' +default_version = '1.0.0' +module_pathname = '$libdir/babelfishpg_common' +relocatable = true +superuser = true +requires = '' diff --git a/contrib/babelfishpg_common/sql/babelfishpg_common.in b/contrib/babelfishpg_common/sql/babelfishpg_common.in new file mode 100644 index 00000000000..8a43693c404 --- /dev/null +++ b/contrib/babelfishpg_common/sql/babelfishpg_common.in @@ -0,0 +1,39 @@ +/* + * All objects created by the included files will be created in sys + */ + + +CREATE SCHEMA sys; +GRANT USAGE ON SCHEMA sys TO PUBLIC; + + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +#include "money/fixeddecimal--1.1.0_base_parallel.sql" +#include "money/fixeddecimal--parallelaggs.sql" +#include "money/fixeddecimal--brin.sql" +#include "bpchar.sql" +#include "varchar.sql" +#include "numerics.sql" +#include "strings.sql" +#include "bit.sql" +#include "varbinary.sql" +#include "binary.sql" +#include "uniqueidentifier.sql" +#include "datetime.sql" +#include "datetime2.sql" +#include "smalldatetime.sql" +#include "datetimeoffset.sql" +#include "sqlvariant.sql" +#include "string_operators.sql" +#include "coerce.sql" + +#include "utils.sql" + +/* + * Remove schema sys from search_path otherwise it causes BABEL-257 for some reason + * Notice schema sys will be automatically added to implicitly-searched namespaces by + * recomputeNamespacePath() in tsql dialect + */ +SELECT set_config('search_path', trim(leading 'sys, ' from current_setting('search_path')), false); +RESET client_min_messages; diff --git a/contrib/babelfishpg_common/sql/binary.sql b/contrib/babelfishpg_common/sql/binary.sql new file mode 100644 index 00000000000..74ca1a2d210 --- /dev/null +++ b/contrib/babelfishpg_common/sql/binary.sql @@ -0,0 +1,281 @@ +-- sys.BINARY +CREATE TYPE sys.BBF_BINARY; + +CREATE OR REPLACE FUNCTION sys.binaryin(cstring, oid, integer) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'varbinaryin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.binaryout(sys.BBF_BINARY) +RETURNS cstring +AS 'babelfishpg_common', 'varbinaryout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.binaryrecv(internal, oid, integer) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'varbinaryrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.binarysend(sys.BBF_BINARY) +RETURNS bytea +AS 'babelfishpg_common', 'varbinarysend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.binarytypmodin(cstring[]) +RETURNS integer +AS 'babelfishpg_common', 'varbinarytypmodin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.binarytypmodout(integer) +RETURNS cstring +AS 'babelfishpg_common', 'varbinarytypmodout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.BBF_BINARY ( + INPUT = sys.binaryin, + OUTPUT = sys.binaryout, + RECEIVE = sys.binaryrecv, + SEND = sys.binarysend, + TYPMOD_IN = sys.binarytypmodin, + TYPMOD_OUT = sys.binarytypmodout, + INTERNALLENGTH = VARIABLE, + ALIGNMENT = 'int4', + STORAGE = 'extended', + CATEGORY = 'U', + PREFERRED = false, + COLLATABLE = false +); + +-- casting functions for sys.BINARY +CREATE OR REPLACE FUNCTION sys.varcharbinary(sys.VARCHAR, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'varcharbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS sys.BBF_BINARY) +WITH FUNCTION sys.varcharbinary (sys.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varcharbinary(pg_catalog.VARCHAR, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'varcharbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS sys.BBF_BINARY) +WITH FUNCTION sys.varcharbinary (pg_catalog.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharbinary(pg_catalog.BPCHAR, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'bpcharbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.BPCHAR AS sys.BBF_BINARY) +WITH FUNCTION sys.bpcharbinary (pg_catalog.BPCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharbinary(sys.BPCHAR, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'bpcharbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.BBF_BINARY) +WITH FUNCTION sys.bpcharbinary (sys.BPCHAR, integer, boolean) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.binarysysvarchar(sys.BBF_BINARY) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY AS sys.VARCHAR) +WITH FUNCTION sys.binarysysvarchar (sys.BBF_BINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.binaryvarchar(sys.BBF_BINARY) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY AS pg_catalog.VARCHAR) +WITH FUNCTION sys.binaryvarchar (sys.BBF_BINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int2binary(INT2, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'int2binary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT2 AS sys.BBF_BINARY) +WITH FUNCTION sys.int2binary (INT2, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int4binary(INT4, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'int4binary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT4 AS sys.BBF_BINARY) +WITH FUNCTION sys.int4binary (INT4, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int8binary(INT8, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'int8binary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT8 AS sys.BBF_BINARY) +WITH FUNCTION sys.int8binary (INT8, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.binaryint2(sys.BBF_BINARY) +RETURNS INT2 +AS 'babelfishpg_common', 'binaryint2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY as INT2) +WITH FUNCTION sys.binaryint2 (sys.BBF_BINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.binaryint4(sys.BBF_BINARY) +RETURNS INT4 +AS 'babelfishpg_common', 'binaryint4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY as INT4) +WITH FUNCTION sys.binaryint4 (sys.BBF_BINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.binaryint8(sys.BBF_BINARY) +RETURNS INT8 +AS 'babelfishpg_common', 'binaryint8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY as INT8) +WITH FUNCTION sys.binaryint8 (sys.BBF_BINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.float4binary(REAL, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'float4binary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (REAL AS sys.BBF_BINARY) +WITH FUNCTION sys.float4binary (REAL, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.float8binary(DOUBLE PRECISION, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'float8binary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (DOUBLE PRECISION AS sys.BBF_BINARY) +WITH FUNCTION sys.float8binary (DOUBLE PRECISION, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.binaryfloat4(sys.BBF_BINARY) +RETURNS REAL +AS 'babelfishpg_common', 'binaryfloat4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY as REAL) +WITH FUNCTION sys.binaryfloat4 (sys.BBF_BINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.binaryfloat8(sys.BBF_BINARY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_common', 'binaryfloat8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY as DOUBLE PRECISION) +WITH FUNCTION sys.binaryfloat8 (sys.BBF_BINARY) AS ASSIGNMENT; + +CREATE DOMAIN sys.IMAGE AS sys.BBF_VARBINARY; + +SET enable_domain_typmod = TRUE; +CREATE DOMAIN sys.BINARY AS sys.BBF_BINARY; +RESET enable_domain_typmod; + +CREATE OR REPLACE FUNCTION sys.binary(sys.BINARY, integer, boolean) +RETURNS sys.BINARY +AS 'babelfishpg_common', 'binary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +SET client_min_messages = 'ERROR'; +CREATE CAST (sys.BINARY AS sys.BINARY) +WITH FUNCTION sys.binary (sys.BINARY, integer, BOOLEAN) AS ASSIGNMENT; +SET client_min_messages = 'WARNING'; + +CREATE FUNCTION sys.binary_eq(leftarg sys.bbf_binary, rightarg sys.bbf_binary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.bbf_binary, + RIGHTARG = sys.bbf_binary, + FUNCTION = sys.binary_eq, + COMMUTATOR = = +); + + +CREATE FUNCTION sys.binary_neq(leftarg sys.bbf_binary, rightarg sys.bbf_binary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_neq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.bbf_binary, + RIGHTARG = sys.bbf_binary, + FUNCTION = sys.binary_neq, + COMMUTATOR = <> +); + +CREATE FUNCTION sys.binary_gt(leftarg sys.bbf_binary, rightarg sys.bbf_binary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.> ( + LEFTARG = sys.bbf_binary, + RIGHTARG = sys.bbf_binary, + FUNCTION = sys.binary_gt, + COMMUTATOR = < +); + +CREATE FUNCTION sys.binary_geq(leftarg sys.bbf_binary, rightarg sys.bbf_binary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_geq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.bbf_binary, + RIGHTARG = sys.bbf_binary, + FUNCTION = sys.binary_geq, + COMMUTATOR = <= +); + +CREATE FUNCTION sys.binary_lt(leftarg sys.bbf_binary, rightarg sys.bbf_binary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.< ( + LEFTARG = sys.bbf_binary, + RIGHTARG = sys.bbf_binary, + FUNCTION = sys.binary_lt, + COMMUTATOR = > +); + +CREATE FUNCTION sys.binary_leq(leftarg sys.bbf_binary, rightarg sys.bbf_binary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_leq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.bbf_binary, + RIGHTARG = sys.bbf_binary, + FUNCTION = sys.binary_leq, + COMMUTATOR = >= +); + +CREATE FUNCTION sys.bbf_binary_cmp(sys.bbf_binary, sys.bbf_binary) +RETURNS int +AS 'babelfishpg_common', 'varbinary_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.bbf_binary_ops +DEFAULT FOR TYPE sys.bbf_binary USING btree AS + OPERATOR 1 < (sys.bbf_binary, sys.bbf_binary), + OPERATOR 2 <= (sys.bbf_binary, sys.bbf_binary), + OPERATOR 3 = (sys.bbf_binary, sys.bbf_binary), + OPERATOR 4 >= (sys.bbf_binary, sys.bbf_binary), + OPERATOR 5 > (sys.bbf_binary, sys.bbf_binary), + FUNCTION 1 sys.bbf_binary_cmp(sys.bbf_binary, sys.bbf_binary); + diff --git a/contrib/babelfishpg_common/sql/bit.sql b/contrib/babelfishpg_common/sql/bit.sql new file mode 100644 index 00000000000..2e0dea70c16 --- /dev/null +++ b/contrib/babelfishpg_common/sql/bit.sql @@ -0,0 +1,431 @@ +CREATE TYPE sys.BIT; + +CREATE OR REPLACE FUNCTION sys.bitin(cstring) +RETURNS sys.BIT +AS 'babelfishpg_common', 'bitin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bitout(sys.BIT) +RETURNS cstring +AS 'babelfishpg_common', 'bitout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bitrecv(internal) +RETURNS sys.BIT +AS 'babelfishpg_common', 'bitrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bitsend(sys.BIT) +RETURNS bytea +AS 'babelfishpg_common', 'bitsend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.BIT ( + INPUT = sys.bitin, + OUTPUT = sys.bitout, + RECEIVE = sys.bitrecv, + SEND = sys.bitsend, + INTERNALLENGTH = 1, + PASSEDBYVALUE, + ALIGNMENT = 'char', + STORAGE = 'plain', + CATEGORY = 'B', + PREFERRED = true, + COLLATABLE = false + ); + +CREATE OR REPLACE FUNCTION sys.int2bit(INT2) +RETURNS sys.BIT +AS 'babelfishpg_common', 'int2bit' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT2 AS sys.BIT) +WITH FUNCTION sys.int2bit (INT2) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int4bit(INT4) +RETURNS sys.BIT +AS 'babelfishpg_common', 'int4bit' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT4 AS sys.BIT) +WITH FUNCTION sys.int4bit (INT4) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int8bit(INT8) +RETURNS sys.BIT +AS 'babelfishpg_common', 'int8bit' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT8 AS sys.BIT) +WITH FUNCTION sys.int8bit (INT8) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.ftobit(REAL) +RETURNS sys.BIT +AS 'babelfishpg_common', 'ftobit' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (REAL AS sys.BIT) +WITH FUNCTION sys.ftobit (REAL) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.dtobit(DOUBLE PRECISION) +RETURNS sys.BIT +AS 'babelfishpg_common', 'dtobit' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (DOUBLE PRECISION AS sys.BIT) +WITH FUNCTION sys.dtobit (DOUBLE PRECISION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.numeric_bit(NUMERIC) +RETURNS sys.BIT +AS 'babelfishpg_common', 'numeric_bit' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (NUMERIC AS sys.BIT) +WITH FUNCTION sys.numeric_bit (NUMERIC) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bit2int2(sys.BIT) +RETURNS INT2 +AS 'babelfishpg_common', 'bit2int2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BIT AS INT2) +WITH FUNCTION sys.bit2int2 (sys.BIT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bit2int4(sys.BIT) +RETURNS INT4 +AS 'babelfishpg_common', 'bit2int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BIT AS INT4) +WITH FUNCTION sys.bit2int4 (sys.BIT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bit2int8(sys.BIT) +RETURNS INT8 +AS 'babelfishpg_common', 'bit2int8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BIT AS INT8) +WITH FUNCTION sys.bit2int8 (sys.BIT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bit2numeric(sys.BIT) +RETURNS NUMERIC +AS 'babelfishpg_common', 'bit2numeric' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BIT AS NUMERIC) +WITH FUNCTION sys.bit2numeric (sys.BIT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bit2fixeddec(sys.BIT) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_common', 'bit2fixeddec' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BIT AS FIXEDDECIMAL) +WITH FUNCTION sys.bit2fixeddec (sys.BIT) AS IMPLICIT; + +CREATE FUNCTION sys.bitneg(sys.BIT) +RETURNS sys.BIT +AS 'babelfishpg_common', 'bitneg' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.biteq(sys.BIT, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'biteq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitne(sys.BIT, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'bitne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitlt(sys.BIT, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'bitlt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitle(sys.BIT, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'bitle' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitgt(sys.BIT, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'bitgt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitge(sys.BIT, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'bitge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bit_cmp(sys.BIT, sys.BIT) +RETURNS int +AS 'babelfishpg_common', 'bit_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +/* Operators for sys.BIT. TSQL doesn't support + - * / for bit */ +CREATE OPERATOR sys.- ( + RIGHTARG = sys.BIT, + PROCEDURE = sys.bitneg +); + +CREATE OPERATOR sys.= ( + LEFTARG = sys.BIT, + RIGHTARG = sys.BIT, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.biteq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.BIT, + RIGHTARG = sys.BIT, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.bitne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.BIT, + RIGHTARG = sys.BIT, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.bitlt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.BIT, + RIGHTARG = sys.BIT, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.bitle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.BIT, + RIGHTARG = sys.BIT, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.bitgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.BIT, + RIGHTARG = sys.BIT, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.bitge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS sys.bit_ops +DEFAULT FOR TYPE sys.bit USING btree AS + OPERATOR 1 < (sys.bit, sys.bit), + OPERATOR 2 <= (sys.bit, sys.bit), + OPERATOR 3 = (sys.bit, sys.bit), + OPERATOR 4 >= (sys.bit, sys.bit), + OPERATOR 5 > (sys.bit, sys.bit), + FUNCTION 1 sys.bit_cmp(sys.bit, sys.bit); + +/* Comparison between int and bit */ +CREATE FUNCTION sys.int4biteq(INT4, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'int4biteq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4bitne(INT4, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'int4bitne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4bitlt(INT4, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'int4bitlt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4bitle(INT4, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'int4bitle' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4bitgt(INT4, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'int4bitgt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4bitge(INT4, sys.BIT) +RETURNS bool +AS 'babelfishpg_common', 'int4bitge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = INT4, + RIGHTARG = sys.BIT, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.int4biteq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = INT4, + RIGHTARG = sys.BIT, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.int4bitne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = INT4, + RIGHTARG = sys.BIT, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.int4bitlt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = INT4, + RIGHTARG = sys.BIT, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.int4bitle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = INT4, + RIGHTARG = sys.BIT, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.int4bitgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = INT4, + RIGHTARG = sys.BIT, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.int4bitge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +/* Comparison between bit and int */ +CREATE FUNCTION sys.bitint4eq(sys.BIT, INT4) +RETURNS bool +AS 'babelfishpg_common', 'bitint4eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitint4ne(sys.BIT, INT4) +RETURNS bool +AS 'babelfishpg_common', 'bitint4ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitint4lt(sys.BIT, INT4) +RETURNS bool +AS 'babelfishpg_common', 'bitint4lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitint4le(sys.BIT, INT4) +RETURNS bool +AS 'babelfishpg_common', 'bitint4le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitint4gt(sys.BIT, INT4) +RETURNS bool +AS 'babelfishpg_common', 'bitint4gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bitint4ge(sys.BIT, INT4) +RETURNS bool +AS 'babelfishpg_common', 'bitint4ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.BIT, + RIGHTARG = INT4, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.bitint4eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.BIT, + RIGHTARG = INT4, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.bitint4ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.BIT, + RIGHTARG = INT4, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.bitint4lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.BIT, + RIGHTARG = INT4, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.bitint4le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.BIT, + RIGHTARG = INT4, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.bitint4gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.BIT, + RIGHTARG = INT4, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.bitint4ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OR REPLACE FUNCTION sys.bitxor(leftarg pg_catalog.bit, rightarg pg_catalog.bit) +RETURNS pg_catalog.bit +AS $$ +SELECT (leftarg & ~rightarg) | (~leftarg & rightarg); +$$ +LANGUAGE SQL; + diff --git a/contrib/babelfishpg_common/sql/bpchar.sql b/contrib/babelfishpg_common/sql/bpchar.sql new file mode 100644 index 00000000000..d5f294f786f --- /dev/null +++ b/contrib/babelfishpg_common/sql/bpchar.sql @@ -0,0 +1,258 @@ +CREATE TYPE sys.BPCHAR; + +-- Basic functions +CREATE OR REPLACE FUNCTION sys.bpcharin(cstring) +RETURNS sys.BPCHAR +AS 'babelfishpg_common', 'bpcharin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bpcharout(sys.BPCHAR) +RETURNS cstring +AS 'bpcharout' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bpcharrecv(internal) +RETURNS sys.BPCHAR +AS 'babelfishpg_common', 'bpcharrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bpcharsend(sys.BPCHAR) +RETURNS bytea +AS 'bpcharsend' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.BPCHAR ( + INPUT = sys.bpcharin, + OUTPUT = sys.bpcharout, + RECEIVE = sys.bpcharrecv, + SEND = sys.bpcharsend, + TYPMOD_IN = bpchartypmodin, + TYPMOD_OUT = bpchartypmodout, + CATEGORY = 'S', + COLLATABLE = True, + LIKE = pg_catalog.BPCHAR +); + +-- Basic operator functions +CREATE FUNCTION sys.bpchareq(sys.BPCHAR, sys.BPCHAR) +RETURNS bool +AS 'bpchareq' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpcharne(sys.BPCHAR, sys.BPCHAR) +RETURNS bool +AS 'bpcharne' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpcharlt(sys.BPCHAR, sys.BPCHAR) +RETURNS bool +AS 'bpcharlt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpcharle(sys.BPCHAR, sys.BPCHAR) +RETURNS bool +AS 'bpcharle' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpchargt(sys.BPCHAR, sys.BPCHAR) +RETURNS bool +AS 'bpchargt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpcharge(sys.BPCHAR, sys.BPCHAR) +RETURNS bool +AS 'bpcharge' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +-- Basic operators +-- Note that if those operators are not in pg_catalog, we will see different behaviors depending on sql_dialect +CREATE OPERATOR pg_catalog.= ( + LEFTARG = sys.BPCHAR, + RIGHTARG = sys.BPCHAR, + COMMUTATOR = OPERATOR(pg_catalog.=), + NEGATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.bpchareq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES, + HASHES +); + +CREATE OPERATOR pg_catalog.<> ( + LEFTARG = sys.BPCHAR, + RIGHTARG = sys.BPCHAR, + NEGATOR = OPERATOR(pg_catalog.=), + COMMUTATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.bpcharne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR pg_catalog.< ( + LEFTARG = sys.BPCHAR, + RIGHTARG = sys.BPCHAR, + NEGATOR = OPERATOR(pg_catalog.>=), + COMMUTATOR = OPERATOR(pg_catalog.>), + PROCEDURE = sys.bpcharlt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR pg_catalog.<= ( + LEFTARG = sys.BPCHAR, + RIGHTARG = sys.BPCHAR, + NEGATOR = OPERATOR(pg_catalog.>), + COMMUTATOR = OPERATOR(pg_catalog.>=), + PROCEDURE = sys.bpcharle, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR pg_catalog.> ( + LEFTARG = sys.BPCHAR, + RIGHTARG = sys.BPCHAR, + NEGATOR = OPERATOR(pg_catalog.<=), + COMMUTATOR = OPERATOR(pg_catalog.<), + PROCEDURE = sys.bpchargt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR pg_catalog.>= ( + LEFTARG = sys.BPCHAR, + RIGHTARG = sys.BPCHAR, + NEGATOR = OPERATOR(pg_catalog.<), + COMMUTATOR = OPERATOR(pg_catalog.<=), + PROCEDURE = sys.bpcharge, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +-- Operator classes +CREATE FUNCTION sys.bpcharcmp(sys.BPCHAR, sys.BPCHAR) +RETURNS INT4 +AS 'bpcharcmp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.hashbpchar(sys.BPCHAR) +RETURNS INT4 +AS 'hashbpchar' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS bpchar_ops + DEFAULT FOR TYPE sys.BPCHAR USING btree AS + OPERATOR 1 pg_catalog.< (sys.BPCHAR, sys.BPCHAR), + OPERATOR 2 pg_catalog.<= (sys.BPCHAR, sys.BPCHAR), + OPERATOR 3 pg_catalog.= (sys.BPCHAR, sys.BPCHAR), + OPERATOR 4 pg_catalog.>= (sys.BPCHAR, sys.BPCHAR), + OPERATOR 5 pg_catalog.> (sys.BPCHAR, sys.BPCHAR), + FUNCTION 1 sys.bpcharcmp(sys.BPCHAR, sys.BPCHAR); + +CREATE OPERATOR CLASS bpchar_ops + DEFAULT FOR TYPE sys.BPCHAR USING hash AS + OPERATOR 1 pg_catalog.= (sys.BPCHAR, sys.BPCHAR), + FUNCTION 1 sys.hashbpchar(sys.BPCHAR); + +CREATE OR REPLACE FUNCTION sys.bpchar(sys.BPCHAR, integer, boolean) +RETURNS sys.BPCHAR +AS 'babelfishpg_common', 'bpchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- To sys.BPCHAR +CREATE CAST (sys.BPCHAR AS sys.BPCHAR) +WITH FUNCTION sys.BPCHAR (sys.BPCHAR, integer, boolean) AS IMPLICIT; + +CREATE CAST (pg_catalog.VARCHAR as sys.BPCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (pg_catalog.TEXT as sys.BPCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (pg_catalog.BOOL as sys.BPCHAR) +WITH FUNCTION pg_catalog.text(pg_catalog.BOOL) AS ASSIGNMENT; + +-- From sys.BPCHAR +CREATE CAST (sys.BPCHAR AS pg_catalog.BPCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (sys.BPCHAR as pg_catalog.VARCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (sys.BPCHAR as pg_catalog.TEXT) +WITHOUT FUNCTION AS IMPLICIT; + +-- Operators between different types +CREATE FUNCTION sys.bpchareq(sys.BPCHAR, pg_catalog.TEXT) +RETURNS bool +AS 'bpchareq' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpchareq(pg_catalog.TEXT, sys.BPCHAR) +RETURNS bool +AS 'bpchareq' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpcharne(sys.BPCHAR, pg_catalog.TEXT) +RETURNS bool +AS 'bpcharne' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpcharne(pg_catalog.TEXT, sys.BPCHAR) +RETURNS bool +AS 'bpcharne' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR pg_catalog.= ( + LEFTARG = sys.BPCHAR, + RIGHTARG = pg_catalog.TEXT, + COMMUTATOR = OPERATOR(pg_catalog.=), + NEGATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.bpchareq, + RESTRICT = eqsel, + JOIN = eqjoinsel +); + +CREATE OPERATOR pg_catalog.= ( + LEFTARG = pg_catalog.TEXT, + RIGHTARG = sys.BPCHAR, + COMMUTATOR = OPERATOR(pg_catalog.=), + NEGATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.bpchareq, + RESTRICT = eqsel, + JOIN = eqjoinsel +); + +CREATE OPERATOR pg_catalog.<> ( + LEFTARG = sys.BPCHAR, + RIGHTARG = pg_catalog.TEXT, + NEGATOR = OPERATOR(pg_catalog.=), + COMMUTATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.bpcharne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR pg_catalog.<> ( + LEFTARG = pg_catalog.TEXT, + RIGHTARG = sys.BPCHAR, + NEGATOR = OPERATOR(pg_catalog.=), + COMMUTATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.bpcharne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +SET enable_domain_typmod = TRUE; +CREATE DOMAIN sys.NCHAR AS sys.BPCHAR; +RESET enable_domain_typmod; + +CREATE OR REPLACE FUNCTION sys.nchar(sys.nchar, integer, boolean) +RETURNS sys.nchar +AS 'babelfishpg_common', 'bpchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + + +SET client_min_messages = 'ERROR'; +CREATE CAST (sys.nchar AS sys.nchar) +WITH FUNCTION sys.nchar (sys.nchar, integer, BOOLEAN) AS ASSIGNMENT; +SET client_min_messages = 'WARNING'; diff --git a/contrib/babelfishpg_common/sql/coerce.sql b/contrib/babelfishpg_common/sql/coerce.sql new file mode 100644 index 00000000000..23c5f9a3544 --- /dev/null +++ b/contrib/babelfishpg_common/sql/coerce.sql @@ -0,0 +1,145 @@ +-- Add Missing casting functions +-- Casting functions used in catalog should use the exact type of castsource and casttarget. + +-- double precision -> int8 +CREATE OR REPLACE FUNCTION sys.dtrunci8(double precision) +RETURNS INT8 +AS 'babelfishpg_common', 'dtrunci8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- double precision -> int4 +CREATE OR REPLACE FUNCTION sys.dtrunci4(double precision) +RETURNS INT4 +AS 'babelfishpg_common', 'dtrunci4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- double precision -> int2 +CREATE OR REPLACE FUNCTION sys.dtrunci2(double precision) +RETURNS INT2 +AS 'babelfishpg_common', 'dtrunci2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- real -> int8 +CREATE OR REPLACE FUNCTION sys.ftrunci8(real) +RETURNS INT8 +AS 'babelfishpg_common', 'ftrunci8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- real -> int4 +CREATE OR REPLACE FUNCTION sys.ftrunci4(real) +RETURNS INT4 +AS 'babelfishpg_common', 'ftrunci4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- real -> int2 +CREATE OR REPLACE FUNCTION sys.ftrunci2(real) +RETURNS INT2 +AS 'babelfishpg_common', 'ftrunci2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +--- XXX: it is desriable to use SQL (or C) rather than plpgsql. But SQL function is not working +--- if tsql is enabled for some reasons. (BABEL-766) + +-- fixeddecimal -> int8 +CREATE OR REPLACE FUNCTION sys._round_fixeddecimal_to_int8(In arg sys.fixeddecimal) +RETURNS INT8 AS $$ +BEGIN + RETURN CAST(round(arg) AS INT8); +END; +$$ LANGUAGE plpgsql; + +-- fixeddecimal -> int8 +CREATE OR REPLACE FUNCTION sys._round_fixeddecimal_to_int4(In arg sys.fixeddecimal) +RETURNS INT4 AS $$ +BEGIN + RETURN CAST(round(arg) AS INT4); +END; +$$ LANGUAGE plpgsql; + +-- fixeddecimal -> int8 +CREATE OR REPLACE FUNCTION sys._round_fixeddecimal_to_int2(In arg sys.fixeddecimal) +RETURNS INT2 AS $$ +BEGIN + RETURN CAST(round(arg) AS INT2); +END; +$$ LANGUAGE plpgsql; + +-- numeric -> int8 +CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int8(In arg numeric) +RETURNS INT8 AS $$ +BEGIN + RETURN CAST(trunc(arg) AS INT8); +END; +$$ LANGUAGE plpgsql; + +-- numeric -> int4 +CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int4(In arg numeric) +RETURNS INT4 AS $$ +BEGIN + RETURN CAST(trunc(arg) AS INT4); +END; +$$ LANGUAGE plpgsql; + +-- numeric -> int2 +CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int2(In arg numeric) +RETURNS INT2 AS $$ +BEGIN + RETURN CAST(trunc(arg) AS INT2); +END; +$$ LANGUAGE plpgsql; + +-- text -> fixeddecimal +CREATE FUNCTION sys.char_to_fixeddecimal(text) +RETURNS sys.FIXEDDECIMAL +AS 'babelfishpg_money', 'char_to_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- char -> fixeddecimal +CREATE FUNCTION sys.char_to_fixeddecimal(char) +RETURNS sys.FIXEDDECIMAL +AS 'babelfishpg_money', 'char_to_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.char_to_fixeddecimal(sys.bpchar) +RETURNS sys.FIXEDDECIMAL +AS 'babelfishpg_money', 'char_to_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- varchar -> fixeddecimal +CREATE FUNCTION sys.char_to_fixeddecimal(sys.varchar) +RETURNS sys.FIXEDDECIMAL +AS 'babelfishpg_money', 'char_to_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.char_to_fixeddecimal(pg_catalog.varchar) +RETURNS sys.FIXEDDECIMAL +AS 'babelfishpg_money', 'char_to_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- text -> name +CREATE FUNCTION sys.text_to_name(text) +RETURNS pg_catalog.name +AS 'babelfishpg_common', 'pltsql_text_name' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- bpchar -> name +CREATE FUNCTION sys.bpchar_to_name(pg_catalog.bpchar) +RETURNS pg_catalog.name +AS 'babelfishpg_common', 'pltsql_bpchar_name' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.bpchar_to_name(sys.bpchar) +RETURNS pg_catalog.name +AS 'babelfishpg_common', 'pltsql_bpchar_name' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- varchar -> name +CREATE FUNCTION sys.varchar_to_name(sys.varchar) +RETURNS pg_catalog.name +AS 'babelfishpg_common', 'pltsql_text_name' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.varchar_to_name(pg_catalog.varchar) +RETURNS pg_catalog.name +AS 'babelfishpg_common', 'pltsql_text_name' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; diff --git a/contrib/babelfishpg_common/sql/datetime.sql b/contrib/babelfishpg_common/sql/datetime.sql new file mode 100644 index 00000000000..d2f17d36908 --- /dev/null +++ b/contrib/babelfishpg_common/sql/datetime.sql @@ -0,0 +1,392 @@ +CREATE TYPE sys.DATETIME; + +CREATE OR REPLACE FUNCTION sys.datetimein(cstring) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'datetime_in' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeout(sys.DATETIME) +RETURNS cstring +AS 'babelfishpg_common', 'datetime_out' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimerecv(internal) +RETURNS sys.DATETIME +AS 'timestamp_recv' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimesend(sys.DATETIME) +RETURNS bytea +AS 'timestamp_send' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimetypmodin(cstring[]) +RETURNS integer +AS 'timestamptypmodin' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimetypmodout(integer) +RETURNS cstring +AS 'timestamptypmodout' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.DATETIME ( + INPUT = sys.datetimein, + OUTPUT = sys.datetimeout, + RECEIVE = sys.datetimerecv, + SEND = sys.datetimesend, + TYPMOD_IN = sys.datetimetypmodin, + TYPMOD_OUT = sys.datetimetypmodout, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = 'plain', + CATEGORY = 'D', + PREFERRED = false, + COLLATABLE = false, + DEFAULT = '1900-01-01 00:00:00', + PASSEDBYVALUE +); + +CREATE FUNCTION sys.datetimeeq(sys.DATETIME, sys.DATETIME) +RETURNS bool +AS 'timestamp_eq' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimene(sys.DATETIME, sys.DATETIME) +RETURNS bool +AS 'timestamp_ne' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimelt(sys.DATETIME, sys.DATETIME) +RETURNS bool +AS 'timestamp_lt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimele(sys.DATETIME, sys.DATETIME) +RETURNS bool +AS 'timestamp_le' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimegt(sys.DATETIME, sys.DATETIME) +RETURNS bool +AS 'timestamp_gt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimege(sys.DATETIME, sys.DATETIME) +RETURNS bool +AS 'timestamp_ge' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.DATETIME, + RIGHTARG = sys.DATETIME, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.datetimeeq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.DATETIME, + RIGHTARG = sys.DATETIME, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.datetimene, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.DATETIME, + RIGHTARG = sys.DATETIME, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.datetimelt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.DATETIME, + RIGHTARG = sys.DATETIME, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.datetimele, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.DATETIME, + RIGHTARG = sys.DATETIME, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.datetimegt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.DATETIME, + RIGHTARG = sys.DATETIME, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.datetimege, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +-- datetime <-> int operators for datetime-int +/- arithmetic +CREATE FUNCTION sys.datetimeplint4(sys.DATETIME, INT4) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'datetime_pl_int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4pldatetime(INT4, sys.DATETIME) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'int4_pl_datetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimemiint4(sys.DATETIME, INT4) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'datetime_mi_int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4midatetime(INT4, sys.DATETIME) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'int4_mi_datetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.DATETIME, + RIGHTARG = INT4, + PROCEDURE = sys.datetimeplint4 +); + +CREATE OPERATOR sys.+ ( + LEFTARG = INT4, + RIGHTARG = sys.DATETIME, + PROCEDURE = sys.int4pldatetime +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.DATETIME, + RIGHTARG = INT4, + PROCEDURE = sys.datetimemiint4 +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT4, + RIGHTARG = sys.DATETIME, + PROCEDURE = sys.int4midatetime +); + + + +CREATE FUNCTION sys.datetimeplfloat8(sys.DATETIME, float8) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'datetime_pl_float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.DATETIME, + RIGHTARG = float8, + PROCEDURE = sys.datetimeplfloat8 +); + +CREATE FUNCTION sys.datetimemifloat8(sys.DATETIME, float8) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'datetime_mi_float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.- ( + LEFTARG = sys.DATETIME, + RIGHTARG = float8, + PROCEDURE = sys.datetimemifloat8 +); + +CREATE FUNCTION sys.float8pldatetime(float8, sys.DATETIME) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'float8_pl_datetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = float8, + RIGHTARG = sys.DATETIME, + PROCEDURE = sys.float8pldatetime +); + +CREATE FUNCTION sys.float8midatetime(float8, sys.DATETIME) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'float8_mi_datetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.- ( + LEFTARG = float8, + RIGHTARG = sys.DATETIME, + PROCEDURE = sys.float8midatetime +); + + + + +CREATE FUNCTION datetime_cmp(sys.DATETIME, sys.DATETIME) +RETURNS INT4 +AS 'timestamp_cmp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION datetime_hash(sys.DATETIME) +RETURNS INT4 +AS 'timestamp_hash' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.datetime_ops +DEFAULT FOR TYPE sys.DATETIME USING btree AS + OPERATOR 1 < (sys.DATETIME, sys.DATETIME), + OPERATOR 2 <= (sys.DATETIME, sys.DATETIME), + OPERATOR 3 = (sys.DATETIME, sys.DATETIME), + OPERATOR 4 >= (sys.DATETIME, sys.DATETIME), + OPERATOR 5 > (sys.DATETIME, sys.DATETIME), + FUNCTION 1 datetime_cmp(sys.DATETIME, sys.DATETIME); + +CREATE OPERATOR CLASS sys.datetime_ops +DEFAULT FOR TYPE sys.DATETIME USING hash AS + OPERATOR 1 = (sys.DATETIME, sys.DATETIME), + FUNCTION 1 datetime_hash(sys.DATETIME); + +-- cast TO datetime +CREATE OR REPLACE FUNCTION sys.timestamp2datetime(TIMESTAMP) +RETURNS DATETIME +AS 'babelfishpg_common', 'timestamp_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (TIMESTAMP AS DATETIME) +WITH FUNCTION sys.timestamp2datetime(TIMESTAMP) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.timestamptz2datetime(TIMESTAMPTZ) +RETURNS DATETIME +AS 'babelfishpg_common', 'timestamptz_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (TIMESTAMPTZ AS DATETIME) +WITH FUNCTION sys.timestamptz2datetime (TIMESTAMPTZ) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.date2datetime(DATE) +RETURNS DATETIME +AS 'babelfishpg_common', 'date_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATE AS DATETIME) +WITH FUNCTION sys.date2datetime (DATE) AS IMPLICIT; + + +CREATE OR REPLACE FUNCTION sys.time2datetime(TIME) +RETURNS DATETIME +AS 'babelfishpg_common', 'time_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (TIME AS DATETIME) +WITH FUNCTION sys.time2datetime (TIME) AS IMPLICIT; + + +CREATE OR REPLACE FUNCTION sys.varchar2datetime(sys.VARCHAR) +RETURNS DATETIME +AS 'babelfishpg_common', 'varchar_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS DATETIME) +WITH FUNCTION sys.varchar2datetime (sys.VARCHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varchar2datetime(pg_catalog.VARCHAR) +RETURNS DATETIME +AS 'babelfishpg_common', 'varchar_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS DATETIME) +WITH FUNCTION sys.varchar2datetime (pg_catalog.VARCHAR) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.char2datetime(CHAR) +RETURNS DATETIME +AS 'babelfishpg_common', 'char_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (CHAR AS DATETIME) +WITH FUNCTION sys.char2datetime (CHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpchar2datetime(sys.BPCHAR) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'char_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.DATETIME) +WITH FUNCTION sys.bpchar2datetime (sys.BPCHAR) AS ASSIGNMENT; + +-- cast FROM datetime +CREATE CAST (DATETIME AS TIMESTAMP) +WITHOUT FUNCTION AS IMPLICIT; + + +CREATE OR REPLACE FUNCTION sys.datetime2timestamptz(DATETIME) +RETURNS TIMESTAMPTZ +AS 'timestamp_timestamptz' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME AS TIMESTAMPTZ) +WITH FUNCTION sys.datetime2timestamptz (DATETIME) AS IMPLICIT; + + +CREATE OR REPLACE FUNCTION sys.datetime2date(DATETIME) +RETURNS DATE +AS 'timestamp_date' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME AS DATE) +WITH FUNCTION sys.datetime2date (DATETIME) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.datetime2time(DATETIME) +RETURNS TIME +AS 'timestamp_time' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME AS TIME) +WITH FUNCTION sys.datetime2time (DATETIME) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.datetime2sysvarchar(DATETIME) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'datetime_varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME AS sys.VARCHAR) +WITH FUNCTION sys.datetime2sysvarchar (DATETIME) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.datetime2varchar(DATETIME) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'datetime_varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME AS pg_catalog.VARCHAR) +WITH FUNCTION sys.datetime2varchar (DATETIME) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.datetime2char(DATETIME) +RETURNS CHAR +AS 'babelfishpg_common', 'datetime_char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME AS CHAR) +WITH FUNCTION sys.datetime2char (DATETIME) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.datetime2bpchar(sys.DATETIME) +RETURNS sys.BPCHAR +AS 'babelfishpg_common', 'datetime_char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.DATETIME AS sys.BPCHAR) +WITH FUNCTION sys.datetime2bpchar (sys.DATETIME) AS ASSIGNMENT; diff --git a/contrib/babelfishpg_common/sql/datetime2.sql b/contrib/babelfishpg_common/sql/datetime2.sql new file mode 100644 index 00000000000..3a98109b581 --- /dev/null +++ b/contrib/babelfishpg_common/sql/datetime2.sql @@ -0,0 +1,321 @@ +CREATE TYPE sys.DATETIME2; + +CREATE OR REPLACE FUNCTION sys.datetime2in(cstring) +RETURNS sys.DATETIME2 +AS 'babelfishpg_common', 'datetime2_in' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2out(sys.DATETIME2) +RETURNS cstring +AS 'timestamp_out' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2recv(internal) +RETURNS sys.DATETIME2 +AS 'timestamp_recv' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2send(sys.DATETIME2) +RETURNS bytea +AS 'timestamp_send' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2typmodin(cstring[]) +RETURNS integer +AS 'timestamptypmodin' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2typmodout(integer) +RETURNS cstring +AS 'timestamptypmodout' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.DATETIME2 ( + INPUT = sys.datetime2in, + OUTPUT = sys.datetime2out, + RECEIVE = sys.datetime2recv, + SEND = sys.datetime2send, + TYPMOD_IN = sys.datetime2typmodin, + TYPMOD_OUT = sys.datetime2typmodout, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = 'plain', + CATEGORY = 'D', + PREFERRED = false, + COLLATABLE = false, + DEFAULT = '1900-01-01 00:00:00', + PASSEDBYVALUE +); + +CREATE FUNCTION sys.datetime2eq(sys.DATETIME2, sys.DATETIME2) +RETURNS bool +AS 'timestamp_eq' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetime2ne(sys.DATETIME2, sys.DATETIME2) +RETURNS bool +AS 'timestamp_ne' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetime2lt(sys.DATETIME2, sys.DATETIME2) +RETURNS bool +AS 'timestamp_lt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetime2le(sys.DATETIME2, sys.DATETIME2) +RETURNS bool +AS 'timestamp_le' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetime2gt(sys.DATETIME2, sys.DATETIME2) +RETURNS bool +AS 'timestamp_gt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetime2ge(sys.DATETIME2, sys.DATETIME2) +RETURNS bool +AS 'timestamp_ge' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.DATETIME2, + RIGHTARG = sys.DATETIME2, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.datetime2eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.DATETIME2, + RIGHTARG = sys.DATETIME2, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.datetime2ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.DATETIME2, + RIGHTARG = sys.DATETIME2, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.datetime2lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.DATETIME2, + RIGHTARG = sys.DATETIME2, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.datetime2le, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.DATETIME2, + RIGHTARG = sys.DATETIME2, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.datetime2gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.DATETIME2, + RIGHTARG = sys.DATETIME2, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.datetime2ge, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +CREATE FUNCTION datetime2_cmp(sys.DATETIME2, sys.DATETIME2) +RETURNS INT4 +AS 'timestamp_cmp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION datetime2_hash(sys.DATETIME2) +RETURNS INT4 +AS 'timestamp_hash' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.datetime2_ops +DEFAULT FOR TYPE sys.DATETIME2 USING btree AS + OPERATOR 1 < (sys.DATETIME2, sys.DATETIME2), + OPERATOR 2 <= (sys.DATETIME2, sys.DATETIME2), + OPERATOR 3 = (sys.DATETIME2, sys.DATETIME2), + OPERATOR 4 >= (sys.DATETIME2, sys.DATETIME2), + OPERATOR 5 > (sys.DATETIME2, sys.DATETIME2), + FUNCTION 1 datetime2_cmp(sys.DATETIME2, sys.DATETIME2); + +CREATE OPERATOR CLASS sys.datetime2_ops +DEFAULT FOR TYPE sys.DATETIME2 USING hash AS + OPERATOR 1 = (sys.DATETIME2, sys.DATETIME2), + FUNCTION 1 datetime2_hash(sys.DATETIME2); + +-- cast TO datetime2 +CREATE OR REPLACE FUNCTION sys.timestamp2datetime2(TIMESTAMP) +RETURNS DATETIME2 +AS 'babelfishpg_common', 'timestamp_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (TIMESTAMP AS DATETIME2) +WITH FUNCTION sys.timestamp2datetime2(TIMESTAMP) AS ASSIGNMENT; +-- CREATE CAST (TIMESTAMP AS DATETIME2) +-- WITHOUT FUNCTION AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.timestamptz2datetime2(TIMESTAMPTZ) +RETURNS DATETIME2 +AS 'babelfishpg_common', 'timestamptz_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (TIMESTAMPTZ AS DATETIME2) +WITH FUNCTION sys.timestamptz2datetime2 (TIMESTAMPTZ) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.date2datetime2(DATE) +RETURNS DATETIME2 +AS 'babelfishpg_common', 'date_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATE AS DATETIME2) +WITH FUNCTION sys.date2datetime2 (DATE) AS IMPLICIT; + + +CREATE OR REPLACE FUNCTION sys.time2datetime2(TIME) +RETURNS DATETIME2 +AS 'babelfishpg_common', 'time_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (TIME AS DATETIME2) +WITH FUNCTION sys.time2datetime2 (TIME) AS IMPLICIT; + + +CREATE CAST (DATETIME AS DATETIME2) +WITHOUT FUNCTION AS IMPLICIT; + + +-- BABEL-1465 CAST from VARCHAR/NVARCHAR/CHAR/NCHAR to datetime2 is VOLATILE +CREATE OR REPLACE FUNCTION sys.varchar2datetime2(sys.VARCHAR) +RETURNS DATETIME2 +AS 'babelfishpg_common', 'varchar_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS DATETIME2) +WITH FUNCTION sys.varchar2datetime2 (sys.VARCHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varchar2datetime2(pg_catalog.VARCHAR) +RETURNS DATETIME2 +AS 'babelfishpg_common', 'varchar_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS DATETIME2) +WITH FUNCTION sys.varchar2datetime2 (pg_catalog.VARCHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.char2datetime2(CHAR) +RETURNS DATETIME2 +AS 'babelfishpg_common', 'char_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (CHAR AS DATETIME2) +WITH FUNCTION sys.char2datetime2 (CHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpchar2datetime2(sys.BPCHAR) +RETURNS sys.DATETIME2 +AS 'babelfishpg_common', 'char_datetime2' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.DATETIME2) +WITH FUNCTION sys.bpchar2datetime2 (sys.BPCHAR) AS ASSIGNMENT; + +-- cast FROM datetime2 +CREATE CAST (DATETIME2 AS TIMESTAMP) +WITHOUT FUNCTION AS IMPLICIT; + + +CREATE OR REPLACE FUNCTION sys.datetime22datetime(DATETIME2) +RETURNS DATETIME +AS 'babelfishpg_common', 'timestamp_datetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME2 AS DATETIME) +WITH FUNCTION sys.datetime22datetime(DATETIME2) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.datetime22timestamptz(DATETIME2) +RETURNS TIMESTAMPTZ +AS 'timestamp_timestamptz' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME2 AS TIMESTAMPTZ) +WITH FUNCTION sys.datetime22timestamptz (DATETIME2) AS IMPLICIT; + + +CREATE OR REPLACE FUNCTION sys.datetime22date(DATETIME2) +RETURNS DATE +AS 'timestamp_date' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME2 AS DATE) +WITH FUNCTION sys.datetime22date (DATETIME2) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.datetime22time(DATETIME2) +RETURNS TIME +AS 'timestamp_time' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME2 AS TIME) +WITH FUNCTION sys.datetime22time (DATETIME2) AS ASSIGNMENT; + + +CREATE FUNCTION sys.datetime2scale(sys.DATETIME2, INT4) +RETURNS sys.DATETIME2 +AS 'babelfishpg_common', 'datetime2_scale' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.DATETIME2 AS sys.DATETIME2) +WITH FUNCTION datetime2scale (sys.DATETIME2, INT4) AS ASSIGNMENT; + + +-- BABEL-1465 CAST from datetime2 to VARCHAR/NVARCHAR/CHAR/NCHAR is VOLATILE +CREATE OR REPLACE FUNCTION sys.datetime22sysvarchar(DATETIME2) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'datetime2_varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME2 AS sys.VARCHAR) +WITH FUNCTION sys.datetime22sysvarchar (DATETIME2) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.datetime22varchar(DATETIME2) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'datetime2_varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME2 AS pg_catalog.VARCHAR) +WITH FUNCTION sys.datetime22varchar (DATETIME2) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.datetime22char(DATETIME2) +RETURNS CHAR +AS 'babelfishpg_common', 'datetime2_char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (DATETIME2 AS CHAR) +WITH FUNCTION sys.datetime22char (DATETIME2) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.datetime22bpchar(sys.DATETIME2) +RETURNS sys.BPCHAR +AS 'babelfishpg_common', 'datetime2_char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.DATETIME2 AS sys.BPCHAR) +WITH FUNCTION sys.datetime22bpchar (sys.DATETIME2) AS ASSIGNMENT; diff --git a/contrib/babelfishpg_common/sql/datetimeoffset.sql b/contrib/babelfishpg_common/sql/datetimeoffset.sql new file mode 100644 index 00000000000..c967f312cc1 --- /dev/null +++ b/contrib/babelfishpg_common/sql/datetimeoffset.sql @@ -0,0 +1,304 @@ +CREATE TYPE sys.DATETIMEOFFSET; + +CREATE OR REPLACE FUNCTION sys.datetimeoffsetin(cstring) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_in' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffsetout(sys.DATETIMEOFFSET) +RETURNS cstring +AS 'babelfishpg_common', 'datetimeoffset_out' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffsetrecv(internal) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_recv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffsetsend(sys.DATETIMEOFFSET) +RETURNS bytea +AS 'babelfishpg_common', 'datetimeoffset_send' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeofftypmodin(cstring[]) +RETURNS integer +AS 'timestamptztypmodin' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeofftypmodout(integer) +RETURNS cstring +AS 'timestamptztypmodout' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.DATETIMEOFFSET ( + INPUT = sys.datetimeoffsetin, + OUTPUT = sys.datetimeoffsetout, + RECEIVE = sys.datetimeoffsetrecv, + SEND = sys.datetimeoffsetsend, + TYPMOD_IN = sys.datetimeofftypmodin, + TYPMOD_OUT = sys.datetimeofftypmodout, + INTERNALLENGTH = 10, + ALIGNMENT = 'double', + STORAGE = 'plain', + CATEGORY = 'D', + PREFERRED = false, + DEFAULT = '1900-01-01 00:00+0' +); + +CREATE FUNCTION sys.datetimeoffseteq(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS bool +AS 'babelfishpg_common', 'datetimeoffset_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetne(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS bool +AS 'babelfishpg_common', 'datetimeoffset_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetlt(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS bool +AS 'babelfishpg_common', 'datetimeoffset_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetle(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS bool +AS 'babelfishpg_common', 'datetimeoffset_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetgt(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS bool +AS 'babelfishpg_common', 'datetimeoffset_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetge(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS bool +AS 'babelfishpg_common', 'datetimeoffset_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetplinterval(sys.DATETIMEOFFSET, INTERVAL) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_pl_interval' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.intervalpldatetimeoffset(INTERVAL, sys.DATETIMEOFFSET) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'interval_pl_datetimeoffset' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetmiinterval(sys.DATETIMEOFFSET, INTERVAL) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_mi_interval' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.datetimeoffsetmi(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS INTERVAL +AS 'babelfishpg_common', 'datetimeoffset_mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = sys.DATETIMEOFFSET, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.datetimeoffseteq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = sys.DATETIMEOFFSET, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.datetimeoffsetne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = sys.DATETIMEOFFSET, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.datetimeoffsetlt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = sys.DATETIMEOFFSET, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.datetimeoffsetle, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = sys.DATETIMEOFFSET, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.datetimeoffsetgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = sys.DATETIMEOFFSET, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.datetimeoffsetge, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = interval, + PROCEDURE = sys.datetimeoffsetplinterval +); + +CREATE OPERATOR sys.+ ( + LEFTARG = interval, + RIGHTARG = sys.DATETIMEOFFSET, + PROCEDURE = sys.intervalpldatetimeoffset +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = interval, + PROCEDURE = sys.datetimeoffsetmiinterval +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.DATETIMEOFFSET, + RIGHTARG = sys.DATETIMEOFFSET, + PROCEDURE = sys.datetimeoffsetmi +); + +CREATE FUNCTION datetimeoffset_cmp(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS INT4 +AS 'babelfishpg_common', 'datetimeoffset_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION datetimeoffset_hash(sys.DATETIMEOFFSET) +RETURNS INT4 +AS 'babelfishpg_common', 'datetimeoffset_hash' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.datetimeoffset_ops +DEFAULT FOR TYPE sys.DATETIMEOFFSET USING btree AS + OPERATOR 1 < (sys.DATETIMEOFFSET, sys.DATETIMEOFFSET), + OPERATOR 2 <= (sys.DATETIMEOFFSET, sys.DATETIMEOFFSET), + OPERATOR 3 = (sys.DATETIMEOFFSET, sys.DATETIMEOFFSET), + OPERATOR 4 >= (sys.DATETIMEOFFSET, sys.DATETIMEOFFSET), + OPERATOR 5 > (sys.DATETIMEOFFSET, sys.DATETIMEOFFSET), + FUNCTION 1 datetimeoffset_cmp(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET); + +CREATE OPERATOR CLASS sys.datetimeoffset_ops +DEFAULT FOR TYPE sys.DATETIMEOFFSET USING hash AS + OPERATOR 1 = (sys.DATETIMEOFFSET, sys.DATETIMEOFFSET), + FUNCTION 1 datetimeoffset_hash(sys.DATETIMEOFFSET); + +-- Casts +CREATE FUNCTION sys.datetimeoffsetscale(sys.DATETIMEOFFSET, INT4) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_scale' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.timestamp2datetimeoffset(TIMESTAMP) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'timestamp_datetimeoffset' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset2timestamp(sys.DATETIMEOFFSET) +RETURNS TIMESTAMP +AS 'babelfishpg_common', 'datetimeoffset_timestamp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.date2datetimeoffset(DATE) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'date_datetimeoffset' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset2date(sys.DATETIMEOFFSET) +RETURNS DATE +AS 'babelfishpg_common', 'datetimeoffset_date' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.time2datetimeoffset(TIME) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'time_datetimeoffset' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset2time(sys.DATETIMEOFFSET) +RETURNS TIME +AS 'babelfishpg_common', 'datetimeoffset_time' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetime2datetimeoffset(sys.SMALLDATETIME) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'smalldatetime_datetimeoffset' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset2smalldatetime(sys.DATETIMEOFFSET) +RETURNS sys.SMALLDATETIME +AS 'babelfishpg_common', 'datetimeoffset_smalldatetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2datetimeoffset(sys.DATETIME) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetime_datetimeoffset' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset2datetime(sys.DATETIMEOFFSET) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'datetimeoffset_datetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime22datetimeoffset(sys.DATETIME2) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetime2_datetimeoffset' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset2datetime2(sys.DATETIMEOFFSET) +RETURNS sys.DATETIME2 +AS 'babelfishpg_common', 'datetimeoffset_datetime2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.DATETIMEOFFSET AS sys.DATETIMEOFFSET) +WITH FUNCTION datetimeoffsetscale (sys.DATETIMEOFFSET, INT4) AS ASSIGNMENT; + +CREATE CAST (TIMESTAMP AS sys.DATETIMEOFFSET) +WITH FUNCTION sys.timestamp2datetimeoffset(TIMESTAMP) AS IMPLICIT; +CREATE CAST (sys.DATETIMEOFFSET AS TIMESTAMP) +WITH FUNCTION sys.datetimeoffset2timestamp(sys.DATETIMEOFFSET) AS ASSIGNMENT; + +CREATE CAST (DATE AS sys.DATETIMEOFFSET) +WITH FUNCTION sys.date2datetimeoffset(DATE) AS IMPLICIT; +CREATE CAST (sys.DATETIMEOFFSET AS DATE) +WITH FUNCTION sys.datetimeoffset2date(sys.DATETIMEOFFSET) AS ASSIGNMENT; + +CREATE CAST (TIME AS sys.DATETIMEOFFSET) +WITH FUNCTION sys.time2datetimeoffset(TIME) AS IMPLICIT; +CREATE CAST (sys.DATETIMEOFFSET AS TIME) +WITH FUNCTION sys.datetimeoffset2time(sys.DATETIMEOFFSET) AS ASSIGNMENT; + +CREATE CAST (sys.SMALLDATETIME AS sys.DATETIMEOFFSET) +WITH FUNCTION sys.smalldatetime2datetimeoffset(sys.SMALLDATETIME) AS IMPLICIT; +CREATE CAST (sys.DATETIMEOFFSET AS sys.SMALLDATETIME) +WITH FUNCTION sys.datetimeoffset2smalldatetime(sys.DATETIMEOFFSET) AS ASSIGNMENT; + +CREATE CAST (sys.DATETIME AS sys.DATETIMEOFFSET) +WITH FUNCTION sys.datetime2datetimeoffset(sys.DATETIME) AS IMPLICIT; +CREATE CAST (sys.DATETIMEOFFSET AS sys.DATETIME) +WITH FUNCTION sys.datetimeoffset2datetime(sys.DATETIMEOFFSET) AS ASSIGNMENT; + +CREATE CAST (sys.DATETIME2 AS sys.DATETIMEOFFSET) +WITH FUNCTION sys.datetime22datetimeoffset(sys.DATETIME2) AS IMPLICIT; +CREATE CAST (sys.DATETIMEOFFSET AS sys.DATETIME2) +WITH FUNCTION sys.datetimeoffset2datetime2(sys.DATETIMEOFFSET) AS ASSIGNMENT; diff --git a/contrib/babelfishpg_common/sql/money/fixeddecimal--1.1.0_base_parallel.sql b/contrib/babelfishpg_common/sql/money/fixeddecimal--1.1.0_base_parallel.sql new file mode 100755 index 00000000000..41e2955af32 --- /dev/null +++ b/contrib/babelfishpg_common/sql/money/fixeddecimal--1.1.0_base_parallel.sql @@ -0,0 +1,1530 @@ +------------------ +-- FIXEDDECIMAL -- +------------------ + +CREATE TYPE sys.FIXEDDECIMAL; + +CREATE FUNCTION sys.fixeddecimalin(cstring, oid, int4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalout(fixeddecimal) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimalout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalrecv(internal) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalsend(FIXEDDECIMAL) +RETURNS bytea +AS 'babelfishpg_money', 'fixeddecimalsend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimaltypmodin(_cstring) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimaltypmodin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimaltypmodout(INT4) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimaltypmodout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + + +CREATE TYPE sys.FIXEDDECIMAL ( + INPUT = fixeddecimalin, + OUTPUT = fixeddecimalout, + RECEIVE = fixeddecimalrecv, + SEND = fixeddecimalsend, + TYPMOD_IN = fixeddecimaltypmodin, + TYPMOD_OUT = fixeddecimaltypmodout, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = plain, + CATEGORY = 'N', + PREFERRED = false, + COLLATABLE = false, + PASSEDBYVALUE -- But not always.. XXX fix that. +); + +-- FIXEDDECIMAL, NUMERIC +CREATE FUNCTION sys.fixeddecimaleq(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimaleq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalne(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimallt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimallt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalle(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalle' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalgt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalgt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalge(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalum(FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalum' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalpl(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalmi(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalmul(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimaldiv(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.abs(FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalabs' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimallarger(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimallarger' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalsmaller(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalsmaller' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_hash(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_hash' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- +-- Operators. +-- + +-- FIXEDDECIMAL op FIXEDDECIMAL +CREATE OPERATOR sys.= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimaleq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimalne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimallt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimalle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimalge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimalgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalmi +); + +CREATE OPERATOR sys.- ( + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalum +); + +CREATE OPERATOR sys.* ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimaldiv +); + +CREATE OPERATOR CLASS sys.fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 2 <= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 3 = (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 4 >= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 5 > (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL); + +CREATE OPERATOR CLASS sys.fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- FIXEDDECIMAL, NUMERIC +CREATE FUNCTION sys.fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_numeric_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'numeric_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_numeric_eq(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_numeric_ne(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_numeric_lt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_numeric_le(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_numeric_gt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_numeric_ge(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_numeric_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_numeric_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_numeric_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_numeric_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_numeric_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_numeric_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS sys.fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, NUMERIC), + OPERATOR 2 <= (FIXEDDECIMAL, NUMERIC), + OPERATOR 3 = (FIXEDDECIMAL, NUMERIC), + OPERATOR 4 >= (FIXEDDECIMAL, NUMERIC), + OPERATOR 5 > (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC); + +CREATE OPERATOR CLASS sys.fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- NUMERIC, FIXEDDECIMAL +CREATE FUNCTION sys.numeric_fixeddecimal_eq(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.numeric_fixeddecimal_ne(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.numeric_fixeddecimal_lt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.numeric_fixeddecimal_le(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.numeric_fixeddecimal_gt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.numeric_fixeddecimal_ge(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = numeric_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = numeric_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = numeric_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = numeric_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = numeric_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = numeric_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS sys.numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 2 <= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 3 = (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 4 >= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 5 > (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + FUNCTION 1 numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL); + +CREATE OPERATOR CLASS sys.numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (NUMERIC, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int8 +-- + +-- FIXEDDECIMAL, INT8 +CREATE FUNCTION sys.fixeddecimal_int8_cmp(FIXEDDECIMAL, INT8) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int8_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int8_eq(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int8_ne(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int8_lt(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int8_le(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int8_gt(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int8_ge(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8pl(FIXEDDECIMAL, INT8) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint8pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8mi(FIXEDDECIMAL, INT8) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint8mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8mul(FIXEDDECIMAL, INT8) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint8mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8div(FIXEDDECIMAL, INT8) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint8div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int8_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int8_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int8_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int8_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int8_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int8_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint8pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + PROCEDURE = fixeddecimalint8mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint8mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + PROCEDURE = fixeddecimalint8div +); + +CREATE OPERATOR CLASS sys.fixeddecimal_int8_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT8), + OPERATOR 2 <= (FIXEDDECIMAL, INT8), + OPERATOR 3 = (FIXEDDECIMAL, INT8), + OPERATOR 4 >= (FIXEDDECIMAL, INT8), + OPERATOR 5 > (FIXEDDECIMAL, INT8), + FUNCTION 1 fixeddecimal_int8_cmp(FIXEDDECIMAL, INT8); + +CREATE OPERATOR CLASS sys.fixeddecimal_int8_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT8), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT8, FIXEDDECIMAL +CREATE FUNCTION sys.int8_fixeddecimal_cmp(INT8, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int8_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8_fixeddecimal_eq(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8_fixeddecimal_ne(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8_fixeddecimal_lt(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8_fixeddecimal_le(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8_fixeddecimal_gt(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8_fixeddecimal_ge(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimalpl(INT8, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int8fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimalmi(INT8, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int8fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimalmul(INT8, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int8fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimaldiv(INT8, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int8fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int8_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int8_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int8_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int8_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int8_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int8_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int8fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int8fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int8fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int8fixeddecimaldiv +); + +CREATE OPERATOR CLASS sys.int8_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT8, FIXEDDECIMAL), + OPERATOR 2 <= (INT8, FIXEDDECIMAL), + OPERATOR 3 = (INT8, FIXEDDECIMAL), + OPERATOR 4 >= (INT8, FIXEDDECIMAL), + OPERATOR 5 > (INT8, FIXEDDECIMAL), + FUNCTION 1 int8_fixeddecimal_cmp(INT8, FIXEDDECIMAL); + +CREATE OPERATOR CLASS sys.int8_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT8, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int4 +-- + +-- FIXEDDECIMAL, INT4 +CREATE FUNCTION sys.fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int4_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int4_eq(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int4_ne(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int4_lt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int4_le(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int4_gt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int4_ge(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4pl(FIXEDDECIMAL, INT4) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint4pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4mi(FIXEDDECIMAL, INT4) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint4mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4mul(FIXEDDECIMAL, INT4) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint4mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4div(FIXEDDECIMAL, INT4) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint4div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int4_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int4_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int4_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int4_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int4_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int4_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint4pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint4mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4div +); + +CREATE OPERATOR CLASS sys.fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT4), + OPERATOR 2 <= (FIXEDDECIMAL, INT4), + OPERATOR 3 = (FIXEDDECIMAL, INT4), + OPERATOR 4 >= (FIXEDDECIMAL, INT4), + OPERATOR 5 > (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4); + +CREATE OPERATOR CLASS sys.fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT4, FIXEDDECIMAL +CREATE FUNCTION sys.int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int4_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4_fixeddecimal_eq(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4_fixeddecimal_ne(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4_fixeddecimal_lt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4_fixeddecimal_le(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4_fixeddecimal_gt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4_fixeddecimal_ge(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimalpl(INT4, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int4fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimalmi(INT4, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int4fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimalmul(INT4, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int4fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimaldiv(INT4, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int4fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int4_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int4_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int4_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int4_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int4_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int4_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int4fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int4fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimaldiv +); + +CREATE OPERATOR CLASS sys.int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT4, FIXEDDECIMAL), + OPERATOR 2 <= (INT4, FIXEDDECIMAL), + OPERATOR 3 = (INT4, FIXEDDECIMAL), + OPERATOR 4 >= (INT4, FIXEDDECIMAL), + OPERATOR 5 > (INT4, FIXEDDECIMAL), + FUNCTION 1 int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL); + +CREATE OPERATOR CLASS sys.int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT4, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int2 +-- +-- FIXEDDECIMAL, INT2 +CREATE FUNCTION sys.fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int2_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int2_eq(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int2_ne(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int2_lt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int2_le(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int2_gt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_int2_ge(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2pl(FIXEDDECIMAL, INT2) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint2pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2mi(FIXEDDECIMAL, INT2) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint2mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2mul(FIXEDDECIMAL, INT2) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint2mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2div(FIXEDDECIMAL, INT2) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimalint2div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int2_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int2_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int2_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int2_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int2_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int2_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint2pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint2mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2div +); + +CREATE OPERATOR CLASS sys.fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT2), + OPERATOR 2 <= (FIXEDDECIMAL, INT2), + OPERATOR 3 = (FIXEDDECIMAL, INT2), + OPERATOR 4 >= (FIXEDDECIMAL, INT2), + OPERATOR 5 > (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2); + +CREATE OPERATOR CLASS sys.fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT2, FIXEDDECIMAL +CREATE FUNCTION sys.int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int2_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2_fixeddecimal_eq(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2_fixeddecimal_ne(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2_fixeddecimal_lt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2_fixeddecimal_le(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2_fixeddecimal_gt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2_fixeddecimal_ge(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimalpl(INT2, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int2fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimalmi(INT2, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int2fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimalmul(INT2, FIXEDDECIMAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'int2fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimaldiv(INT2, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int2fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int2_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int2_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int2_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int2_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int2_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int2_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.+ ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int2fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int2fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimaldiv +); + +CREATE OPERATOR CLASS sys.int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT2, FIXEDDECIMAL), + OPERATOR 2 <= (INT2, FIXEDDECIMAL), + OPERATOR 3 = (INT2, FIXEDDECIMAL), + OPERATOR 4 >= (INT2, FIXEDDECIMAL), + OPERATOR 5 > (INT2, FIXEDDECIMAL), + FUNCTION 1 int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL); + +CREATE OPERATOR CLASS sys.int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT2, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Casts +-- + +CREATE FUNCTION sys.fixeddecimal(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimal(INT8) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int8fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8(FIXEDDECIMAL) +RETURNS INT8 +AS 'babelfishpg_money', 'fixeddecimalint8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimal(INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimalint4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimal(INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2(FIXEDDECIMAL) +RETURNS INT2 +AS 'babelfishpg_money', 'fixeddecimalint2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimaltod(FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'fixeddecimaltod' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.dtofixeddecimal(DOUBLE PRECISION) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'dtofixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimaltof(FIXEDDECIMAL) +RETURNS REAL +AS 'babelfishpg_money', 'fixeddecimaltof' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.ftofixeddecimal(REAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'ftofixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_numeric(FIXEDDECIMAL) +RETURNS NUMERIC +AS 'babelfishpg_money', 'fixeddecimal_numeric' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.numeric_fixeddecimal(NUMERIC) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'numeric_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (FIXEDDECIMAL AS FIXEDDECIMAL) + WITH FUNCTION fixeddecimal (FIXEDDECIMAL, INT4) AS ASSIGNMENT; + +CREATE CAST (INT8 AS FIXEDDECIMAL) + WITH FUNCTION int8fixeddecimal (INT8) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT8) + WITH FUNCTION fixeddecimalint8 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (INT4 AS FIXEDDECIMAL) + WITH FUNCTION int4fixeddecimal (INT4) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT4) + WITH FUNCTION fixeddecimalint4 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (INT2 AS FIXEDDECIMAL) + WITH FUNCTION int2fixeddecimal (INT2) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT2) + WITH FUNCTION fixeddecimalint2 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (FIXEDDECIMAL AS DOUBLE PRECISION) + WITH FUNCTION fixeddecimaltod (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (DOUBLE PRECISION AS FIXEDDECIMAL) + WITH FUNCTION dtofixeddecimal (DOUBLE PRECISION) AS ASSIGNMENT; -- XXX? or Implicit? + +CREATE CAST (FIXEDDECIMAL AS REAL) + WITH FUNCTION fixeddecimaltof (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (REAL AS FIXEDDECIMAL) + WITH FUNCTION ftofixeddecimal (REAL) AS ASSIGNMENT; -- XXX or Implicit? + +CREATE CAST (FIXEDDECIMAL AS NUMERIC) + WITH FUNCTION fixeddecimal_numeric (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (NUMERIC AS FIXEDDECIMAL) + WITH FUNCTION numeric_fixeddecimal (NUMERIC) AS IMPLICIT; + +CREATE DOMAIN sys.MONEY as sys.FIXEDDECIMAL CHECK (VALUE >= -922337203685477.5808 AND VALUE <= 922337203685477.5807); +CREATE DOMAIN sys.SMALLMONEY as sys.FIXEDDECIMAL CHECK (VALUE >= -214748.3648 AND VALUE <= 214748.3647); diff --git a/contrib/babelfishpg_common/sql/money/fixeddecimal--brin.sql b/contrib/babelfishpg_common/sql/money/fixeddecimal--brin.sql new file mode 100644 index 00000000000..7969a0a60e2 --- /dev/null +++ b/contrib/babelfishpg_common/sql/money/fixeddecimal--brin.sql @@ -0,0 +1,12 @@ +CREATE OPERATOR CLASS sys.fixeddecimal_minmax_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING brin AS + OPERATOR 1 < (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 2 <= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 3 = (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 4 >= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 5 > (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 brin_minmax_opcinfo(INTERNAL), + FUNCTION 2 brin_minmax_add_value(INTERNAL, INTERNAL, INTERNAL, INTERNAL), + FUNCTION 3 brin_minmax_consistent(INTERNAL, INTERNAL, INTERNAL), + FUNCTION 4 brin_minmax_union(INTERNAL, INTERNAL, INTERNAL); + diff --git a/contrib/babelfishpg_common/sql/money/fixeddecimal--parallelaggs.sql b/contrib/babelfishpg_common/sql/money/fixeddecimal--parallelaggs.sql new file mode 100644 index 00000000000..c30c0463494 --- /dev/null +++ b/contrib/babelfishpg_common/sql/money/fixeddecimal--parallelaggs.sql @@ -0,0 +1,70 @@ + +-- Aggregate Support + +CREATE FUNCTION sys.fixeddecimalaggstatecombine(INTERNAL, INTERNAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimalaggstatecombine' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalaggstateserialize(INTERNAL) +RETURNS BYTEA +AS 'babelfishpg_money', 'fixeddecimalaggstateserialize' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalaggstatedeserialize(BYTEA, INTERNAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimalaggstatedeserialize' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_avg_accum(INTERNAL, FIXEDDECIMAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimal_avg_accum' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_sum(INTERNAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimal_sum' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimal_avg(INTERNAL) +RETURNS sys.MONEY +AS 'babelfishpg_money', 'fixeddecimal_avg' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE AGGREGATE sys.min(FIXEDDECIMAL) ( + SFUNC = fixeddecimalsmaller, + STYPE = FIXEDDECIMAL, + SORTOP = <, + COMBINEFUNC = fixeddecimalsmaller, + PARALLEL = SAFE +); + +CREATE AGGREGATE sys.max(FIXEDDECIMAL) ( + SFUNC = fixeddecimallarger, + STYPE = FIXEDDECIMAL, + SORTOP = >, + COMBINEFUNC = fixeddecimallarger, + PARALLEL = SAFE +); + +CREATE AGGREGATE sys.sum(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + FINALFUNC = fixeddecimal_sum, + STYPE = INTERNAL, + COMBINEFUNC = fixeddecimalaggstatecombine, + SERIALFUNC = fixeddecimalaggstateserialize, + DESERIALFUNC = fixeddecimalaggstatedeserialize, + PARALLEL = SAFE +); + +CREATE AGGREGATE sys.avg(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + FINALFUNC = fixeddecimal_avg, + STYPE = INTERNAL, + COMBINEFUNC = fixeddecimalaggstatecombine, + SERIALFUNC = fixeddecimalaggstateserialize, + DESERIALFUNC = fixeddecimalaggstatedeserialize, + PARALLEL = SAFE +); + + diff --git a/contrib/babelfishpg_common/sql/numerics.sql b/contrib/babelfishpg_common/sql/numerics.sql new file mode 100644 index 00000000000..8a40ec6f801 --- /dev/null +++ b/contrib/babelfishpg_common/sql/numerics.sql @@ -0,0 +1,77 @@ +CREATE DOMAIN sys.TINYINT AS SMALLINT CHECK (VALUE >= 0 AND VALUE <= 255); +CREATE DOMAIN sys.INT AS INTEGER; +CREATE DOMAIN sys.BIGINT AS BIGINT; +CREATE DOMAIN sys.REAL AS REAL; +CREATE DOMAIN sys.FLOAT AS DOUBLE PRECISION; + +-- Types with different default typmod behavior +SET enable_domain_typmod = TRUE; +CREATE DOMAIN sys.DECIMAL AS NUMERIC; +RESET enable_domain_typmod; + +-- Domain Self Cast Functions to support Typmod Cast in Domain +CREATE OR REPLACE FUNCTION sys.decimal(sys.nchar, integer, boolean) +RETURNS sys.nchar +AS 'numeric' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + + +CREATE OR REPLACE FUNCTION sys.tinyintxor(leftarg sys.tinyint, rightarg sys.tinyint) +RETURNS sys.tinyint +AS $$ +SELECT CAST(CAST(sys.bitxor(CAST(CAST(leftarg AS int4) AS pg_catalog.bit(16)), + CAST(CAST(rightarg AS int4) AS pg_catalog.bit(16))) AS int4) AS sys.tinyint); +$$ +LANGUAGE SQL; + +CREATE OPERATOR sys.^ ( + LEFTARG = sys.tinyint, + RIGHTARG = sys.tinyint, + FUNCTION = sys.tinyintxor, + COMMUTATOR = ^ +); + +CREATE OR REPLACE FUNCTION sys.int2xor(leftarg int2, rightarg int2) +RETURNS int2 +AS $$ +SELECT CAST(CAST(sys.bitxor(CAST(CAST(leftarg AS int4) AS pg_catalog.bit(16)), + CAST(CAST(rightarg AS int4) AS pg_catalog.bit(16))) AS int4) AS int2); +$$ +LANGUAGE SQL; + +CREATE OPERATOR sys.^ ( + LEFTARG = int2, + RIGHTARG = int2, + FUNCTION = sys.int2xor, + COMMUTATOR = ^ +); + +CREATE OR REPLACE FUNCTION sys.intxor(leftarg int4, rightarg int4) +RETURNS int4 +AS $$ +SELECT CAST(sys.bitxor(CAST(leftarg AS pg_catalog.bit(32)), + CAST(rightarg AS pg_catalog.bit(32))) AS int4) +$$ +LANGUAGE SQL; + +CREATE OPERATOR sys.^ ( + LEFTARG = int4, + RIGHTARG = int4, + FUNCTION = sys.intxor, + COMMUTATOR = ^ +); + +CREATE OR REPLACE FUNCTION sys.int8xor(leftarg int8, rightarg int8) +RETURNS int8 +AS $$ +SELECT CAST(sys.bitxor(CAST(leftarg AS pg_catalog.bit(64)), + CAST(rightarg AS pg_catalog.bit(64))) AS int8) +$$ +LANGUAGE SQL; + +CREATE OPERATOR sys.^ ( + LEFTARG = int8, + RIGHTARG = int8, + FUNCTION = sys.int8xor, + COMMUTATOR = ^ +); diff --git a/contrib/babelfishpg_common/sql/smalldatetime.sql b/contrib/babelfishpg_common/sql/smalldatetime.sql new file mode 100644 index 00000000000..a6504df6530 --- /dev/null +++ b/contrib/babelfishpg_common/sql/smalldatetime.sql @@ -0,0 +1,588 @@ +CREATE TYPE sys.SMALLDATETIME; + +CREATE OR REPLACE FUNCTION sys.smalldatetimein(cstring) +RETURNS sys.SMALLDATETIME +AS 'babelfishpg_common', 'smalldatetime_in' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetimeout(sys.SMALLDATETIME) +RETURNS cstring +AS 'timestamp_out' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetimerecv(internal) +RETURNS sys.SMALLDATETIME +AS 'timestamp_recv' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetimesend(sys.SMALLDATETIME) +RETURNS bytea +AS 'timestamp_send' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalltypmodin(cstring[]) +RETURNS integer +AS 'timestamptypmodin' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalltypmodout(integer) +RETURNS cstring +AS 'timestamptypmodout' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.SMALLDATETIME ( + INPUT = sys.smalldatetimein, + OUTPUT = sys.smalldatetimeout, + RECEIVE = sys.smalldatetimerecv, + SEND = sys.smalldatetimesend, + TYPMOD_IN = sys.smalltypmodin, + TYPMOD_OUT = sys.smalltypmodout, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = 'plain', + CATEGORY = 'D', + PREFERRED = false, + COLLATABLE = false, + DEFAULT = '1900-01-01 00:00', + PASSEDBYVALUE +); + +CREATE FUNCTION sys.smalldatetimeeq(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS bool +AS 'timestamp_eq' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetimene(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS bool +AS 'timestamp_ne' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetimelt(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS bool +AS 'timestamp_lt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetimele(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS bool +AS 'timestamp_le' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetimegt(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS bool +AS 'timestamp_gt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetimege(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS bool +AS 'timestamp_ge' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = sys.SMALLDATETIME, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.smalldatetimeeq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.smalldatetimene, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.smalldatetimelt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.smalldatetimele, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.smalldatetimegt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.smalldatetimege, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +-- smalldate vs pg_catalog.date +CREATE FUNCTION sys.smalldatetime_eq_date(sys.SMALLDATETIME, pg_catalog.date) +RETURNS bool +AS 'timestamp_eq_date' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetime_ne_date(sys.SMALLDATETIME, pg_catalog.date) +RETURNS bool +AS 'timestamp_ne_date' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetime_lt_date(sys.SMALLDATETIME, pg_catalog.date) +RETURNS bool +AS 'timestamp_lt_date' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetime_le_date(sys.SMALLDATETIME, pg_catalog.date) +RETURNS bool +AS 'timestamp_le_date' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetime_gt_date(sys.SMALLDATETIME, pg_catalog.date) +RETURNS bool +AS 'timestamp_gt_date' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetime_ge_date(sys.SMALLDATETIME, pg_catalog.date) +RETURNS bool +AS 'timestamp_ge_date' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = pg_catalog.date, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = smalldatetime_eq_date, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = pg_catalog.date, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = smalldatetime_ne_date, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = pg_catalog.date, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = smalldatetime_lt_date, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = pg_catalog.date, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = smalldatetime_le_date, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = pg_catalog.date, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = smalldatetime_gt_date, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.SMALLDATETIME, + RIGHTARG = pg_catalog.date, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = smalldatetime_ge_date, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +-- pg_catalog.date vs smalldate +CREATE FUNCTION sys.date_eq_smalldatetime(pg_catalog.date, sys.SMALLDATETIME) +RETURNS bool +AS 'date_eq_timestamp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.date_ne_smalldatetime(pg_catalog.date, sys.SMALLDATETIME) +RETURNS bool +AS 'date_ne_timestamp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.date_lt_smalldatetime(pg_catalog.date, sys.SMALLDATETIME) +RETURNS bool +AS 'date_lt_timestamp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.date_le_smalldatetime(pg_catalog.date, sys.SMALLDATETIME) +RETURNS bool +AS 'date_le_timestamp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.date_gt_smalldatetime(pg_catalog.date, sys.SMALLDATETIME) +RETURNS bool +AS 'date_gt_timestamp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.date_ge_smalldatetime(pg_catalog.date, sys.SMALLDATETIME) +RETURNS bool +AS 'date_ge_timestamp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = pg_catalog.date, + RIGHTARG = sys.SMALLDATETIME, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = date_eq_smalldatetime, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = pg_catalog.date, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = date_ne_smalldatetime, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = pg_catalog.date, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = date_lt_smalldatetime, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = pg_catalog.date, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = date_le_smalldatetime, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = pg_catalog.date, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = date_gt_smalldatetime, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = pg_catalog.date, + RIGHTARG = sys.SMALLDATETIME, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = date_ge_smalldatetime, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + + +-- smalldatetime <-> int/float operators for smalldatetime-int +/- arithmetic +CREATE FUNCTION sys.smalldatetimeplint4(sys.smalldatetime, INT4) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'smalldatetime_pl_int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4plsmalldatetime(INT4, sys.smalldatetime) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'int4_pl_smalldatetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smalldatetimemiint4(sys.smalldatetime, INT4) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'smalldatetime_mi_int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4mismalldatetime(INT4, sys.smalldatetime) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'int4_mi_smalldatetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.smalldatetime, + RIGHTARG = INT4, + PROCEDURE = sys.smalldatetimeplint4 +); + +CREATE OPERATOR sys.+ ( + LEFTARG = INT4, + RIGHTARG = sys.smalldatetime, + PROCEDURE = sys.int4plsmalldatetime +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.smalldatetime, + RIGHTARG = INT4, + PROCEDURE = sys.smalldatetimemiint4 +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT4, + RIGHTARG = sys.smalldatetime, + PROCEDURE = sys.int4mismalldatetime +); + + + +CREATE FUNCTION sys.smalldatetimeplfloat8(sys.smalldatetime, float8) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'smalldatetime_pl_float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.smalldatetime, + RIGHTARG = float8, + PROCEDURE = sys.smalldatetimeplfloat8 +); + +CREATE FUNCTION sys.smalldatetimemifloat8(sys.smalldatetime, float8) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'smalldatetime_mi_float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.- ( + LEFTARG = sys.smalldatetime, + RIGHTARG = float8, + PROCEDURE = sys.smalldatetimemifloat8 +); + +CREATE FUNCTION sys.float8plsmalldatetime(float8, sys.smalldatetime) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'float8_pl_smalldatetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = float8, + RIGHTARG = sys.smalldatetime, + PROCEDURE = sys.float8plsmalldatetime +); + +CREATE FUNCTION sys.float8mismalldatetime(float8, sys.smalldatetime) +RETURNS sys.smalldatetime +AS 'babelfishpg_common', 'float8_mi_smalldatetime' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.- ( + LEFTARG = float8, + RIGHTARG = sys.smalldatetime, + PROCEDURE = sys.float8mismalldatetime +); + + + +CREATE FUNCTION smalldatetime_cmp(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS INT4 +AS 'timestamp_cmp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION smalldatetime_hash(sys.SMALLDATETIME) +RETURNS INT4 +AS 'timestamp_hash' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.smalldatetime_ops +DEFAULT FOR TYPE sys.SMALLDATETIME USING btree AS + OPERATOR 1 < (sys.SMALLDATETIME, sys.SMALLDATETIME), + OPERATOR 2 <= (sys.SMALLDATETIME, sys.SMALLDATETIME), + OPERATOR 3 = (sys.SMALLDATETIME, sys.SMALLDATETIME), + OPERATOR 4 >= (sys.SMALLDATETIME, sys.SMALLDATETIME), + OPERATOR 5 > (sys.SMALLDATETIME, sys.SMALLDATETIME), + FUNCTION 1 smalldatetime_cmp(sys.SMALLDATETIME, sys.SMALLDATETIME); + +CREATE OPERATOR CLASS sys.smalldatetime_ops +DEFAULT FOR TYPE sys.SMALLDATETIME USING hash AS + OPERATOR 1 = (sys.SMALLDATETIME, sys.SMALLDATETIME), + FUNCTION 1 smalldatetime_hash(sys.SMALLDATETIME); + +CREATE OR REPLACE FUNCTION sys.timestamp2smalldatetime(TIMESTAMP) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'timestamp_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2smalldatetime(DATETIME) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'timestamp_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime22smalldatetime(DATETIME2) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'timestamp_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.date2smalldatetime(DATE) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'date_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetime2date(SMALLDATETIME) +RETURNS DATE +AS 'timestamp_date' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.timestamptz2smalldatetime(TIMESTAMPTZ) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'timestamptz_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetime2timestamptz(SMALLDATETIME) +RETURNS TIMESTAMPTZ +AS 'timestamp_timestamptz' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.time2smalldatetime(TIME) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'time_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetime2time(SMALLDATETIME) +RETURNS TIME +AS 'timestamp_time' +LANGUAGE internal VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (TIMESTAMP AS SMALLDATETIME) +WITH FUNCTION sys.timestamp2smalldatetime(TIMESTAMP) AS ASSIGNMENT; + +CREATE CAST (DATETIME AS SMALLDATETIME) +WITH FUNCTION sys.datetime2smalldatetime(DATETIME) AS ASSIGNMENT; + +CREATE CAST (DATETIME2 AS SMALLDATETIME) +WITH FUNCTION sys.datetime22smalldatetime(DATETIME2) AS ASSIGNMENT; + +CREATE CAST (SMALLDATETIME AS DATETIME) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (SMALLDATETIME AS DATETIME2) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (SMALLDATETIME AS TIMESTAMP) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (TIMESTAMPTZ AS SMALLDATETIME) +WITH FUNCTION sys.timestamptz2smalldatetime (TIMESTAMPTZ) AS IMPLICIT; + +CREATE CAST (SMALLDATETIME AS TIMESTAMPTZ) +WITH FUNCTION sys.smalldatetime2timestamptz (SMALLDATETIME) AS ASSIGNMENT; + +CREATE CAST (DATE AS SMALLDATETIME) +WITH FUNCTION sys.date2smalldatetime (DATE) AS IMPLICIT; + +CREATE CAST (SMALLDATETIME AS DATE) +WITH FUNCTION sys.smalldatetime2date (SMALLDATETIME) AS ASSIGNMENT; + +CREATE CAST (TIME AS SMALLDATETIME) +WITH FUNCTION sys.time2smalldatetime (TIME) AS IMPLICIT; + +CREATE CAST (SMALLDATETIME AS TIME) +WITH FUNCTION sys.smalldatetime2time (SMALLDATETIME) AS ASSIGNMENT; + +-- BABEL-1465 CAST from VARCHAR/NVARCHAR/CHAR/NCHAR to smalldatetime is VOLATILE +CREATE OR REPLACE FUNCTION sys.varchar2smalldatetime(sys.VARCHAR) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'varchar_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS SMALLDATETIME) +WITH FUNCTION sys.varchar2smalldatetime (sys.VARCHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varchar2smalldatetime(pg_catalog.VARCHAR) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'varchar_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS SMALLDATETIME) +WITH FUNCTION sys.varchar2smalldatetime (pg_catalog.VARCHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.char2smalldatetime(CHAR) +RETURNS SMALLDATETIME +AS 'babelfishpg_common', 'char_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (CHAR AS SMALLDATETIME) +WITH FUNCTION sys.char2smalldatetime (CHAR) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpchar2smalldatetime(sys.BPCHAR) +RETURNS sys.SMALLDATETIME +AS 'babelfishpg_common', 'char_smalldatetime' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.SMALLDATETIME) +WITH FUNCTION sys.bpchar2smalldatetime (sys.BPCHAR) AS ASSIGNMENT; + +-- BABEL-1465 CAST from smalldatetime to VARCHAR/NVARCHAR/CHAR/NCHAR is VOLATILE +CREATE OR REPLACE FUNCTION sys.smalldatetime2sysvarchar(SMALLDATETIME) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'smalldatetime_varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (SMALLDATETIME AS sys.VARCHAR) +WITH FUNCTION sys.smalldatetime2sysvarchar (SMALLDATETIME) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.smalldatetime2varchar(SMALLDATETIME) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'smalldatetime_varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (SMALLDATETIME AS pg_catalog.VARCHAR) +WITH FUNCTION sys.smalldatetime2varchar (SMALLDATETIME) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.smalldatetime2char(SMALLDATETIME) +RETURNS CHAR +AS 'babelfishpg_common', 'smalldatetime_char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (SMALLDATETIME AS CHAR) +WITH FUNCTION sys.smalldatetime2char (SMALLDATETIME) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.smalldatetime2bpchar(sys.SMALLDATETIME) +RETURNS sys.BPCHAR +AS 'babelfishpg_common', 'smalldatetime_char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SMALLDATETIME AS sys.BPCHAR) +WITH FUNCTION sys.smalldatetime2bpchar (sys.SMALLDATETIME) AS ASSIGNMENT; diff --git a/contrib/babelfishpg_common/sql/sqlvariant.sql b/contrib/babelfishpg_common/sql/sqlvariant.sql new file mode 100644 index 00000000000..5c7342e00ca --- /dev/null +++ b/contrib/babelfishpg_common/sql/sqlvariant.sql @@ -0,0 +1,548 @@ +CREATE TYPE sys.SQL_VARIANT; + +CREATE OR REPLACE FUNCTION sys.sqlvariantin(cstring, oid, integer) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'sqlvariantin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sqlvariantout(sys.SQL_VARIANT) +RETURNS cstring +AS 'babelfishpg_common', 'sqlvariantout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sqlvariantrecv(internal, oid, integer) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'sqlvariantrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sqlvariantsend(sys.SQL_VARIANT) +RETURNS bytea +AS 'babelfishpg_common', 'sqlvariantsend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.SQL_VARIANT ( + INPUT = sys.sqlvariantin, + OUTPUT = sys.sqlvariantout, + RECEIVE = sys.sqlvariantrecv, + SEND = sys.sqlvariantsend, + INTERNALLENGTH = VARIABLE, + ALIGNMENT = 'int4', + STORAGE = 'extended', + CATEGORY = 'U', + PREFERRED = false, + COLLATABLE = true +); + +-- DATALENGTH function for SQL_VARIANT +CREATE OR REPLACE FUNCTION sys.datalength(sys.SQL_VARIANT) +RETURNS integer +AS 'babelfishpg_common', 'datalength_sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- CAST FUNCTIONS to SQL_VARIANT + +-- cast functions from domain types are overloaded such that we support casts both in pg and tsql: +-- money/smallmoney, smallint/tinyint, varchar/nvarchar, char/nchar +-- in pg, we will have minimal support of casts since domains are not distinguished +-- in tsql, we will allow domain casts in coerce.sql such that exact type info are saved +-- this is required for sql_variant since we may call sql_variant_property() to retrieve base type + +CREATE OR REPLACE FUNCTION sys.datetime_sqlvariant(sys.DATETIME) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'datetime2sqlvariant' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.DATETIME AS sys.SQL_VARIANT) +WITH FUNCTION sys.datetime_sqlvariant (sys.DATETIME) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.datetime2_sqlvariant(sys.DATETIME2) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'datetime22sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.DATETIME2 AS sys.SQL_VARIANT) +WITH FUNCTION sys.datetime2_sqlvariant (sys.DATETIME2) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset_sqlvariant(sys.DATETIMEOFFSET) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'datetimeoffset2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.DATETIMEOFFSET AS sys.SQL_VARIANT) +WITH FUNCTION sys.datetimeoffset_sqlvariant (sys.DATETIMEOFFSET) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.smalldatetime_sqlvariant(sys.SMALLDATETIME) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'smalldatetime2sqlvariant' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SMALLDATETIME AS sys.SQL_VARIANT) +WITH FUNCTION sys.smalldatetime_sqlvariant (sys.SMALLDATETIME) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.date_sqlvariant(DATE) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'date2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (DATE AS sys.SQL_VARIANT) +WITH FUNCTION sys.date_sqlvariant (DATE) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.time_sqlvariant(TIME) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'time2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (TIME AS sys.SQL_VARIANT) +WITH FUNCTION sys.time_sqlvariant (TIME) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.float_sqlvariant(FLOAT) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'float2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (FLOAT AS sys.SQL_VARIANT) +WITH FUNCTION sys.float_sqlvariant (FLOAT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.real_sqlvariant(REAL) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'real2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (REAL AS sys.SQL_VARIANT) +WITH FUNCTION sys.real_sqlvariant (REAL) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.numeric_sqlvariant(NUMERIC) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'numeric2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (NUMERIC AS sys.SQL_VARIANT) +WITH FUNCTION sys.numeric_sqlvariant (NUMERIC) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.money_sqlvariant(FIXEDDECIMAL) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'money2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.money_sqlvariant(sys.money) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'money2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smallmoney_sqlvariant(sys.smallmoney) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'smallmoney2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (FIXEDDECIMAL AS sys.SQL_VARIANT) +WITH FUNCTION sys.money_sqlvariant (FIXEDDECIMAL) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bigint_sqlvariant(BIGINT) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'bigint2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (BIGINT AS sys.SQL_VARIANT) +WITH FUNCTION sys.bigint_sqlvariant (BIGINT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.int_sqlvariant(INT) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'int2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT AS sys.SQL_VARIANT) +WITH FUNCTION sys.int_sqlvariant (INT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.smallint_sqlvariant(SMALLINT) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'smallint2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smallint_sqlvariant(smallint) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'smallint2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.tinyint_sqlvariant(sys.tinyint) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'tinyint2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (SMALLINT AS sys.SQL_VARIANT) +WITH FUNCTION sys.smallint_sqlvariant (SMALLINT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bit_sqlvariant(sys.BIT) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'bit2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BIT AS sys.SQL_VARIANT) +WITH FUNCTION sys.bit_sqlvariant (sys.BIT) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar_sqlvariant(sys.varchar) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'varchar2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.nvarchar_sqlvariant(sys.nvarchar) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'nvarchar2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS sys.SQL_VARIANT) +WITH FUNCTION sys.varchar_sqlvariant (sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar_sqlvariant(pg_catalog.varchar) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'varchar2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS sys.SQL_VARIANT) +WITH FUNCTION sys.varchar_sqlvariant (pg_catalog.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.char_sqlvariant(CHAR) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'char2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.nchar_sqlvariant(sys.nchar) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'nchar2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (CHAR AS sys.SQL_VARIANT) +WITH FUNCTION sys.char_sqlvariant (CHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.char_sqlvariant(sys.BPCHAR) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'char2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.SQL_VARIANT) +WITH FUNCTION sys.char_sqlvariant (sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bbfvarbinary_sqlvariant(sys.BBF_VARBINARY) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'bbfvarbinary2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY AS sys.SQL_VARIANT) +WITH FUNCTION sys.bbfvarbinary_sqlvariant (sys.BBF_VARBINARY) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bbfbinary_sqlvariant(sys.BBF_BINARY) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'bbfbinary2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY AS sys.SQL_VARIANT) +WITH FUNCTION sys.bbfbinary_sqlvariant (sys.BBF_BINARY) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.uniqueidentifier_sqlvariant(sys.UNIQUEIDENTIFIER) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'uniqueidentifier2sqlvariant' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.UNIQUEIDENTIFIER AS sys.SQL_VARIANT) +WITH FUNCTION sys.uniqueidentifier_sqlvariant (sys.UNIQUEIDENTIFIER) AS IMPLICIT; + +-- CAST functions from SQL_VARIANT + +CREATE OR REPLACE FUNCTION sys.sqlvariant_datetime(sys.SQL_VARIANT) +RETURNS sys.DATETIME +AS 'babelfishpg_common', 'sqlvariant2timestamp' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.DATETIME) +WITH FUNCTION sys.sqlvariant_datetime (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_datetime2(sys.SQL_VARIANT) +RETURNS sys.DATETIME2 +AS 'babelfishpg_common', 'sqlvariant2timestamp' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.DATETIME2) +WITH FUNCTION sys.sqlvariant_datetime2 (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_datetimeoffset(sys.SQL_VARIANT) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'sqlvariant2datetimeoffset' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.DATETIMEOFFSET) +WITH FUNCTION sys.sqlvariant_datetimeoffset (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_smalldatetime(sys.SQL_VARIANT) +RETURNS sys.SMALLDATETIME +AS 'babelfishpg_common', 'sqlvariant2timestamp' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.SMALLDATETIME) +WITH FUNCTION sys.sqlvariant_smalldatetime (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_date(sys.SQL_VARIANT) +RETURNS DATE +AS 'babelfishpg_common', 'sqlvariant2date' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS DATE) +WITH FUNCTION sys.sqlvariant_date (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_time(sys.SQL_VARIANT) +RETURNS TIME +AS 'babelfishpg_common', 'sqlvariant2time' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS TIME) +WITH FUNCTION sys.sqlvariant_time (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_float(sys.SQL_VARIANT) +RETURNS FLOAT +AS 'babelfishpg_common', 'sqlvariant2float' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS FLOAT) +WITH FUNCTION sys.sqlvariant_float (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_real(sys.SQL_VARIANT) +RETURNS REAL +AS 'babelfishpg_common', 'sqlvariant2real' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS REAL) +WITH FUNCTION sys.sqlvariant_real (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_numeric(sys.SQL_VARIANT) +RETURNS NUMERIC +AS 'babelfishpg_common', 'sqlvariant2numeric' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS NUMERIC) +WITH FUNCTION sys.sqlvariant_numeric (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_money(sys.SQL_VARIANT) +RETURNS sys.MONEY +AS 'babelfishpg_common', 'sqlvariant2fixeddecimal' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_smallmoney(sys.SQL_VARIANT) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_common', 'sqlvariant2fixeddecimal' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS FIXEDDECIMAL) +WITH FUNCTION sys.sqlvariant_money (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_bigint(sys.SQL_VARIANT) +RETURNS BIGINT +AS 'babelfishpg_common', 'sqlvariant2bigint' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS BIGINT) +WITH FUNCTION sys.sqlvariant_bigint (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_int(sys.SQL_VARIANT) +RETURNS INT +AS 'babelfishpg_common', 'sqlvariant2int' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS INT) +WITH FUNCTION sys.sqlvariant_int (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_smallint(sys.SQL_VARIANT) +RETURNS SMALLINT +AS 'babelfishpg_common', 'sqlvariant2smallint' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_tinyint(sys.SQL_VARIANT) +RETURNS sys.TINYINT +AS 'babelfishpg_common', 'sqlvariant2smallint' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS SMALLINT) +WITH FUNCTION sys.sqlvariant_smallint (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_bit(sys.SQL_VARIANT) +RETURNS sys.BIT +AS 'babelfishpg_common', 'sqlvariant2bit' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.BIT) +WITH FUNCTION sys.sqlvariant_bit (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_sysvarchar(sys.SQL_VARIANT) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'sqlvariant2varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.VARCHAR) +WITH FUNCTION sys.sqlvariant_sysvarchar (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_varchar(sys.SQL_VARIANT) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'sqlvariant2varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS pg_catalog.VARCHAR) +WITH FUNCTION sys.sqlvariant_varchar (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_nvarchar(sys.SQL_VARIANT) +RETURNS sys.NVARCHAR +AS 'babelfishpg_common', 'sqlvariant2varchar' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_char(sys.SQL_VARIANT) +RETURNS CHAR +AS 'babelfishpg_common', 'sqlvariant2char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_nchar(sys.SQL_VARIANT) +RETURNS sys.NCHAR +AS 'babelfishpg_common', 'sqlvariant2char' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS CHAR) +WITH FUNCTION sys.sqlvariant_char (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_bbfvarbinary(sys.SQL_VARIANT) +RETURNS sys.VARBINARY +AS 'babelfishpg_common', 'sqlvariant2bbfvarbinary' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.BBF_VARBINARY) +WITH FUNCTION sys.sqlvariant_bbfvarbinary (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_bbfbinary(sys.SQL_VARIANT) +RETURNS sys.BINARY +AS 'babelfishpg_common', 'sqlvariant2bbfbinary' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.BBF_BINARY) +WITH FUNCTION sys.sqlvariant_bbfbinary (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.sqlvariant_uniqueidentifier(sys.SQL_VARIANT) +RETURNS sys.UNIQUEIDENTIFIER +AS 'babelfishpg_common', 'sqlvariant2uniqueidentifier' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE CAST (sys.SQL_VARIANT AS sys.UNIQUEIDENTIFIER) +WITH FUNCTION sys.sqlvariant_uniqueidentifier (sys.SQL_VARIANT) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.SQL_VARIANT_PROPERTY(sys.SQL_VARIANT, sys.VARCHAR(20)) +RETURNS sys.SQL_VARIANT +AS 'babelfishpg_common', 'sql_variant_property' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.sqlvarianteq(sys.SQL_VARIANT, sys.SQL_VARIANT) +RETURNS bool +AS 'babelfishpg_common', 'sqlvarianteq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.sqlvariantne(sys.SQL_VARIANT, sys.SQL_VARIANT) +RETURNS bool +AS 'babelfishpg_common', 'sqlvariantne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.sqlvariantlt(sys.SQL_VARIANT, sys.SQL_VARIANT) +RETURNS bool +AS 'babelfishpg_common', 'sqlvariantlt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.sqlvariantle(sys.SQL_VARIANT, sys.SQL_VARIANT) +RETURNS bool +AS 'babelfishpg_common', 'sqlvariantle' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.sqlvariantgt(sys.SQL_VARIANT, sys.SQL_VARIANT) +RETURNS bool +AS 'babelfishpg_common', 'sqlvariantgt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.sqlvariantge(sys.SQL_VARIANT, sys.SQL_VARIANT) +RETURNS bool +AS 'babelfishpg_common', 'sqlvariantge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.SQL_VARIANT, + RIGHTARG = sys.SQL_VARIANT, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.sqlvarianteq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.SQL_VARIANT, + RIGHTARG = sys.SQL_VARIANT, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.sqlvariantne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.SQL_VARIANT, + RIGHTARG = sys.SQL_VARIANT, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.sqlvariantlt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.SQL_VARIANT, + RIGHTARG = sys.SQL_VARIANT, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.sqlvariantle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.SQL_VARIANT, + RIGHTARG = sys.SQL_VARIANT, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.sqlvariantgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.SQL_VARIANT, + RIGHTARG = sys.SQL_VARIANT, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.sqlvariantge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE FUNCTION sqlvariant_cmp(sys.SQL_VARIANT, sys.SQL_VARIANT) +RETURNS INT4 +AS 'babelfishpg_common', 'sqlvariant_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sqlvariant_hash(sys.SQL_VARIANT) +RETURNS INT4 +AS 'babelfishpg_common', 'sqlvariant_hash' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.sqlvariant_ops +DEFAULT FOR TYPE sys.SQL_VARIANT USING btree AS + OPERATOR 1 < (sys.SQL_VARIANT, sys.SQL_VARIANT), + OPERATOR 2 <= (sys.SQL_VARIANT, sys.SQL_VARIANT), + OPERATOR 3 = (sys.SQL_VARIANT, sys.SQL_VARIANT), + OPERATOR 4 >= (sys.SQL_VARIANT, sys.SQL_VARIANT), + OPERATOR 5 > (sys.SQL_VARIANT, sys.SQL_VARIANT), + FUNCTION 1 sqlvariant_cmp(sys.SQL_VARIANT, sys.SQL_VARIANT); + +CREATE OPERATOR CLASS sys.sqlvariant_ops +DEFAULT FOR TYPE sys.SQL_VARIANT USING hash AS + OPERATOR 1 = (sys.SQL_VARIANT, sys.SQL_VARIANT), + FUNCTION 1 sqlvariant_hash(sys.SQL_VARIANT); diff --git a/contrib/babelfishpg_common/sql/string_operators.sql b/contrib/babelfishpg_common/sql/string_operators.sql new file mode 100644 index 00000000000..7d2b9a79a86 --- /dev/null +++ b/contrib/babelfishpg_common/sql/string_operators.sql @@ -0,0 +1,67 @@ +-- Wrap built-in CONCAT function to accept two text arguments. +-- This is necessary because CONCAT accepts arguments of type VARIADIC "any". +-- CONCAT also automatically handles NULL which || does not. +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg text, rightarg text) RETURNS TEXT AS +$$ + SELECT + CASE WHEN (current_setting('babelfishpg_tsql.concat_null_yields_null') = 'on') THEN + CASE + WHEN leftarg IS NULL OR rightarg IS NULL THEN NULL + ELSE CONCAT(leftarg, rightarg) + END + ELSE + CONCAT(leftarg, rightarg) + END +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = text, + RIGHTARG = text, + FUNCTION = sys.babelfish_concat_wrapper +); + +create or replace function sys.CHAR(x in int)returns char +AS +$body$ +BEGIN +/*************************************************************** +EXTENSION PACK function CHAR(x) +***************************************************************/ + if x between 1 and 255 then + return chr(x); + else + return null; + end if; +END; +$body$ +language plpgsql; + +CREATE OR REPLACE FUNCTION sys.nchar(IN x INTEGER) RETURNS sys.nvarchar +AS +$body$ +BEGIN + --- 1114111 is 0x10FFFF - max value permitted as specified by documentation + if x between 1 and 1114111 then + return(select chr(x))::sys.nvarchar; + else + return null; + end if; +END; +$body$ +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.nchar(IN x varbinary) RETURNS sys.nvarchar +AS +$body$ +BEGIN + --- 1114111 is 0x10FFFF - max value permitted as specified by documentation + if x::integer between 1 and 1114111 then + return(select chr(x::integer))::sys.nvarchar; + else + return null; + end if; +END; +$body$ +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE; diff --git a/contrib/babelfishpg_common/sql/strings.sql b/contrib/babelfishpg_common/sql/strings.sql new file mode 100644 index 00000000000..2f98a63bbf7 --- /dev/null +++ b/contrib/babelfishpg_common/sql/strings.sql @@ -0,0 +1,2 @@ +CREATE DOMAIN sys.NTEXT AS TEXT; +CREATE DOMAIN sys.SYSNAME AS sys.VARCHAR(128); diff --git a/contrib/babelfishpg_common/sql/uniqueidentifier.sql b/contrib/babelfishpg_common/sql/uniqueidentifier.sql new file mode 100644 index 00000000000..8c9ea554629 --- /dev/null +++ b/contrib/babelfishpg_common/sql/uniqueidentifier.sql @@ -0,0 +1,214 @@ +CREATE TYPE sys.UNIQUEIDENTIFIER; + +CREATE OR REPLACE FUNCTION sys.uniqueidentifierin(cstring) +RETURNS sys.UNIQUEIDENTIFIER +AS 'babelfishpg_common', 'uniqueidentifier_in' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.uniqueidentifierout(sys.UNIQUEIDENTIFIER) +RETURNS cstring +AS 'babelfishpg_common', 'uniqueidentifier_out' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.uniqueidentifierrecv(internal) +RETURNS sys.UNIQUEIDENTIFIER +AS 'uuid_recv' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.uniqueidentifiersend(sys.UNIQUEIDENTIFIER) +RETURNS bytea +AS 'uuid_send' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.UNIQUEIDENTIFIER ( + INPUT = sys.uniqueidentifierin, + OUTPUT = sys.uniqueidentifierout, + RECEIVE = sys.uniqueidentifierrecv, + SEND = sys.uniqueidentifiersend, + INTERNALLENGTH = 16, + ALIGNMENT = 'int4', + STORAGE = 'plain', + CATEGORY = 'U', + PREFERRED = false, + COLLATABLE = false +); + +CREATE OR REPLACE FUNCTION sys.newid() +RETURNS sys.UNIQUEIDENTIFIER +AS 'uuid-ossp', 'uuid_generate_v4' -- uuid-ossp was added as dependency +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +/* + * in tsql, NEWSEQUENTIALID() produces a new unique value + * greater than a sequence of previous values. Since PG doesn't + * have this capability, we will reuse the NEWID() functionality and be + * aware of the functional shortcoming + */ +CREATE OR REPLACE FUNCTION sys.NEWSEQUENTIALID() +RETURNS sys.UNIQUEIDENTIFIER +AS 'uuid-ossp', 'uuid_generate_v4' +LANGUAGE C VOLATILE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.uniqueidentifiereq(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER) +RETURNS bool +AS 'uuid_eq' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.uniqueidentifierne(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER) +RETURNS bool +AS 'uuid_ne' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.uniqueidentifierlt(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER) +RETURNS bool +AS 'uuid_lt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.uniqueidentifierle(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER) +RETURNS bool +AS 'uuid_le' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.uniqueidentifiergt(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER) +RETURNS bool +AS 'uuid_gt' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.uniqueidentifierge(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER) +RETURNS bool +AS 'uuid_ge' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.UNIQUEIDENTIFIER, + RIGHTARG = sys.UNIQUEIDENTIFIER, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = sys.uniqueidentifiereq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.UNIQUEIDENTIFIER, + RIGHTARG = sys.UNIQUEIDENTIFIER, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = sys.uniqueidentifierne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR sys.< ( + LEFTARG = sys.UNIQUEIDENTIFIER, + RIGHTARG = sys.UNIQUEIDENTIFIER, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = sys.uniqueidentifierlt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.UNIQUEIDENTIFIER, + RIGHTARG = sys.UNIQUEIDENTIFIER, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = sys.uniqueidentifierle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR sys.> ( + LEFTARG = sys.UNIQUEIDENTIFIER, + RIGHTARG = sys.UNIQUEIDENTIFIER, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = sys.uniqueidentifiergt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.UNIQUEIDENTIFIER, + RIGHTARG = sys.UNIQUEIDENTIFIER, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = sys.uniqueidentifierge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE FUNCTION uniqueidentifier_cmp(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER) +RETURNS INT4 +AS 'uuid_cmp' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION uniqueidentifier_hash(sys.UNIQUEIDENTIFIER) +RETURNS INT4 +AS 'uuid_hash' +LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.uniqueidentifier_ops +DEFAULT FOR TYPE sys.UNIQUEIDENTIFIER USING btree AS + OPERATOR 1 < (sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER), + OPERATOR 2 <= (sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER), + OPERATOR 3 = (sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER), + OPERATOR 4 >= (sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER), + OPERATOR 5 > (sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER), + FUNCTION 1 uniqueidentifier_cmp(sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER); + +CREATE OPERATOR CLASS sys.uniqueidentifier_ops +DEFAULT FOR TYPE sys.UNIQUEIDENTIFIER USING hash AS + OPERATOR 1 = (sys.UNIQUEIDENTIFIER, sys.UNIQUEIDENTIFIER), + FUNCTION 1 uniqueidentifier_hash(sys.UNIQUEIDENTIFIER); + +CREATE FUNCTION sys.varchar2uniqueidentifier(pg_catalog.VARCHAR, integer, boolean) +RETURNS sys.UNIQUEIDENTIFIER +AS 'babelfishpg_common', 'varchar2uniqueidentifier' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR as sys.UNIQUEIDENTIFIER) +WITH FUNCTION sys.varchar2uniqueidentifier(pg_catalog.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE FUNCTION sys.varchar2uniqueidentifier(sys.VARCHAR, integer, boolean) +RETURNS sys.UNIQUEIDENTIFIER +AS 'babelfishpg_common', 'varchar2uniqueidentifier' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR as sys.UNIQUEIDENTIFIER) +WITH FUNCTION sys.varchar2uniqueidentifier(sys.VARCHAR, integer, boolean) AS ASSIGNMENT; + + +CREATE FUNCTION sys.varbinary2uniqueidentifier(sys.bbf_varbinary, integer, boolean) +RETURNS sys.UNIQUEIDENTIFIER +AS 'babelfishpg_common', 'varbinary2uniqueidentifier' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.bbf_varbinary as sys.UNIQUEIDENTIFIER) +WITH FUNCTION sys.varbinary2uniqueidentifier(sys.bbf_varbinary, integer, boolean) AS ASSIGNMENT; + +CREATE FUNCTION sys.binary2uniqueidentifier(sys.bbf_binary, integer, boolean) +RETURNS sys.UNIQUEIDENTIFIER +AS 'babelfishpg_common', 'varbinary2uniqueidentifier' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.bbf_binary as sys.UNIQUEIDENTIFIER) +WITH FUNCTION sys.binary2uniqueidentifier(sys.bbf_binary, integer, boolean) AS ASSIGNMENT; + +CREATE FUNCTION sys.uniqueidentifier2varbinary(sys.UNIQUEIDENTIFIER, integer, boolean) +RETURNS sys.bbf_varbinary +AS 'babelfishpg_common', 'uniqueidentifier2varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.UNIQUEIDENTIFIER as sys.bbf_varbinary) +WITH FUNCTION sys.uniqueidentifier2varbinary(sys.UNIQUEIDENTIFIER, integer, boolean) AS IMPLICIT; + +CREATE FUNCTION sys.uniqueidentifier2binary(sys.UNIQUEIDENTIFIER, integer, boolean) +RETURNS sys.bbf_binary +AS 'babelfishpg_common', 'uniqueidentifier2binary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.UNIQUEIDENTIFIER as sys.bbf_binary) +WITH FUNCTION sys.uniqueidentifier2binary(sys.UNIQUEIDENTIFIER, integer, boolean) AS IMPLICIT; diff --git a/contrib/babelfishpg_common/sql/utils.sql b/contrib/babelfishpg_common/sql/utils.sql new file mode 100644 index 00000000000..8f9e1f8c8b9 --- /dev/null +++ b/contrib/babelfishpg_common/sql/utils.sql @@ -0,0 +1,16 @@ +CREATE OR REPLACE PROCEDURE sys.babel_type_initializer() +LANGUAGE C +AS 'babelfishpg_common', 'init_tcode_trans_tab'; +CALL sys.babel_type_initializer(); +DROP PROCEDURE sys.babel_type_initializer(); + +CREATE OR REPLACE FUNCTION sys.babelfish_typecode_list() +RETURNS table ( + oid int, + pg_namespace text, + pg_typname text, + tsql_typname text, + type_family_priority smallint, + priority smallint, + sql_variant_hdr_size smallint +) AS 'babelfishpg_common', 'typecode_list' LANGUAGE C; diff --git a/contrib/babelfishpg_common/sql/varbinary.sql b/contrib/babelfishpg_common/sql/varbinary.sql new file mode 100644 index 00000000000..69aa4e5f2b1 --- /dev/null +++ b/contrib/babelfishpg_common/sql/varbinary.sql @@ -0,0 +1,284 @@ +-- VARBINARY +CREATE TYPE sys.BBF_VARBINARY; + +CREATE OR REPLACE FUNCTION sys.varbinaryin(cstring, oid, integer) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'varbinaryin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varbinaryout(sys.BBF_VARBINARY) +RETURNS cstring +AS 'babelfishpg_common', 'varbinaryout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varbinaryrecv(internal, oid, integer) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'varbinaryrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varbinarysend(sys.BBF_VARBINARY) +RETURNS bytea +AS 'babelfishpg_common', 'varbinarysend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varbinarytypmodin(cstring[]) +RETURNS integer +AS 'babelfishpg_common', 'varbinarytypmodin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varbinarytypmodout(integer) +RETURNS cstring +AS 'babelfishpg_common', 'varbinarytypmodout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.BBF_VARBINARY ( + INPUT = sys.varbinaryin, + OUTPUT = sys.varbinaryout, + RECEIVE = sys.varbinaryrecv, + SEND = sys.varbinarysend, + TYPMOD_IN = sys.varbinarytypmodin, + TYPMOD_OUT = sys.varbinarytypmodout, + INTERNALLENGTH = VARIABLE, + ALIGNMENT = 'int4', + STORAGE = 'extended', + CATEGORY = 'U', + PREFERRED = false, + COLLATABLE = false +); + +CREATE OR REPLACE FUNCTION sys.varcharvarbinary(sys.VARCHAR, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'varcharvarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS sys.BBF_VARBINARY) +WITH FUNCTION sys.varcharvarbinary (sys.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varcharvarbinary(pg_catalog.VARCHAR, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'varcharvarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS sys.BBF_VARBINARY) +WITH FUNCTION sys.varcharvarbinary (pg_catalog.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharvarbinary(pg_catalog.BPCHAR, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'bpcharvarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.BPCHAR AS sys.BBF_VARBINARY) +WITH FUNCTION sys.bpcharvarbinary (pg_catalog.BPCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharvarbinary(sys.BPCHAR, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'bpcharvarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.BBF_VARBINARY) +WITH FUNCTION sys.bpcharvarbinary (sys.BPCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinarysysvarchar(sys.BBF_VARBINARY, integer, boolean) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY AS sys.VARCHAR) +WITH FUNCTION sys.varbinarysysvarchar (sys.BBF_VARBINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryvarchar(sys.BBF_VARBINARY, integer, boolean) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY AS pg_catalog.VARCHAR) +WITH FUNCTION sys.varbinaryvarchar (sys.BBF_VARBINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int2varbinary(INT2, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'int2varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT2 AS sys.BBF_VARBINARY) +WITH FUNCTION sys.int2varbinary (INT2, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int4varbinary(INT4, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'int4varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT4 AS sys.BBF_VARBINARY) +WITH FUNCTION sys.int4varbinary (INT4, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int8varbinary(INT8, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'int8varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT8 AS sys.BBF_VARBINARY) +WITH FUNCTION sys.int8varbinary (INT8, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.float4varbinary(REAL, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'float4varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (REAL AS sys.BBF_VARBINARY) +WITH FUNCTION sys.float4varbinary (REAL, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.float8varbinary(DOUBLE PRECISION, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'float8varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (DOUBLE PRECISION AS sys.BBF_VARBINARY) +WITH FUNCTION sys.float8varbinary (DOUBLE PRECISION, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryint2(sys.BBF_VARBINARY) +RETURNS INT2 +AS 'babelfishpg_common', 'varbinaryint2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY as INT2) +WITH FUNCTION sys.varbinaryint2 (sys.BBF_VARBINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryint4(sys.BBF_VARBINARY) +RETURNS INT4 +AS 'babelfishpg_common', 'varbinaryint4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY as INT4) +WITH FUNCTION sys.varbinaryint4 (sys.BBF_VARBINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryint8(sys.BBF_VARBINARY) +RETURNS INT8 +AS 'babelfishpg_common', 'varbinaryint8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY as INT8) +WITH FUNCTION sys.varbinaryint8 (sys.BBF_VARBINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryfloat4(sys.BBF_VARBINARY) +RETURNS REAL +AS 'babelfishpg_common', 'varbinaryfloat4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY as REAL) +WITH FUNCTION sys.varbinaryfloat4 (sys.BBF_VARBINARY) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryfloat8(sys.BBF_VARBINARY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_common', 'varbinaryfloat8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY as DOUBLE PRECISION) +WITH FUNCTION sys.varbinaryfloat8 (sys.BBF_VARBINARY) AS ASSIGNMENT; + +SET enable_domain_typmod = TRUE; +CREATE DOMAIN sys.VARBINARY AS sys.BBF_VARBINARY; +RESET enable_domain_typmod; + +CREATE OR REPLACE FUNCTION sys.varbinary(sys.VARBINARY, integer, boolean) +RETURNS sys.VARBINARY +AS 'babelfishpg_common', 'varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +SET client_min_messages = 'ERROR'; +CREATE CAST (sys.VARBINARY AS sys.VARBINARY) +WITH FUNCTION sys.varbinary (sys.VARBINARY, integer, BOOLEAN) AS ASSIGNMENT; +SET client_min_messages = 'WARNING'; + +-- Add support for varbinary and binary with operators +-- Support equals +CREATE FUNCTION sys.varbinary_eq(leftarg sys.bbf_varbinary, rightarg sys.bbf_varbinary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.bbf_varbinary, + RIGHTARG = sys.bbf_varbinary, + FUNCTION = sys.varbinary_eq, + COMMUTATOR = = +); + +-- Support not equals +CREATE FUNCTION sys.varbinary_neq(leftarg sys.bbf_varbinary, rightarg sys.bbf_varbinary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_neq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.bbf_varbinary, + RIGHTARG = sys.bbf_varbinary, + FUNCTION = sys.varbinary_neq, + COMMUTATOR = <> +); + +-- Support greater than +CREATE FUNCTION sys.varbinary_gt(leftarg sys.bbf_varbinary, rightarg sys.bbf_varbinary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.> ( + LEFTARG = sys.bbf_varbinary, + RIGHTARG = sys.bbf_varbinary, + FUNCTION = sys.varbinary_gt, + COMMUTATOR = < +); + +-- Support greater than equals +CREATE FUNCTION sys.varbinary_geq(leftarg sys.bbf_varbinary, rightarg sys.bbf_varbinary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_geq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.bbf_varbinary, + RIGHTARG = sys.bbf_varbinary, + FUNCTION = sys.varbinary_geq, + COMMUTATOR = <= +); + +-- Support less than +CREATE FUNCTION sys.varbinary_lt(leftarg sys.bbf_varbinary, rightarg sys.bbf_varbinary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.< ( + LEFTARG = sys.bbf_varbinary, + RIGHTARG = sys.bbf_varbinary, + FUNCTION = sys.varbinary_lt, + COMMUTATOR = > +); + +-- Support less than equals +CREATE FUNCTION sys.varbinary_leq(leftarg sys.bbf_varbinary, rightarg sys.bbf_varbinary) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_leq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.bbf_varbinary, + RIGHTARG = sys.bbf_varbinary, + FUNCTION = sys.varbinary_leq, + COMMUTATOR = >= +); + +CREATE FUNCTION sys.bbf_varbinary_cmp(sys.bbf_varbinary, sys.bbf_varbinary) +RETURNS int +AS 'babelfishpg_common', 'varbinary_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + + +CREATE OPERATOR CLASS sys.bbf_varbinary_ops +DEFAULT FOR TYPE sys.bbf_varbinary USING btree AS + OPERATOR 1 < (sys.bbf_varbinary, sys.bbf_varbinary), + OPERATOR 2 <= (sys.bbf_varbinary, sys.bbf_varbinary), + OPERATOR 3 = (sys.bbf_varbinary, sys.bbf_varbinary), + OPERATOR 4 >= (sys.bbf_varbinary, sys.bbf_varbinary), + OPERATOR 5 > (sys.bbf_varbinary, sys.bbf_varbinary), + FUNCTION 1 sys.bbf_varbinary_cmp(sys.bbf_varbinary, sys.bbf_varbinary); + diff --git a/contrib/babelfishpg_common/sql/varchar.sql b/contrib/babelfishpg_common/sql/varchar.sql new file mode 100644 index 00000000000..ce1499ed818 --- /dev/null +++ b/contrib/babelfishpg_common/sql/varchar.sql @@ -0,0 +1,200 @@ +CREATE TYPE sys.VARCHAR; + +-- Basic functions +CREATE OR REPLACE FUNCTION sys.varcharin(cstring) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'varcharin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varcharout(sys.VARCHAR) +RETURNS cstring +AS 'varcharout' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varcharrecv(internal) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'varcharrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varcharsend(sys.VARCHAR) +RETURNS bytea +AS 'varcharsend' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.VARCHAR ( + INPUT = sys.varcharin, + OUTPUT = sys.varcharout, + RECEIVE = sys.varcharrecv, + SEND = sys.varcharsend, + TYPMOD_IN = varchartypmodin, + TYPMOD_OUT = varchartypmodout, + CATEGORY = 'S', + COLLATABLE = True, + LIKE = pg_catalog.VARCHAR +); + +-- Basic operator functions +CREATE FUNCTION sys.varchareq(sys.VARCHAR, sys.VARCHAR) +RETURNS bool +AS 'babelfishpg_common', 'varchareq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.varcharne(sys.VARCHAR, sys.VARCHAR) +RETURNS bool +AS 'babelfishpg_common', 'varcharne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.varcharlt(sys.VARCHAR, sys.VARCHAR) +RETURNS bool +AS 'babelfishpg_common', 'varcharlt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.varcharle(sys.VARCHAR, sys.VARCHAR) +RETURNS bool +AS 'babelfishpg_common', 'varcharle' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.varchargt(sys.VARCHAR, sys.VARCHAR) +RETURNS bool +AS 'babelfishpg_common', 'varchargt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.varcharge(sys.VARCHAR, sys.VARCHAR) +RETURNS bool +AS 'babelfishpg_common', 'varcharge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- Basic operators +-- Note that if those operators are not in pg_catalog, we will see different behaviors depending on sql_dialect +CREATE OPERATOR pg_catalog.= ( + LEFTARG = sys.VARCHAR, + RIGHTARG = sys.VARCHAR, + COMMUTATOR = OPERATOR(pg_catalog.=), + NEGATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.varchareq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES, + HASHES +); + +CREATE OPERATOR pg_catalog.<> ( + LEFTARG = sys.VARCHAR, + RIGHTARG = sys.VARCHAR, + NEGATOR = OPERATOR(pg_catalog.=), + COMMUTATOR = OPERATOR(pg_catalog.<>), + PROCEDURE = sys.varcharne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR pg_catalog.< ( + LEFTARG = sys.VARCHAR, + RIGHTARG = sys.VARCHAR, + NEGATOR = OPERATOR(pg_catalog.>=), + COMMUTATOR = OPERATOR(pg_catalog.>), + PROCEDURE = sys.varcharlt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR pg_catalog.<= ( + LEFTARG = sys.VARCHAR, + RIGHTARG = sys.VARCHAR, + NEGATOR = OPERATOR(pg_catalog.>), + COMMUTATOR = OPERATOR(pg_catalog.>=), + PROCEDURE = sys.varcharle, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR pg_catalog.> ( + LEFTARG = sys.VARCHAR, + RIGHTARG = sys.VARCHAR, + NEGATOR = OPERATOR(pg_catalog.<=), + COMMUTATOR = OPERATOR(pg_catalog.<), + PROCEDURE = sys.varchargt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR pg_catalog.>= ( + LEFTARG = sys.VARCHAR, + RIGHTARG = sys.VARCHAR, + NEGATOR = OPERATOR(pg_catalog.<), + COMMUTATOR = OPERATOR(pg_catalog.<=), + PROCEDURE = sys.varcharge, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +-- Operator classes +CREATE FUNCTION sys.varcharcmp(sys.VARCHAR, sys.VARCHAR) +RETURNS INT4 +AS 'babelfishpg_common', 'varcharcmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.hashvarchar(sys.VARCHAR) +RETURNS INT4 +AS 'babelfishpg_common', 'hashvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS varchar_ops + DEFAULT FOR TYPE sys.VARCHAR USING btree AS + OPERATOR 1 pg_catalog.< (sys.VARCHAR, sys.VARCHAR), + OPERATOR 2 pg_catalog.<= (sys.VARCHAR, sys.VARCHAR), + OPERATOR 3 pg_catalog.= (sys.VARCHAR, sys.VARCHAR), + OPERATOR 4 pg_catalog.>= (sys.VARCHAR, sys.VARCHAR), + OPERATOR 5 pg_catalog.> (sys.VARCHAR, sys.VARCHAR), + FUNCTION 1 sys.varcharcmp(sys.VARCHAR, sys.VARCHAR); + +CREATE OPERATOR CLASS varchar_ops + DEFAULT FOR TYPE sys.VARCHAR USING hash AS + OPERATOR 1 pg_catalog.= (sys.VARCHAR, sys.VARCHAR), + FUNCTION 1 sys.hashvarchar(sys.VARCHAR); + +-- Typmode cast function +CREATE OR REPLACE FUNCTION sys.varchar(sys.VARCHAR, integer, boolean) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'varchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- To sys.VARCHAR +CREATE CAST (sys.VARCHAR AS sys.VARCHAR) +WITH FUNCTION sys.VARCHAR (sys.VARCHAR, integer, boolean) AS IMPLICIT; + +CREATE CAST (pg_catalog.VARCHAR as sys.VARCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (pg_catalog.TEXT as sys.VARCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (sys.BPCHAR as sys.VARCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (pg_catalog.BOOL as sys.VARCHAR) +WITH FUNCTION pg_catalog.text(pg_catalog.BOOL) AS ASSIGNMENT; + +-- From sys.VARCHAR +CREATE CAST (sys.VARCHAR AS pg_catalog.VARCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (sys.VARCHAR as pg_catalog.TEXT) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE CAST (sys.VARCHAR as pg_catalog.BPCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +SET enable_domain_typmod = TRUE; +CREATE DOMAIN sys.NVARCHAR AS sys.VARCHAR; +RESET enable_domain_typmod; + +CREATE OR REPLACE FUNCTION sys.nvarchar(sys.nvarchar, integer, boolean) +RETURNS sys.nvarchar +AS 'babelfishpg_common', 'varchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +SET client_min_messages = 'ERROR'; +CREATE CAST (sys.nvarchar AS sys.nvarchar) +WITH FUNCTION sys.nvarchar (sys.nvarchar, integer, BOOLEAN) AS ASSIGNMENT; +SET client_min_messages = 'WARNING'; diff --git a/contrib/babelfishpg_common/src/babelfishpg_common.c b/contrib/babelfishpg_common/src/babelfishpg_common.c new file mode 100644 index 00000000000..df0cca9d22d --- /dev/null +++ b/contrib/babelfishpg_common/src/babelfishpg_common.c @@ -0,0 +1,26 @@ +#include "postgres.h" + +#include "fmgr.h" +#include "instr.h" + +extern Datum init_tcode_trans_tab(PG_FUNCTION_ARGS); + +PG_MODULE_MAGIC; + +/* Module callbacks */ +void _PG_init(void); +void _PG_fini(void); + +void +_PG_init(void) +{ + FunctionCallInfo fcinfo = NULL; /* empty interface */ + + init_instr(); + init_tcode_trans_tab(fcinfo); +} + +void +_PG_fini(void) +{ +} diff --git a/contrib/babelfishpg_common/src/bit.c b/contrib/babelfishpg_common/src/bit.c new file mode 100644 index 00000000000..835c74f9f4d --- /dev/null +++ b/contrib/babelfishpg_common/src/bit.c @@ -0,0 +1,548 @@ +/*------------------------------------------------------------------------- + * + * bit.c + * Functions for the type "bit". + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include + +#include "libpq/pqformat.h" +#include "utils/builtins.h" +#include "utils/numeric.h" + +#include "instr.h" +#include "typecode.h" +#include "numeric.h" + + +PG_FUNCTION_INFO_V1(bitin); +PG_FUNCTION_INFO_V1(bitout); +PG_FUNCTION_INFO_V1(bitrecv); +PG_FUNCTION_INFO_V1(bitsend); +PG_FUNCTION_INFO_V1(int2bit); +PG_FUNCTION_INFO_V1(int4bit); +PG_FUNCTION_INFO_V1(int8bit); +PG_FUNCTION_INFO_V1(numeric_bit); +PG_FUNCTION_INFO_V1(ftobit); +PG_FUNCTION_INFO_V1(dtobit); +PG_FUNCTION_INFO_V1(bitneg); +PG_FUNCTION_INFO_V1(biteq); +PG_FUNCTION_INFO_V1(bitne); +PG_FUNCTION_INFO_V1(bitlt); +PG_FUNCTION_INFO_V1(bitle); +PG_FUNCTION_INFO_V1(bitgt); +PG_FUNCTION_INFO_V1(bitge); +PG_FUNCTION_INFO_V1(bit_cmp); +PG_FUNCTION_INFO_V1(bit2int2); +PG_FUNCTION_INFO_V1(bit2int4); +PG_FUNCTION_INFO_V1(bit2int8); +PG_FUNCTION_INFO_V1(bit2numeric); +PG_FUNCTION_INFO_V1(bit2fixeddec); + +/* Comparison between int and bit */ +PG_FUNCTION_INFO_V1(int4biteq); +PG_FUNCTION_INFO_V1(int4bitne); +PG_FUNCTION_INFO_V1(int4bitlt); +PG_FUNCTION_INFO_V1(int4bitle); +PG_FUNCTION_INFO_V1(int4bitgt); +PG_FUNCTION_INFO_V1(int4bitge); + +/* Comparison between bit and int */ +PG_FUNCTION_INFO_V1(bitint4eq); +PG_FUNCTION_INFO_V1(bitint4ne); +PG_FUNCTION_INFO_V1(bitint4lt); +PG_FUNCTION_INFO_V1(bitint4le); +PG_FUNCTION_INFO_V1(bitint4gt); +PG_FUNCTION_INFO_V1(bitint4ge); + +/* + * Try to interpret value as boolean value. Valid values are: true, + * false, TRUE, FALSE, digital string as well as unique prefixes thereof. + * If the string parses okay, return true, else false. + * If okay and result is not NULL, return the value in *result. + */ + +static bool +parse_bit_with_len(const char *value, size_t len, bool *result) +{ + switch (*value) + { + case 't': + case 'T': + if (len == 4 && pg_strncasecmp(value, "true", len) == 0) + { + if (result) + *result = true; + return true; + } + break; + case 'f': + case 'F': + if (len == 5 && pg_strncasecmp(value, "false", len) == 0) + { + if (result) + *result = false; + return true; + } + break; + default: + { + int i = 0; + + /* Skip the minus sign */ + if (*value == '-') + i = 1; + /* Is it all 0's? */ + for (; i < len; i++) + { + if (value[i] != '0') + break; + } + /* all 0's */ + if (i == len) + { + if (result) + *result = false; + return true; + } + + /* So it's not all 0's, is it all digits? */ + /* Skip the minus sign */ + if (*value == '-') + i = 1; + else + i = 0; + for (; i < len; i++) + { + if (!isdigit(value[i])) + break; + } + /* all digits and not all 0's, result should be true */ + if (i == len) + { + if (result) + *result = true; + return true; + } + /* not all digits, meaning invalid input */ + break; + } + } + + if (result) + *result = false; /* suppress compiler warning */ + return false; +} + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * bitin - converts "t" or "f" to 1 or 0 + * + * Check explicitly for "true/false" and TRUE/FALSE, 1/0 and any digital string + * Reject other values. + * + * In the switch statement, check the most-used possibilities first. + */ +Datum +bitin(PG_FUNCTION_ARGS) +{ + const char *in_str = PG_GETARG_CSTRING(0); + const char *str; + size_t len; + bool result; + + /* + * Skip leading and trailing whitespace + */ + str = in_str; + while (isspace((unsigned char) *str)) + str++; + + len = strlen(str); + while (len > 0 && isspace((unsigned char) str[len - 1])) + len--; + + if (parse_bit_with_len(str, len, &result)) + PG_RETURN_BOOL(result); + + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "bit", in_str))); + + /* not reached */ + PG_RETURN_BOOL(false); +} + +/* + * bitout - converts 1 or 0 to "t" or "f" + */ +Datum +bitout(PG_FUNCTION_ARGS) +{ + bool b = PG_GETARG_BOOL(0); + char *result = (char *) palloc(2); + + result[0] = (b) ? '1' : '0'; + result[1] = '\0'; + PG_RETURN_CSTRING(result); +} + +/* + * bitrecv - converts external binary format to bit + * + * The external representation is one byte. Any nonzero value is taken + * as "true". + */ +Datum +bitrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + int ext; + + INSTR_METRIC_INC(INSTR_TSQL_BIT_RECV); + + ext = pq_getmsgbyte(buf); + PG_RETURN_BOOL((ext != 0) ? true : false); +} + +/* + * bitsend - converts bit to binary format + */ +Datum +bitsend(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + StringInfoData buf; + + INSTR_METRIC_INC(INSTR_TSQL_BIT_SEND); + + pq_begintypsend(&buf); + pq_sendbyte(&buf, arg1 ? 1 : 0); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + +/* + * Cast functions + */ +Datum +int2bit(PG_FUNCTION_ARGS) +{ + int input = PG_GETARG_INT16(0); + bool result = input == 0 ? false : true; + + PG_RETURN_BOOL(result); +} + +Datum +int4bit(PG_FUNCTION_ARGS) +{ + int32 input = PG_GETARG_INT32(0); + bool result = input == 0 ? false : true; + + PG_RETURN_BOOL(result); +} + +Datum +int8bit(PG_FUNCTION_ARGS) +{ + int64 input = PG_GETARG_INT64(0); + bool result = input == 0 ? false : true; + + PG_RETURN_BOOL(result); +} + +/* Convert float4 to fixeddecimal */ +Datum +ftobit(PG_FUNCTION_ARGS) +{ + float4 arg = PG_GETARG_FLOAT4(0); + bool result = arg == 0 ? false : true; + + PG_RETURN_BOOL(result); +} + +/* Convert float8 to fixeddecimal */ +Datum +dtobit(PG_FUNCTION_ARGS) +{ + float8 arg = PG_GETARG_FLOAT8(0); + bool result = arg == 0 ? false : true; + + PG_RETURN_BOOL(result); +} + +Datum +numeric_bit(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + char *tmp; + bool result = false; + int len; + int i; + + if (numeric_is_nan(num)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert NaN to bit"))); + + tmp = DatumGetCString(DirectFunctionCall1(numeric_out, + NumericGetDatum(num))); + + len = strlen(tmp); + for(i = 0; i < len; i++) + { + /* Skip the decimal point */ + if (tmp[i] == '.') + continue; + if (tmp[i] != '0') + { + result = true; + break; + } + } + + PG_RETURN_BOOL(result); +} + +/* Arithmetic operators on bit */ +Datum +bitneg(PG_FUNCTION_ARGS) +{ + bool arg = PG_GETARG_BOOL(0); + + PG_RETURN_BOOL(arg); +} + +Datum +biteq(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 == arg2); +} + +Datum +bitne(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 != arg2); +} + +Datum +bitlt(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 < arg2); +} + +Datum +bitgt(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 > arg2); +} + +Datum +bitle(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 <= arg2); +} + +Datum +bitge(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 >= arg2); +} + +Datum +bit_cmp(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_INT32((arg1 < arg2) ? -1 : ((arg1 > arg2) ? 1 : 0)); +} + +/* Comparison between int and bit */ +Datum +int4biteq(PG_FUNCTION_ARGS) +{ + int input1 = PG_GETARG_INT32(0); + bool arg1 = input1 == 0 ? false : true; + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 == arg2); +} + +Datum +int4bitne(PG_FUNCTION_ARGS) +{ + int input1 = PG_GETARG_INT32(0); + bool arg1 = input1 == 0 ? false : true; + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 != arg2); +} + +Datum +int4bitlt(PG_FUNCTION_ARGS) +{ + int input1 = PG_GETARG_INT32(0); + bool arg1 = input1 == 0 ? false : true; + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 < arg2); +} + +Datum +int4bitle(PG_FUNCTION_ARGS) +{ + int input1 = PG_GETARG_INT32(0); + bool arg1 = input1 == 0 ? false : true; + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 <= arg2); +} + +Datum +int4bitgt(PG_FUNCTION_ARGS) +{ + int input1 = PG_GETARG_INT32(0); + bool arg1 = input1 == 0 ? false : true; + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 > arg2); +} + +Datum +int4bitge(PG_FUNCTION_ARGS) +{ + int input1 = PG_GETARG_INT32(0); + bool arg1 = input1 == 0 ? false : true; + bool arg2 = PG_GETARG_BOOL(1); + + PG_RETURN_BOOL(arg1 >= arg2); +} + +/* Comparison between bit and int */ +Datum +bitint4eq(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + int input2 = PG_GETARG_INT32(1); + bool arg2 = input2 == 0 ? false : true; + + PG_RETURN_BOOL(arg1 == arg2); +} + +Datum +bitint4ne(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + int input2 = PG_GETARG_INT32(1); + bool arg2 = input2 == 0 ? false : true; + + PG_RETURN_BOOL(arg1 != arg2); +} + +Datum +bitint4lt(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + int input2 = PG_GETARG_INT32(1); + bool arg2 = input2 == 0 ? false : true; + + PG_RETURN_BOOL(arg1 < arg2); +} + +Datum +bitint4le(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + int input2 = PG_GETARG_INT32(1); + bool arg2 = input2 == 0 ? false : true; + + PG_RETURN_BOOL(arg1 <= arg2); +} + +Datum +bitint4gt(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + int input2 = PG_GETARG_INT32(1); + bool arg2 = input2 == 0 ? false : true; + + PG_RETURN_BOOL(arg1 > arg2); +} + +Datum +bitint4ge(PG_FUNCTION_ARGS) +{ + bool arg1 = PG_GETARG_BOOL(0); + int input2 = PG_GETARG_INT32(1); + bool arg2 = input2 == 0 ? false : true; + + PG_RETURN_BOOL(arg1 >= arg2); +} + +Datum +bit2int2(PG_FUNCTION_ARGS) +{ + bool bit = PG_GETARG_BOOL(0); + + PG_RETURN_INT16(bit ? 1 : 0); +} + +Datum +bit2int4(PG_FUNCTION_ARGS) +{ + bool bit = PG_GETARG_BOOL(0); + + PG_RETURN_INT32(bit ? 1 : 0); +} + +Datum +bit2int8(PG_FUNCTION_ARGS) +{ + bool bit = PG_GETARG_BOOL(0); + + PG_RETURN_INT64(bit ? 1 : 0); +} + +Datum +bit2numeric(PG_FUNCTION_ARGS) +{ + bool bit = PG_GETARG_BOOL(0); + Numeric num = bit ? tsql_set_var_from_str_wrapper("1") : tsql_set_var_from_str_wrapper("0"); + + PG_RETURN_NUMERIC(num); +} + +Datum +bit2fixeddec(PG_FUNCTION_ARGS) +{ + bool bit = PG_GETARG_BOOL(0); + + PG_RETURN_INT64(bit ? 1*FIXEDDECIMAL_MULTIPLIER : 0); +} diff --git a/contrib/babelfishpg_common/src/coerce.c b/contrib/babelfishpg_common/src/coerce.c new file mode 100644 index 00000000000..85d33d1fe71 --- /dev/null +++ b/contrib/babelfishpg_common/src/coerce.c @@ -0,0 +1,339 @@ +/*------------------------------------------------------------------------- + * + * pltsql_coerce.c + * Datatype Coercion Utility for Babel + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/parallel.h" /* InitializingParallelWorker */ +#include "miscadmin.h" +#include "catalog/pg_authid.h" +#include "catalog/pg_cast.h" +#include "catalog/pg_type.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_namespace.h" +#include "executor/spi.h" +#include "mb/pg_wchar.h" +#include "parser/parse_coerce.h" +#include "parser/parse_func.h" +#include "utils/builtins.h" +#include "utils/float.h" +#include "utils/guc.h" +#include "common/int.h" +#include "utils/int8.h" +#include "utils/numeric.h" +#include "utils/memutils.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + + +#include + +/* + * Additional Casting Functions for T-SQL + * + * Some castings in T-SQL has different behavior with PG. + * (i.e. real datatype to integral type - PG uses round but T-SQL uses trunc) + */ + +// dtrunc in float.c +inline static float8 dtrunc_(float8 arg1) +{ + float8 result; + + if (arg1 >= 0) + result = floor(arg1); + else + result = -floor(-arg1); + + return result; +} + +inline static float4 ftrunc_(float4 arg1) +{ + float8 result; + + if (arg1 >= 0) + result = floor(arg1); + else + result = -floor(-arg1); + + return result; +} + +/* dtrunci8(X) = dtoi8(dtrunc(X)) */ +PG_FUNCTION_INFO_V1(dtrunci8); + +Datum +dtrunci8(PG_FUNCTION_ARGS) +{ + float8 num = PG_GETARG_FLOAT8(0); + + /* + * Get rid of any fractional part in the input. This is so we don't fail + * on just-out-of-range values that would round into range. Note + * assumption that rint() will pass through a NaN or Inf unchanged. + */ + num = rint(dtrunc_(num)); + + /* Range check */ + if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT64(num))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT64((int64) num); +} + + +/* dtrunci4(X) = dtoi4(dtrunc(X)) */ +PG_FUNCTION_INFO_V1(dtrunci4); + +Datum +dtrunci4(PG_FUNCTION_ARGS) +{ + float8 num = PG_GETARG_FLOAT8(0); + + /* + * Get rid of any fractional part in the input. This is so we don't fail + * on just-out-of-range values that would round into range. Note + * assumption that rint() will pass through a NaN or Inf unchanged. + */ + num = rint(dtrunc_(num)); + + /* Range check */ + if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT32(num))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT32((int32) num); +} + + +/* dtrunci2(X) = dtoi2(dtrunc(X)) */ +PG_FUNCTION_INFO_V1(dtrunci2); + +Datum +dtrunci2(PG_FUNCTION_ARGS) +{ + float8 num = PG_GETARG_FLOAT8(0); + + /* + * Get rid of any fractional part in the input. This is so we don't fail + * on just-out-of-range values that would round into range. Note + * assumption that rint() will pass through a NaN or Inf unchanged. + */ + num = rint(dtrunc_(num)); + + /* Range check */ + if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT16(num))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + + PG_RETURN_INT16((int16) num); +} + + +/* ftrunci8(X) = ftoi8(ftrunc(X)) */ +PG_FUNCTION_INFO_V1(ftrunci8); + +Datum +ftrunci8(PG_FUNCTION_ARGS) +{ + float4 num = PG_GETARG_FLOAT4(0); + + /* + * Get rid of any fractional part in the input. This is so we don't fail + * on just-out-of-range values that would round into range. Note + * assumption that rint() will pass through a NaN or Inf unchanged. + */ + num = rint(ftrunc_(num)); + + /* Range check */ + if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT64(num))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT64((int64) num); +} + + +/* ftrunci4(X) = ftoi4(ftrunc(X)) */ +PG_FUNCTION_INFO_V1(ftrunci4); + +Datum +ftrunci4(PG_FUNCTION_ARGS) +{ + float4 num = PG_GETARG_FLOAT4(0); + + /* + * Get rid of any fractional part in the input. This is so we don't fail + * on just-out-of-range values that would round into range. Note + * assumption that rint() will pass through a NaN or Inf unchanged. + */ + num = rint(ftrunc_(num)); + + /* Range check */ + if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT32(num))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT32((int32) num); +} + + +/* ftrunci2(X) = ftoi2(ftrunc(X)) */ +PG_FUNCTION_INFO_V1(ftrunci2); + +Datum +ftrunci2(PG_FUNCTION_ARGS) +{ + float4 num = PG_GETARG_FLOAT4(0); + + /* + * Get rid of any fractional part in the input. This is so we don't fail + * on just-out-of-range values that would round into range. Note + * assumption that rint() will pass through a NaN or Inf unchanged. + */ + num = rint(ftrunc_(num)); + + /* Range check */ + if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT16(num))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT16((int16) num); +} + + + +PG_FUNCTION_INFO_V1(pltsql_text_name); +PG_FUNCTION_INFO_V1(pltsql_bpchar_name); + +/* replace text_name() to handle t-sql identifier truncation */ +Datum +pltsql_text_name(PG_FUNCTION_ARGS) +{ + text *s = PG_GETARG_TEXT_PP(0); + Name result; + int len; + const char *saved_dialect = GetConfigOption("babelfishpg_tsql.sql_dialect", true, true); + + len = VARSIZE_ANY_EXHDR(s); + + /* Truncate oversize input */ + if (len >= NAMEDATALEN) + { + if (cstr_to_name_hook) /* to apply special truncation logic */ + { + Name n; + PG_TRY(); + { + /* T-SQL casting. follow T-SQL truncation rule */ + set_config_option("babelfishpg_tsql.sql_dialect", "tsql", + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + n = (*cstr_to_name_hook)(VARDATA_ANY(s), len); + } + PG_CATCH(); + { + set_config_option("babelfishpg_tsql.sql_dialect", saved_dialect, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + PG_RE_THROW(); + } + PG_END_TRY(); + set_config_option("babelfishpg_tsql.sql_dialect", saved_dialect, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + + PG_RETURN_NAME(n); + } + + len = pg_mbcliplen(VARDATA_ANY(s), len, NAMEDATALEN - 1); + } + + /* We use palloc0 here to ensure result is zero-padded */ + result = (Name) palloc0(NAMEDATALEN); + memcpy(NameStr(*result), VARDATA_ANY(s), len); + + PG_RETURN_NAME(result); +} + +/* replace bpchar_name() to handle t-sql identifier truncation */ +Datum +pltsql_bpchar_name(PG_FUNCTION_ARGS) +{ + BpChar *s = PG_GETARG_BPCHAR_PP(0); + char *s_data; + Name result; + int len; + const char *saved_dialect = GetConfigOption("babelfishpg_tsql.sql_dialect", true, true); + + len = VARSIZE_ANY_EXHDR(s); + s_data = VARDATA_ANY(s); + + /* Truncate oversize input */ + if (len >= NAMEDATALEN) + { + if (cstr_to_name_hook) /* to apply special truncation logic */ + { + Name n; + + /* Remove trailing blanks */ + while (len > 0) + { + if (s_data[len - 1] != ' ') + break; + len--; + } + + PG_TRY(); + { + /* T-SQL casting. follow T-SQL truncation rule */ + set_config_option("babelfishpg_tsql.sql_dialect", "tsql", + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + n = (*cstr_to_name_hook)(VARDATA_ANY(s), len); + } + PG_CATCH(); + { + set_config_option("babelfishpg_tsql.sql_dialect", saved_dialect, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + PG_RE_THROW(); + } + PG_END_TRY(); + set_config_option("babelfishpg_tsql.sql_dialect", saved_dialect, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + + PG_RETURN_NAME(n); + } + + len = pg_mbcliplen(s_data, len, NAMEDATALEN - 1); + } + + /* Remove trailing blanks */ + while (len > 0) + { + if (s_data[len - 1] != ' ') + break; + len--; + } + + /* We use palloc0 here to ensure result is zero-padded */ + result = (Name) palloc0(NAMEDATALEN); + memcpy(NameStr(*result), s_data, len); + + PG_RETURN_NAME(result); +} diff --git a/contrib/babelfishpg_common/src/datetime.c b/contrib/babelfishpg_common/src/datetime.c new file mode 100644 index 00000000000..e3a68147bda --- /dev/null +++ b/contrib/babelfishpg_common/src/datetime.c @@ -0,0 +1,600 @@ +/*------------------------------------------------------------------------- + * + * datetime.c + * Functions for the type "datetime". + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datetime.h" +#include "utils/timestamp.h" +#include "libpq/pqformat.h" + +#include "miscadmin.h" +#include "datetime.h" + + +PG_FUNCTION_INFO_V1(datetime_in); +PG_FUNCTION_INFO_V1(datetime_out); +PG_FUNCTION_INFO_V1(datetime_recv); +PG_FUNCTION_INFO_V1(date_datetime); +PG_FUNCTION_INFO_V1(time_datetime); +PG_FUNCTION_INFO_V1(timestamp_datetime); +PG_FUNCTION_INFO_V1(timestamptz_datetime); +PG_FUNCTION_INFO_V1(datetime_varchar); +PG_FUNCTION_INFO_V1(varchar_datetime); +PG_FUNCTION_INFO_V1(datetime_char); +PG_FUNCTION_INFO_V1(char_datetime); +PG_FUNCTION_INFO_V1(datetime_pl_int4); +PG_FUNCTION_INFO_V1(int4_mi_datetime); +PG_FUNCTION_INFO_V1(int4_pl_datetime); +PG_FUNCTION_INFO_V1(datetime_mi_int4); + +PG_FUNCTION_INFO_V1(datetime_pl_float8); +PG_FUNCTION_INFO_V1(datetime_mi_float8); +PG_FUNCTION_INFO_V1(float8_pl_datetime); +PG_FUNCTION_INFO_V1(float8_mi_datetime); + + + +void CheckDatetimeRange(const Timestamp time); +void CheckDatetimePrecision(fsec_t fsec); +Datum datetime_in_str(char *str); + +Datum +datetime_in_str(char *str) +{ +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + Timestamp result; + fsec_t fsec; + struct pg_tm tt, + *tm = &tt; + int tz; + int dtype; + int nf; + int dterr; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char workbuf[MAXDATELEN + MAXDATEFIELDS]; + + dterr = ParseDateTime(str, workbuf, sizeof(workbuf), + field, ftype, MAXDATEFIELDS, &nf); + if (dterr == 0) + dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); + // dterr == 1 means that input is TIME format(e.g 12:34:59.123) + // initialize other necessary date parts and accept input format + if (dterr == 1) + { + tm->tm_year = 1900; + tm->tm_mon = 1; + tm->tm_mday = 1; + dterr = 0; + } + if (dterr != 0) + DateTimeParseError(dterr, str, "datetime"); + switch (dtype) + { + case DTK_DATE: + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetime out of range: \"%s\"", str))); + break; + + case DTK_EPOCH: + result = SetEpochTimestamp(); + break; + + case DTK_LATE: + TIMESTAMP_NOEND(result); + break; + + case DTK_EARLY: + TIMESTAMP_NOBEGIN(result); + break; + + default: + elog(ERROR, "unexpected dtype %d while parsing datetime \"%s\"", + dtype, str); + TIMESTAMP_NOEND(result); + } + /* TODO: round datetime fsec to fixed bins (e.g. .000, .003, .007) + * see: BABEL-1081 + */ + CheckDatetimeRange(result); + CheckDatetimePrecision(fsec); + + PG_RETURN_TIMESTAMP(result); + +} + +/* datetime_in() + * Convert a string to internal form. + * Most parts of this functions is same as timestamp_in(), + * but we use a different rounding function for datetime. + */ +Datum +datetime_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + return datetime_in_str(str); +} + +/* datetime_out() + * Convert a datetime to external form. + */ +Datum +datetime_out(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + char *result; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + EncodeSpecialTimestamp(timestamp, buf); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + { + // round fractional seconds to datetime precision + fsec = DTROUND(fsec); + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + } + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + result = pstrdup(buf); + PG_RETURN_CSTRING(result); +} + +/* + * CheckDatetimeRange --- Check if timestamp is out of range for datetime + */ +void +CheckDatetimeRange(const Timestamp time) +{ + if (!IS_VALID_DATETIME(time)) + { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime"))); + } +} + +/* + * CheckDatetimePrecision --- Check precision for datetime + */ +void +CheckDatetimePrecision(fsec_t fsec) +{ + if (!IS_VALID_DT_PRECISION(fsec)) + { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data precision out of range for datetime"))); + } +} + +/* date_datetime() + * Convert date to datetime + */ +Datum +date_datetime(PG_FUNCTION_ARGS) +{ + DateADT dateVal = PG_GETARG_DATEADT(0); + Timestamp result; + + if (DATE_IS_NOBEGIN(dateVal)) + TIMESTAMP_NOBEGIN(result); + else if (DATE_IS_NOEND(dateVal)) + TIMESTAMP_NOEND(result); + else + result = dateVal * USECS_PER_DAY; + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* time_datetime() + * Convert time to datetime + */ +Datum +time_datetime(PG_FUNCTION_ARGS) +{ + TimeADT timeVal = PG_GETARG_TIMEADT(0); + Timestamp result; + + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + + // Initialize default year, month, day + tm->tm_year = 1900; + tm->tm_mon = 1; + tm->tm_mday = 1; + + // Convert TimeADT type to tm + time2tm(timeVal, tm, &fsec); + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime"))); + + PG_RETURN_TIMESTAMP(result); +} + +/* timestamp_datetime() + * Convert timestamp to datetime + */ +Datum +timestamp_datetime(PG_FUNCTION_ARGS) +{ + Timestamp result = PG_GETARG_TIMESTAMP(0); + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* timestamptz_datetime() + * Convert timestamptz to datetime + */ +Datum +timestamptz_datetime(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); + Timestamp result; + + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + result = timestamp; + else + { + if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime"))); + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime"))); + } + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* datetime_varchar() + * Convert a datetime to varchar. + */ +Datum +datetime_varchar(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + char *s; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + VarChar *result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + EncodeSpecialTimestamp(timestamp, buf); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + { + // round fractional seconds to datetime precision + fsec = DTROUND(fsec); + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + } + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + s = pstrdup(buf); + result = (VarChar *) cstring_to_text(s); + PG_RETURN_VARCHAR_P(result); +} + +/* + * varchar_datetime() + * Convert a VARCHAR to datetime + */ +Datum +varchar_datetime(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + char *str = TextDatumGetCString(txt); + + return datetime_in_str(str); +} + +/* datetime_char() + * Convert a datetime to char. + */ +Datum +datetime_char(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + char *s; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + VarChar *result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + EncodeSpecialTimestamp(timestamp, buf); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + { + // round fractional seconds to datetime precision + fsec = DTROUND(fsec); + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + } + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + s = pstrdup(buf); + result = (BpChar *) cstring_to_text(s); + PG_RETURN_BPCHAR_P(result); +} + +/* + * char_datetime() + * Convert a CHAR type to datetime + */ +Datum +char_datetime(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + char *str = TextDatumGetCString(txt); + + return datetime_in_str(str); +} + +/* + * datetime_pl_int4() + * operator function for adding datetime plus int + * + * simply add number of days to date value, while preserving the time + * component + */ +Datum +datetime_pl_int4(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + int32 days = PG_GETARG_INT32(1); + Timestamp result; + Interval *input_interval; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, days, 0, 0, 0); + + /* add interval */ + result = DirectFunctionCall2(timestamp_pl_interval, timestamp, PointerGetDatum(input_interval)); + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * int4_mi_datetime() + * Operator function for subtracting int minus datetime + * + * Convert the input int32 value d to datetime(1/1/1900) + d days. + * Then add the difference between the input datetime value and the one + * above to the default datetime value (1/1/1900). + * + * ex: + * d = 9, dt = '1/11/1900' + * dt_left = datetime(1/1/1900) + 9 days = datetime(1/10/1900) + * diff = dt_left - dt = -1 day + * result = 1/1/1900 + diff = 1899-12-31 + */ +Datum +int4_mi_datetime(PG_FUNCTION_ARGS) +{ + int32 days = PG_GETARG_INT32(0); + Timestamp timestamp_right = PG_GETARG_TIMESTAMP(1); + Timestamp result; + Timestamp default_timestamp; + Timestamp timestamp_left; + Interval *input_interval; + Interval *result_interval; + + if (TIMESTAMP_NOT_FINITE(timestamp_right)) + PG_RETURN_TIMESTAMP(timestamp_right); + + /* inialize input int(days) as timestamp */ + default_timestamp = DirectFunctionCall6(make_timestamp, 1900, 1, 1, 0, 0,0); + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, days, 0, 0, 0); + timestamp_left = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(input_interval)); + + /* calculate timestamp diff */ + result_interval = (Interval *) DirectFunctionCall2(timestamp_mi, timestamp_left, timestamp_right); + + /* if the diff between left and right timestamps is positive, then we add the interval. else, subtract */ + result = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(result_interval)); + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * int4_pl_datetime() + * operator function for adding int plus datetime + */ +Datum +int4_pl_datetime(PG_FUNCTION_ARGS) +{ + int32 days = PG_GETARG_INT32(0); + Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + PG_RETURN_TIMESTAMP(DirectFunctionCall2(datetime_pl_int4, timestamp, days)); +} + +/* + * datetime_mi_int4() + * operator function for subtracting datetime minus int + */ +Datum +datetime_mi_int4(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + int32 days = PG_GETARG_INT32(1); + PG_RETURN_TIMESTAMP(DirectFunctionCall2(datetime_pl_int4, timestamp, -days)); +} + + +/* + * datetime_pl_float8() + * operator function for adding datetime plus float + * + * simply add number of days/secs to date value, while preserving the time + * component + */ +Datum +datetime_pl_float8(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + double days = PG_GETARG_FLOAT8(1); + double day_whole, day_fract, sec_whole; + Interval *input_interval; + Timestamp result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + + /* add interval */ + result = DirectFunctionCall2(timestamp_pl_interval, timestamp, PointerGetDatum(input_interval)); + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + + +/* + * float8_mi_datetime() + * Operator function for subtracting float8 minus datetime + * + * Convert the input float8 value d to datetime(1/1/1900) + d days. + * Then add the difference between the input datetime value and the one + * above to the default datetime value (1/1/1900). + */ +Datum +float8_mi_datetime(PG_FUNCTION_ARGS) +{ + double days = PG_GETARG_FLOAT8(0); + Timestamp timestamp_right = PG_GETARG_TIMESTAMP(1); + double day_whole, day_fract, sec_whole; + Timestamp result; + Timestamp default_timestamp; + Timestamp timestamp_left; + Interval *input_interval; + Interval *result_interval; + + if (TIMESTAMP_NOT_FINITE(timestamp_right)) + PG_RETURN_TIMESTAMP(timestamp_right); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + + + /* inialize input int(days) as timestamp */ + default_timestamp = DirectFunctionCall6(make_timestamp, 1900, 1, 1, 0, 0,0); + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + timestamp_left = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(input_interval)); + + /* calculate timestamp diff */ + result_interval = (Interval *) DirectFunctionCall2(timestamp_mi, timestamp_left, timestamp_right); + + /* if the diff between left and right timestamps is positive, then we add the interval. else, subtract */ + result = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(result_interval)); + + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * float8_pl_datetime() + * operator function for adding float8 plus datetime + */ +Datum +float8_pl_datetime(PG_FUNCTION_ARGS) +{ + double days = PG_GETARG_FLOAT8(0); + Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + double day_whole, day_fract, sec_whole; + Interval *input_interval; + Timestamp result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + + /* add interval */ + result = DirectFunctionCall2(timestamp_pl_interval, timestamp, PointerGetDatum(input_interval)); + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * datetime_mi_float8() + * operator function for subtracting datetime minus float8 + */ +Datum +datetime_mi_float8(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + double days = PG_GETARG_FLOAT8(1); + double day_whole, day_fract, sec_whole; + Interval *input_interval; + Timestamp result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + + + /* subtract interval */ + result = DirectFunctionCall2(timestamp_mi_interval, timestamp, PointerGetDatum(input_interval)); + + CheckDatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} \ No newline at end of file diff --git a/contrib/babelfishpg_common/src/datetime.h b/contrib/babelfishpg_common/src/datetime.h new file mode 100644 index 00000000000..8e6cc69e5a9 --- /dev/null +++ b/contrib/babelfishpg_common/src/datetime.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * datetime.h + * Definitions for the TSQL "datetime" type. + * + *------------------------------------------------------------------------- + */ +#ifndef PLTSQL_DATETIME_H +#define PLTSQL_DATETIME_H + +/* Round off to MAX_DATETIME_PRECISION decimal places. */ +#define DT_PREC_INV 1000 +#define DTROUND(j) ((((int) (j / DT_PREC_INV)) * DT_PREC_INV)) + +/* TODO: round datetime fsec to fixed bins (e.g. .000, .003, .007) + * see: BABEL-1081 + */ + +/* Check precision is valid for datetime */ +#define IS_VALID_DT_PRECISION(j) (j % (int) DT_PREC_INV == 0) + +/* Datetime limits */ +/* lower bound: 1753-01-01 00:00:00.000 */ +#define MIN_DATETIME INT64CONST(-7794489600000000) +/* upper bond: 9999-12-31 23:59:29.999 */ +#define END_DATETIME INT64CONST(252455615999999000) + +/* Range-check a datetime */ +#define IS_VALID_DATETIME(t) (MIN_DATETIME <= (t) && (t) < END_DATETIME) + +#endif /* PLTSQL_DATETIME_H */ diff --git a/contrib/babelfishpg_common/src/datetime2.c b/contrib/babelfishpg_common/src/datetime2.c new file mode 100644 index 00000000000..9bd3c2150e6 --- /dev/null +++ b/contrib/babelfishpg_common/src/datetime2.c @@ -0,0 +1,416 @@ +/*------------------------------------------------------------------------- + * + * datetime2.c + * Functions for the type "datetime2". + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datetime.h" +#include "utils/timestamp.h" +#include "libpq/pqformat.h" + +#include "miscadmin.h" +#include "datetime2.h" + + +PG_FUNCTION_INFO_V1(datetime2_in); +PG_FUNCTION_INFO_V1(datetime2_out); +PG_FUNCTION_INFO_V1(datetime2_recv); +PG_FUNCTION_INFO_V1(date_datetime2); +PG_FUNCTION_INFO_V1(time_datetime2); +PG_FUNCTION_INFO_V1(timestamp_datetime2); +PG_FUNCTION_INFO_V1(timestamptz_datetime2); +PG_FUNCTION_INFO_V1(datetime2_scale); +PG_FUNCTION_INFO_V1(datetime2_varchar); +PG_FUNCTION_INFO_V1(varchar_datetime2); +PG_FUNCTION_INFO_V1(datetime2_char); +PG_FUNCTION_INFO_V1(char_datetime2); + +static void AdjustDatetime2ForTypmod(Timestamp *time, int32 typmod); +static Datum datetime2_in_str(char *str, int32 typmod); +void CheckDatetime2Range(const Timestamp time); + +/* datetime2_in_str() + * Convert a string to internal form. + * Most parts of this functions is same as timestamp_in(), + * but we use a different rounding function for datetime2. + */ +static Datum +datetime2_in_str(char *str, int32 typmod) +{ + Timestamp result; + fsec_t fsec; + struct pg_tm tt, + *tm = &tt; + int tz; + int dtype; + int nf; + int dterr; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char workbuf[MAXDATELEN + MAXDATEFIELDS]; + + dterr = ParseDateTime(str, workbuf, sizeof(workbuf), + field, ftype, MAXDATEFIELDS, &nf); + if (dterr == 0) + dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); + + /* + * dterr == 1 means that input is TIME format(e.g 12:34:59.123) + * initialize other necessary date parts and accept input format + */ + if (dterr == 1) + { + tm->tm_year = 1900; + tm->tm_mon = 1; + tm->tm_mday = 1; + dterr = 0; + } + + if (dterr != 0) + DateTimeParseError(dterr, str, "datetime2"); + + /* + * Caps upper limit on fractional seconds(999999 microseconds) so + * that the upper boundary for datetime2 is not exceeded when + * the Date and Time parts are at the upper value limit + */ + if ((fsec == USECS_PER_SEC) && + (tm->tm_year == 9999) && + (tm->tm_mon == 12) && + (tm->tm_mday == 31) && + (tm->tm_hour == 23) && + (tm->tm_min == 59) && + (tm->tm_sec == 59)) + fsec = 999999; + + switch (dtype) + { + case DTK_DATE: + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetime2 out of range: \"%s\"", str))); + break; + + case DTK_EPOCH: + result = SetEpochTimestamp(); + break; + + case DTK_LATE: + TIMESTAMP_NOEND(result); + break; + + case DTK_EARLY: + TIMESTAMP_NOBEGIN(result); + break; + + default: + elog(ERROR, "unexpected dtype %d while parsing datetime2 \"%s\"", + dtype, str); + TIMESTAMP_NOEND(result); + } + AdjustDatetime2ForTypmod(&result, typmod); + CheckDatetime2Range(result); + + PG_RETURN_TIMESTAMP(result); +} + +/* datetime2_in() + * Convert a string to internal form. + * Most parts of this functions is same as timestamp_in(), + * but we use a different rounding function for datetime2. + */ +Datum +datetime2_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + #ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); + #endif + int32 typmod = PG_GETARG_INT32(2); + + return datetime2_in_str(str, typmod); +} + +/* AdjustDatetime2ForTypmod() + * round off a datetime2 to suit given typmod + */ +static void +AdjustDatetime2ForTypmod(Timestamp *time, int32 typmod) +{ + static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = { + INT64CONST(1000000), + INT64CONST(100000), + INT64CONST(10000), + INT64CONST(1000), + INT64CONST(100), + INT64CONST(10), + INT64CONST(1) + }; + + static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = { + INT64CONST(500000), + INT64CONST(50000), + INT64CONST(5000), + INT64CONST(500), + INT64CONST(50), + INT64CONST(5), + INT64CONST(0) + }; + + /* new offset for negative timestamp value */ + static const int64 TimestampOffsetsNegative[MAX_TIMESTAMP_PRECISION + 1] = { + INT64CONST(499999), + INT64CONST(49999), + INT64CONST(4999), + INT64CONST(499), + INT64CONST(49), + INT64CONST(4), + INT64CONST(0) + }; + + int64 adjustedTime; + + if (!TIMESTAMP_NOT_FINITE(*time) + && (typmod != -1)) + { + if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("datetime2(%d) precision must be between %d and %d", + typmod, 0, MAX_TIMESTAMP_PRECISION))); + + if (*time >= INT64CONST(0)) + { + adjustedTime = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) * + TimestampScales[typmod]; + /* Make sure typmod doesn't push datetime2 out of range */ + if (adjustedTime < END_DATETIME2) + *time = adjustedTime; + /* + * If applying typmod pushes datetime2 out of range, simply + * truncate fractional seconds to typmod precision + */ + else + { + *time = (*time / TimestampScales[typmod]) * TimestampScales[typmod]; + } + } + else + { + *time = -((((-*time) + TimestampOffsetsNegative[typmod]) / TimestampScales[typmod]) + * TimestampScales[typmod]); + } + } +} + +/* + * CheckDatetime2Range() + * Check if timestamp is out of range for datetime2 + */ +void +CheckDatetime2Range(const Timestamp time) +{ + if (!IS_VALID_DATETIME2(time)) + { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime2"))); + } +} + +/* date_datetime2() + * Convert date to datetime2 + */ +Datum +date_datetime2(PG_FUNCTION_ARGS) +{ + DateADT dateVal = PG_GETARG_DATEADT(0); + Timestamp result; + + if (DATE_IS_NOBEGIN(dateVal)) + TIMESTAMP_NOBEGIN(result); + else if (DATE_IS_NOEND(dateVal)) + TIMESTAMP_NOEND(result); + else + result = dateVal * USECS_PER_DAY; + + PG_RETURN_TIMESTAMP(result); +} + +/* time_datetime2() + * Convert time to datetime2 + */ +Datum +time_datetime2(PG_FUNCTION_ARGS) +{ + TimeADT timeVal = PG_GETARG_TIMEADT(0); + Timestamp result; + + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + + /* Initialize default year, month, day */ + tm->tm_year = 1900; + tm->tm_mon = 1; + tm->tm_mday = 1; + + /* Convert TimeADT type to tm */ + time2tm(timeVal, tm, &fsec); + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime2"))); + + PG_RETURN_TIMESTAMP(result); +} + +/* timestamp_datetime2() + * Convert timestamp to datetime2 + */ +Datum +timestamp_datetime2(PG_FUNCTION_ARGS) +{ + Timestamp result = PG_GETARG_TIMESTAMP(0); + CheckDatetime2Range(result); + PG_RETURN_TIMESTAMP(result); +} + +/* timestamptz_datetime2() + * Convert timestamptz to datetime2 + */ +Datum +timestamptz_datetime2(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); + Timestamp result; + + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + result = timestamp; + else + { + if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime2"))); + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetime2"))); + } + CheckDatetime2Range(result); + PG_RETURN_TIMESTAMP(result); +} + +/* datetime2_scale() + * Adjust datetime2_scale type for specified scale factor. + * Used by PostgreSQL type system to stuff columns. + */ +Datum +datetime2_scale(PG_FUNCTION_ARGS) +{ + Timestamp result = PG_GETARG_TIMESTAMP(0); + int32 typmod = PG_GETARG_INT32(1); + + AdjustDatetime2ForTypmod(&result, typmod); + PG_RETURN_TIMESTAMP(result); +} + +/* datetime2_varchar() + * Convert a datetime2 to varchar. + * The function is the same as timestamp_out() except the return type is a VARCHAR Datum. + */ +Datum +datetime2_varchar(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + char *s; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + VarChar *result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + EncodeSpecialTimestamp(timestamp, buf); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + s = pstrdup(buf); + result = (VarChar *) cstring_to_text(s); + PG_RETURN_VARCHAR_P(result); +} + +/* + * varchar_datetime2() + * Convert a varchar to datetime2 + */ +Datum +varchar_datetime2(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + char *str = TextDatumGetCString(txt); + + return datetime2_in_str(str, MAX_TIMESTAMP_PRECISION); +} + +/* datetime2_char() + * Convert a datetim2 to char. + * The function is the same as timestamp_out() except the return type is a CHAR Datum. + */ +Datum +datetime2_char(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + char *s; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + VarChar *result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + EncodeSpecialTimestamp(timestamp, buf); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + s = pstrdup(buf); + result = (BpChar *) cstring_to_text(s); + PG_RETURN_BPCHAR_P(result); +} + +/* + * char_datetime2() + * Convert a CHAR to datetim2 + */ +Datum +char_datetime2(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + char *str = TextDatumGetCString(txt); + + return datetime2_in_str(str, MAX_TIMESTAMP_PRECISION); +} + + + diff --git a/contrib/babelfishpg_common/src/datetime2.h b/contrib/babelfishpg_common/src/datetime2.h new file mode 100644 index 00000000000..cfe285fc832 --- /dev/null +++ b/contrib/babelfishpg_common/src/datetime2.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- +* +* datetime2.h +* Definitions for the TSQL "datetime2" type. +* +*------------------------------------------------------------------------- +*/ +#ifndef PLTSQL_DATETIME2_H +#define PLTSQL_DATETIME2_H + +/* Maximum precision for datetime2 + * TODO: alter datetime2 to have max precision == 7 + */ +#define MAX_DATETIME2_PRECISION 6 + +/* Datetime2 limits */ +/* lower bound: 0001-01-01 00:00:00.000 */ +#define MIN_DATETIME2 INT64CONST(-63082281600000000) +/* upper bound: 10000-00-00 00:00:00 */ +#define END_DATETIME2 INT64CONST(252455616000000000) + +/* Range-check a datetime */ +#define IS_VALID_DATETIME2(t) (MIN_DATETIME2 <= (t) && (t) < END_DATETIME2) + +#endif /* PLTSQL_DATETIME2_H */ \ No newline at end of file diff --git a/contrib/babelfishpg_common/src/datetimeoffset.c b/contrib/babelfishpg_common/src/datetimeoffset.c new file mode 100644 index 00000000000..379f10b1ccd --- /dev/null +++ b/contrib/babelfishpg_common/src/datetimeoffset.c @@ -0,0 +1,773 @@ +/*------------------------------------------------------------------------- + * + * datetimeoffset.c + * Functions for the type "datetimeoffset". + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "access/hash.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datetime.h" +#include "libpq/pqformat.h" +#include "utils/timestamp.h" + +#include "fmgr.h" +#include "miscadmin.h" +#include "datetimeoffset.h" + +static void AdjustDatetimeoffsetForTypmod(Timestamp *time, int32 typmod); +static void CheckDatetimeoffsetRange(const tsql_datetimeoffset* df); +static int datetimeoffset_cmp_internal(tsql_datetimeoffset* df1, tsql_datetimeoffset* df2); +static void datetimeoffset_timestamp_internal(const tsql_datetimeoffset *df, Timestamp* time); +static void EncodeDatetimeoffsetTimezone(char *str, int tz, int style); + +PG_FUNCTION_INFO_V1(datetimeoffset_in); +PG_FUNCTION_INFO_V1(datetimeoffset_out); +PG_FUNCTION_INFO_V1(datetimeoffset_recv); +PG_FUNCTION_INFO_V1(datetimeoffset_send); + +PG_FUNCTION_INFO_V1(datetimeoffset_eq); +PG_FUNCTION_INFO_V1(datetimeoffset_ne); +PG_FUNCTION_INFO_V1(datetimeoffset_lt); +PG_FUNCTION_INFO_V1(datetimeoffset_le); +PG_FUNCTION_INFO_V1(datetimeoffset_gt); +PG_FUNCTION_INFO_V1(datetimeoffset_ge); +PG_FUNCTION_INFO_V1(datetimeoffset_cmp); + +PG_FUNCTION_INFO_V1(datetimeoffset_pl_interval); +PG_FUNCTION_INFO_V1(datetimeoffset_mi_interval); +PG_FUNCTION_INFO_V1(interval_pl_datetimeoffset); +PG_FUNCTION_INFO_V1(datetimeoffset_mi); + +PG_FUNCTION_INFO_V1(datetimeoffset_hash); +PG_FUNCTION_INFO_V1(datetimeoffset_hash_extended); + +PG_FUNCTION_INFO_V1(timestamp_datetimeoffset); +PG_FUNCTION_INFO_V1(datetimeoffset_timestamp); +PG_FUNCTION_INFO_V1(date_datetimeoffset); +PG_FUNCTION_INFO_V1(datetimeoffset_date); +PG_FUNCTION_INFO_V1(time_datetimeoffset); +PG_FUNCTION_INFO_V1(datetimeoffset_time); +PG_FUNCTION_INFO_V1(smalldatetime_datetimeoffset); +PG_FUNCTION_INFO_V1(datetimeoffset_smalldatetime); +PG_FUNCTION_INFO_V1(datetime_datetimeoffset); +PG_FUNCTION_INFO_V1(datetimeoffset_datetime); +PG_FUNCTION_INFO_V1(datetime2_datetimeoffset); +PG_FUNCTION_INFO_V1(datetimeoffset_datetime2); +PG_FUNCTION_INFO_V1(datetimeoffset_scale); + +PG_FUNCTION_INFO_V1(get_datetimeoffset_tzoffset_internal); + + +/* datetimeoffset_in() + * Convert a string to internal form. + * Most parts of this functions is same as timestamptz_in(), + * but we store the timezone in a seperate int16 variable. + */ +Datum +datetimeoffset_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 typmod = PG_GETARG_INT32(2); + tsql_datetimeoffset* datetimeoffset; + Timestamp tsql_ts; + fsec_t fsec; + struct pg_tm tt, + *tm = &tt; + int tz; + int dtype; + int nf; + int dterr; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char workbuf[MAXDATELEN + MAXDATEFIELDS]; + + datetimeoffset = (tsql_datetimeoffset *)palloc(DATETIMEOFFSET_LEN); + dterr = ParseDateTime(str, workbuf, sizeof(workbuf), + field, ftype, MAXDATEFIELDS, &nf); + + if (dterr == 0) + dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); + // dterr == 1 means that input is TIME format(e.g 12:34:59.123) + // initialize other necessary date parts and accept input format + if (dterr == 1) + { + tm->tm_year = 1900; + tm->tm_mon = 1; + tm->tm_mday = 1; + dterr = 0; + } + if (dterr != 0) + DateTimeParseError(dterr, str, "timestamp with time zone"); + + datetimeoffset->tsql_tz = (int16)(tz/60); + tz = 0; + switch (dtype) + { + case DTK_DATE: + if (tm2timestamp(tm, fsec, &tz, &tsql_ts) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range: \"%s\"", str))); + break; + + case DTK_EPOCH: + tsql_ts = SetEpochTimestamp(); + break; + + case DTK_LATE: + TIMESTAMP_NOEND(tsql_ts); + break; + + case DTK_EARLY: + TIMESTAMP_NOBEGIN(tsql_ts); + break; + + default: + elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"", + dtype, str); + TIMESTAMP_NOEND(tsql_ts); + } + AdjustDatetimeoffsetForTypmod(&tsql_ts, typmod); + datetimeoffset->tsql_ts = (int64)tsql_ts; + CheckDatetimeoffsetRange(datetimeoffset); + + PG_RETURN_DATETIMEOFFSET(datetimeoffset); +} + +/* datetimeoffset_out() + * Convert datetimeoffset to external form. + */ +Datum +datetimeoffset_out(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + char *result; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + Timestamp timestamp; + + timestamp = df->tsql_ts; + if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetimeoffset out of range"))); + EncodeDatetimeoffsetTimezone(buf, df->tsql_tz, DateStyle); + result = pstrdup(buf); + + PG_RETURN_CSTRING(result); +} + +/* + * datetimeoffset_recv - converts external binary format to datetimeoffset + */ +Datum +datetimeoffset_recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 typmod = PG_GETARG_INT32(2); + tsql_datetimeoffset *result; + + result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + + result->tsql_ts = pq_getmsgint64(buf); + + result->tsql_tz = pq_getmsgint(buf, sizeof(int16)); + /* Check for sane GMT displacement; see notes in datatype/timestamp.h */ + if (result->tsql_tz <= -DATETIMEOFFSET_TIMEZONE_LIMIT || result->tsql_tz >= DATETIMEOFFSET_TIMEZONE_LIMIT) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE), + errmsg("datetimeoffset time zone out of range"))); + + AdjustDatetimeoffsetForTypmod(&(result->tsql_ts), typmod); + CheckDatetimeoffsetRange(result); + + PG_RETURN_DATETIMEOFFSET(result); +} + +/* + * datetimeoffset_send - converts datetimeoffset to external binary format + */ +Datum +datetimeoffset_send(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *datetimeoffset = PG_GETARG_DATETIMEOFFSET(0); + StringInfoData buffer; + + pq_begintypsend(&buffer); + pq_sendint64(&buffer, datetimeoffset->tsql_ts); + pq_sendint16(&buffer, datetimeoffset->tsql_tz); + + PG_RETURN_BYTEA_P(pq_endtypsend(&buffer)); +} + +/* cast datetimeoffset to timestamp internal representation */ +static void +datetimeoffset_timestamp_internal(const tsql_datetimeoffset *df, Timestamp* time) +{ + *time = df->tsql_ts + (int64)df->tsql_tz * SECS_PER_MINUTE * USECS_PER_SEC; +} + +/* + * This function converts datetimeoffset to timestamp and do the comparision. + */ +static int +datetimeoffset_cmp_internal(tsql_datetimeoffset* df1, tsql_datetimeoffset* df2) +{ + Timestamp t1; + Timestamp t2; + datetimeoffset_timestamp_internal(df1, &t1); + datetimeoffset_timestamp_internal(df2, &t2); + + return (t1 < t2) ? -1 : ((t1 > t2) ? 1 : 0); +} + +Datum +datetimeoffset_eq(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + + PG_RETURN_BOOL(datetimeoffset_cmp_internal(df1, df2) == 0); +} + +Datum +datetimeoffset_ne(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + + PG_RETURN_BOOL(datetimeoffset_cmp_internal(df1, df2) != 0); +} + +Datum +datetimeoffset_lt(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + + PG_RETURN_BOOL(datetimeoffset_cmp_internal(df1, df2) < 0); +} + +Datum +datetimeoffset_gt(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + + PG_RETURN_BOOL(datetimeoffset_cmp_internal(df1, df2) > 0); +} + +Datum +datetimeoffset_le(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + + PG_RETURN_BOOL(datetimeoffset_cmp_internal(df1, df2) <= 0); +} + +Datum +datetimeoffset_ge(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + + PG_RETURN_BOOL(datetimeoffset_cmp_internal(df1, df2) >= 0); +} + +Datum +datetimeoffset_cmp(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + PG_RETURN_INT32(datetimeoffset_cmp_internal(df1, df2)); +} + +/* datetimeoffset_pl_interval() + * This function is similar to timestamptz_pl_interval, + * adding some logic to handle the timezone. + */ +Datum +datetimeoffset_pl_interval(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Interval *span = PG_GETARG_INTERVAL_P(1); + tsql_datetimeoffset *result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + Timestamp tmp = df->tsql_ts; + int tz; + + if (span->month != 0) + { + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + + if (timestamp2tm(tmp, &tz, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetimeoffset out of range"))); + + tm->tm_mon += span->month; + if (tm->tm_mon > MONTHS_PER_YEAR) + { + tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR; + tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1; + } + else if (tm->tm_mon < 1) + { + tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1; + tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR; + } + + /* adjust for end of month boundary problems... */ + if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) + tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); + + tz = 0; + if (tm2timestamp(tm, fsec, &tz, &tmp) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetimeoffset out of range"))); + } + + if (span->day != 0) + { + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + int julian; + + if (timestamp2tm(tmp, &tz, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetimeoffset out of range"))); + + /* Add days by converting to and from Julian */ + julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day; + j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); + + tz = 0; + if (tm2timestamp(tm, fsec, &tz, &tmp) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetimeoffset out of range"))); + } + + tmp += span->time; + result->tsql_ts = tmp + df->tsql_tz * USECS_PER_MINUTE; + result->tsql_tz = df->tsql_tz; + CheckDatetimeoffsetRange(result); + + PG_RETURN_DATETIMEOFFSET(result); +} + +Datum +datetimeoffset_mi_interval(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Interval *span = PG_GETARG_INTERVAL_P(1); + Interval tspan; + + tspan.month = -span->month; + tspan.day = -span->day; + tspan.time = -span->time; + + return DirectFunctionCall2(datetimeoffset_pl_interval, + DatetimeoffsetGetDatum(df), + PointerGetDatum(&tspan)); +} + +Datum +interval_pl_datetimeoffset(PG_FUNCTION_ARGS) +{ + Interval *span = PG_GETARG_INTERVAL_P(0); + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(1); + + return DirectFunctionCall2(datetimeoffset_pl_interval, + DatetimeoffsetGetDatum(df), + PointerGetDatum(span)); +} + +Datum +datetimeoffset_mi(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + Timestamp t1; + Timestamp t2; + Interval *result; + + datetimeoffset_timestamp_internal(df1, &t1); + datetimeoffset_timestamp_internal(df2, &t2); + result = (Interval *) palloc(sizeof(Interval)); + + result->time = t1 - t2; + + result->month = 0; + result->day = 0; + + + result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours, + IntervalPGetDatum(result))); + + PG_RETURN_INTERVAL_P(result); +} + +/* hash index support */ +Datum +datetimeoffset_hash(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + return hash_any((unsigned char *)df, DATETIMEOFFSET_LEN); +} + +/* smalldatetime_datetimeoffset() + * Convert smalldatetime to datetimeoffset + */ +Datum +smalldatetime_datetimeoffset(PG_FUNCTION_ARGS) +{ + Timestamp time = PG_GETARG_TIMESTAMP(0); + tsql_datetimeoffset* result; + + result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + result->tsql_ts = time; + result->tsql_tz = 0; + CheckDatetimeoffsetRange(result); + + PG_RETURN_DATETIMEOFFSET(result); +} + +/* datetimeoffset_smalldatetime() + * Convert datetimeoffset to smalldatetime + */ +Datum +datetimeoffset_smalldatetime(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Timestamp result; + datetimeoffset_timestamp_internal(df, &result); + CheckSmalldatetimeRange(result); + AdjustTimestampForSmallDatetime(&result); + + PG_RETURN_TIMESTAMP(result); +} + +/* datetime_datetimeoffset() + * Convert datetime to datetimeoffset + */ +Datum +datetime_datetimeoffset(PG_FUNCTION_ARGS) +{ + Timestamp time = PG_GETARG_TIMESTAMP(0); + tsql_datetimeoffset* result; + + result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + result->tsql_ts = time; + result->tsql_tz = 0; + CheckDatetimeoffsetRange(result); + + PG_RETURN_DATETIMEOFFSET(result); +} + +/* datetimeoffset_datetime() + * Convert datetimeoffset to datetime + */ +Datum +datetimeoffset_datetime(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Timestamp result; + datetimeoffset_timestamp_internal(df, &result); + CheckDatetimeRange(result); + + PG_RETURN_TIMESTAMP(result); +} + +/* datetime2_datetimeoffset() + * Convert datetime2 to datetimeoffset + */ +Datum +datetime2_datetimeoffset(PG_FUNCTION_ARGS) +{ + Timestamp time = PG_GETARG_TIMESTAMP(0); + tsql_datetimeoffset* result; + + result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + result->tsql_ts = time; + result->tsql_tz = 0; + CheckDatetimeoffsetRange(result); + + PG_RETURN_DATETIMEOFFSET(result); +} + +/* datetimeoffset_datetime2() + * Convert datetimeoffset to datetime + */ +Datum +datetimeoffset_datetime2(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Timestamp result; + datetimeoffset_timestamp_internal(df, &result); + CheckDatetime2Range(result); + + PG_RETURN_TIMESTAMP(result); +} + +/* timestamp_datetimeoffset() + * Convert timestamp to datetimeoffset + */ +Datum +timestamp_datetimeoffset(PG_FUNCTION_ARGS) +{ + Timestamp time = PG_GETARG_TIMESTAMP(0); + tsql_datetimeoffset* result; + + result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + result->tsql_ts = time; + result->tsql_tz = 0; + CheckDatetimeoffsetRange(result); + + PG_RETURN_DATETIMEOFFSET(result); +} + +/* datetimeoffset_timestamp() + * Convert datetimeoffset to timestamp + */ +Datum +datetimeoffset_timestamp(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Timestamp result; + datetimeoffset_timestamp_internal(df, &result); + + PG_RETURN_TIMESTAMP(result); +} + +/* date_datetimeoffset() + * Convert date to datetimeoffset + */ +Datum +date_datetimeoffset(PG_FUNCTION_ARGS) +{ + DateADT dateVal = PG_GETARG_DATEADT(0); + tsql_datetimeoffset* result; + + result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + result->tsql_ts = (int64)dateVal * USECS_PER_DAY; + result->tsql_tz = 0; + CheckDatetimeoffsetRange(result); + + PG_RETURN_DATETIMEOFFSET(result); +} + +/* datetimeoffset_date() + * Convert datetimeoffset to date + */ +Datum +datetimeoffset_date(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Timestamp time; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + int tz; + DateADT result; + + datetimeoffset_timestamp_internal(df, &time); + if (timestamp2tm(time, &tz, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("datetimeoffset out of range"))); + + result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; + + PG_RETURN_DATEADT(result); +} + +/* datetimeoffset_time() + * Convert datetimeoffset to time data type. + */ +Datum +datetimeoffset_time(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + Timestamp time; + TimeADT result; + + datetimeoffset_timestamp_internal(df, &time); + if (time < 0) + result = time - (time / USECS_PER_DAY * USECS_PER_DAY) + USECS_PER_DAY; + else + result = time - (time / USECS_PER_DAY * USECS_PER_DAY); + + PG_RETURN_TIMEADT(result); +} + +/* time_datetimeoffset() + * Convert time to datetimeoffset data type. + */ +Datum +time_datetimeoffset(PG_FUNCTION_ARGS) +{ + TimeADT time = PG_GETARG_TIMEADT(0); + tsql_datetimeoffset *result; + + result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + result->tsql_ts = DATETIMEOFFSET_DEFAULT_TS + time; + result->tsql_tz = 0; + + PG_RETURN_DATETIMEOFFSET(result); +} + +/* datetimeoffset_scale() + * Adjust datetimeoffset_scale type for specified scale factor. + * Used by PostgreSQL type system to stuff columns. + */ +Datum +datetimeoffset_scale(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + int32 typmod = PG_GETARG_INT32(1); + tsql_datetimeoffset *result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + + result->tsql_ts = df->tsql_ts; + result->tsql_tz = df->tsql_tz; + AdjustDatetimeoffsetForTypmod(&(result->tsql_ts), typmod); + + PG_RETURN_DATETIMEOFFSET(result); +} + + +Datum +get_datetimeoffset_tzoffset_internal(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df = PG_GETARG_DATETIMEOFFSET(0); + PG_RETURN_INT16(-df->tsql_tz); +} + +/* + * CheckDatetimeoffsetRange --- Check if datetimeoffset is out of range + * for 0001-01-01 through 9999-12-31 + */ +static void +CheckDatetimeoffsetRange(const tsql_datetimeoffset* df) +{ + Timestamp time; + /* the lower bound and uppbound stands for 0001-01-01 00:00:00 and 10000-01-01 00:00:00 */ + static const int64 lower_bound = -63082281600000000; + static const int64 upper_bound = 252455616000000000; + + datetimeoffset_timestamp_internal(df, &time); + if (time < lower_bound || time >= upper_bound) + { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for datetimeoffset"))); + } +} + +/* + * AdjustDatetimeoffsetForTypmod --- round off a datetimeoffset to suit given typmod + * this function is from timestamp.c + */ +static void +AdjustDatetimeoffsetForTypmod(Timestamp *time, int32 typmod) +{ + static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = { + INT64CONST(1000000), + INT64CONST(100000), + INT64CONST(10000), + INT64CONST(1000), + INT64CONST(100), + INT64CONST(10), + INT64CONST(1) + }; + + static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = { + INT64CONST(500000), + INT64CONST(50000), + INT64CONST(5000), + INT64CONST(500), + INT64CONST(50), + INT64CONST(5), + INT64CONST(0) + }; + + /* new offset for negative timestamp value */ + static const int64 TimestampOffsetsNegative[MAX_TIMESTAMP_PRECISION + 1] = { + INT64CONST(499999), + INT64CONST(49999), + INT64CONST(4999), + INT64CONST(499), + INT64CONST(49), + INT64CONST(4), + INT64CONST(0) + }; + + if (!TIMESTAMP_NOT_FINITE(*time) + && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION)) + { + if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("datetimeoffset(%d) precision must be between %d and %d", + typmod, 0, MAX_TIMESTAMP_PRECISION))); + + if (*time >= INT64CONST(0)) + { + *time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) * + TimestampScales[typmod]; + } + else + { + *time = -((((-*time) + TimestampOffsetsNegative[typmod]) / TimestampScales[typmod]) + * TimestampScales[typmod]); + } + } +} + +/* EncodeDatetimeoffsetTimezone() + * Copies representation of a numeric timezone offset to str. + * Note: we need to hanlde the '\0' at the end of original input string. + */ +static void +EncodeDatetimeoffsetTimezone(char *str, int tz, int style) +{ + int hour, + min; + char *tmp; + + min = abs(tz); + hour = min / MINS_PER_HOUR; + min = min % MINS_PER_HOUR; + /* point tmp to '\0' */ + tmp = str + strlen(str); + *tmp++ = ' '; + /* TZ is negated compared to sign we wish to display ... */ + *tmp++ = (tz <= 0 ? '+' : '-'); + + tmp = pg_ultostr_zeropad(tmp, hour, 2); + *tmp++ = ':'; + tmp = pg_ultostr_zeropad(tmp, min, 2); + + *tmp = '\0'; +} diff --git a/contrib/babelfishpg_common/src/datetimeoffset.h b/contrib/babelfishpg_common/src/datetimeoffset.h new file mode 100644 index 00000000000..ae1c9d8e554 --- /dev/null +++ b/contrib/babelfishpg_common/src/datetimeoffset.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * datetimeoffset.h + * Definitions for the TSQL "datetimeoffset" type. + * + *------------------------------------------------------------------------- + */ +#ifndef DATETIMEOFFSET_H +#define DATETIMEOFFSET_H +#include "fmgr.h" // check if necessary + +/* datetimeoffset size in bytes */ +#define DATETIMEOFFSET_LEN MAXALIGN(sizeof(tsql_datetimeoffset)) +/* datetimeoffset default value in internal representation */ +#define DATETIMEOFFSET_DEFAULT_TS -3155673600000000 +/* datetimeoffset timezone limit, it is valid for -14:00 to +14:00 + * So the limit to mins will be 14*60+1 = 841 + */ +#define DATETIMEOFFSET_TIMEZONE_LIMIT 841 + + +extern void AdjustTimestampForSmallDatetime(Timestamp *time); +extern void CheckSmalldatetimeRange(const Timestamp time); +extern void CheckDatetimeRange(const Timestamp time); +extern void CheckDatetime2Range(const Timestamp time); +typedef struct tsql_datetimeoffset +{ + int64 tsql_ts; + int16 tsql_tz; +} tsql_datetimeoffset; + +/* fmgr interface macros */ +#define DatetimeoffsetGetDatum(X) PointerGetDatum(X) +#define PG_RETURN_DATETIMEOFFSET(X) return DatetimeoffsetGetDatum(X) +#define DatumGetDatetimeoffset(X) ((tsql_datetimeoffset *) DatumGetPointer(X)) +#define PG_GETARG_DATETIMEOFFSET(X) DatumGetDatetimeoffset(PG_GETARG_DATUM(X)) + +#endif /* DATETIMEOFFSET_H */ diff --git a/contrib/babelfishpg_common/src/instr.c b/contrib/babelfishpg_common/src/instr.c new file mode 100644 index 00000000000..37b3c60b443 --- /dev/null +++ b/contrib/babelfishpg_common/src/instr.c @@ -0,0 +1,18 @@ +#include "postgres.h" +#include "fmgr.h" + +#include "instr.h" + +instr_plugin *instr_plugin_ptr = NULL; + +void +init_instr(void) +{ + instr_plugin **rendezvous; + + rendezvous = (instr_plugin **) find_rendezvous_variable("PLtsql_instr_plugin"); + + if (rendezvous && *rendezvous) + instr_plugin_ptr = *rendezvous; +} + diff --git a/contrib/babelfishpg_common/src/instr.h b/contrib/babelfishpg_common/src/instr.h new file mode 100644 index 00000000000..a9d2fc6ccd8 --- /dev/null +++ b/contrib/babelfishpg_common/src/instr.h @@ -0,0 +1,473 @@ +#ifndef INSTR_H +#define INSTR_H + +#include "postgres.h" + +typedef struct instr_plugin +{ + /* Function pointers set up by the plugin */ + void (*instr_increment_metric) (int metric); + bool (*instr_increment_func_metric) (const char *funcName); +} instr_plugin; + +extern instr_plugin *instr_plugin_ptr; +extern void init_instr(void); + + +#define INSTR_ENABLED() \ + (instr_plugin_ptr && instr_plugin_ptr->instr_increment_metric) + +#define INSTR_METRIC_INC(metric) \ +({ if (INSTR_ENABLED()) \ + instr_plugin_ptr->instr_increment_metric(metric); \ +}) + +/* copy from pltsql_instr.h */ +typedef enum PgTsqlInstrMetricType { + + INSTR_START = -1, + INSTR_TSQL_ALTER_COLUMN, + INSTR_TSQL_IDENTITY_COLUMN, + INSTR_TSQL_COMPUTED_COLUMN, + INSTR_TSQL_CREATE_TEMP_TABLE, + INSTR_TSQL_CREATE_FUNCTION_RETURNS_TABLE, + + INSTR_UNSUPPORTED_TSQL_TOP_PERCENT_IN_STMT, + INSTR_UNSUPPORTED_TSQL_XML_OPTION_AUTO, + INSTR_UNSUPPORTED_TSQL_XML_OPTION_EXPLICIT, + INSTR_TSQL_OPTION_CLUSTERED, + INSTR_TSQL_OPTION_NON_CLUSTERED, + INSTR_TSQL_DATEADD, + INSTR_TSQL_DATEDIFF, + INSTR_TSQL_DATEPART, + INSTR_TSQL_DATENAME, + INSTR_TSQL_DAY, + INSTR_TSQL_MONTH, + INSTR_TSQL_YEAR, + INSTR_TSQL_DB_ID, + INSTR_TSQL_DB_NAME, + INSTR_TSQL_CHARINDEX, + INSTR_TSQL_DATALENGTH, + INSTR_TSQL_NCHAR, + INSTR_TSQL_PATINDEX, + INSTR_TSQL_QUOTENAME, + INSTR_TSQL_REPLICATE, + INSTR_TSQL_SPACE, + INSTR_TSQL_STRING_ESCAPE, + INSTR_TSQL_STRING_SPLIT, + INSTR_TSQL_ISNUMERIC, + INSTR_TSQL_CEILING, + INSTR_TSQL_FLOOR, + INSTR_TSQL_ROUND, + + + + INSTR_TSQL_FUNCTION_IIF, + INSTR_TSQL_FUNCTION_CHOOSE, + INSTR_UNSUPPORTED_TSQL_TEXTIMAGE_ON, + INSTR_TSQL_FUNCTION_TRY_CAST, + INSTR_TSQL_TRY_CONVERT, + INSTR_TSQL_PARSE, + INSTR_TSQL_FUNCTION_PARSE, + INSTR_TSQL_FUNCTION_CONVERT, + + INSTR_TSQL_SCHEMABINDING, + INSTR_TSQL_OPTION_IDENTITY_INSERT, + INSTR_TSQL_OPTION_LANGUAGE, + INSTR_UNSUPPORTED_TSQL_OPTION_ANSI_NULL_DFLT, + INSTR_UNSUPPORTED_TSQL_OPTION_ANSI_PADDING, + INSTR_UNSUPPORTED_TSQL_OPTION_ANSI_WARNINGS, + INSTR_UNSUPPORTED_TSQL_OPTION_ALLOW_SNAPSHOT_ISOLATION, + INSTR_UNSUPPORTED_TSQL_OPTION_ARITHABORT, + INSTR_UNSUPPORTED_TSQL_OPTION_ARITHIGNORE, + INSTR_UNSUPPORTED_TSQL_OPTION_NUMERIC_ROUNDABORT, + + INSTR_TSQL_INSERT_STMT, + INSTR_TSQL_DELETE_STMT, + INSTR_TSQL_UPDATE_STMT, + INSTR_TSQL_SELECT_STMT, + INSTR_TSQL_TRANS_STMT_START, + INSTR_TSQL_TRANS_STMT_COMMIT, + INSTR_TSQL_TRANS_STMT_ROLLBACK, + INSTR_TSQL_TRANS_STMT_SAVEPOINT, + INSTR_TSQL_TRANS_STMT_RELEASE, + INSTR_TSQL_TRANS_STMT_PREPARE, + INSTR_TSQL_TRANS_STMT_COMMIT_PREPARED, + INSTR_TSQL_TRANS_STMT_ROLLBACK_PREPARED, + INSTR_TSQL_TRANS_STMT_START_ISO_LEVEL_READ_UNCOMMITTED, + INSTR_TSQL_TRANS_STMT_START_ISO_LEVEL_READ_COMMITTED, + INSTR_TSQL_TRANS_STMT_START_ISO_LEVEL_REPEATABLE_READ, + INSTR_TSQL_TRANS_STMT_START_ISO_LEVEL_LEVEL_SERIALIZABLE, + INSTR_TSQL_DECLARE_CURSOR, + INSTR_TSQL_CLOSE_CURSOR_ALL, + INSTR_TSQL_CLOSE_CURSOR, + INSTR_TSQL_MOVE_CURSOR, + INSTR_TSQL_FETCH_CURSOR, + INSTR_TSQL_CREATE_DOMAIN, + INSTR_TSQL_CREATE_SCHEMA, + INSTR_TSQL_CREATE_TABLE_IF_NOT_EXISTS, + INSTR_TSQL_CREATE_TABLE, + INSTR_TSQL_CREATE_TABLESPACE, + INSTR_TSQL_DROP_TABLESPACE, + INSTR_TSQL_ALTER_TABLESPACE, + INSTR_TSQL_CREATE_EXTENSION, + INSTR_TSQL_ALTER_EXTENSION, + INSTR_TSQL_ALTER_EXTENSION_CONTENTS_STMT, + INSTR_TSQL_CREATE_FOREIGN_DATA_WRAPPER, + INSTR_TSQL_ALTER_FOREIGN_DATA_WRAPPER, + INSTR_TSQL_CREATE_SERVER, + INSTR_TSQL_ALTER_SERVER, + INSTR_TSQL_CREATE_USER_MAPPING, + INSTR_TSQL_ALTER_USER_MAPPING, + INSTR_TSQL_DROP_USER_MAPPING, + INSTR_TSQL_CREATE_FOREIGN_TABLE, + INSTR_TSQL_IMPORT_FOREIGN_SCHEMA, + INSTR_TSQL_DROP_TABLE, + INSTR_TSQL_DROP_SEQUENCE, + INSTR_TSQL_DROP_VIEW, + INSTR_TSQL_DROP_MATERIALIZED_VIEW, + INSTR_TSQL_DROP_INDEX, + INSTR_TSQL_DROP_TYPE, + INSTR_TSQL_DROP_DOMAIN, + INSTR_TSQL_DROP_COLLATION, + INSTR_TSQL_DROP_CONVERSION, + INSTR_TSQL_DROP_SCHEMA, + INSTR_TSQL_DROP_TEXT_SEARCH_PARSER, + INSTR_TSQL_DROP_TEXT_SEARCH_DICTIONARY, + INSTR_TSQL_DROP_TEXT_SEARCH_TEMPLATE, + INSTR_TSQL_DROP_TEXT_SEARCH_CONFIGURATION, + INSTR_TSQL_DROP_FOREIGN_TABLE, + INSTR_TSQL_DROP_EXTENSION, + INSTR_TSQL_DROP_FUNCTION, + INSTR_TSQL_DROP_PROCEDURE, + INSTR_TSQL_DROP_ROUTINE, + INSTR_TSQL_DROP_AGGREGATE, + INSTR_TSQL_DROP_OPERATOR, + INSTR_TSQL_DROP_LANGUAGE, + INSTR_TSQL_DROP_CAST, + INSTR_TSQL_DROP_TRIGGER, + INSTR_TSQL_DROP_EVENT_TRIGGER, + INSTR_TSQL_DROP_RULE, + INSTR_TSQL_DROP_FOREIGN_DATA_WRAPPER, + INSTR_TSQL_DROP_SERVER, + INSTR_TSQL_DROP_OPERATOR_CLASS, + INSTR_TSQL_DROP_OPERATOR_FAMILY, + INSTR_TSQL_DROP_POLICY, + INSTR_TSQL_DROP_TRANSFORM, + INSTR_TSQL_DROP_ACCESS_METHOD, + INSTR_TSQL_DROP_PUBLICATION, + INSTR_TSQL_DROP_STATISTICS, + INSTR_TSQL_TRUNCATE_TABLE, + INSTR_TSQL_COMMENT_STMT, + INSTR_TSQL_SECURITY_LABEL, + INSTR_TSQL_COPY_STMT, + INSTR_TSQL_RENAME_STMT, + INSTR_TSQL_ALTER_OBJECT_DEPENDS_STMT, + INSTR_TSQL_ALTER_OBJECT_SCHEMA_STMT, + INSTR_TSQL_ALTER_OWNER_STMT, + INSTR_TSQL_ALTER_TABLE_MOVE_ALL_STMT, + INSTR_TSQL_ALTER_TABLE_STMT, + INSTR_TSQL_ALTER_DOMAIN, + INSTR_TSQL_ALTER_FUNCTION, + INSTR_TSQL_ALTER_PROCEDURE, + INSTR_TSQL_ALTER_ROUTINE, + INSTR_TSQL_GRANT_STMT, + INSTR_TSQL_REVOKE_STMT, + INSTR_TSQL_GRANT_ROLE, + INSTR_TSQL_REVOKE_ROLE, + INSTR_TSQL_ALTER_DEFAULT_PRIVILEGES, + INSTR_TSQL_CREATE_AGGREGATE, + INSTR_TSQL_CREATE_OPERATOR, + INSTR_TSQL_CREATE_TYPE, + INSTR_TSQL_CREATE_TEXT_SEARCH_PARSER, + INSTR_TSQL_CREATE_TEXT_SEARCH_DICTIONARY, + INSTR_TSQL_CREATE_TEXT_SEARCH_TEMPLATE, + INSTR_TSQL_CREATE_TEXT_SEARCH_CONFIGURATION, + INSTR_TSQL_CREATE_COLLATION, + INSTR_TSQL_CREATE_ACCESS_METHOD, + INSTR_TSQL_CREATE_COMPOSITE_TYPE, + INSTR_TSQL_CREATE_ENUM_STMT, + INSTR_TSQL_CREATE_RANGE_STMT, + INSTR_TSQL_ALTER_ENUM, + INSTR_TSQL_CREATE_VIEW, + INSTR_TSQL_CREATE_PROCEDURE, + INSTR_TSQL_CREATE_FUNCTION, + INSTR_TSQL_CREATE_INDEX, + INSTR_TSQL_CREATE_RULE, + INSTR_TSQL_CREATE_SEQUENCE, + INSTR_TSQL_ALTER_SEQUENCE, + INSTR_TSQL_DO_STMT, + INSTR_TSQL_CREATE_DATABASE, + INSTR_TSQL_ALTER_DATABASE, + INSTR_TSQL_DROP_DATABASE, + INSTR_TSQL_NOTIFY_STMT, + INSTR_TSQL_LISTEN_STMT, + INSTR_TSQL_UNLISTEN_STMT, + INSTR_TSQL_LOAD_STMT, + INSTR_TSQL_CALL_STMT, + INSTR_TSQL_CLUSTER_STMT, + INSTR_TSQL_VACUUM_STMT, + INSTR_TSQL_ANALYZE_STMT, + INSTR_TSQL_EXPLAIN_STMT, + INSTR_TSQL_SELECT_INTO, + INSTR_TSQL_CREATE_TABLE_AS, + INSTR_TSQL_CREATE_MATERIALIZED_VIEW, + INSTR_TSQL_REFRESH_MATERIALIZED_VIEW, + INSTR_TSQL_ALTER_SYSTEM, + INSTR_TSQL_SET, + INSTR_TSQL_RESET, + INSTR_TSQL_VARIABLE_SHOW_STMT, + INSTR_TSQL_DISCARD_ALL, + INSTR_TSQL_DISCARD_PLANS, + INSTR_TSQL_DISCARD_TEMP, + INSTR_TSQL_DISCARD_SEQUENCES, + INSTR_TSQL_CREATE_TRANSFORM, + INSTR_TSQL_CREATE_TRIGGER, + INSTR_TSQL_CREATE_EVENT_TRIGGER, + INSTR_TSQL_ALTER_EVENT_TRIGGER, + INSTR_TSQL_CREATE_LANGUAGE, + INSTR_TSQL_CREATE_ROLE, + INSTR_TSQL_ALTER_ROLE, + INSTR_TSQL_DROP_ROLE, + INSTR_TSQL_DROP_OWNED, + INSTR_TSQL_REASSIGN_OWNED, + INSTR_TSQL_LOCK_TABLE, + INSTR_TSQL_SET_CONSTRAINTS, + INSTR_TSQL_CHECKPOINT, + INSTR_TSQL_REINDEX, + INSTR_TSQL_CREATE_CONVERSION, + INSTR_TSQL_CREATE_CAST, + INSTR_TSQL_CREATE_OPERATOR_CLASS, + INSTR_TSQL_CREATE_OPERATOR_FAMILY, + INSTR_TSQL_ALTER_OPERATOR_FAMILY, + INSTR_TSQL_ALTER_OPERATOR, + INSTR_TSQL_ALTER_TEXT_SEARCH_DICTIONARY, + INSTR_TSQL_ALTER_TEXT_SEARCH_CONFIGURATION, + INSTR_TSQL_CREATE_POLICY, + INSTR_TSQL_ALTER_POLICY, + INSTR_TSQL_CREATE_PUBLICATION, + INSTR_TSQL_ALTER_PUBLICATION, + INSTR_TSQL_CREATE_SUBSCRIPTION, + INSTR_TSQL_ALTER_SUBSCRIPTION, + INSTR_TSQL_DROP_SUBSCRIPTION, + INSTR_TSQL_ALTER_COLLATION, + INSTR_TSQL_PREPARE, + INSTR_TSQL_EXECUTE, + INSTR_TSQL_CREATE_STATISTICS, + INSTR_TSQL_DEALLOCATE_ALL, + INSTR_TSQL_DEALLOCATE, + INSTR_TSQL_SELECT_FOR_KEY_SHARE, + INSTR_TSQL_SELECT_FOR_SHARE, + INSTR_TSQL_SELECT_FOR_NO_KEY_UPDATE, + INSTR_TSQL_SELECT_FOR_UPDATE, + + INSTR_TSQL_BITIN, + INSTR_TSQL_BIT_RECV, + INSTR_TSQL_BITOUT, + INSTR_TSQL_BIT_SEND, + INSTR_TSQL_INT2BIT, + INSTR_TSQL_INT4BIT, + INSTR_TSQL_INT8BIT, + INSTR_TSQL_FTOBIT, + INSTR_TSQL_DTOBIT, + INSTR_TSQL_NUMERIC_BIT, + INSTR_TSQL_BITNEG, + INSTR_TSQL_BITEQ, + INSTR_TSQL_BITNE, + INSTR_TSQL_BITLT, + INSTR_TSQL_BITGT, + INSTR_TSQL_BITLE, + INSTR_TSQL_BITGE, + INSTR_TSQL_BIT_CMP, + INSTR_TSQL_INT4BITEQ, + INSTR_TSQL_INT4BITNE, + INSTR_TSQL_INT4BITLT, + INSTR_TSQL_INT4BITLE, + INSTR_TSQL_INT4BITGT, + INSTR_TSQL_INT4BITGE, + INSTR_TSQL_BITINT4EQ, + INSTR_TSQL_BITINT4NE, + INSTR_TSQL_BITINT4LT, + INSTR_TSQL_BITINT4LE, + INSTR_TSQL_BITINT4GT, + INSTR_TSQL_BITINT4GE, + INSTR_TSQL_BIT2INT2, + INSTR_TSQL_BIT2INT4, + INSTR_TSQL_BIT2INT8, + INSTR_TSQL_BIT2NUMERIC, + INSTR_TSQL_BIT2FIXEDDEC, + + INSTR_TSQL_VARBINARYIN, + INSTR_TSQL_VARBINARYOUT, + INSTR_TSQL_VARBINARY_RECV, + INSTR_TSQL_VARBINARY_SEND, + INSTR_TSQL_VARCHARVARBINARY, + INSTR_TSQL_BPCHARVARBINARY, + INSTR_TSQL_VARBINARYVARCHAR, + INSTR_TSQL_VARCHARBINARY, + INSTR_TSQL_BPCHARBINARY, + INSTR_TSQL_INT2VARBINARY, + INSTR_TSQL_INT4VARBINARY, + INSTR_TSQL_INT8VARBINARY, + INSTR_TSQL_VARBINARYINT2, + INSTR_TSQL_VARBINARYINT4, + INSTR_TSQL_VARBINARYINT8, + INSTR_TSQL_FLOAT4VARBINARY, + INSTR_TSQL_FLOAT8VARBINARY, + INSTR_TSQL_VARBINARYFLOAT4, + INSTR_TSQL_VARBINARYFLOAT8, + INSTR_TSQL_INT2BINARY, + INSTR_TSQL_INT4BINARY, + INSTR_TSQL_INT8BINARY, + INSTR_TSQL_BINARYINT2, + INSTR_TSQL_BINARYINT4, + INSTR_TSQL_BINARYINT8, + INSTR_TSQL_FLOAT4BINARY, + INSTR_TSQL_FLOAT8BINARY, + INSTR_TSQL_BINARYFLOAT4, + INSTR_TSQL_BINARYFLOAT8, + INSTR_TSQL_VARBINARY_COMPARE, + + INSTR_TSQL_SMALLDATETIMEIN, + INSTR_TSQL_TIME2SMALLDATETIME, + INSTR_TSQL_DATE2SMALLDATETIME, + INSTR_TSQL_TIMESTAMP2SMALLDATETIME, + INSTR_TSQL_TIMESTAMPTZ2SMALLDATETIME, + INSTR_TSQL_SMALLDATETIME2VARCHAR, + INSTR_TSQL_CONVERT_VARCHAR_SMALLDATETIME, + INSTR_TSQL_CONVERT_SMALLDATETIME_CHAR, + INSTR_TSQL_CONVERT_CHAR_SMALLDATETIME, + + INSTR_TSQL_DATETIMEIN, + INSTR_TSQL_DATETIMEOUT, + INSTR_TSQL_DATE2DATETIME, + INSTR_TSQL_TIME2DATETIME, + INSTR_TSQL_TIMESTAMP2DATETIME, + INSTR_TSQL_TIMESTAMPTZ2DATETIME, + INSTR_TSQL_DATETIME2VARCHAR, + INSTR_TSQL_VARCHAR2DATETIME, + INSTR_TSQL_DATETIME2CHAR, + INSTR_TSQL_CHAR2DATETIME, + + INSTR_TSQL_DATETIME22VARCHAR, + INSTR_TSQL_VARCHAR2DATETIME2, + INSTR_TSQL_DATETIME22CHAR, + INSTR_TSQL_CHAR2DATETIME2, + + INSTR_TSQL_SQLVARIANTIN, + INSTR_TSQL_SQLVARIANTOUT, + INSTR_TSQL_SQLVARIANT_RECV, + INSTR_TSQL_SQLVARIANT_SEND, + INSTR_TSQL_DATETIME_SQLVARIANT, + INSTR_TSQL_DATETIME2_SQLVARIANT, + INSTR_TSQL_SMALLDATETIME_SQLVARIANT, + INSTR_TSQL_DATETIMEOFFSET_SQLVARIANT, + INSTR_TSQL_DATE_SQLVARIANT, + INSTR_TSQL_TIME_SQLVARIANT, + INSTR_TSQL_FLOAT4_SQLVARIANT, + INSTR_TSQL_FLOAT8_SQLVARIANT, + INSTR_TSQL_NUMERIC_SQLVARIANT, + INSTR_TSQL_MONEY_SQLVARIANT, + INSTR_TSQL_SMALLMONEY_SQLVARIANT, + INSTR_TSQL_BIGINT_SQLVARIANT, + INSTR_TSQL_INT_SQLVARIANT, + INSTR_TSQL_SMALLINT_SQLVARIANT, + INSTR_TSQL_TINYINT_SQLVARIANT, + INSTR_TSQL_BIT_SQLVARIANT, + INSTR_TSQL_VARCHAR_SQLVARIANT, + INSTR_TSQL_NVARCHAR_SQLVARIANT, + INSTR_TSQL_CHAR_SQLVARIANT, + INSTR_TSQL_NCHAR_SQLVARIANT, + INSTR_TSQL_BBFVARBINARY_SQLVARIANT, + INSTR_TSQL_BBFBINARY_SQLVARIANT, + INSTR_TSQL_UNIQUEIDENTIFIER_SQLVARIANT, + INSTR_TSQL_SQLVARIANT_TIMESTAMP, + INSTR_TSQL_SQLVARIANT_DATETIMEOFFSET, + INSTR_TSQL_SQLVARIANT_DATE, + INSTR_TSQL_SQLVARIANT_TIME, + INSTR_TSQL_SQLVARIANT_FLOAT4, + INSTR_TSQL_SQLVARIANT_FLOAT8, + INSTR_TSQL_SQLVARIANT_NUMERIC, + INSTR_TSQL_SQLVARIANT_FIXEDDEC, + INSTR_TSQL_SQLVARIANT_BIGINT, + INSTR_TSQL_SQLVARIANT_INT, + INSTR_TSQL_SQLVARIANT_SMALLINT, + INSTR_TSQL_SQLVARIANT_BIT, + INSTR_TSQL_SQLVARIANT_VARCHAR, + INSTR_TSQL_SQLVARIANT_CHAR, + INSTR_TSQL_SQLVARIANT_BBFVARBINARY, + INSTR_TSQL_SQLVARIANT_BBFBINARY, + INSTR_TSQL_SQLVARIANT_UNIQUEINDETIFIER, + INSTR_TSQL_SQLVARIANTLT, + INSTR_TSQL_SQLVARIANTLE, + INSTR_TSQL_SQLVARIANTEQ, + INSTR_TSQL_SQLVARIANTGE, + INSTR_TSQL_SQLVARIANTGT, + INSTR_TSQL_SQLVARIANTNE, + + INSTR_TSQL_SERVERPROPERTY_BUILDCLRVERSION, + INSTR_TSQL_SERVERPROPERTY_COLLATION, + INSTR_TSQL_SERVERPROPERTY_COLLATIONID, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_COMPARISON_STYLE, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_COMPUTERNAME_PHYSICAL_NETBIOS, + INSTR_TSQL_SERVERPROPERTY_EDITION, + INSTR_TSQL_SERVERPROPERTY_EDITIONID, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_ENGINE_EDITION, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_HADR_MANAGER_STATUS, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_INSTANCE_DEFAULT_PATH, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_INSTANCE_DEFAULT_LOG_PATH, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_INSTANCE_NAME, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_ADVANCED_ANALYTICS_INSTALLED, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_BIG_DATA_CLUSTER, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_CLUSTERED, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_FULL_TEXT_INSTALLED, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_HADR_ENABLED, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_INTEGRATED_SECURITY, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_LOCAL_DB, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_POLYBASE_INSTALLED, + INSTR_TSQL_SERVERPROPERTY_IS_SINGLE_USER, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_IS_XTP_SUPPORTED, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_LCID, + INSTR_UNSUPPORTED_TSQL_SERVERPROPERTY_LICENSE_TYPE, + INSTR_TSQL_XACT_STATE, + + INSTR_TSQL_TRANCOUNT, + INSTR_TSQL_ERROR, + INSTR_UNSUPPORTED_TSQL_PROCID, + INSTR_TSQL_VERSION, + INSTR_TSQL_SERVERNAME, + + INSTR_UNSUPPORTED_TSQL_OPTION_ROWCOUNT, + INSTR_TSQL_FETCH_STATUS, + + INSTR_TSQL_TRY_CATCH_BLOCK, + INSTR_TSQL_TRY_BLOCK, + INSTR_TSQL_CATCH_BLOCK, + INSTR_TSQL_GOTO_STMT, + + INSTR_TSQL_INIT_TSQL_COERCE_HASH_TAB, + INSTR_TSQL_INIT_TSQL_DATATYPE_PRECEDENCE_HASH_TAB, + INSTR_TSQL_DTRUNCI8, + INSTR_TSQL_DTRUNCI4, + INSTR_TSQL_DTRUNCI2, + INSTR_TSQL_FTRUNCI8, + INSTR_TSQL_FTRUNCI4, + INSTR_TSQL_FTRUNCI2, + + INSTR_TSQL_SP_EXECUTESQL, + INSTR_TSQL_SP_PREPARE, + INSTR_TSQL_SP_UNPREPARE, + INSTR_TSQL_SP_GETAPPLOCK, + INSTR_TSQL_SP_RELEASEAPPLOCK, + INSTR_TSQL_SP_REMOVEAPPLOCKCACHE, + + INSTR_TSQL_ISOLATION_LEVEL_READ_UNCOMMITTED, + INSTR_TSQL_ISOLATION_LEVEL_READ_COMMITTED, + INSTR_UNSUPPORTED_TSQL_ISOLATION_LEVEL_REPEATABLE_READ, + INSTR_TSQL_ISOLATION_LEVEL_SNAPSHOT, + INSTR_UNSUPPORTED_TSQL_ISOLATION_LEVEL_SERIALIZABLE, + + INSTR_TSQL_COUNT +} PgTsqlInstrMetricType; + +#endif diff --git a/contrib/babelfishpg_common/src/numeric.c b/contrib/babelfishpg_common/src/numeric.c new file mode 100644 index 00000000000..3cab6709a06 --- /dev/null +++ b/contrib/babelfishpg_common/src/numeric.c @@ -0,0 +1,1031 @@ +/*------------------------------------------------------------------------- + * + * numeric.c + * An exact numeric data type for the Postgres database system + * + * Original coding 1998, Jan Wieck. Heavily revised 2003, Tom Lane. + * + * Many of the algorithmic ideas are borrowed from David M. Smith's "FM" + * multiple-precision math library, most recently published as Algorithm + * 786: Multiple-Precision Complex Arithmetic and Functions, ACM + * Transactions on Mathematical Software, Vol. 24, No. 4, December 1998, + * pages 359-367. + * + * Copyright (c) 1998-2018, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/adt/numeric.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "fmgr.h" + +#include +#include +#include +#include + +#include "access/hash.h" +#include "catalog/pg_type.h" +#include "common/int.h" +#include "funcapi.h" +#include "lib/hyperloglog.h" +#include "libpq/pqformat.h" +#include "miscadmin.h" +#include "nodes/nodeFuncs.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/int8.h" +#include "utils/numeric.h" +#include "utils/sortsupport.h" + +#include "numeric.h" + +/* ---------- + * Uncomment the following to enable compilation of dump_numeric() + * and dump_var() and to get a dump of any result produced by make_result(). + * ---------- +#define NUMERIC_DEBUG + */ + + +/* ---------- + * Local data types + * + * Numeric values are represented in a base-NBASE floating point format. + * Each "digit" ranges from 0 to NBASE-1. The type NumericDigit is signed + * and wide enough to store a digit. We assume that NBASE*NBASE can fit in + * an int. Although the purely calculational routines could handle any even + * NBASE that's less than sqrt(INT_MAX), in practice we are only interested + * in NBASE a power of ten, so that I/O conversions and decimal rounding + * are easy. Also, it's actually more efficient if NBASE is rather less than + * sqrt(INT_MAX), so that there is "headroom" for mul_var and div_var_fast to + * postpone processing carries. + * + * Values of NBASE other than 10000 are considered of historical interest only + * and are no longer supported in any sense; no mechanism exists for the client + * to discover the base, so every client supporting binary mode expects the + * base-10000 format. If you plan to change this, also note the numeric + * abbreviation code, which assumes NBASE=10000. + * ---------- + */ + +#if 0 +#define NBASE 10 +#define HALF_NBASE 5 +#define DEC_DIGITS 1 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 4 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 8 + +typedef signed char NumericDigit; +#endif + +#if 0 +#define NBASE 100 +#define HALF_NBASE 50 +#define DEC_DIGITS 2 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 3 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 6 + +typedef signed char NumericDigit; +#endif + +#if 1 +#define NBASE 10000 +#define HALF_NBASE 5000 +#define DEC_DIGITS 4 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 4 + +typedef int16 NumericDigit; +#endif + +/* + * The Numeric type as stored on disk. + * + * If the high bits of the first word of a NumericChoice (n_header, or + * n_short.n_header, or n_long.n_sign_dscale) are NUMERIC_SHORT, then the + * numeric follows the NumericShort format; if they are NUMERIC_POS or + * NUMERIC_NEG, it follows the NumericLong format. If they are NUMERIC_NAN, + * it is a NaN. We currently always store a NaN using just two bytes (i.e. + * only n_header), but previous releases used only the NumericLong format, + * so we might find 4-byte NaNs on disk if a database has been migrated using + * pg_upgrade. In either case, when the high bits indicate a NaN, the + * remaining bits are never examined. Currently, we always initialize these + * to zero, but it might be possible to use them for some other purpose in + * the future. + * + * In the NumericShort format, the remaining 14 bits of the header word + * (n_short.n_header) are allocated as follows: 1 for sign (positive or + * negative), 6 for dynamic scale, and 7 for weight. In practice, most + * commonly-encountered values can be represented this way. + * + * In the NumericLong format, the remaining 14 bits of the header word + * (n_long.n_sign_dscale) represent the display scale; and the weight is + * stored separately in n_weight. + * + * NOTE: by convention, values in the packed form have been stripped of + * all leading and trailing zero digits (where a "digit" is of base NBASE). + * In particular, if the value is zero, there will be no digits at all! + * The weight is arbitrary in that case, but we normally set it to zero. + */ + +struct NumericShort +{ + uint16 n_header; /* Sign + display scale + weight */ + NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ +}; + +struct NumericLong +{ + uint16 n_sign_dscale; /* Sign + display scale */ + int16 n_weight; /* Weight of 1st digit */ + NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ +}; + +union NumericChoice +{ + uint16 n_header; /* Header word */ + struct NumericLong n_long; /* Long form (4-byte header) */ + struct NumericShort n_short; /* Short form (2-byte header) */ +}; + +struct NumericData +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + union NumericChoice choice; /* choice of format */ +}; + + +/* + * Interpretation of high bits. + */ + +#define NUMERIC_SIGN_MASK 0xC000 +#define NUMERIC_POS 0x0000 +#define NUMERIC_NEG 0x4000 +#define NUMERIC_SHORT 0x8000 +#define NUMERIC_NAN 0xC000 + +#define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK) +#define NUMERIC_IS_NAN(n) (NUMERIC_FLAGBITS(n) == NUMERIC_NAN) +#define NUMERIC_IS_SHORT(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT) + +#define NUMERIC_HDRSZ (VARHDRSZ + sizeof(uint16) + sizeof(int16)) +#define NUMERIC_HDRSZ_SHORT (VARHDRSZ + sizeof(uint16)) + +/* + * If the flag bits are NUMERIC_SHORT or NUMERIC_NAN, we want the short header; + * otherwise, we want the long one. Instead of testing against each value, we + * can just look at the high bit, for a slight efficiency gain. + */ +#define NUMERIC_HEADER_IS_SHORT(n) (((n)->choice.n_header & 0x8000) != 0) +#define NUMERIC_HEADER_SIZE(n) \ + (VARHDRSZ + sizeof(uint16) + \ + (NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16))) + +/* + * Short format definitions. + */ + +#define NUMERIC_SHORT_SIGN_MASK 0x2000 +#define NUMERIC_SHORT_DSCALE_MASK 0x1F80 +#define NUMERIC_SHORT_DSCALE_SHIFT 7 +#define NUMERIC_SHORT_DSCALE_MAX \ + (NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT) +#define NUMERIC_SHORT_WEIGHT_SIGN_MASK 0x0040 +#define NUMERIC_SHORT_WEIGHT_MASK 0x003F +#define NUMERIC_SHORT_WEIGHT_MAX NUMERIC_SHORT_WEIGHT_MASK +#define NUMERIC_SHORT_WEIGHT_MIN (-(NUMERIC_SHORT_WEIGHT_MASK+1)) + +/* + * Extract sign, display scale, weight. + */ + +#define NUMERIC_DSCALE_MASK 0x3FFF + +#define NUMERIC_SIGN(n) \ + (NUMERIC_IS_SHORT(n) ? \ + (((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \ + NUMERIC_NEG : NUMERIC_POS) : NUMERIC_FLAGBITS(n)) +#define NUMERIC_DSCALE(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ + ((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \ + >> NUMERIC_SHORT_DSCALE_SHIFT \ + : ((n)->choice.n_long.n_sign_dscale & NUMERIC_DSCALE_MASK)) +#define NUMERIC_WEIGHT(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ + (((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \ + ~NUMERIC_SHORT_WEIGHT_MASK : 0) \ + | ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \ + : ((n)->choice.n_long.n_weight)) + +/* ---------- + * NumericVar is the format we use for arithmetic. The digit-array part + * is the same as the NumericData storage format, but the header is more + * complex. + * + * The value represented by a NumericVar is determined by the sign, weight, + * ndigits, and digits[] array. + * + * Note: the first digit of a NumericVar's value is assumed to be multiplied + * by NBASE ** weight. Another way to say it is that there are weight+1 + * digits before the decimal point. It is possible to have weight < 0. + * + * buf points at the physical start of the palloc'd digit buffer for the + * NumericVar. digits points at the first digit in actual use (the one + * with the specified weight). We normally leave an unused digit or two + * (preset to zeroes) between buf and digits, so that there is room to store + * a carry out of the top digit without reallocating space. We just need to + * decrement digits (and increment weight) to make room for the carry digit. + * (There is no such extra space in a numeric value stored in the database, + * only in a NumericVar in memory.) + * + * If buf is NULL then the digit buffer isn't actually palloc'd and should + * not be freed --- see the constants below for an example. + * + * dscale, or display scale, is the nominal precision expressed as number + * of digits after the decimal point (it must always be >= 0 at present). + * dscale may be more than the number of physically stored fractional digits, + * implying that we have suppressed storage of significant trailing zeroes. + * It should never be less than the number of stored digits, since that would + * imply hiding digits that are present. NOTE that dscale is always expressed + * in *decimal* digits, and so it may correspond to a fractional number of + * base-NBASE digits --- divide by DEC_DIGITS to convert to NBASE digits. + * + * rscale, or result scale, is the target precision for a computation. + * Like dscale it is expressed as number of *decimal* digits after the decimal + * point, and is always >= 0 at present. + * Note that rscale is not stored in variables --- it's figured on-the-fly + * from the dscales of the inputs. + * + * While we consistently use "weight" to refer to the base-NBASE weight of + * a numeric value, it is convenient in some scale-related calculations to + * make use of the base-10 weight (ie, the approximate log10 of the value). + * To avoid confusion, such a decimal-units weight is called a "dweight". + * + * NB: All the variable-level functions are written in a style that makes it + * possible to give one and the same variable as argument and destination. + * This is feasible because the digit buffer is separate from the variable. + * ---------- + */ +typedef struct NumericVar +{ + int ndigits; /* # of digits in digits[] - can be 0! */ + int weight; /* weight of first digit */ + int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */ + int dscale; /* display scale */ + NumericDigit *buf; /* start of palloc'd space for digits[] */ + NumericDigit *digits; /* base-NBASE digits */ +} NumericVar; + + +/* ---------- + * Data for generate_series + * ---------- + */ +typedef struct +{ + NumericVar current; + NumericVar stop; + NumericVar step; +} generate_series_numeric_fctx; + + +/* ---------- + * Sort support. + * ---------- + */ +typedef struct +{ + void *buf; /* buffer for short varlenas */ + int64 input_count; /* number of non-null values seen */ + bool estimating; /* true if estimating cardinality */ + + hyperLogLogState abbr_card; /* cardinality estimator */ +} NumericSortSupport; + + +/* ---------- + * Fast sum accumulator. + * + * NumericSumAccum is used to implement SUM(), and other standard aggregates + * that track the sum of input values. It uses 32-bit integers to store the + * digits, instead of the normal 16-bit integers (with NBASE=10000). This + * way, we can safely accumulate up to NBASE - 1 values without propagating + * carry, before risking overflow of any of the digits. 'num_uncarried' + * tracks how many values have been accumulated without propagating carry. + * + * Positive and negative values are accumulated separately, in 'pos_digits' + * and 'neg_digits'. This is simpler and faster than deciding whether to add + * or subtract from the current value, for each new value (see sub_var() for + * the logic we avoid by doing this). Both buffers are of same size, and + * have the same weight and scale. In accum_sum_final(), the positive and + * negative sums are added together to produce the final result. + * + * When a new value has a larger ndigits or weight than the accumulator + * currently does, the accumulator is enlarged to accommodate the new value. + * We normally have one zero digit reserved for carry propagation, and that + * is indicated by the 'have_carry_space' flag. When accum_sum_carry() uses + * up the reserved digit, it clears the 'have_carry_space' flag. The next + * call to accum_sum_add() will enlarge the buffer, to make room for the + * extra digit, and set the flag again. + * + * To initialize a new accumulator, simply reset all fields to zeros. + * + * The accumulator does not handle NaNs. + * ---------- + */ +typedef struct NumericSumAccum +{ + int ndigits; + int weight; + int dscale; + int num_uncarried; + bool have_carry_space; + int32 *pos_digits; + int32 *neg_digits; +} NumericSumAccum; + + +/* + * We define our own macros for packing and unpacking abbreviated-key + * representations for numeric values in order to avoid depending on + * USE_FLOAT8_BYVAL. The type of abbreviation we use is based only on + * the size of a datum, not the argument-passing convention for float8. + */ +#define NUMERIC_ABBREV_BITS (SIZEOF_DATUM * BITS_PER_BYTE) +#if SIZEOF_DATUM == 8 +#define NumericAbbrevGetDatum(X) ((Datum) (X)) +#define DatumGetNumericAbbrev(X) ((int64) (X)) +#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT64_MIN) +#else +#define NumericAbbrevGetDatum(X) ((Datum) (X)) +#define DatumGetNumericAbbrev(X) ((int32) (X)) +#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT32_MIN) +#endif + +/* ---------- + * Local functions + * ---------- + */ + +#ifdef NUMERIC_DEBUG +static void dump_numeric(const char *str, Numeric num); +static void dump_var(const char *str, NumericVar *var); +#else +#define dump_numeric(s,n) +#define dump_var(s,v) +#endif + +#define digitbuf_alloc(ndigits) \ + ((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit))) +#define digitbuf_free(buf) \ + do { \ + if ((buf) != NULL) \ + pfree(buf); \ + } while (0) + +#define init_var(v) MemSetAligned(v, 0, sizeof(NumericVar)) + +#define NUMERIC_DIGITS(num) (NUMERIC_HEADER_IS_SHORT(num) ? \ + (num)->choice.n_short.n_data : (num)->choice.n_long.n_data) +#define NUMERIC_NDIGITS(num) \ + ((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit)) +#define NUMERIC_CAN_BE_SHORT(scale,weight) \ + ((scale) <= NUMERIC_SHORT_DSCALE_MAX && \ + (weight) <= NUMERIC_SHORT_WEIGHT_MAX && \ + (weight) >= NUMERIC_SHORT_WEIGHT_MIN) + +static const NumericVar const_nan = +{0, 0, NUMERIC_NAN, 0, NULL, NULL}; +#if DEC_DIGITS == 4 +static const int round_powers[4] = {0, 1000, 100, 10}; +#endif + +PG_FUNCTION_INFO_V1(tsql_numeric_round); +PG_FUNCTION_INFO_V1(tsql_numeric_trunc); + +static void alloc_var(NumericVar *var, int ndigits); +static void free_var(NumericVar *var); +static const char *set_var_from_str(const char *str, const char *cp, + NumericVar *dest); +static Numeric make_result(const NumericVar *var); +static void strip_var(NumericVar *var); + +/* ---------------------------------------------------------------------- + * + * Local functions follow + * + * In general, these do not support NaNs --- callers must eliminate + * the possibility of NaN first. (make_result() is an exception.) + * + * ---------------------------------------------------------------------- + */ + + +/* + * alloc_var() - + * + * Allocate a digit buffer of ndigits digits (plus a spare digit for rounding) + */ +static void +alloc_var(NumericVar *var, int ndigits) +{ + digitbuf_free(var->buf); + var->buf = digitbuf_alloc(ndigits + 1); + var->buf[0] = 0; /* spare digit for rounding */ + var->digits = var->buf + 1; + var->ndigits = ndigits; +} + + +/* + * free_var() - + * + * Return the digit buffer of a variable to the free pool + */ +static void +free_var(NumericVar *var) +{ + digitbuf_free(var->buf); + var->buf = NULL; + var->digits = NULL; + var->sign = NUMERIC_NAN; +} + +/* + * set_var_from_str() + * + * Parse a string and put the number into a variable + * + * This function does not handle leading or trailing spaces, and it doesn't + * accept "NaN" either. It returns the end+1 position so that caller can + * check for trailing spaces/garbage if deemed necessary. + * + * cp is the place to actually start parsing; str is what to use in error + * reports. (Typically cp would be the same except advanced over spaces.) + */ +static const char * +set_var_from_str(const char *str, const char *cp, NumericVar *dest) +{ + bool have_dp = false; + int i; + unsigned char *decdigits; + int sign = NUMERIC_POS; + int dweight = -1; + int ddigits; + int dscale = 0; + int weight; + int ndigits; + int offset; + NumericDigit *digits; + + /* + * We first parse the string to extract decimal digits and determine the + * correct decimal weight. Then convert to NBASE representation. + */ + switch (*cp) + { + case '+': + sign = NUMERIC_POS; + cp++; + break; + + case '-': + sign = NUMERIC_NEG; + cp++; + break; + } + + if (*cp == '.') + { + have_dp = true; + cp++; + } + + if (!isdigit((unsigned char) *cp)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + + decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2); + + /* leading padding for digit alignment later */ + memset(decdigits, 0, DEC_DIGITS); + i = DEC_DIGITS; + + while (*cp) + { + if (isdigit((unsigned char) *cp)) + { + decdigits[i++] = *cp++ - '0'; + if (!have_dp) + dweight++; + else + dscale++; + } + else if (*cp == '.') + { + if (have_dp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + have_dp = true; + cp++; + } + else + break; + } + + ddigits = i - DEC_DIGITS; + /* trailing padding for digit alignment later */ + memset(decdigits + i, 0, DEC_DIGITS - 1); + + /* Handle exponent, if any */ + if (*cp == 'e' || *cp == 'E') + { + long exponent; + char *endptr; + + cp++; + exponent = strtol(cp, &endptr, 10); + if (endptr == cp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + cp = endptr; + + /* + * At this point, dweight and dscale can't be more than about + * INT_MAX/2 due to the MaxAllocSize limit on string length, so + * constraining the exponent similarly should be enough to prevent + * integer overflow in this function. If the value is too large to + * fit in storage format, make_result() will complain about it later; + * for consistency use the same ereport errcode/text as make_result(). + */ + if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + dweight += (int) exponent; + dscale -= (int) exponent; + if (dscale < 0) + dscale = 0; + } + + /* + * Okay, convert pure-decimal representation to base NBASE. First we need + * to determine the converted weight and ndigits. offset is the number of + * decimal zeroes to insert before the first given digit to have a + * correctly aligned first NBASE digit. + */ + if (dweight >= 0) + weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1; + else + weight = -((-dweight - 1) / DEC_DIGITS + 1); + offset = (weight + 1) * DEC_DIGITS - (dweight + 1); + ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS; + + alloc_var(dest, ndigits); + dest->sign = sign; + dest->weight = weight; + dest->dscale = dscale; + + i = DEC_DIGITS - offset; + digits = dest->digits; + + while (ndigits-- > 0) + { +#if DEC_DIGITS == 4 + *digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 + + decdigits[i + 2]) * 10 + decdigits[i + 3]; +#elif DEC_DIGITS == 2 + *digits++ = decdigits[i] * 10 + decdigits[i + 1]; +#elif DEC_DIGITS == 1 + *digits++ = decdigits[i]; +#else +#error unsupported NBASE +#endif + i += DEC_DIGITS; + } + + pfree(decdigits); + + /* Strip any leading/trailing zeroes, and normalize weight if zero */ + strip_var(dest); + + /* Return end+1 position for caller */ + return cp; +} + +/* + * make_result() - + * + * Create the packed db numeric format in palloc()'d memory from + * a variable. + */ +static Numeric +make_result(const NumericVar *var) +{ + Numeric result; + NumericDigit *digits = var->digits; + int weight = var->weight; + int sign = var->sign; + int n; + Size len; + + if (sign == NUMERIC_NAN) + { + result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT); + + SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT); + result->choice.n_header = NUMERIC_NAN; + /* the header word is all we need */ + + dump_numeric("make_result()", result); + return result; + } + + n = var->ndigits; + + /* truncate leading zeroes */ + while (n > 0 && *digits == 0) + { + digits++; + weight--; + n--; + } + /* truncate trailing zeroes */ + while (n > 0 && digits[n - 1] == 0) + n--; + + /* If zero result, force to weight=0 and positive sign */ + if (n == 0) + { + weight = 0; + sign = NUMERIC_POS; + } + + /* Build the result */ + if (NUMERIC_CAN_BE_SHORT(var->dscale, weight)) + { + len = NUMERIC_HDRSZ_SHORT + n * sizeof(NumericDigit); + result = (Numeric) palloc(len); + SET_VARSIZE(result, len); + result->choice.n_short.n_header = + (sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) + : NUMERIC_SHORT) + | (var->dscale << NUMERIC_SHORT_DSCALE_SHIFT) + | (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0) + | (weight & NUMERIC_SHORT_WEIGHT_MASK); + } + else + { + len = NUMERIC_HDRSZ + n * sizeof(NumericDigit); + result = (Numeric) palloc(len); + SET_VARSIZE(result, len); + result->choice.n_long.n_sign_dscale = + sign | (var->dscale & NUMERIC_DSCALE_MASK); + result->choice.n_long.n_weight = weight; + } + + Assert(NUMERIC_NDIGITS(result) == n); + if (n > 0) + memcpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit)); + + /* Check for overflow of int16 fields */ + if (NUMERIC_WEIGHT(result) != weight || + NUMERIC_DSCALE(result) != var->dscale) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + + dump_numeric("make_result()", result); + return result; +} + +/* + * strip_var + * + * Strip any leading and trailing zeroes from a numeric variable + */ +static void +strip_var(NumericVar *var) +{ + NumericDigit *digits = var->digits; + int ndigits = var->ndigits; + + /* Strip leading zeroes */ + while (ndigits > 0 && *digits == 0) + { + digits++; + var->weight--; + ndigits--; + } + + /* Strip trailing zeroes */ + while (ndigits > 0 && digits[ndigits - 1] == 0) + ndigits--; + + /* If it's zero, normalize the sign and weight */ + if (ndigits == 0) + { + var->sign = NUMERIC_POS; + var->weight = 0; + } + + var->digits = digits; + var->ndigits = ndigits; +} + +/* + * Converts input string to numeric value in TDS receiver side + */ +Numeric +tsql_set_var_from_str_wrapper(const char *str) +{ + NumericVar value; + Numeric res; + init_var(&value); + set_var_from_str(str, str, &value); + res = make_result(&value); + free_var(&value); + return res; +} + +/* + * set_var_from_num() - + * Set NumericVar from Numeric + */ +static void +set_var_from_num(Numeric num, NumericVar *dest) +{ + int ndigits; + + ndigits = NUMERIC_NDIGITS(num); + + alloc_var(dest, ndigits); + + dest->weight = NUMERIC_WEIGHT(num); + dest->sign = NUMERIC_SIGN(num); + dest->dscale = NUMERIC_DSCALE(num); + + memcpy(dest->digits, NUMERIC_DIGITS(num), ndigits * sizeof(NumericDigit)); +} + + +/* + * tsql_round_var + * + * This function is similar to round_var, + * but we check if rounding up will cause an overflow + */ +static void +tsql_round_var(NumericVar *var, int rscale) +{ + NumericDigit *digits = var->digits; + int di; + int ndigits; + int carry; + + var->dscale = rscale; + + /* decimal digits wanted */ + di = (var->weight + 1) * DEC_DIGITS + rscale; + + /* checking numeric overflow for TSQL */ + if (rscale < 0) + { + int integral_digits = di - DEC_DIGITS; + int leading_digits = digits[0]; + static const int32 timescales[DEC_DIGITS] = { + 1000, + 100, + 10, + 1, + }; + + for(int i = 0; i < DEC_DIGITS; i++) + { + if(leading_digits >= timescales[i]) + { + integral_digits += (4-i); + break; + } + } + /* Overflow when rounding up*/ + if (integral_digits == 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows for numeric format"))); + /* should lose all digits */ + else if (integral_digits < 0) + di = -1; + } + + /* + * If di < 0, the value loses all digits + */ + if (di < 0) + { + var->ndigits = 0; + var->weight = 0; + var->sign = NUMERIC_POS; + } + else + { + /* NBASE digits wanted */ + ndigits = (di + DEC_DIGITS - 1) / DEC_DIGITS; + + /* 0, or number of decimal digits to keep in last NBASE digit */ + di %= DEC_DIGITS; + + if (ndigits < var->ndigits || + (ndigits == var->ndigits && di > 0)) + { + var->ndigits = ndigits; + +#if DEC_DIGITS == 1 + /* di must be zero */ + carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0; +#else + if (di == 0) + carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0; + else + { + /* Must round within last NBASE digit */ + int extra, + pow10; + +#if DEC_DIGITS == 4 + pow10 = round_powers[di]; +#elif DEC_DIGITS == 2 + pow10 = 10; +#else +#error unsupported NBASE +#endif + extra = digits[--ndigits] % pow10; + digits[ndigits] -= extra; + carry = 0; + if (extra >= pow10 / 2) + { + pow10 += digits[ndigits]; + if (pow10 >= NBASE) + { + pow10 -= NBASE; + carry = 1; + } + digits[ndigits] = pow10; + } + } +#endif + + /* Propagate carry if needed */ + while (carry) + { + carry += digits[--ndigits]; + if (carry >= NBASE) + { + digits[ndigits] = carry - NBASE; + carry = 1; + } + else + { + digits[ndigits] = carry; + carry = 0; + } + } + + if (ndigits < 0) + { + Assert(ndigits == -1); /* better not have added > 1 digit */ + Assert(var->digits > var->buf); + var->digits--; + var->ndigits++; + var->weight++; + } + } + } +} + +/* + * tsql_numeric_round() - + * + * compared to numeric_round(), we handle NULL input parameter in this function, + * and calls tsql_round_var() for rounding + */ +Datum +tsql_numeric_round(PG_FUNCTION_ARGS) +{ + Numeric num; + int32 scale; + Numeric res; + NumericVar arg; + /* when num or scale is NULL, return NULL */ + if(PG_ARGISNULL(0) || PG_ARGISNULL(1)) + PG_RETURN_NULL(); + + num = PG_GETARG_NUMERIC(0); + scale = PG_GETARG_INT32(1); + + /* + * Handle NaN + */ + if (NUMERIC_IS_NAN(num)) + PG_RETURN_NUMERIC(make_result(&const_nan)); + + /* + * Limit the scale value to avoid possible overflow in calculations + */ + scale = Max(scale, -NUMERIC_MAX_RESULT_SCALE); + scale = Min(scale, NUMERIC_MAX_RESULT_SCALE); + + /* + * Unpack the argument and round it at the proper digit position + */ + init_var(&arg); + set_var_from_num(num, &arg); + + tsql_round_var(&arg, scale); + + /* We don't allow negative output dscale */ + if (scale < 0) + arg.dscale = 0; + + /* + * Return the rounded result + */ + res = make_result(&arg); + + free_var(&arg); + PG_RETURN_NUMERIC(res); +} + +/* + * tsql_numeric_round() - + * + * Directly call numeric_trunc() or + * call tsql_numeric_round() when 'function' is 0 or null +*/ +Datum +tsql_numeric_trunc(PG_FUNCTION_ARGS) +{ + Numeric num; + int32 scale ; + /* when num or scale is NULL, return NULL */ + if(PG_ARGISNULL(0) || PG_ARGISNULL(1)) + PG_RETURN_NULL(); + num = PG_GETARG_NUMERIC(0); + scale = PG_GETARG_INT32(1); + if(PG_ARGISNULL(2) || PG_GETARG_INT32(2) == 0) + return DirectFunctionCall2(tsql_numeric_round, + NumericGetDatum(num), + Int32GetDatum(scale)); + + + return DirectFunctionCall2(numeric_trunc, + NumericGetDatum(num), + Int32GetDatum(scale)); +} + +/* + * Get Precision & Scale from Numeric Value + */ +int32_t +tsql_numeric_get_typmod(Numeric num) +{ + int32_t scale = NUMERIC_DSCALE(num); + int32_t weight = NUMERIC_WEIGHT(num); + int32_t precision; + + if (weight >= 0) + { + static const int32 timescales[DEC_DIGITS] = { + 1000, + 100, + 10, + 1, + }; + int leading_digits = NUMERIC_DIGITS(num)[0]; + precision = weight * DEC_DIGITS + scale; + + for(int i = 0; i < DEC_DIGITS; i++) + { + if(leading_digits >= timescales[i]) + { + precision += (4-i); + break; + } + } + } + else + /* weight < 0 means the integral part of the number is 0 */ + precision = 1 + scale; + + return (((precision & 0xFFFF) << 16 ) | (scale & 0xFFFF)) + VARHDRSZ; +} diff --git a/contrib/babelfishpg_common/src/numeric.h b/contrib/babelfishpg_common/src/numeric.h new file mode 100644 index 00000000000..19cccc238ff --- /dev/null +++ b/contrib/babelfishpg_common/src/numeric.h @@ -0,0 +1,9 @@ +#ifndef NUMERIC_H +#define NUMERIC_H + +#include "utils/numeric.h" + +extern Numeric tsql_set_var_from_str_wrapper(const char *str); +extern int32_t tsql_numeric_get_typmod(Numeric num); + +#endif diff --git a/contrib/babelfishpg_common/src/smalldatetime.c b/contrib/babelfishpg_common/src/smalldatetime.c new file mode 100644 index 00000000000..9596dcf0f39 --- /dev/null +++ b/contrib/babelfishpg_common/src/smalldatetime.c @@ -0,0 +1,594 @@ +/*------------------------------------------------------------------------- + * + * smalldatetime.c + * Functions for the type "smalldatetime". + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datetime.h" +#include "utils/timestamp.h" + +#include "miscadmin.h" + +PG_FUNCTION_INFO_V1(smalldatetime_in); +PG_FUNCTION_INFO_V1(smalldatetime_recv); +PG_FUNCTION_INFO_V1(time_smalldatetime); +PG_FUNCTION_INFO_V1(date_smalldatetime); +PG_FUNCTION_INFO_V1(timestamp_smalldatetime); +PG_FUNCTION_INFO_V1(timestamptz_smalldatetime); +PG_FUNCTION_INFO_V1(smalldatetime_varchar); +PG_FUNCTION_INFO_V1(varchar_smalldatetime); +PG_FUNCTION_INFO_V1(smalldatetime_char); +PG_FUNCTION_INFO_V1(char_smalldatetime); + +PG_FUNCTION_INFO_V1(smalldatetime_pl_int4); +PG_FUNCTION_INFO_V1(int4_mi_smalldatetime); +PG_FUNCTION_INFO_V1(int4_pl_smalldatetime); +PG_FUNCTION_INFO_V1(smalldatetime_mi_int4); + +PG_FUNCTION_INFO_V1(smalldatetime_pl_float8); +PG_FUNCTION_INFO_V1(smalldatetime_mi_float8); +PG_FUNCTION_INFO_V1(float8_pl_smalldatetime); +PG_FUNCTION_INFO_V1(float8_mi_smalldatetime); + +void AdjustTimestampForSmallDatetime(Timestamp *time); +void CheckSmalldatetimeRange(const Timestamp time); +static Datum smalldatetime_in_str(char *str); + +/* smalldatetime_in_str() + * Convert a string to internal form. + * Most parts of this functions is same as timestamp_in(), + * but we use a different rounding function for smalldatetime. + */ +static Datum +smalldatetime_in_str(char *str) +{ +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + Timestamp result; + fsec_t fsec; + struct pg_tm tt, + *tm = &tt; + int tz; + int dtype; + int nf; + int dterr; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char workbuf[MAXDATELEN + MAXDATEFIELDS]; + + dterr = ParseDateTime(str, workbuf, sizeof(workbuf), + field, ftype, MAXDATEFIELDS, &nf); + + if (dterr == 0) + dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); + // dterr == 1 means that input is TIME format(e.g 12:34:59.123) + // initialize other necessary date parts and accept input format + if (dterr == 1) + { + tm->tm_year = 1900; + tm->tm_mon = 1; + tm->tm_mday = 1; + dterr = 0; + } + if (dterr != 0) + DateTimeParseError(dterr, str, "smalldatetime"); + switch (dtype) + { + case DTK_DATE: + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("smalldatetime out of range: \"%s\"", str))); + break; + + case DTK_EPOCH: + result = SetEpochTimestamp(); + break; + + case DTK_LATE: + TIMESTAMP_NOEND(result); + break; + + case DTK_EARLY: + TIMESTAMP_NOBEGIN(result); + break; + + default: + elog(ERROR, "unexpected dtype %d while parsing smalldatetime \"%s\"", + dtype, str); + TIMESTAMP_NOEND(result); + } + + CheckSmalldatetimeRange(result); + AdjustTimestampForSmallDatetime(&result); + + PG_RETURN_TIMESTAMP(result); +} + +/* smalldatetime_in() + * Convert a string to internal form. + * Most parts of this functions is same as timestamp_in(), + * but we use a different rounding function for smalldatetime. + */ +Datum +smalldatetime_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + + return smalldatetime_in_str(str); +} + +/* + * CheckSmalldatetimeRange --- Check if timestamp is out of range for smalldatetime + */ +void +CheckSmalldatetimeRange(const Timestamp time) +{ + /* the lower bound and uppbound stands for 1899-12-31 23:59:29.999 and 2079-06-06 23:59:29.999 */ + static const int64 lower_bound = -3155673630001000; + static const int64 upper_bound = 2506636769999000; + if (time < lower_bound || time >= upper_bound) + { + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for smalldatetime"))); + } +} + +/* + * AdjustTimestampForSmallDatetime --- round off a timestamp to suit smalldatetime. + * The rounding logic: if second is larger or equal to 29.999 round up, otherwise round down. + */ +void +AdjustTimestampForSmallDatetime(Timestamp *time) +{ + static const int64 SmallDatetimeRoundsThresould[2] = { + 29999000, + 30001000 + }; + + if (*time >= INT64CONST(0)) + { + if( *time % USECS_PER_MINUTE >= SmallDatetimeRoundsThresould[0]) + { + *time = *time / USECS_PER_MINUTE * USECS_PER_MINUTE + USECS_PER_MINUTE; + } + else + { + *time = *time / USECS_PER_MINUTE * USECS_PER_MINUTE; + } + } + else + { + if( (-(*time)) % USECS_PER_MINUTE <= SmallDatetimeRoundsThresould[1]) + { + *time = *time / USECS_PER_MINUTE * USECS_PER_MINUTE; + } + else + { + *time = *time / USECS_PER_MINUTE * USECS_PER_MINUTE - USECS_PER_MINUTE; + } + } +} + +/* time_smalldatetime() + * Convert time to smalldatetime + */ +Datum +time_smalldatetime(PG_FUNCTION_ARGS) +{ + TimeADT timeVal = PG_GETARG_TIMEADT(0); + Timestamp result; + + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + + // Initialize default year, month, day to 1900-01-01 + tm->tm_year = 1900; + tm->tm_mon = 1; + tm->tm_mday = 1; + + // Convert TimeADT type to tm + time2tm(timeVal, tm, &fsec); + + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for smalldatetime"))); + + AdjustTimestampForSmallDatetime(&result); + + PG_RETURN_TIMESTAMP(result); +} + +/* date_smalldatetime() + * Convert date to smalldatetime + */ +Datum +date_smalldatetime(PG_FUNCTION_ARGS) +{ + DateADT dateVal = PG_GETARG_DATEADT(0); + Timestamp result; + + if (DATE_IS_NOBEGIN(dateVal)) + TIMESTAMP_NOBEGIN(result); + else if (DATE_IS_NOEND(dateVal)) + TIMESTAMP_NOEND(result); + else + result = dateVal * USECS_PER_DAY; + + CheckSmalldatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* timestamp_smalldatetime() + * Convert timestamp to smalldatetime + */ +Datum +timestamp_smalldatetime(PG_FUNCTION_ARGS) +{ + Timestamp result = PG_GETARG_TIMESTAMP(0); + + CheckSmalldatetimeRange(result); + AdjustTimestampForSmallDatetime(&result); + PG_RETURN_TIMESTAMP(result); +} + +/* timestamptz_smalldatetime() + * Convert timestamptz to smalldatetime + */ +Datum +timestamptz_smalldatetime(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); + Timestamp result; + + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + int tz; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + result = timestamp; + else + { + if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for smalldatetime"))); + if (tm2timestamp(tm, fsec, NULL, &result) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("data out of range for smalldatetime"))); + } + CheckSmalldatetimeRange(result); + AdjustTimestampForSmallDatetime(&result); + PG_RETURN_TIMESTAMP(result); +} + +/* smalldatetime_varchar() + * Convert a smalldatetime to varchar. + * The function is the same as timestamp_out() except the return type is a VARCHAR Datum. + */ +Datum +smalldatetime_varchar(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + char *s; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + VarChar *result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + EncodeSpecialTimestamp(timestamp, buf); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + s = pstrdup(buf); + result = (VarChar *) cstring_to_text(s); + PG_RETURN_VARCHAR_P(result); +} + +/* + * varchar_smalldatetime() + * Convert a varchar to smalldatetime + */ +Datum +varchar_smalldatetime(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + char *str = TextDatumGetCString(txt); + + return smalldatetime_in_str(str); +} + +/* smalldatetime_char() + * Convert a smalldatetime to char. + * The function is the same as timestamp_out() except the return type is a CHAR Datum. + */ +Datum +smalldatetime_char(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + char *s; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + VarChar *result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + EncodeSpecialTimestamp(timestamp, buf); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + s = pstrdup(buf); + result = (BpChar *) cstring_to_text(s); + PG_RETURN_BPCHAR_P(result); +} + +/* + * char_smalldatetime() + * Convert a CHAR to smalldatetime + */ +Datum +char_smalldatetime(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + char *str = TextDatumGetCString(txt); + + return smalldatetime_in_str(str); +} + +/* + * smalldatetime_pl_int4() + * operator function for adding smalldatetime plus int + * + * simply add number of days to date value, while preserving the time + * component + */ +Datum +smalldatetime_pl_int4(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + int32 days = PG_GETARG_INT32(1); + Timestamp result; + Interval *input_interval; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, days, 0, 0, 0); + + /* add interval */ + result = DirectFunctionCall2(timestamp_pl_interval, timestamp, PointerGetDatum(input_interval)); + + CheckSmalldatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * int4_mi_smalldatetime() + * Operator function for subtracting int minus smalldatetime + * + * Convert the input int32 value d to smalldatetime(1/1/1900) + d days. + * Then add the difference between the input smalldatetime value and the one + * above to the default smalldatetime value (1/1/1900). + * + * ex: + * d = 9, dt = '1/11/1900' + * dt_left = smalldatetime(1/1/1900) + 9 days = smalldatetime(1/10/1900) + * diff = dt_left - dt = -1 day + * result = 1/1/1900 + diff = 1899-12-31 + */ +Datum +int4_mi_smalldatetime(PG_FUNCTION_ARGS) +{ + int32 days = PG_GETARG_INT32(0); + Timestamp timestamp_right = PG_GETARG_TIMESTAMP(1); + Timestamp result; + Timestamp default_timestamp; + Timestamp timestamp_left; + Interval *input_interval; + Interval *result_interval; + + if (TIMESTAMP_NOT_FINITE(timestamp_right)) + PG_RETURN_TIMESTAMP(timestamp_right); + + /* inialize input int(days) as timestamp */ + default_timestamp = DirectFunctionCall6(make_timestamp, 1900, 1, 1, 0, 0,0); + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, days, 0, 0, 0); + timestamp_left = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(input_interval)); + + /* calculate timestamp diff */ + result_interval = (Interval *) DirectFunctionCall2(timestamp_mi, timestamp_left, timestamp_right); + + /* if the diff between left and right timestamps is positive, then we add the interval. else, subtract */ + result = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(result_interval)); + + CheckSmalldatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * int4_pl_smalldatetime() + * operator function for adding int plus smalldatetime + */ +Datum +int4_pl_smalldatetime(PG_FUNCTION_ARGS) +{ + int32 days = PG_GETARG_INT32(0); + Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + PG_RETURN_TIMESTAMP(DirectFunctionCall2(smalldatetime_pl_int4, timestamp, days)); +} + +/* + * smalldatetime_mi_int4() + * operator function for subtracting smalldatetime minus int + */ +Datum +smalldatetime_mi_int4(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + int32 days = PG_GETARG_INT32(1); + PG_RETURN_TIMESTAMP(DirectFunctionCall2(smalldatetime_pl_int4, timestamp, -days)); +} + + +/* + * smalldatetime_pl_float8() + * operator function for adding smalldatetime plus float + * + * simply add number of days/secs to date value, while preserving the time + * component + */ +Datum +smalldatetime_pl_float8(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + double days = PG_GETARG_FLOAT8(1); + double day_whole, day_fract, sec_whole; + Interval *input_interval; + Timestamp result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + // fsec_whole = TSROUND(TS_PREC_INV*sec_fract); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + + /* add interval */ + result = DirectFunctionCall2(timestamp_pl_interval, timestamp, PointerGetDatum(input_interval)); + + CheckSmalldatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + + +/* + * float8_mi_smalldatetime() + * Operator function for subtracting float8 minus smalldatetime + * + * Convert the input float8 value d to smalldatetime(1/1/1900) + d days. + * Then add the difference between the input smalldatetime value and the one + * above to the default smalldatetime value (1/1/1900). + */ +Datum +float8_mi_smalldatetime(PG_FUNCTION_ARGS) +{ + double days = PG_GETARG_FLOAT8(0); + Timestamp timestamp_right = PG_GETARG_TIMESTAMP(1); + double day_whole, day_fract, sec_whole; + Timestamp result; + Timestamp default_timestamp; + Timestamp timestamp_left; + Interval *input_interval; + Interval *result_interval; + + if (TIMESTAMP_NOT_FINITE(timestamp_right)) + PG_RETURN_TIMESTAMP(timestamp_right); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + + + /* inialize input int(days) as timestamp */ + default_timestamp = DirectFunctionCall6(make_timestamp, 1900, 1, 1, 0, 0,0); + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + timestamp_left = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(input_interval)); + + /* calculate timestamp diff */ + result_interval = (Interval *) DirectFunctionCall2(timestamp_mi, timestamp_left, timestamp_right); + + /* if the diff between left and right timestamps is positive, then we add the interval. else, subtract */ + result = DirectFunctionCall2(timestamp_pl_interval, default_timestamp, PointerGetDatum(result_interval)); + + + CheckSmalldatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * float8_pl_smalldatetime() + * operator function for adding float8 plus smalldatetime + */ +Datum +float8_pl_smalldatetime(PG_FUNCTION_ARGS) +{ + double days = PG_GETARG_FLOAT8(0); + Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + double day_whole, day_fract, sec_whole; + Interval *input_interval; + Timestamp result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + + /* add interval */ + result = DirectFunctionCall2(timestamp_pl_interval, timestamp, PointerGetDatum(input_interval)); + + CheckSmalldatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} + +/* + * smalldatetime_mi_float8() + * operator function for subtracting smalldatetime minus float8 + */ +Datum +smalldatetime_mi_float8(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + double days = PG_GETARG_FLOAT8(1); + double day_whole, day_fract, sec_whole; + Interval *input_interval; + Timestamp result; + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + /* split day into whole and fractional parts */ + day_fract = modf(days, &day_whole); + day_fract = modf(SECS_PER_DAY*day_fract, &sec_whole); + + /* make interval */ + input_interval = (Interval *) DirectFunctionCall7(make_interval, 0, 0, 0, (int32) day_whole, 0, 0, Float8GetDatum(sec_whole)); + + + /* subtract interval */ + result = DirectFunctionCall2(timestamp_mi_interval, timestamp, PointerGetDatum(input_interval)); + + CheckSmalldatetimeRange(result); + PG_RETURN_TIMESTAMP(result); +} \ No newline at end of file diff --git a/contrib/babelfishpg_common/src/sqlvariant.c b/contrib/babelfishpg_common/src/sqlvariant.c new file mode 100644 index 00000000000..12e78ab841a --- /dev/null +++ b/contrib/babelfishpg_common/src/sqlvariant.c @@ -0,0 +1,2257 @@ +/*------------------------------------------------------------------------- + * + * sqlvariant.c + * Functions for the type "sql_variant". + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "executor/spi.h" +#include "fmgr.h" +#include "miscadmin.h" +#include "access/hash.h" +#include "access/htup_details.h" +#include "catalog/namespace.h" +#include "catalog/pg_authid.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_database.h" +#include "catalog/pg_type.h" +#include "catalog/pg_operator.h" +#include "commands/dbcommands.h" +#include "lib/stringinfo.h" +#include "libpq/pqformat.h" +#include "port.h" +#include "utils/array.h" +#include "utils/date.h" +#include "parser/parse_coerce.h" +#include "parser/parse_oper.h" +#include "instr.h" +#include "utils/builtins.h" +#include "utils/elog.h" +#include "utils/guc.h" +#include "utils/hsearch.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include "utils/syscache.h" +#include "utils/timestamp.h" +#include "utils/uuid.h" +#include "utils/varbit.h" + +#include "datetimeoffset.h" +#include "typecode.h" +#include "numeric.h" + +/* + * macros for supporting sqlvariant datatype on TDS side + */ + +#define VARIANT_TYPE_TINYINT 48 +#define VARIANT_TYPE_BIT 50 +#define VARIANT_TYPE_SMALLINT 52 +#define VARIANT_TYPE_INT 56 +#define VARIANT_TYPE_BIGINT 127 +#define VARIANT_TYPE_REAL 59 +#define VARIANT_TYPE_FLOAT 62 +#define VARIANT_TYPE_NUMERIC 108 +#define VARIANT_TYPE_MONEY 60 +#define VARIANT_TYPE_SMALLMONEY 122 +#define VARIANT_TYPE_DATE 40 +#define VARIANT_TYPE_CHAR 175 +#define VARIANT_TYPE_VARCHAR 167 +#define VARIANT_TYPE_NCHAR 239 +#define VARIANT_TYPE_NVARCHAR 231 +#define VARIANT_TYPE_BINARY 173 +#define VARIANT_TYPE_VARBINARY 165 +#define VARIANT_TYPE_UNIQUEIDENTIFIER 36 +#define VARIANT_TYPE_TIME 41 +#define VARIANT_TYPE_SMALLDATETIME 58 +#define VARIANT_TYPE_DATETIME 61 +#define VARIANT_TYPE_DATETIME2 42 +#define VARIANT_TYPE_DATETIMEOFFSET 43 + +/* Function Registeration */ +PG_FUNCTION_INFO_V1(sqlvariantin); +PG_FUNCTION_INFO_V1(sqlvariantout); +PG_FUNCTION_INFO_V1(sqlvariantrecv); +PG_FUNCTION_INFO_V1(sqlvariantsend); + + +/* Header version + * For now, we assume that headers of all types use Header Version 1. + * However, in future we may want to define header versions for each types, + * for example we may want to store addional data for a type. + * As of now there's no use cases where we could use that. + */ +#define HDR_VER 1 + +/* Header related macros */ +#define SV_HDR_1B(PTR) ((svhdr_1B_t *) (VARDATA_ANY(PTR))) +#define SV_HDR_2B(PTR) ((svhdr_2B_t *) (VARDATA_ANY(PTR))) +#define SV_HDR_3B(PTR) ((svhdr_3B_t *) (VARDATA_ANY(PTR))) +#define SV_HDR_5B(PTR) ((svhdr_5B_t *) (VARDATA_ANY(PTR))) + +#define SV_GET_TYPCODE(HEADER) (HEADER->metadata >> 3) +#define SV_GET_TYPCODE_PTR(PTR) (SV_HDR_1B(PTR)->metadata >> 3) +#define SV_GET_MDVER(HEADER) (HEADER->metadata & 0x07) +#define SV_SET_METADATA(HEADER, TYPCODE, MDVER) (HEADER->metadata = TYPCODE << 3 | MDVER) + +#define SV_DATA(PTR, SVHDR) (VARDATA_ANY(PTR) + SVHDR) +#define SV_DATUM(PTR, SVHDR) ((Datum) (VARDATA_ANY(PTR) + SVHDR)) +#define SV_DATUM_PTR(PTR, SVHDR) ((Datum *) (VARDATA_ANY(PTR) + SVHDR)) + +#define SV_CAN_USE_SHORT_VALENA(DATALEN, SVHDR) (DATALEN + SVHDR + VARHDRSZ_SHORT <= VARATT_SHORT_MAX) + +bytea *convertVarcharToSQLVariantByteA(VarChar *vch, Oid coll); +bytea *convertIntToSQLVariantByteA(int ret); + + +/******************** Collation Utilities *************************/ + +/* match definition in babelfishpg_tsql:collation.h */ +typedef struct Tsql_collation_callbacks +{ + /* Function pointers set up by the plugin */ + Oid (*get_tsql_collation_oid_f)(int persist_coll_id); + int (*get_persist_collation_id_f)(Oid coll_oid); + int (*get_server_collation_collidx_f)(void); + int8_t (*cmp_collation_f)(uint16_t coll1, uint16_t coll2); + +} Tsql_collation_callbacks; + +Tsql_collation_callbacks *collation_callbacks_ptr = NULL; + +static void init_collation_callbacks(void); +static Oid get_tsql_collation_oid(int persist_coll_id); +static int get_persist_collation_id(Oid coll_oid); +static int get_server_collation_collidx(void); +static int8_t cmp_collation(uint16_t coll1, uint16_t coll2); + +/* extract type and coll related info*/ +extern type_info_t type_infos[]; +extern HTAB *ht_oid2collid; + + +/* + * Storage Layout of SQL_VARIANT Header + * Total length 2-9 bytes : varlena header (1-4B) + sv header (1-5B) + * Bytes are interpreted differently for different types + * WARNING: Modification on storage layout need backward compatibility support + * Place holders MUST filled with 0 to avoid ambiguity in the future + */ + +/* HDR: Basic format + * following custom of PG, typmod is stored + * It could be interpreted differently for difffernt types + * e.g + * for decimal types, precision and scale are encoded + * for datetime2, datetimeoffset it is scale + */ +typedef struct __attribute__((packed)) svhdr_1B +{ + uint8_t metadata; +} svhdr_1B_t; + +typedef struct __attribute__((packed)) svhdr_2B +{ + uint8_t metadata; + int8_t typmod; +} svhdr_2B_t; + +typedef struct __attribute__((packed)) svhdr_3B +{ + uint8_t metadata; + int16_t typmod; +} svhdr_3B_t; + +typedef struct __attribute__((packed)) svhdr_5B +{ + uint8_t metadata; + int16_t typmod; + uint16_t collid; +} svhdr_5B_t; + +/* + * SQL_VARINT does not have its own textual representation + * All supported types are expected to be cased into SQL_VARIANT + * String values are treated as VARCHAR(len) type + */ + +Datum +sqlvariantin(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + Oid typelem = PG_GETARG_OID(1); + int32 atttypmod = PG_GETARG_INT32(2); + bytea *result; + text *data_val; + size_t data_size; + size_t total_size; + Oid type = type_infos[VARCHAR_T].oid; /* hardcoded varchar */ + uint8_t svhdr_size = type_infos[VARCHAR_T].svhdr_size; + Oid input_func; + Oid typIOParam; + svhdr_5B_t *svhdr; + + getTypeInputInfo(type, &input_func, &typIOParam); + /* evalute input fuction */ + data_val = (text*) OidInputFunctionCall(input_func, str, typelem, atttypmod); + + /* Copy Data */ + data_size = VARSIZE_ANY(data_val); + if (SV_CAN_USE_SHORT_VALENA(data_size, svhdr_size)) + { + total_size = VARHDRSZ_SHORT + svhdr_size + data_size; + result = (bytea *) palloc(total_size); + SET_VARSIZE_SHORT(result, total_size); + } + else + { + total_size = VARHDRSZ + svhdr_size + data_size; + result = (bytea *) palloc(total_size); + SET_VARSIZE(result, total_size); + } + memcpy(SV_DATA(result, svhdr_size), data_val, data_size); + + /* Set Metadata */ + svhdr = SV_HDR_5B(result); + SV_SET_METADATA(svhdr, VARCHAR_T, HDR_VER); /* hardcode as VARCHAR */ + svhdr->typmod = VARSIZE_ANY_EXHDR(data_val); + svhdr->collid = get_server_collation_collidx(); + + // Cleanup + pfree(data_val); + + PG_RETURN_BYTEA_P(result); +} + +/* + * SQL_VARIANT does not have its own textual representation + * It always calls internal types's output function + */ + +Datum +sqlvariantout(PG_FUNCTION_ARGS) +{ + char *result = NULL; + bytea *vlena = PG_GETARG_BYTEA_PP(0); + uint8_t type_code = SV_GET_TYPCODE_PTR(vlena); + Oid type = (Oid) type_infos[type_code].oid; + uint8_t svhdr_size = type_infos[type_code].svhdr_size; + Oid output_func; + bool typIsVarlena; + size_t data_len = VARSIZE_ANY_EXHDR(vlena) - svhdr_size; + Datum *output_datum = palloc0(SIZEOF_DATUM); + + if (!get_typbyval(type)) /* pass by reference */ + *output_datum = SV_DATUM(vlena, svhdr_size); + else /* pass by value */ + { + memcpy(output_datum, SV_DATUM_PTR(vlena, svhdr_size), data_len); + } + + getTypeOutputInfo(type, &output_func, &typIsVarlena); + result = OidOutputFunctionCall(output_func, *output_datum); + + PG_FREE_IF_COPY(vlena, 0); + PG_RETURN_CSTRING(result); +} + +/* + * Binary representation is identical, only do memory copy in RECV/SEND functions + */ + +Datum +sqlvariantrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + bytea *result; + int nbytes; + + INSTR_METRIC_INC(INSTR_TSQL_SQLVARIANT_RECV); + + nbytes = buf->len - buf->cursor; + + if (SV_CAN_USE_SHORT_VALENA(nbytes, 0)) + { + result = (bytea *) palloc(nbytes + VARHDRSZ_SHORT); + SET_VARSIZE_SHORT(result, nbytes + VARHDRSZ_SHORT); + } + else + { + result = (bytea *) palloc(nbytes + VARHDRSZ); + SET_VARSIZE(result, nbytes + VARHDRSZ); + } + + pq_copymsgbytes(buf, VARDATA_ANY(result), nbytes); + PG_RETURN_BYTEA_P(result); +} + +Datum +sqlvariantsend(PG_FUNCTION_ARGS) +{ + bytea *vlena = PG_GETARG_BYTEA_P_COPY(0); + + INSTR_METRIC_INC(INSTR_TSQL_SQLVARIANT_SEND); + + PG_RETURN_BYTEA_P(vlena); +} + +/* Helper functions */ +static Datum get_varchar128_sv_datum(const char *value); +static Datum get_int_sv_datum(int32_t value); + +Datum +get_varchar128_sv_datum(const char *value) +{ + size_t len = strlen(value); + bytea *result; + svhdr_5B_t *svhdr; + size_t sv_size; + uint8_t svhdr_size = type_infos[VARCHAR_T].svhdr_size; + + /* return varchar(128) */ + sv_size = VARHDRSZ + svhdr_size + VARHDRSZ + len; + result = palloc(sv_size); + SET_VARSIZE(result, sv_size); + SET_VARSIZE(SV_DATA(result, svhdr_size), VARHDRSZ + len); + memcpy(VARDATA(SV_DATA(result, svhdr_size)), value, len); + + /* Header */ + svhdr = SV_HDR_5B(result); + SV_SET_METADATA(svhdr, VARCHAR_T, HDR_VER); + svhdr->typmod = len; /* Actual Data Length */ + svhdr->collid = get_server_collation_collidx(); + + PG_RETURN_BYTEA_P(result); +} + +Datum +get_int_sv_datum(int32_t value) +{ + bytea *result; + svhdr_1B_t *svhdr; + uint8_t svhdr_size = type_infos[INT_T].svhdr_size; + + result = palloc(VARHDRSZ_SHORT + svhdr_size + sizeof(int32_t)); + SET_VARSIZE_SHORT(result, VARHDRSZ_SHORT + svhdr_size + sizeof(int32_t)); + *(int32_t *)(SV_DATA(result, svhdr_size)) = value; + + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, INT_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +/* Helper functions for CAST and COMPARE */ +static Datum do_cast(Oid source_type, Oid target_type, Datum value, int32_t typmod, Oid coll, + CoercionContext cc, bool *cast_by_relabel); + +static Datum compare_value(char *oprname, Oid type, Datum d1, Datum d2, Oid coll); + +static bytea* gen_sqlvariant_bytea_from_type_datum(size_t typcode, Datum data); + +static Datum gen_type_datum_from_sqlvariant_bytea(bytea *sv, uint8_t target_typcode, int32_t typmod, Oid coll); + +/* only called from the same type family */ +Datum do_compare(char *oprname, bytea *arg1, bytea *arg2, Oid fncollation); + +Datum comp_time(char * oprname, uint16_t t1, uint16_t t2); + +Datum +compare_value(char *oprname, Oid type, Datum d1, Datum d2, Oid coll) +{ + Operator operator; + Oid oprcode; + + operator = compatible_oper(NULL, list_make1(makeString(oprname)), type, type, false, -1); + oprcode = oprfuncid(operator); + ReleaseSysCache(operator); + + return OidFunctionCall2Coll(oprcode, coll, d1, d2); +} + +Datum +do_cast(Oid source_type, Oid target_type, Datum value, int32_t typmod, Oid coll, + CoercionContext ccontext, bool *cast_by_relabel) +{ + Oid funcid; + CoercionPathType path; + Oid typioparam; + bool isVarlena; + path = find_coercion_pathway(target_type, source_type, ccontext, &funcid); + + + switch (path){ + case COERCION_PATH_FUNC: + *cast_by_relabel = false; + return OidFunctionCall3Coll(funcid, coll, value, (Datum) typmod, (Datum) ccontext == COERCION_EXPLICIT); + break; + case COERCION_PATH_COERCEVIAIO: + *cast_by_relabel = false; + if (TypeCategory(source_type) == TYPCATEGORY_STRING) + { + getTypeInputInfo(target_type, &funcid, &typioparam); + return OidInputFunctionCall(funcid, TextDatumGetCString(value), typioparam, typmod); + } + else + { + getTypeOutputInfo(source_type, &funcid, &isVarlena); + return CStringGetTextDatum(OidOutputFunctionCall(funcid, value)); + } + break; + case COERCION_PATH_RELABELTYPE: + *cast_by_relabel = true; + return value; + break; + default: + *cast_by_relabel = false; + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("unable to cast from internal type %s to %s", + format_type_be(source_type), format_type_be(target_type)))); + } + return value; +} + +bytea * +gen_sqlvariant_bytea_from_type_datum(size_t typcode, Datum data) +{ + Oid typoid = type_infos[typcode].oid; + int8_t svhdr_size = type_infos[typcode].svhdr_size; + int16_t typlen = get_typlen(typoid); + size_t data_len; + + bytea *result; + size_t result_len; + + if (IS_STRING_TYPE(typcode) || IS_BINARY_TYPE(typcode) || typcode == NUMERIC_T) /* varlena datatype */ + { + data_len = VARSIZE_ANY(data); + if (SV_CAN_USE_SHORT_VALENA(data_len, svhdr_size)) + { + result_len = VARHDRSZ_SHORT + svhdr_size + data_len; + result = palloc(result_len); + SET_VARSIZE_SHORT(result, result_len); + } + else + { + result_len = VARHDRSZ + svhdr_size + data_len; + result = palloc(result_len); + SET_VARSIZE(result, result_len); + } + /* Copy Data */ + memcpy(SV_DATA(result, svhdr_size), (bytea *) DatumGetPointer(data), data_len); + } + else /* fixed length datatype */ + { + result_len = VARHDRSZ_SHORT + svhdr_size + typlen; + result = palloc(result_len); + SET_VARSIZE_SHORT(result, result_len); + + if (typlen <= SIZEOF_DATUM) /* pass by value */ + memcpy(SV_DATA(result, svhdr_size), &data, typlen); + else + memcpy(SV_DATA(result, svhdr_size), (bytea *) DatumGetPointer(data), typlen); + } + + return result; +} + +Datum +gen_type_datum_from_sqlvariant_bytea(bytea *sv, uint8_t target_typcode, int32_t typmod, Oid coll) +{ + uint8_t typcode = SV_GET_TYPCODE_PTR(sv); + Oid type_oid = (Oid) type_infos[typcode].oid; + uint8_t svhdr_size = type_infos[typcode].svhdr_size; + Oid target_oid = (Oid) type_infos[target_typcode].oid; + Datum *target_datum = palloc0(SIZEOF_DATUM); + size_t data_len = VARSIZE_ANY_EXHDR(sv) - svhdr_size; + bool cast_by_relabel; + + if (!get_typbyval(type_oid)) /* Pass by reference */ + *target_datum = SV_DATUM(sv, svhdr_size); + else /* Pass by value */ + { + memcpy(target_datum, SV_DATUM_PTR(sv, svhdr_size), data_len); + } + + set_config_option("babelfishpg_tsql.sql_dialect", "tsql", + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + + if (typcode == target_typcode) + return *target_datum; + else + return do_cast(type_oid, target_oid, *target_datum, typmod, coll, COERCION_EXPLICIT, &cast_by_relabel); +} + +/* + * Time could not be implicitly cast to any other date & time types + * Within SQL_VARIANT type, we regard time is alwasy smaller than + * other date & time types + */ +Datum +comp_time(char * oprname, uint16_t t1, uint16_t t2) +{ + /* + * Notice: THIS IS NOT A GENERATL COMPARISON FUNCTION + * Assumption : 1 and ONLY 1 of t1,t2 is of TIME_T + */ + if (pg_strncasecmp(oprname, "<>", 2) == 0) + PG_RETURN_BOOL(true); + else if (pg_strncasecmp(oprname, ">", 1) == 0) /* including >= */ + PG_RETURN_BOOL(t1 != TIME_T && t2 == TIME_T); + else if (pg_strncasecmp(oprname, "<", 1) == 0) /* including <= */ + PG_RETURN_BOOL(t1 == TIME_T && t2 != TIME_T); + else /* (pg_strncasecmp(oprname, "=", 2) == 0) */ + PG_RETURN_BOOL(false); + +} + +Datum +do_compare(char *oprname, bytea *arg1, bytea *arg2, Oid fncollation) +{ + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + Oid type_oid1 = (Oid) type_infos[type_code1].oid; + Oid type_oid2 = (Oid) type_infos[type_code2].oid; + uint8_t svhdr_size1 = type_infos[type_code1].svhdr_size; + uint8_t svhdr_size2 = type_infos[type_code2].svhdr_size; + bool d1_pass_by_ref = get_typbyval(type_oid1) == false; + bool d2_pass_by_ref = get_typbyval(type_oid2) == false; + size_t data_len1 = VARSIZE_ANY_EXHDR(arg1) - svhdr_size1; + size_t data_len2 = VARSIZE_ANY_EXHDR(arg2) - svhdr_size2; + Datum d1 = 0; + Datum d2 = 0; + if (d1_pass_by_ref) + d1 = SV_DATUM(arg1, svhdr_size1); + else + memcpy(&d1, SV_DATUM_PTR(arg1, svhdr_size1), data_len1); + if (d2_pass_by_ref) + d2 = SV_DATUM(arg2, svhdr_size2); + else + memcpy(&d2, SV_DATUM_PTR(arg2, svhdr_size2), data_len2); + + set_config_option("babelfishpg_tsql.sql_dialect", "tsql", + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + + /* Check Type Code */ + if (type_code1 == type_code2) /* same type */ + { + if (IS_STRING_TYPE(type_code1)) /* handle string with different collation */ + { + svhdr_5B_t *str_header1 = SV_HDR_5B(arg1); + svhdr_5B_t *str_header2 = SV_HDR_5B(arg2); + if (str_header1->collid != str_header2->collid) { + int8_t coll_cmp_result = cmp_collation(str_header1->collid, str_header2->collid); + if (pg_strncasecmp(oprname, "<>", 2) == 0) + PG_RETURN_BOOL(true); + else if (pg_strncasecmp(oprname, ">", 1) == 0) /* including >= */ + PG_RETURN_BOOL(coll_cmp_result > 0); + else if (pg_strncasecmp(oprname, "<", 1) == 0) /* including <= */ + PG_RETURN_BOOL(coll_cmp_result < 0); + else /* (pg_strncasecmp(oprname, "=", 1) == 0) */ + PG_RETURN_BOOL(false); + } + } + return compare_value(oprname, type_oid1, d1, d2, fncollation); + } + else /* implicit cast within type family */ + { + Datum temp_datum; + Datum result; + Operator direct_cmp; + Oid oprcode; + bool cast_by_relabel; + + // handle sql_variant specific cases + if (type_code1 == TIME_T || type_code2 == TIME_T) + return comp_time(oprname, type_code1, type_code2); + + // find direct comparisions without casting + direct_cmp = compatible_oper(NULL, list_make1(makeString(oprname)), + type_oid1, type_oid2, true, -1); + if (direct_cmp) + { + oprcode = oprfuncid(direct_cmp); + ReleaseSysCache(direct_cmp); + return OidFunctionCall2Coll(oprcode, fncollation, d1, d2); + } + + // do implicit cast + // typmod is not considered during a implicit cast comparison + if (type_code1 < type_code2) /* CAST arg2 to arg1 */ + { + temp_datum = do_cast(type_oid2, type_oid1, d2, -1, fncollation, COERCION_IMPLICIT, &cast_by_relabel); + result = compare_value(oprname, type_oid1, d1, temp_datum, fncollation); + if (d1_pass_by_ref && !cast_by_relabel) /* delete temporary variable */ + pfree((char *) temp_datum); + + return result; + } + else /* CAST arg1 to arg2 */ + { + temp_datum = do_cast(type_oid1, type_oid2, d1, -1, fncollation, COERCION_IMPLICIT, &cast_by_relabel); + result = compare_value(oprname, type_oid2, temp_datum, d2, fncollation); + if (d2_pass_by_ref && !cast_by_relabel) /* delete temporary variable */ + pfree((char *) temp_datum); + + return result; + } + } +} + + +/* + * CAST functions to SQL_VARIANT + */ + +PG_FUNCTION_INFO_V1(datetime2sqlvariant); +PG_FUNCTION_INFO_V1(datetime22sqlvariant); +PG_FUNCTION_INFO_V1(smalldatetime2sqlvariant); +PG_FUNCTION_INFO_V1(datetimeoffset2sqlvariant); +PG_FUNCTION_INFO_V1(date2sqlvariant); +PG_FUNCTION_INFO_V1(time2sqlvariant); +PG_FUNCTION_INFO_V1(float2sqlvariant); +PG_FUNCTION_INFO_V1(real2sqlvariant); +PG_FUNCTION_INFO_V1(numeric2sqlvariant); +PG_FUNCTION_INFO_V1(money2sqlvariant); +PG_FUNCTION_INFO_V1(smallmoney2sqlvariant); +PG_FUNCTION_INFO_V1(bigint2sqlvariant); +PG_FUNCTION_INFO_V1(int2sqlvariant); +PG_FUNCTION_INFO_V1(smallint2sqlvariant); +PG_FUNCTION_INFO_V1(tinyint2sqlvariant); +PG_FUNCTION_INFO_V1(bit2sqlvariant); +PG_FUNCTION_INFO_V1(varchar2sqlvariant); +PG_FUNCTION_INFO_V1(nvarchar2sqlvariant); +PG_FUNCTION_INFO_V1(char2sqlvariant); +PG_FUNCTION_INFO_V1(nchar2sqlvariant); +PG_FUNCTION_INFO_V1(bbfvarbinary2sqlvariant); +PG_FUNCTION_INFO_V1(bbfbinary2sqlvariant); +PG_FUNCTION_INFO_V1(uniqueidentifier2sqlvariant); + +/* Date and time */ +Datum +datetime2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(DATETIME_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, DATETIME_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +datetime22sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(DATETIME2_T, data); + svhdr_2B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_2B(result); + SV_SET_METADATA(svhdr, DATETIME2_T, HDR_VER); + svhdr->typmod = -1; + + PG_RETURN_BYTEA_P(result); +} + +Datum +smalldatetime2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(SMALLDATETIME_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, SMALLDATETIME_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +datetimeoffset2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(DATETIMEOFFSET_T, data); + svhdr_2B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_2B(result); + SV_SET_METADATA(svhdr, DATETIMEOFFSET_T, HDR_VER); + svhdr->typmod = -1; + + PG_RETURN_BYTEA_P(result); +} + +Datum +date2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(DATE_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, DATE_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +time2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(TIME_T, data); + svhdr_2B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_2B(result); + SV_SET_METADATA(svhdr, TIME_T, HDR_VER); + svhdr->typmod = -1; + + PG_RETURN_BYTEA_P(result); +} + +/* Approximate numerics */ +Datum +float2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(FLOAT_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, FLOAT_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +real2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(REAL_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, REAL_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +/* Exact numerics */ +Datum +numeric2sqlvariant(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(NUMERIC_T, NumericGetDatum(num)); + svhdr_3B_t *svhdr; + int16_t precision; + int16_t scale; + int32_t typmod_container; + + /* Type Specific Header */ + svhdr = SV_HDR_3B(result); + SV_SET_METADATA(svhdr, NUMERIC_T, HDR_VER); + + /* tsql_numeric_get_typmod() returns 32bit int. need to convert it to 16bit*/ + typmod_container = tsql_numeric_get_typmod(num); + if (typmod_container != -1) + { + precision = ((typmod_container - VARHDRSZ) >> 16) & 0xFF; + scale = (typmod_container - VARHDRSZ) & 0xFF; + svhdr->typmod = (precision << 8) | scale; + } + else + { + svhdr->typmod = -1; + } + + + PG_RETURN_BYTEA_P(result); +} + +Datum +money2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(MONEY_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, MONEY_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +smallmoney2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(SMALLMONEY_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, SMALLMONEY_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bigint2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(BIGINT_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, BIGINT_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(INT_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, INT_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +smallint2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(SMALLINT_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, SMALLINT_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +tinyint2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(TINYINT_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, TINYINT_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bit2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(BIT_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, BIT_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + +/* Character strings */ +Datum +varchar2sqlvariant(PG_FUNCTION_ARGS) +{ + VarChar *vch = PG_GETARG_VARCHAR_PP(0); + Oid coll = PG_GET_COLLATION(); + bytea *result = gen_sqlvariant_bytea_from_type_datum(VARCHAR_T, PointerGetDatum(vch)); + svhdr_5B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_5B(result); + SV_SET_METADATA(svhdr, VARCHAR_T, HDR_VER); + svhdr->typmod = VARSIZE_ANY_EXHDR(vch); + svhdr->collid = get_persist_collation_id(coll); + + PG_RETURN_BYTEA_P(result); +} + +Datum +nvarchar2sqlvariant(PG_FUNCTION_ARGS) +{ + VarChar *vch = PG_GETARG_VARCHAR_PP(0); + Oid coll = PG_GET_COLLATION(); + bytea *result = gen_sqlvariant_bytea_from_type_datum(NVARCHAR_T, PointerGetDatum(vch)); + svhdr_5B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_5B(result); + SV_SET_METADATA(svhdr, NVARCHAR_T, HDR_VER); + svhdr->typmod = VARSIZE_ANY_EXHDR(vch); + svhdr->collid = get_persist_collation_id(coll); + + PG_RETURN_BYTEA_P(result); +} + +Datum +char2sqlvariant(PG_FUNCTION_ARGS) +{ + BpChar *bpch = PG_GETARG_BPCHAR_PP(0); + Oid coll = PG_GET_COLLATION(); + bytea *result = gen_sqlvariant_bytea_from_type_datum(CHAR_T, PointerGetDatum(bpch)); + svhdr_5B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_5B(result); + SV_SET_METADATA(svhdr, CHAR_T, HDR_VER); + svhdr->typmod = VARSIZE_ANY_EXHDR(bpch); + svhdr->collid = get_persist_collation_id(coll); + + PG_RETURN_BYTEA_P(result); +} + +Datum +nchar2sqlvariant(PG_FUNCTION_ARGS) +{ + BpChar *bpch = PG_GETARG_BPCHAR_PP(0); + Oid coll = PG_GET_COLLATION(); + bytea *result = gen_sqlvariant_bytea_from_type_datum(NCHAR_T, PointerGetDatum(bpch)); + svhdr_5B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_5B(result); + SV_SET_METADATA(svhdr, NCHAR_T, HDR_VER); + svhdr->typmod = VARSIZE_ANY_EXHDR(bpch); + svhdr->collid = get_persist_collation_id(coll); + + PG_RETURN_BYTEA_P(result); +} + +/* Binary strings */ +Datum +bbfvarbinary2sqlvariant(PG_FUNCTION_ARGS) +{ + bytea *bt = PG_GETARG_BYTEA_PP(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(VARBINARY_T, PointerGetDatum(bt)); + svhdr_3B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_3B(result); + SV_SET_METADATA(svhdr, VARBINARY_T, HDR_VER); + svhdr->typmod = VARSIZE_ANY_EXHDR(bt); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bbfbinary2sqlvariant(PG_FUNCTION_ARGS) +{ + bytea *bt = PG_GETARG_BYTEA_PP(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(BINARY_T, PointerGetDatum(bt)); + svhdr_3B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_3B(result); + SV_SET_METADATA(svhdr, BINARY_T, HDR_VER); + svhdr->typmod = VARSIZE_ANY_EXHDR(bt); + + PG_RETURN_BYTEA_P(result); +} + +Datum +uniqueidentifier2sqlvariant(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + bytea *result = gen_sqlvariant_bytea_from_type_datum(UNIQUEIDENTIFIER_T, data); + svhdr_1B_t *svhdr; + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, UNIQUEIDENTIFIER_T, HDR_VER); + + PG_RETURN_BYTEA_P(result); +} + + +/* + * CAST functions from SQL_VARIANT + */ + +PG_FUNCTION_INFO_V1(sqlvariant2timestamp); +PG_FUNCTION_INFO_V1(sqlvariant2datetimeoffset); +PG_FUNCTION_INFO_V1(sqlvariant2date); +PG_FUNCTION_INFO_V1(sqlvariant2time); +PG_FUNCTION_INFO_V1(sqlvariant2float); +PG_FUNCTION_INFO_V1(sqlvariant2real); +PG_FUNCTION_INFO_V1(sqlvariant2numeric); +PG_FUNCTION_INFO_V1(sqlvariant2fixeddecimal); +PG_FUNCTION_INFO_V1(sqlvariant2bigint); +PG_FUNCTION_INFO_V1(sqlvariant2int); +PG_FUNCTION_INFO_V1(sqlvariant2smallint); +PG_FUNCTION_INFO_V1(sqlvariant2bit); +PG_FUNCTION_INFO_V1(sqlvariant2varchar); +PG_FUNCTION_INFO_V1(sqlvariant2char); +PG_FUNCTION_INFO_V1(sqlvariant2bbfvarbinary); +PG_FUNCTION_INFO_V1(sqlvariant2bbfbinary); +PG_FUNCTION_INFO_V1(sqlvariant2uniqueidentifier); + + +/* Postgres will do self casts to apply typmod + * if we does not apply typmod during type cast. + * However, it may be faster may be if we apply typmod + * directly during type cast. +*/ +Datum +sqlvariant2timestamp(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + Timestamp result; + + result = DatumGetTimestamp(gen_type_datum_from_sqlvariant_bytea(sv, DATETIME2_T, -1, coll)); + + PG_RETURN_TIMESTAMP(result); +} + +Datum +sqlvariant2datetimeoffset(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + tsql_datetimeoffset *result; + + result = DatumGetDatetimeoffset(gen_type_datum_from_sqlvariant_bytea(sv, DATETIMEOFFSET_T, -1, coll)); + + PG_RETURN_DATETIMEOFFSET(result); +} + +Datum +sqlvariant2date(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + DateADT result; + + result = DatumGetDateADT(gen_type_datum_from_sqlvariant_bytea(sv, DATE_T, -1, coll)); + + PG_RETURN_DATEADT(result); +} + +Datum +sqlvariant2time(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + TimeADT result; + + result = DatumGetTimeADT(gen_type_datum_from_sqlvariant_bytea(sv, TIME_T, -1, coll)); + + PG_RETURN_TIMEADT(result); +} + +Datum +sqlvariant2float(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + double result; + + result = DatumGetFloat8(gen_type_datum_from_sqlvariant_bytea(sv, FLOAT_T, -1, coll)); + + PG_RETURN_FLOAT8(result); +} + +Datum +sqlvariant2real(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + float result; + + result = DatumGetFloat4(gen_type_datum_from_sqlvariant_bytea(sv, REAL_T, -1, coll)); + + PG_RETURN_FLOAT4(result); +} + +Datum +sqlvariant2numeric(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + Numeric result; + + result = DatumGetNumeric(gen_type_datum_from_sqlvariant_bytea(sv, NUMERIC_T, -1, coll)); + + PG_RETURN_NUMERIC(result); +} + +Datum +sqlvariant2fixeddecimal(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + int64 result; + + result = DatumGetInt64(gen_type_datum_from_sqlvariant_bytea(sv, MONEY_T, -1, coll)); + + PG_RETURN_INT64(result); +} + +Datum +sqlvariant2bigint(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + int64 result; + + result = DatumGetInt64(gen_type_datum_from_sqlvariant_bytea(sv, BIGINT_T, -1, coll)); + + PG_RETURN_INT64(result); +} + +Datum +sqlvariant2int(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + int32 result; + + result = DatumGetInt32(gen_type_datum_from_sqlvariant_bytea(sv, INT_T, -1, coll)); + + PG_RETURN_INT32(result); +} + +Datum +sqlvariant2smallint(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + int16 result; + + result = DatumGetInt16(gen_type_datum_from_sqlvariant_bytea(sv, SMALLINT_T, -1, coll)); + + PG_RETURN_INT16(result); +} + +Datum +sqlvariant2bit(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + bool result; + + result = DatumGetBool(gen_type_datum_from_sqlvariant_bytea(sv, BIT_T, -1, coll)); + + PG_RETURN_BOOL(result); +} + +Datum +sqlvariant2varchar(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + VarChar *result; + + result = DatumGetVarCharP(gen_type_datum_from_sqlvariant_bytea(sv, VARCHAR_T, -1, coll)); + + PG_RETURN_VARCHAR_P(result); +} + +Datum +sqlvariant2char(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + BpChar *result; + + result = DatumGetBpCharP(gen_type_datum_from_sqlvariant_bytea(sv, CHAR_T, -1, coll)); + + PG_RETURN_BPCHAR_P(result); +} + +Datum +sqlvariant2bbfvarbinary(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + bytea *result; + + result = DatumGetByteaP(gen_type_datum_from_sqlvariant_bytea(sv, VARBINARY_T, -1, coll)); + + PG_RETURN_BYTEA_P(result); +} + +Datum +sqlvariant2bbfbinary(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + bytea *result; + + result = DatumGetByteaP(gen_type_datum_from_sqlvariant_bytea(sv, BINARY_T, -1, coll)); + + PG_RETURN_BYTEA_P(result); +} + +Datum +sqlvariant2uniqueidentifier(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + Oid coll = PG_GET_COLLATION(); + pg_uuid_t *result; + + result = DatumGetUUIDP(gen_type_datum_from_sqlvariant_bytea(sv, UNIQUEIDENTIFIER_T, -1, coll)); + + PG_RETURN_UUID_P(result); +} + +/* + * SQL_VARIANT_PROPERTY + */ + +PG_FUNCTION_INFO_V1(sql_variant_property); +typedef enum sv_property +{ + SV_PROPERTY_BASETYPE, + SV_PROPERTY_PRECISION, + SV_PROPERTY_SCALE, + SV_PROPERTY_TOTALBYTES, + SV_PROPERTY_COLLATION, + SV_PROPERTY_MAXLENGTH, + SV_PROPERTY_INVALID +} sv_property_t; + +static sv_property_t get_property_type(const char *arg, int len); +static Datum get_base_type(bytea *sv_value); +static Datum get_precision(bytea *sv_value); +static Datum get_scale(bytea *sv_value); +static Datum get_total_bytes(bytea *sv_value); +static Datum get_max_length(bytea *sv_value); + +sv_property_t +get_property_type(const char *arg, int len) +{ + /* Incase sensitive match, No prefix/suffix spaces handling */ + if (pg_strncasecmp(arg, "basetype", len) == 0) + return SV_PROPERTY_BASETYPE; + else if (pg_strncasecmp(arg, "precision", len) == 0) + return SV_PROPERTY_PRECISION; + else if (pg_strncasecmp(arg, "scale", len) == 0) + return SV_PROPERTY_SCALE; + else if (pg_strncasecmp(arg, "totalbytes", len) == 0) + return SV_PROPERTY_TOTALBYTES; + else if (pg_strncasecmp(arg, "collation", len) == 0) + return SV_PROPERTY_COLLATION; + else if (pg_strncasecmp(arg, "maxlength", len) == 0) + return SV_PROPERTY_MAXLENGTH; + else + return SV_PROPERTY_INVALID; +} + +Datum +get_base_type(bytea *sv_value) +{ + uint8_t type_code = SV_GET_TYPCODE_PTR(sv_value); + const char *type_name = type_infos[type_code].tsql_typname; + + return get_varchar128_sv_datum(type_name); +} + +Datum +get_precision(bytea *sv_value) +{ + uint8_t type_code = SV_GET_TYPCODE_PTR(sv_value); + uint8_t svhdr_size = type_infos[type_code].svhdr_size; + int16_t typmod; + int precision; + svhdr_2B_t *svhdr_2b; + svhdr_3B_t *svhdr_3b; + svhdr_5B_t *svhdr_5b; + + switch(svhdr_size) + { + case 2: + svhdr_2b = SV_HDR_2B(sv_value); + typmod = svhdr_2b->typmod; + break; + case 3: + svhdr_3b = SV_HDR_3B(sv_value); + typmod = svhdr_3b->typmod; + break; + case 5: + svhdr_5b = SV_HDR_5B(sv_value); + typmod = svhdr_5b->typmod; + break; + default: + typmod = 0; + } + + + switch(type_code) + { + case DATETIME2_T: + if (typmod == -1) + precision = 27; + else if (typmod == 0) + precision = 19; + else + precision = typmod + 20; + break; + case DATETIMEOFFSET_T: + if (typmod == -1) + precision = 34; + else if (typmod == 0) + precision = 26; + else + precision = typmod + 27; + break; + case DATETIME_T: + precision = 23; + break; + case SMALLDATETIME_T: + precision = 16; + break; + case DATE_T: + precision = 10; + break; + case TIME_T: + if (typmod == -1) + precision = 16; + else if (typmod == 0) + precision = 8; + else + precision = typmod + 9; + break; + case FLOAT_T: + precision = 53; + break; + case REAL_T: + precision = 24; + break; + case NUMERIC_T: + if (typmod == -1) + precision = 18; + else + precision = (typmod >> 8) & 0xFF; + break; + break; + case MONEY_T: + precision = 19; + break; + case SMALLMONEY_T: + precision = 10; + break; + case BIGINT_T: + precision = 19; + break; + case INT_T: + precision = 10; + break; + case SMALLINT_T: + precision = 5; + break; + case TINYINT_T: + precision = 3; + break; + case BIT_T: + precision = 1; + break; + case NVARCHAR_T: + case NCHAR_T: + case VARCHAR_T: + case CHAR_T: + case VARBINARY_T: + case BINARY_T: + case UNIQUEIDENTIFIER_T: + precision = 0; + break; + default: + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH ), + errmsg("Unknown Internal data type code %d", type_code))); + } + + return get_int_sv_datum(precision); +} + +Datum +get_scale(bytea *sv_value) +{ + uint8_t type_code = SV_GET_TYPCODE_PTR(sv_value); + uint8_t svhdr_size = type_infos[type_code].svhdr_size; + int16_t typmod; + int scale; + svhdr_2B_t *svhdr_2b; + svhdr_3B_t *svhdr_3b; + svhdr_5B_t *svhdr_5b; + + switch(svhdr_size) + { + case 2: + svhdr_2b = SV_HDR_2B(sv_value); + typmod = svhdr_2b->typmod; + break; + case 3: + svhdr_3b = SV_HDR_3B(sv_value); + typmod = svhdr_3b->typmod; + break; + case 5: + svhdr_5b = SV_HDR_5B(sv_value); + typmod = svhdr_5b->typmod; + break; + default: + typmod = 0; + } + + + switch(type_code) + { + case DATETIME2_T: + if (typmod == -1) + scale = 7; + else + scale = typmod; + break; + case DATETIMEOFFSET_T: + if (typmod == -1) + scale = 7; + else + scale = typmod; + break; + case DATETIME_T: + scale = 3; + break; + case SMALLDATETIME_T: + case DATE_T: + scale = 0; + break; + case TIME_T: + if (typmod == -1) + scale = 7; + else + scale = typmod; + break; + case FLOAT_T: + case REAL_T: + scale = 0; + break; + case NUMERIC_T: + if (typmod == -1) + scale = 0; + else + scale = typmod & 0xFF; + break; + case MONEY_T: + scale = 4; + break; + case SMALLMONEY_T: + scale = 4; + break; + case BIGINT_T: + case INT_T: + case SMALLINT_T: + case TINYINT_T: + case BIT_T: + case NVARCHAR_T: + case NCHAR_T: + case VARCHAR_T: + case CHAR_T: + case VARBINARY_T: + case BINARY_T: + case UNIQUEIDENTIFIER_T: + scale = 0; + break; + default: + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH ), + errmsg("Unknown Internal data type code %d", type_code))); + } + + return get_int_sv_datum(scale); +} + +Datum +get_total_bytes(bytea *sv_value) +{ + return get_int_sv_datum(VARSIZE_ANY(sv_value)); +} + +Datum +get_max_length(bytea *sv_value) +{ + uint8_t type_code = SV_GET_TYPCODE_PTR(sv_value); + int max_len; + + switch(type_code) + { + case DATETIME2_T: + max_len = 8; + break; + case DATETIMEOFFSET_T: + max_len = 10; + break; + case DATETIME_T: + max_len = 8; + break; + case SMALLDATETIME_T: + max_len = 8; + break; + case DATE_T: + max_len = 4; + break; + case TIME_T: + max_len = 8; + break; + case FLOAT_T: + max_len = 8; + break; + case REAL_T: + max_len = 4; + break; + case NUMERIC_T: + max_len = 65535; + break; + case MONEY_T: + max_len = 8; + break; + case SMALLMONEY_T: + max_len = 8; + break; + case BIGINT_T: + max_len = 8; + break; + case INT_T: + max_len = 4; + break; + case SMALLINT_T: + max_len = 2; + break; + case TINYINT_T: + max_len = 2; + break; + case BIT_T: + max_len = 1; + break; + case NVARCHAR_T: + case NCHAR_T: + case VARCHAR_T: + case CHAR_T: + case VARBINARY_T: + case BINARY_T: + max_len = 65535; + break; + case UNIQUEIDENTIFIER_T: + max_len = 16; + break; + default: + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH ), + errmsg("Unknown Internal data type code %d", type_code))); + + } + + return get_int_sv_datum(max_len); +} + +Datum +sql_variant_property(PG_FUNCTION_ARGS) +{ + bytea *sv_value = PG_GETARG_BYTEA_PP(0); + int prop_len = VARSIZE_ANY_EXHDR(PG_GETARG_BYTEA_PP(1)); + const char *prop_str = VARDATA_ANY(PG_GETARG_BYTEA_PP(1)); + sv_property_t prop_type; + + /* CHECK Validity of Property */ + prop_type = get_property_type(prop_str, prop_len); + if (prop_type == SV_PROPERTY_INVALID) + PG_RETURN_NULL(); + + /* Dispatch to property functions */ + switch(prop_type) + { + case SV_PROPERTY_BASETYPE: + return get_base_type(sv_value); + case SV_PROPERTY_PRECISION: + return get_precision(sv_value); + case SV_PROPERTY_SCALE: + return get_scale(sv_value); + case SV_PROPERTY_TOTALBYTES: + return get_total_bytes(sv_value); + case SV_PROPERTY_COLLATION: + { + uint8_t type_code = SV_GET_TYPCODE_PTR(sv_value); + switch (type_code) + { + case NVARCHAR_T: + case NCHAR_T: + case VARCHAR_T: + case CHAR_T: + { + svhdr_5B_t *svhdr_5b = SV_HDR_5B(sv_value); + Oid coll_oid = get_tsql_collation_oid(svhdr_5b->collid); + char *collname; + collname = get_collation_name(coll_oid); + return get_varchar128_sv_datum(collname); + } + default: + break; + } + PG_RETURN_NULL(); + } + case SV_PROPERTY_MAXLENGTH: + return get_max_length(sv_value); + default: + break; + } + PG_RETURN_NULL(); /* SHOULD NOT HAPPEN */ +} + +/* + * Comparision functions + */ + +PG_FUNCTION_INFO_V1(sqlvarianteq); +PG_FUNCTION_INFO_V1(sqlvariantne); +PG_FUNCTION_INFO_V1(sqlvariantlt); +PG_FUNCTION_INFO_V1(sqlvariantle); +PG_FUNCTION_INFO_V1(sqlvariantgt); +PG_FUNCTION_INFO_V1(sqlvariantge); + +Datum +sqlvariantlt(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + uint8_t type_family1 = type_infos[type_code1].family_prio; + uint8_t type_family2 = type_infos[type_code2].family_prio; + char *oprname = "<"; + Datum result; + + if (type_family1 == type_family2) + result = do_compare(oprname, arg1, arg2, PG_GET_COLLATION()); + else /* based on type family precedence */ + result = BoolGetDatum(type_family1 > type_family2); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + return result; +} + +Datum +sqlvariantle(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + uint8_t type_family1 = type_infos[type_code1].family_prio; + uint8_t type_family2 = type_infos[type_code2].family_prio; + char *oprname = "<="; + Datum result; + + if (type_family1 == type_family2) + result = do_compare(oprname, arg1, arg2, PG_GET_COLLATION()); + else /* based on type family precedence */ + result = BoolGetDatum(type_family1 > type_family2); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + return result; +} + +Datum +sqlvarianteq(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + uint8_t type_family1 = type_infos[type_code1].family_prio; + uint8_t type_family2 = type_infos[type_code2].family_prio; + char *oprname = "="; + Datum result; + + if (type_family1 == type_family2) + result = do_compare(oprname, arg1, arg2, PG_GET_COLLATION()); + else /* based on type family precedence */ + result = BoolGetDatum(false); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + return result; +} + +Datum +sqlvariantge(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + uint8_t type_family1 = type_infos[type_code1].family_prio; + uint8_t type_family2 = type_infos[type_code2].family_prio; + char *oprname = ">="; + Datum result; + + if (type_family1 == type_family2) + result = do_compare(oprname, arg1, arg2, PG_GET_COLLATION()); + else /* based on type family precedence */ + result = BoolGetDatum(type_family1 < type_family2); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + return result; +} + +Datum +sqlvariantgt(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + uint8_t type_family1 = type_infos[type_code1].family_prio; + uint8_t type_family2 = type_infos[type_code2].family_prio; + char *oprname = ">"; + Datum result; + + if (type_family1 == type_family2) + result = do_compare(oprname, arg1, arg2, PG_GET_COLLATION()); + else /* based on type family precedence */ + result = BoolGetDatum(type_family1 < type_family2); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + return result; +} + +Datum +sqlvariantne(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + uint8_t type_family1 = type_infos[type_code1].family_prio; + uint8_t type_family2 = type_infos[type_code2].family_prio; + char *oprname = "<>"; + Datum result; + + if (type_family1 == type_family2) + result = do_compare(oprname, arg1, arg2, PG_GET_COLLATION()); + else /* based on type family precedence */ + result = BoolGetDatum(true); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + return result; +} + +/* + * Index Supporting Functions + */ + +PG_FUNCTION_INFO_V1(sqlvariant_cmp); +PG_FUNCTION_INFO_V1(sqlvariant_hash); + +Datum +sqlvariant_cmp(PG_FUNCTION_ARGS) +{ + bytea *arg1 = PG_GETARG_BYTEA_PP(0); + bytea *arg2 = PG_GETARG_BYTEA_PP(1); + uint8_t type_code1 = SV_GET_TYPCODE_PTR(arg1); + uint8_t type_code2 = SV_GET_TYPCODE_PTR(arg2); + uint8_t type_family1 = type_infos[type_code1].family_prio; + uint8_t type_family2 = type_infos[type_code2].family_prio; + Datum result; + + if (type_family1 == type_family2) + { + char *opeq = "="; + char *oplt = "<"; + Datum is_eq; + Datum is_lt; + is_lt = do_compare(oplt, arg1, arg2, PG_GET_COLLATION()); + if (DatumGetBool(is_lt)) + result = Int32GetDatum(-1); + else + { + is_eq = do_compare(opeq, arg1, arg2, PG_GET_COLLATION()); + result = DatumGetBool(is_eq) ? Int32GetDatum(0) : Int32GetDatum(1); + } + } + else + result = (type_family1 > type_family2) ? Int32GetDatum(-1) : Int32GetDatum(1); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + return result; +} + +Datum +sqlvariant_hash(PG_FUNCTION_ARGS) +{ + bytea *key = PG_GETARG_BYTEA_PP(0); + int keylen = VARSIZE_ANY_EXHDR(key); + int hdrlen = VARSIZE_ANY(key) - keylen; + Datum result; + + /* Exclude varlena header for computation + * Size of varlena header could be 1 or 4 bytes, + * Newly created values usually have 4 bytes + * However, values read from storage have 1 bytes if total length is short + */ + result = hash_any((unsigned char *) key + hdrlen, keylen); + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(key, 0); + + return result; +} + + +/* + * DATALENGTH function for SQL_VARIANT + */ + +PG_FUNCTION_INFO_V1(datalength_sqlvariant); + +Datum +datalength_sqlvariant(PG_FUNCTION_ARGS) +{ + bytea *sv = PG_GETARG_BYTEA_PP(0); + uint8_t type_code = SV_GET_TYPCODE_PTR(sv); + uint8_t svhdr_size = type_infos[type_code].svhdr_size; + int32 octet_len = VARSIZE_ANY_EXHDR(sv) - svhdr_size; + + /* For varlen types, exclude the original varlena header */ + if (IS_STRING_TYPE(type_code) || IS_BINARY_TYPE(type_code) || type_code == NUMERIC_T) + octet_len -= VARHDRSZ; + + PG_RETURN_INT32(octet_len); +} + +/* + * TDS side code support on sql variant + */ +/* + * Retrieve PGbaseType code, dataLen, variable header length + * for each base datatype on sql variant + */ +extern void +TdsGetPGbaseType(uint8 variantBaseType, int *pgBaseType, int tempLen, + int *dataLen, int *variantHeaderLen); +extern void +TdsGetPGbaseType(uint8 variantBaseType, int *pgBaseType, int tempLen, + int *dataLen, int *variantHeaderLen) +{ + switch (variantBaseType) + { + case VARIANT_TYPE_BIT: + *pgBaseType = BIT_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_TINYINT: + *pgBaseType = TINYINT_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_SMALLINT: + *pgBaseType = SMALLINT_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_INT: + *pgBaseType = INT_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_BIGINT: + *pgBaseType = BIGINT_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_REAL: + *pgBaseType = REAL_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_FLOAT: + *pgBaseType = FLOAT_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_CHAR: + *pgBaseType = CHAR_T; + *dataLen = tempLen - 5; + break; + case VARIANT_TYPE_NCHAR: + *pgBaseType = NCHAR_T; + *dataLen = (tempLen - 9) / 2 + 4; + break; + case VARIANT_TYPE_VARCHAR: + *pgBaseType = VARCHAR_T; + *dataLen = tempLen - 5; + break; + case VARIANT_TYPE_NVARCHAR: + *pgBaseType = NVARCHAR_T; + *dataLen = (tempLen - 9) / 2 + 4; + break; + case VARIANT_TYPE_BINARY: + *pgBaseType = BINARY_T; + *dataLen = tempLen; + break; + case VARIANT_TYPE_VARBINARY: + *pgBaseType = VARBINARY_T; + *dataLen = tempLen; + break; + case VARIANT_TYPE_DATE: + *pgBaseType = DATE_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_TIME: + *pgBaseType = TIME_T; + *dataLen = tempLen - 3; + break; + case VARIANT_TYPE_SMALLDATETIME: + *pgBaseType = SMALLDATETIME_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_DATETIME: + *pgBaseType = DATETIME_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_DATETIME2: + *pgBaseType = DATETIME2_T; + *dataLen = tempLen - 3; + break; + case VARIANT_TYPE_UNIQUEIDENTIFIER: + *pgBaseType = UNIQUEIDENTIFIER_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_NUMERIC: + *pgBaseType = NUMERIC_T; + *dataLen = tempLen - 5; + break; + case VARIANT_TYPE_MONEY: + *pgBaseType = MONEY_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_SMALLMONEY: + *pgBaseType = SMALLMONEY_T; + *dataLen = tempLen - 2; + break; + case VARIANT_TYPE_DATETIMEOFFSET: + *pgBaseType = DATETIMEOFFSET_T; + *dataLen = tempLen - 3; + break; + default: + elog(ERROR, "%d: datatype not supported in TDS receiver", variantBaseType); + break; + } + + *variantHeaderLen = type_infos[*pgBaseType].svhdr_size; +} + + +/* + * set metadata on sqlvariant header for variable length datatypes + */ +void TdsSetMetaData(bytea *result, int pgBaseType, int scale, + int precision, int maxLen); +void TdsSetMetaData(bytea *result, int pgBaseType, int scale, + int precision, int maxLen) +{ + svhdr_5B_t *svhdr; + svhdr = SV_HDR_5B(result); + + SV_SET_METADATA(svhdr, pgBaseType, HDR_VER); + + if (pgBaseType == TIME_T || pgBaseType == DATETIME2_T || + pgBaseType == DATETIMEOFFSET_T) + { + svhdr->typmod = scale; + } + else if (pgBaseType == NUMERIC_T) + { + svhdr->typmod = (precision << 8) | scale; + } + else if (pgBaseType == BINARY_T || pgBaseType == VARBINARY_T || + pgBaseType == CHAR_T || pgBaseType == NCHAR_T || + pgBaseType == VARCHAR_T || pgBaseType == NVARCHAR_T) + { + svhdr->typmod = (int16)maxLen; + } +} + +int +TdsPGbaseType(bytea *vlena); +int +TdsPGbaseType(bytea *vlena) +{ + /* + * First sql variant header byte contains: + * type code ( 5bit ) + MD ver (3bit) + */ + return SV_GET_TYPCODE_PTR(vlena); +} + +void +TdsGetMetaData(bytea *result, int pgBaseType, int *scale, + int *precision, int *maxLen); +void +TdsGetMetaData(bytea *result, int pgBaseType, int *scale, + int *precision, int *maxLen) +{ + svhdr_5B_t *svhdr; + svhdr = SV_HDR_5B(result); + + if (pgBaseType == TIME_T || pgBaseType == DATETIME2_T || + pgBaseType == DATETIMEOFFSET_T) + { + *scale = svhdr->typmod; + } + else if (pgBaseType == NUMERIC_T) + { + *scale = svhdr->typmod & 0x00ff; + *precision = svhdr->typmod & 0xff00; + } + else if (pgBaseType == BINARY_T || pgBaseType == VARBINARY_T || + pgBaseType == CHAR_T || pgBaseType == NCHAR_T || + pgBaseType == VARCHAR_T || pgBaseType == NVARCHAR_T) + { + *maxLen = (int)svhdr->typmod; + } +} + +void +TdsGetVariantBaseType(int pgBaseType, int *variantBaseType, + bool *isBaseNum, bool *isBaseChar, + bool *isBaseDec, bool *isBaseBin, + bool *isBaseDate, int *variantHeaderLen); +void +TdsGetVariantBaseType(int pgBaseType, int *variantBaseType, + bool *isBaseNum, bool *isBaseChar, + bool *isBaseDec, bool *isBaseBin, + bool *isBaseDate, int *variantHeaderLen) +{ + switch (pgBaseType) + { + case BIT_T: + *variantBaseType = VARIANT_TYPE_BIT; + *isBaseNum = true; + break; + case BIGINT_T: + *variantBaseType = VARIANT_TYPE_BIGINT; + *isBaseNum = true; + break; + case INT_T: + *variantBaseType = VARIANT_TYPE_INT; + *isBaseNum = true; + break; + case SMALLINT_T: + *variantBaseType = VARIANT_TYPE_SMALLINT; + *isBaseNum = true; + break; + case TINYINT_T: + *variantBaseType = VARIANT_TYPE_TINYINT; + *isBaseNum = true; + break; + case REAL_T: + *variantBaseType = VARIANT_TYPE_REAL; + *isBaseNum = true; + break; + case FLOAT_T: + *variantBaseType = VARIANT_TYPE_FLOAT; + *isBaseNum = true; + break; + case MONEY_T: + *variantBaseType = VARIANT_TYPE_MONEY; + *isBaseNum = true; + break; + case SMALLMONEY_T: + *variantBaseType = VARIANT_TYPE_SMALLMONEY; + *isBaseNum = true; + break; + case DATE_T: + *variantBaseType = VARIANT_TYPE_DATE; + *isBaseDate = true; + break; + case SMALLDATETIME_T: + *variantBaseType = VARIANT_TYPE_SMALLDATETIME; + *isBaseDate = true; + break; + case DATETIME_T: + *variantBaseType = VARIANT_TYPE_DATETIME; + *isBaseDate = true; + break; + case TIME_T: + *variantBaseType = VARIANT_TYPE_TIME; + *isBaseDate = true; + break; + case DATETIME2_T: + *variantBaseType = VARIANT_TYPE_DATETIME2; + *isBaseDate = true; + break; + case DATETIMEOFFSET_T: + *variantBaseType = VARIANT_TYPE_DATETIMEOFFSET; + *isBaseDate = true; + break; + case CHAR_T: + *variantBaseType = VARIANT_TYPE_CHAR; + *isBaseChar = true; + break; + case VARCHAR_T: + *variantBaseType = VARIANT_TYPE_VARCHAR; + *isBaseChar = true; + break; + case NCHAR_T: + *variantBaseType = VARIANT_TYPE_NCHAR; + *isBaseChar = true; + break; + case NVARCHAR_T: + *variantBaseType = VARIANT_TYPE_NVARCHAR; + *isBaseChar = true; + break; + case BINARY_T: + *variantBaseType = VARIANT_TYPE_BINARY; + *isBaseBin = true; + break; + case VARBINARY_T: + *variantBaseType = VARIANT_TYPE_VARBINARY; + *isBaseBin = true; + break; + case UNIQUEIDENTIFIER_T: + *variantBaseType = VARIANT_TYPE_UNIQUEIDENTIFIER; + *isBaseNum = true; + break; + case NUMERIC_T: + *variantBaseType = VARIANT_TYPE_NUMERIC; + *isBaseDec = true; + break; + default: + elog(ERROR, "%d: datatype not supported in TDS sender", pgBaseType); + break; + } + + *variantHeaderLen = type_infos[pgBaseType].svhdr_size; +} + +bytea *convertIntToSQLVariantByteA(int ret) { + Datum data = Int64GetDatum(ret); + bytea *result = gen_sqlvariant_bytea_from_type_datum(INT_T, data); + svhdr_1B_t *svhdr; + + INSTR_METRIC_INC(INSTR_TSQL_INT_SQLVARIANT); + + /* Type Specific Header */ + svhdr = SV_HDR_1B(result); + SV_SET_METADATA(svhdr, INT_T, HDR_VER); + + return result; +} + +bytea *convertVarcharToSQLVariantByteA(VarChar *vch, Oid coll) { + bytea *result = gen_sqlvariant_bytea_from_type_datum(NVARCHAR_T, PointerGetDatum(vch)); + svhdr_5B_t *svhdr; + + INSTR_METRIC_INC(INSTR_TSQL_NVARCHAR_SQLVARIANT); + + /* Type Specific Header */ + svhdr = SV_HDR_5B(result); + SV_SET_METADATA(svhdr, NVARCHAR_T, HDR_VER); + svhdr->typmod = VARSIZE_ANY_EXHDR(vch); + svhdr->collid = get_persist_collation_id(coll); + + return result; +} + +static void +init_collation_callbacks(void) +{ + Tsql_collation_callbacks **callbacks_ptr; + callbacks_ptr = (Tsql_collation_callbacks **) find_rendezvous_variable("PLtsql_collation_callbacks"); + collation_callbacks_ptr = *callbacks_ptr; +} + +static Oid +get_tsql_collation_oid(int persist_coll_id) +{ + if (collation_callbacks_ptr) + init_collation_callbacks(); + + if (collation_callbacks_ptr && collation_callbacks_ptr->get_tsql_collation_oid_f) + return (*collation_callbacks_ptr->get_tsql_collation_oid_f)(persist_coll_id); + else + return -1; +} + +static int +get_persist_collation_id(Oid coll_oid) +{ + if (collation_callbacks_ptr) + init_collation_callbacks(); + + if (collation_callbacks_ptr && collation_callbacks_ptr->get_persist_collation_id_f) + return (*collation_callbacks_ptr->get_persist_collation_id_f)(coll_oid); + else + return -1; +} + +static int +get_server_collation_collidx(void) +{ + if (collation_callbacks_ptr) + init_collation_callbacks(); + + if (collation_callbacks_ptr && collation_callbacks_ptr->get_server_collation_collidx_f) + return (*collation_callbacks_ptr->get_server_collation_collidx_f)(); + else + return -1; +} + +static int8_t +cmp_collation(uint16_t coll1, uint16_t coll2) +{ + if (collation_callbacks_ptr) + init_collation_callbacks(); + + if (collation_callbacks_ptr && collation_callbacks_ptr->cmp_collation_f) + return (*collation_callbacks_ptr->cmp_collation_f)(coll1, coll2); + else + return 0; +} + diff --git a/contrib/babelfishpg_common/src/typecode.c b/contrib/babelfishpg_common/src/typecode.c new file mode 100644 index 00000000000..d2e3eeaf973 --- /dev/null +++ b/contrib/babelfishpg_common/src/typecode.c @@ -0,0 +1,192 @@ +#include "postgres.h" + +#include "typecode.h" +#include "fmgr.h" +#include "nodes/execnodes.h" +#include "utils/hsearch.h" +#include "utils/syscache.h" +#include "utils/memutils.h" +#include "utils/elog.h" +#include "utils/builtins.h" +#include "catalog/namespace.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_type.h" + + +/* Memory context */ +MemoryContext TransMemoryContext = NULL; + +type_info_t type_infos[TOTAL_TYPECODE_COUNT] = +{ + {0, 1, "sql_variant" , "sql_variant" , 1, 1, 1}, + {0, 1, "datetimeoffset" , "datetimeoffset" , 2, 2, 2}, + {0, 1, "datetime2" , "datetime2" , 2, 3, 2}, + {0, 1, "datetime" , "datetime" , 2, 4, 1}, + {0, 1, "smalldatetime" , "smalldatetime" , 2, 5, 1}, + {0, 0, "date" , "date" , 2, 6, 1}, + {0, 0, "time" , "time" , 2, 7, 2}, + {0, 0, "float8" , "float" , 3, 8, 1}, + {0, 0, "float4" , "real" , 3, 9, 1}, + {0, 0, "numeric" , "numeric" , 4, 10, 3}, + {0, 1, "money" , "money" , 4, 11, 1}, + {0, 1, "smallmoney" , "smallmoney" , 4, 12, 1}, + {0, 0, "int8" , "bigint" , 4, 13, 1}, + {0, 0, "int4" , "int" , 4, 14, 1}, + {0, 0, "int2" , "smallint" , 4, 15, 1}, + {0, 1, "tinyint" , "tinyint" , 4, 16, 1}, + {0, 1, "bit" , "bit" , 4, 17, 1}, + {0, 1, "nvarchar" , "nvarchar" , 5, 18, 5}, + {0, 1, "nchar" , "nchar" , 5, 19, 5}, + {0, 1, "varchar" , "varchar" , 5, 20, 5}, + {0, 0, "bpchar" , "char" , 5, 21, 5}, + {0, 1, "varbinary" , "varbinary" , 6, 22, 3}, + {0, 1, "binary" , "binary" , 6, 23, 3}, + {0, 1, "uniqueidentifier", "uniqueidentifier", 7, 24, 1} +}; + +/* Hash tables to help backward searching (from OID to Persist ID) */ +HTAB *ht_oid2typecode = NULL; + +/* + * Translation Table Initializers + * Load information from C arrays into hash tables + * Initializers are called right after shared library loading + * During "CREATE EXTENSION", data types are created after initialization call + * In this case, initializers do nothing + * After data types are created, initializers will be triggered again + * with a built-in procedure + * + */ + +PG_FUNCTION_INFO_V1(init_tcode_trans_tab); + +Datum +init_tcode_trans_tab(PG_FUNCTION_ARGS) +{ + HASHCTL hashCtl; + Oid sys_nspoid; + Oid nspoid; + ht_oid2typecode_entry_t *entry; + + if (TransMemoryContext == NULL) /* initialize memory context */ + { + TransMemoryContext = + AllocSetContextCreateInternal(NULL, + "SQL Variant Memory Context", + ALLOCSET_DEFAULT_SIZES); + } + + if (ht_oid2typecode == NULL) /* create hash table */ + { + MemSet(&hashCtl, 0, sizeof(hashCtl)); + hashCtl.keysize = sizeof(Oid); + hashCtl.entrysize = sizeof(ht_oid2typecode_entry_t); + hashCtl.hcxt = TransMemoryContext; + ht_oid2typecode = hash_create("OID to Persist Type Code Mapping", + TOTAL_TYPECODE_COUNT, + &hashCtl, + HASH_ELEM | HASH_CONTEXT | HASH_BLOBS); + } + + sys_nspoid = get_namespace_oid("sys", false); + /* retrieve oid and setup hashtable*/ + for (int i=0; ipersist_id = i; + } + } + + PG_RETURN_INT32(0); +} + +PG_FUNCTION_INFO_V1(typecode_list); + +Datum +typecode_list(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* need to build tuplestore in query context */ + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + /* + * build tupdesc for result tuples. + */ + tupdesc = CreateTemplateTupleDesc(7); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "oid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pg_namespace", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "pg_typname", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "tsql_typname", + TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "type_family_priority", + INT2OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "priority", + INT2OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "sql_variant_hdr_size", + INT2OID, -1, 0); + + tupstore = + tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, + false, 1024); + /* generate junk in short-term context */ + MemoryContextSwitchTo(oldcontext); + + /* scan all the variables in top estate */ + for (int i = 0; i < TOTAL_TYPECODE_COUNT; i++) + { + type_info_t *info = &type_infos[i]; + Datum values[7]; + bool nulls[7]; + + MemSet(nulls, 0, sizeof(nulls)); + + values[0] = info->oid; + values[1] = info->nsp_is_sys ? CStringGetTextDatum("sys") : CStringGetTextDatum("pg_catalog"); + values[2] = CStringGetTextDatum(info->pg_typname); + values[3] = CStringGetTextDatum(info->tsql_typname); + values[4] = info->family_prio; + values[5] = info->prio; + values[6] = info->svhdr_size; + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + PG_RETURN_NULL(); +} + +Oid get_type_oid(int type_code) +{ + return type_infos[type_code].oid; +} diff --git a/contrib/babelfishpg_common/src/typecode.h b/contrib/babelfishpg_common/src/typecode.h new file mode 100644 index 00000000000..2ad2ade0245 --- /dev/null +++ b/contrib/babelfishpg_common/src/typecode.h @@ -0,0 +1,64 @@ +#ifndef TSQL_TYPECODE_H +#define TSQL_TYPECODE_H + +/* Persistent Type Code for SQL Variant Type */ +/* WARNING: EXISTING VALUES MUST NOT BE CHANGED */ + +#define SQLVARIANT_T 0 +#define DATETIMEOFFSET_T 1 +#define DATETIME2_T 2 +#define DATETIME_T 3 +#define SMALLDATETIME_T 4 +#define DATE_T 5 +#define TIME_T 6 +#define FLOAT_T 7 +#define REAL_T 8 +#define NUMERIC_T 9 +#define MONEY_T 10 +#define SMALLMONEY_T 11 +#define BIGINT_T 12 +#define INT_T 13 +#define SMALLINT_T 14 +#define TINYINT_T 15 +#define BIT_T 16 +#define NVARCHAR_T 17 +#define NCHAR_T 18 +#define VARCHAR_T 19 +#define CHAR_T 20 +#define VARBINARY_T 21 +#define BINARY_T 22 +#define UNIQUEIDENTIFIER_T 23 + +#define IS_STRING_TYPE(t) \ + ( ((t) == NVARCHAR_T) || ((t) == NCHAR_T) \ + || ((t) == VARCHAR_T) || ((t) == CHAR_T)) + +#define IS_MONEY_TYPE(t) (((t) == MONEY_T) || ((t) == SMALLMONEY_T)) +#define IS_BINARY_TYPE(t) (((t) == VARBINARY_T) || ((t) == BINARY_T)) + +/* MACRO from fixed decimal */ +#ifndef FIXEDDECIMAL_MULTIPLIER +#define FIXEDDECIMAL_MULTIPLIER 10000LL +#endif + +#define TOTAL_TYPECODE_COUNT 24 + +typedef struct type_info +{ + Oid oid; /* oid is only retrievable during runtime, so we have to init to 0 */ + bool nsp_is_sys; + const char *pg_typname; + const char *tsql_typname; + uint8_t family_prio; + uint8_t prio; + uint8_t svhdr_size; +} type_info_t; + +typedef struct ht_oid2typecode_entry { + Oid key; + uint8_t persist_id; +} ht_oid2typecode_entry_t; + +extern Oid get_type_oid(int type_code); + +#endif diff --git a/contrib/babelfishpg_common/src/uniqueidentifier.c b/contrib/babelfishpg_common/src/uniqueidentifier.c new file mode 100644 index 00000000000..566a2e23d6d --- /dev/null +++ b/contrib/babelfishpg_common/src/uniqueidentifier.c @@ -0,0 +1,238 @@ +/*------------------------------------------------------------------------- + * + * uniqueidentifier.c + * Functions for the type "uniqueidentifier". + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "utils/builtins.h" +#include "utils/uuid.h" +#include "lib/stringinfo.h" + +static void string_to_uuid(const char *source, pg_uuid_t *uuid); +static void reverse_memcpy(unsigned char *dst, unsigned char *src, size_t n); + +PG_FUNCTION_INFO_V1(uniqueidentifier_in); + +Datum +uniqueidentifier_in(PG_FUNCTION_ARGS) +{ + char *uuid_str = PG_GETARG_CSTRING(0); + pg_uuid_t *uuid; + + uuid = (pg_uuid_t *) palloc(sizeof(*uuid)); + string_to_uuid(uuid_str, uuid); + PG_RETURN_UUID_P(uuid); +} + +PG_FUNCTION_INFO_V1(uniqueidentifier_out); + +Datum +uniqueidentifier_out(PG_FUNCTION_ARGS) +{ + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + static const char hex_chars[] = "0123456789ABCDEF"; + StringInfoData buf; + int i; + + initStringInfo(&buf); + for (i = 0; i < UUID_LEN; i++) + { + int hi; + int lo; + + /* + * We print uuid values as a string of 8, 4, 4, 4, and then 12 + * hexadecimal characters, with each group is separated by a hyphen + * ("-"). Therefore, add the hyphens at the appropriate places here. + */ + if (i == 4 || i == 6 || i == 8 || i == 10) + appendStringInfoChar(&buf, '-'); + + hi = uuid->data[i] >> 4; + lo = uuid->data[i] & 0x0F; + + appendStringInfoChar(&buf, hex_chars[hi]); + appendStringInfoChar(&buf, hex_chars[lo]); + } + + PG_RETURN_CSTRING(buf.data); +} + +/* + * We allow UUIDs as a series of 32 hexadecimal digits with an optional dash + * after each group of 4 hexadecimal digits, and optionally surrounded by {}. + * (The canonical format 8x-4x-4x-4x-12x, where "nx" means n hexadecimal + * digits, is the only one used for output.) + */ +static void +string_to_uuid(const char *source, pg_uuid_t *uuid) +{ + const char *src = source; + bool braces = false; + int i; + + if (src[0] == '{') + { + src++; + braces = true; + } + + for (i = 0; i < UUID_LEN; i++) + { + char str_buf[3]; + + if (src[0] == '\0' || src[1] == '\0') + goto syntax_error; + memcpy(str_buf, src, 2); + if (!isxdigit((unsigned char) str_buf[0]) || + !isxdigit((unsigned char) str_buf[1])) + goto syntax_error; + + str_buf[2] = '\0'; + uuid->data[i] = (unsigned char) strtoul(str_buf, NULL, 16); + src += 2; + if (src[0] == '-' && (i % 2) == 1 && i < UUID_LEN - 1) + src++; + } + + if (braces) + { + if (*src != '}') + goto syntax_error; + src++; + } + + return; + +syntax_error: + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "uuid", source))); +} + +PG_FUNCTION_INFO_V1(varchar2uniqueidentifier); + +Datum +varchar2uniqueidentifier(PG_FUNCTION_ARGS) +{ + pg_uuid_t *uuid; + char *uuid_str = TextDatumGetCString(PG_GETARG_DATUM(0)); + uuid = (pg_uuid_t *) palloc(sizeof(*uuid)); + string_to_uuid(uuid_str, uuid); + PG_RETURN_UUID_P(uuid); + +} + +PG_FUNCTION_INFO_V1(varbinary2uniqueidentifier); + +Datum +varbinary2uniqueidentifier(PG_FUNCTION_ARGS) +{ + pg_uuid_t *uuid; + unsigned char buffer[UUID_LEN]; + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int len = VARSIZE_ANY_EXHDR(source); + + memset(buffer, 0, UUID_LEN); + memcpy(buffer, data, (len > UUID_LEN) ? UUID_LEN : len); + + uuid = (pg_uuid_t *) palloc0(sizeof(*uuid)); + /* T-SQL uses UUID variant 2 which is mixed-endian encoding */ + reverse_memcpy(uuid->data, buffer, 4); + reverse_memcpy(uuid->data+4, buffer+4, 2); + reverse_memcpy(uuid->data+6, buffer+6, 2); + memcpy(uuid->data+8, buffer+8, 8); + PG_RETURN_UUID_P(uuid); +} + + +PG_FUNCTION_INFO_V1(uniqueidentifier2varbinary); + +Datum +uniqueidentifier2varbinary(PG_FUNCTION_ARGS) +{ + char *rp; + bytea *result; + int32 maxlen; + unsigned char buffer[UUID_LEN]; + size_t len = UUID_LEN; + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + int32 typmod = PG_GETARG_INT32(1); + + /* T-SQL uses UUID variant 2 which is mixed-endian encoding */ + reverse_memcpy(buffer, uuid->data, 4); + reverse_memcpy(buffer+4, uuid->data+4, 2); + reverse_memcpy(buffer+6, uuid->data+6, 2); + memcpy(buffer+8, uuid->data+8, 8); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + rp = VARDATA(result); + memcpy(rp, buffer, len); + + PG_RETURN_BYTEA_P(result); +} + +PG_FUNCTION_INFO_V1(uniqueidentifier2binary); + +Datum +uniqueidentifier2binary(PG_FUNCTION_ARGS) +{ + char *rp; + bytea *result; + int32 maxlen; + unsigned char buffer[UUID_LEN]; + size_t len = UUID_LEN; + pg_uuid_t *uuid = PG_GETARG_UUID_P(0); + int32 typmod = PG_GETARG_INT32(1); + + /* T-SQL uses UUID variant 2 which is mixed-endian encoding */ + reverse_memcpy(buffer, uuid->data, 4); + reverse_memcpy(buffer+4, uuid->data+4, 2); + reverse_memcpy(buffer+6, uuid->data+6, 2); + memcpy(buffer+8, uuid->data+8, 8); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + rp = VARDATA(result); + memcpy(rp, buffer, len); + + /* NULL pad the rest of the space */ + memset(rp + len, '\0', maxlen - len); + + PG_RETURN_BYTEA_P(result); +} + +static void +reverse_memcpy(unsigned char *dst, unsigned char *src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + dst[n-1-i] = src[i]; +} diff --git a/contrib/babelfishpg_common/src/varbinary.c b/contrib/babelfishpg_common/src/varbinary.c new file mode 100644 index 00000000000..1a1cf41e49d --- /dev/null +++ b/contrib/babelfishpg_common/src/varbinary.c @@ -0,0 +1,1192 @@ +/*------------------------------------------------------------------------- + * + * varbinary.c + * Functions for the variable-length binary type. + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include +#include + +#include "access/hash.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_type.h" +#include "common/int.h" +#include "lib/hyperloglog.h" +#include "libpq/pqformat.h" +#include "miscadmin.h" +#include "parser/parser.h" +#include "parser/scansup.h" +#include "port/pg_bswap.h" +#include "regex/regex.h" +#include "utils/builtins.h" +#include "utils/bytea.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/pg_locale.h" +#include "utils/sortsupport.h" +#include "utils/varlena.h" + +#include "instr.h" + +PG_FUNCTION_INFO_V1(varbinaryin); +PG_FUNCTION_INFO_V1(varbinaryout); +PG_FUNCTION_INFO_V1(varbinaryrecv); +PG_FUNCTION_INFO_V1(varbinarysend); +PG_FUNCTION_INFO_V1(varbinary); +PG_FUNCTION_INFO_V1(binary); +PG_FUNCTION_INFO_V1(varbinarytypmodin); +PG_FUNCTION_INFO_V1(varbinarytypmodout); +PG_FUNCTION_INFO_V1(varcharvarbinary); +PG_FUNCTION_INFO_V1(bpcharvarbinary); +PG_FUNCTION_INFO_V1(varbinaryvarchar); +PG_FUNCTION_INFO_V1(varcharbinary); +PG_FUNCTION_INFO_V1(bpcharbinary); +PG_FUNCTION_INFO_V1(int2varbinary); +PG_FUNCTION_INFO_V1(int4varbinary); +PG_FUNCTION_INFO_V1(int8varbinary); +PG_FUNCTION_INFO_V1(int2binary); +PG_FUNCTION_INFO_V1(int4binary); +PG_FUNCTION_INFO_V1(int8binary); +PG_FUNCTION_INFO_V1(varbinaryint2); +PG_FUNCTION_INFO_V1(varbinaryint4); +PG_FUNCTION_INFO_V1(varbinaryint8); +PG_FUNCTION_INFO_V1(binaryint2); +PG_FUNCTION_INFO_V1(binaryint4); +PG_FUNCTION_INFO_V1(binaryint8); +PG_FUNCTION_INFO_V1(float4varbinary); +PG_FUNCTION_INFO_V1(float8varbinary); +PG_FUNCTION_INFO_V1(varbinaryfloat4); +PG_FUNCTION_INFO_V1(varbinaryfloat8); +PG_FUNCTION_INFO_V1(float4binary); +PG_FUNCTION_INFO_V1(float8binary); +PG_FUNCTION_INFO_V1(binaryfloat4); +PG_FUNCTION_INFO_V1(binaryfloat8); + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +#define VAL(CH) ((CH) - '0') +#define DIG(VAL) ((VAL) + '0') + +#define MAX_BINARY_SIZE 8000 + +/* + * varbinaryin - input function of varbinary + */ +Datum +varbinaryin(PG_FUNCTION_ARGS) +{ + char *inputText = PG_GETARG_CSTRING(0); + char *rp; + char *tp; + int len; + bytea *result; + int32 typmod = PG_GETARG_INT32(2); + + len = strlen(inputText); + + if (typmod == TSQLHexConstTypmod) + { + /* + * calculate length of the binary code + * e.g. 0xFF should be 1 byte (plus VARHDRSZ) + * and 0xF should also be 1 byte (plus VARHDRSZ). + */ + int bc = (len - 1) / 2 + VARHDRSZ; /* maximum possible length */ + result = palloc(bc); + bc = hex_decode_allow_odd_digits(inputText + 2, len - 2, VARDATA(result)); + SET_VARSIZE(result, bc + VARHDRSZ); /* actual length */ + + PG_RETURN_BYTEA_P(result); + } + + tp = inputText; + + result = (bytea *) palloc(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, tp, len); + + PG_RETURN_BYTEA_P(result); +} + +/* + * varbinaryout - converts to printable representation of byte array + * + * In the traditional escaped format, non-printable characters are + * printed as '\nnn' (octal) and '\' as '\\'. + * This routine is copied from byteaout + */ +Datum +varbinaryout(PG_FUNCTION_ARGS) +{ + bytea *vlena = PG_GETARG_BYTEA_PP(0); + char *result; + char *rp; + + if (bytea_output == BYTEA_OUTPUT_HEX) + { + /* Print hex format */ + rp = result = palloc(VARSIZE_ANY_EXHDR(vlena) * 2 + 2 + 1); + *rp++ = '0'; + *rp++ = 'x'; + rp += hex_encode(VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena), rp); + } + else if (bytea_output == BYTEA_OUTPUT_ESCAPE) + { + /* Print traditional escaped format */ + char *vp; + int len; + int i; + + len = 1; /* empty string has 1 char */ + vp = VARDATA_ANY(vlena); + for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++) + { + if (*vp == '\\') + len += 2; + else if ((unsigned char) *vp < 0x20 || (unsigned char) *vp > 0x7e) + len += 4; + else + len++; + } + rp = result = (char *) palloc(len); + vp = VARDATA_ANY(vlena); + for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++) + { + if (*vp == '\\') + { + *rp++ = '\\'; + *rp++ = '\\'; + } + else if ((unsigned char) *vp < 0x20 || (unsigned char) *vp > 0x7e) + { + int val; /* holds unprintable chars */ + + val = *vp; + rp[0] = '\\'; + rp[3] = DIG(val & 07); + val >>= 3; + rp[2] = DIG(val & 07); + val >>= 3; + rp[1] = DIG(val & 03); + rp += 4; + } + else + *rp++ = *vp; + } + } + else + { + elog(ERROR, "unrecognized bytea_output setting: %d", + bytea_output); + rp = result = NULL; /* keep compiler quiet */ + } + *rp = '\0'; + PG_RETURN_CSTRING(result); +} + +/* + * varbinaryrecv - converts external binary format to bytea + */ +Datum +varbinaryrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + bytea *result; + int nbytes; + + INSTR_METRIC_INC(INSTR_TSQL_VARBINARY_RECV); + + nbytes = buf->len - buf->cursor; + result = (bytea *) palloc(nbytes + VARHDRSZ); + SET_VARSIZE(result, nbytes + VARHDRSZ); + pq_copymsgbytes(buf, VARDATA(result), nbytes); + PG_RETURN_BYTEA_P(result); +} + +/* + * varbinarysend - converts bytea to binary format + * + * This is a special case: just copy the input... + */ +Datum +varbinarysend(PG_FUNCTION_ARGS) +{ + bytea *vlena = PG_GETARG_BYTEA_P_COPY(0); + + INSTR_METRIC_INC(INSTR_TSQL_VARBINARY_SEND); + + PG_RETURN_BYTEA_P(vlena); +} + +/* + * Converts a VARBINARY type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error. + * (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) + */ +Datum +varbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 len, + maxlen; + + len = VARSIZE_ANY_EXHDR(source); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (!isExplicit && + !(suppress_string_truncation_error_hook && (*suppress_string_truncation_error_hook)())) + if (len > maxlen) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("String or binary data would be truncated.\n" + "The statement has been terminated."))); + + /* No work if typmod is invalid or supplied data fits it already */ + if (maxlen < 0 || len <= maxlen) + PG_RETURN_BYTEA_P(source); + + /* + * Truncate the input data using cstring_to_text_with_len, notice text + * and bytea actually have the same struct. + */ + PG_RETURN_BYTEA_P((bytea *) cstring_to_text_with_len(data, maxlen)); +} + +/* + * Converts a BINARY type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error. + * (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) + */ +Datum +binary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 len, + maxlen; + + len = VARSIZE_ANY_EXHDR(source); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (maxlen > MAX_BINARY_SIZE) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("The size (%d) given to the type 'binary' exceeds the maximum allowed (%d)", + maxlen, MAX_BINARY_SIZE))); + + if (!isExplicit && + !(suppress_string_truncation_error_hook && (*suppress_string_truncation_error_hook)())) + if(len > maxlen) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("String or binary data would be truncated.\n" + "The statement has been terminated."))); + + /* No work if maxlen is invalid or supplied data fits it exactly */ + if (maxlen < 0 || len == maxlen) + PG_RETURN_BYTEA_P(source); + + if (len < maxlen) + { + bytea *result; + int total_size = maxlen + VARHDRSZ; + char *tp; + char *rp; + + result = (bytea *) palloc(total_size); + SET_VARSIZE(result, total_size); + tp = VARDATA(source); + rp = VARDATA(result); + + memcpy(rp, tp, len); + /* NULL pad the rest of the space */ + memset(rp + len, '\0', maxlen - len); + + PG_RETURN_BYTEA_P(result); + } + + /* + * Truncate the input data to maxlen using cstring_to_text_with_len, notice text + * and bytea actually have the same struct. + */ + PG_RETURN_BYTEA_P((bytea *) cstring_to_text_with_len(data, maxlen)); +} + +/* common code for varbinarytypmodin, bpchartypmodin and varchartypmodin */ +static int32 +anychar_typmodin(ArrayType *ta, const char *typename) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetIntegerTypmods(ta, &n); + + /* Allow typmod of VARBINARY(MAX) to go through as is */ + if (*tl == TSQLMaxTypmod) + { + return *tl; + } + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for CHAR + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s must be at least 1", typename))); + if (*tl > MaxAttrSize) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s cannot exceed %d", + typename, MaxAttrSize))); + + /* + * For largely historical reasons, the typmod is VARHDRSZ plus the number + * of characters; there is enough client-side code that knows about that + * that we'd better not change it. + */ + typmod = VARHDRSZ + *tl; + + return typmod; +} + +/* + * code for varbinarytypmodout + * copied from bpchartypmodout and varchartypmodout + */ +static char * +anychar_typmodout(int32 typmod) +{ + char *res = (char *) palloc(64); + + if (typmod > VARHDRSZ) + snprintf(res, 64, "(%d)", (int) (typmod - VARHDRSZ)); + else + *res = '\0'; + + return res; +} + +Datum +varbinarytypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anychar_typmodin(ta, "varbinary")); +} + +Datum +varbinarytypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anychar_typmodout(typmod)); +} + +static void +reverse_memcpy(char* dst, char* src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + dst[n-1-i] = src[i]; +} + +/* + * Cast functions + */ +Datum +varcharvarbinary(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type varchar to " + "varbinary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bpcharvarbinary(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type bpchar to " + "varbinary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(len + VARHDRSZ); + SET_VARSIZE(result, len + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + + + +Datum +varbinaryvarchar(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen = typmod - VARHDRSZ; + VarChar *result; + + /* Cast the entire input binary data if maxlen is invalid or supplied data fits it */ + if (maxlen < 0 || len <= maxlen) + result = (VarChar *) cstring_to_text_with_len(data, len); + /* Else truncate it */ + else + result = (VarChar *) cstring_to_text_with_len(data, maxlen); + PG_RETURN_VARCHAR_P(result); +} + +Datum +varcharbinary(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type varchar to " + "binary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + /* NULL pad the rest of the space */ + memset(rp + len, '\0', maxlen - len); + PG_RETURN_BYTEA_P(result); +} + +Datum +bpcharbinary(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 maxlen; + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type char to " + "binary is not allowed. Use the CONVERT function " + "to run this query."))); + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + if (len > maxlen) + len = maxlen; + + result = (bytea *) palloc(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + /* NULL pad the rest of the space */ + memset(rp + len, '\0', maxlen - len); + PG_RETURN_BYTEA_P(result); +} + +Datum +int2varbinary(PG_FUNCTION_ARGS) +{ + int16 input = PG_GETARG_INT16(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int16); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int4varbinary(PG_FUNCTION_ARGS) +{ + int32 input = PG_GETARG_INT32(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int32); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int8varbinary(PG_FUNCTION_ARGS) +{ + int64 input = PG_GETARG_INT64(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int64); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +varbinaryint2(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int16 *result = palloc0(sizeof(int16)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int16) ? sizeof(int16) : len; + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_INT16(*result); +} + +Datum +varbinaryint4(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int32 *result = palloc0(sizeof(int32)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int32) ? sizeof(int32) : len; + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_INT32(*result); +} + +Datum +varbinaryint8(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int64 *result = palloc0(sizeof(int64)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int64) ? sizeof(int64) : len; + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_INT64(*result); +} + +Datum +float4varbinary(PG_FUNCTION_ARGS) +{ + float4 input = PG_GETARG_FLOAT4(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float4); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +float8varbinary(PG_FUNCTION_ARGS) +{ + float8 input = PG_GETARG_FLOAT8(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float8); + int actual_len; + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + actual_len = maxlen < len ? maxlen : len; + + result = (bytea *) palloc(actual_len + VARHDRSZ); + SET_VARSIZE(result, actual_len + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , actual_len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +varbinaryfloat4(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + float4 *result = palloc0(sizeof(float4)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(float4) ? sizeof(float4) : len; + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_FLOAT4(*result); +} + +Datum +varbinaryfloat8(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + float8 *result = palloc0(sizeof(float8)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(float8) ? sizeof(float8) : len; + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_FLOAT8(*result); +} + +Datum +int2binary(PG_FUNCTION_ARGS) +{ + int16 input = PG_GETARG_INT16(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int16); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp+maxlen-len, (char *) &input , len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int4binary(PG_FUNCTION_ARGS) +{ + int32 input = PG_GETARG_INT32(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int32); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp+maxlen-len, (char *) &input , len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int8binary(PG_FUNCTION_ARGS) +{ + int64 input = PG_GETARG_INT64(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(int64); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp+maxlen-len, (char *) &input , len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +binaryint2(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int16 *result = palloc0(sizeof(int16)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int16) ? sizeof(int16) : len; + if (len > sizeof(int16)) + /* Skip the potentially 0 padded part if the input binary is over length */ + reverse_memcpy((char *)result, data+len-sizeof(int16), result_len); + else + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_INT16(*result); +} + +Datum +binaryint4(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int32 *result = palloc0(sizeof(int32)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int32) ? sizeof(int32) : len; + if (len > sizeof(int32)) + /* Skip the potentially 0 padded part if the input binary is over length */ + reverse_memcpy((char *)result, data+len-sizeof(int32), result_len); + else + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_INT32(*result); +} + +Datum +binaryint8(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + int64 *result = palloc0(sizeof(int64)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(int64) ? sizeof(int64) : len; + if (len > sizeof(int64)) + /* Skip the potentially 0 padded part if the input binary is over length */ + reverse_memcpy((char *)result, data+len-sizeof(int64), result_len); + else + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_INT64(*result); +} + +Datum +float4binary(PG_FUNCTION_ARGS) +{ + float4 input = PG_GETARG_FLOAT4(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float4); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp+maxlen-len, (char *) &input , len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +float8binary(PG_FUNCTION_ARGS) +{ + float8 input = PG_GETARG_FLOAT8(0); + int32 typmod = PG_GETARG_INT32(1); + int32 maxlen; + int len = sizeof(float8); + bytea *result; + char *rp; + + /* If typmod is -1 (or invalid), use the actual length */ + if (typmod < (int32) VARHDRSZ) + maxlen = len; + else + maxlen = typmod - VARHDRSZ; + + + result = (bytea *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + if (maxlen <= len) + /* Need reverse copy because endianness is different in MSSQL */ + reverse_memcpy(rp, (char *) &input , maxlen); + else + /* Pad 0 to the left if maxlen is longer than input length */ + reverse_memcpy(rp+maxlen-len, (char *) &input , len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +binaryfloat4(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + float4 *result = palloc0(sizeof(float4)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(float4) ? sizeof(float4) : len; + if (len > sizeof(float4)) + /* Skip the potentially 0 padded part if the input binary is over length */ + reverse_memcpy((char *)result, data+len-sizeof(float4), result_len); + else + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_FLOAT4(*result); +} + +Datum +binaryfloat8(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + char *data = VARDATA_ANY(source); + int32 len; + int32 result_len; + float8 *result = palloc0(sizeof(float8)); + + len = VARSIZE_ANY_EXHDR(source); + result_len = len > sizeof(float8) ? sizeof(float8) : len; + if (len > sizeof(float8)) + /* Skip the potentially 0 padded part if the input binary is over length */ + reverse_memcpy((char *)result, data+len-sizeof(float8), result_len); + else + reverse_memcpy((char *)result, data, result_len); + + PG_RETURN_FLOAT8(*result); +} + +int8 +varbinarycompare(bytea *source1, bytea *source2); + +int8 +inline varbinarycompare(bytea *source1, bytea *source2) +{ + char *data1 = VARDATA_ANY(source1); + int32 len1 = VARSIZE_ANY_EXHDR(source1); + char *data2 = VARDATA_ANY(source2); + int32 len2 = VARSIZE_ANY_EXHDR(source2); + + unsigned char byte1; + unsigned char byte2; + int32 maxlen = len2 > len1 ? len2 : len1; + + INSTR_METRIC_INC(INSTR_TSQL_VARBINARY_COMPARE); + + /* loop all the bytes */ + for (int i=0; i byte2) + return 1; + else if (byte1 < byte2) + return -1; + } + return 0; +} + +PG_FUNCTION_INFO_V1(varbinary_eq); +PG_FUNCTION_INFO_V1(varbinary_neq); +PG_FUNCTION_INFO_V1(varbinary_gt); +PG_FUNCTION_INFO_V1(varbinary_geq); +PG_FUNCTION_INFO_V1(varbinary_lt); +PG_FUNCTION_INFO_V1(varbinary_leq); +PG_FUNCTION_INFO_V1(varbinary_cmp); + +Datum +varbinary_eq (PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) == 0; + PG_RETURN_BOOL(result); +} + +Datum +varbinary_neq (PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) != 0; + PG_RETURN_BOOL(result); +} + +Datum +varbinary_gt (PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) > 0; + PG_RETURN_BOOL(result); +} + +Datum +varbinary_geq (PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) >= 0; + PG_RETURN_BOOL(result); +} + +Datum +varbinary_lt (PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) < 0; + PG_RETURN_BOOL(result); +} + +Datum +varbinary_leq (PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + bool result = varbinarycompare(source1, source2) <= 0; + PG_RETURN_BOOL(result); +} + +Datum +varbinary_cmp (PG_FUNCTION_ARGS) +{ + bytea *source1 = PG_GETARG_BYTEA_PP(0); + bytea *source2 = PG_GETARG_BYTEA_PP(1); + PG_RETURN_INT32(varbinarycompare(source1, source2)); +} diff --git a/contrib/babelfishpg_common/src/varchar.c b/contrib/babelfishpg_common/src/varchar.c new file mode 100644 index 00000000000..a06ef8f5d8e --- /dev/null +++ b/contrib/babelfishpg_common/src/varchar.c @@ -0,0 +1,966 @@ +/*------------------------------------------------------------------------- + * + * varchar.c + * Functions for the built-in types char(n) and varchar(n). + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/utils/adt/varchar.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + + +#include "access/hash.h" +#include "catalog/pg_collation.h" +#include "fmgr.h" +#include "libpq/pqformat.h" +#include "nodes/nodeFuncs.h" +#include "parser/parser.h" /* only needed for GUC variables */ +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/pg_locale.h" +#include "utils/varlena.h" +#include "mb/pg_wchar.h" + +int TsqlUTF8LengthInUTF16(const void *vin, int len); +void TsqlCheckUTF16Length_varchar(const char *s_data, int32 len, int32 maxlen, bool isExplicit); +void TsqlCheckUTF16Length_bpchar(const char *s, int32 len, int32 maxlen, int charlen, bool isExplicit); +void TsqlCheckUTF16Length_bpchar_input(const char *s, int32 len, int32 maxlen, int charlen); +void TsqlCheckUTF16Length_varchar_input(const char *s, int32 len, int32 maxlen); +void *tsql_varchar_input(const char *s, size_t len, int32 atttypmod); + +/* + * GetUTF8CodePoint - extract the next Unicode code point from 1..4 + * bytes at 'in' in UTF-8 encoding. + */ +static inline int32_t +GetUTF8CodePoint(const unsigned char *in, int len, int *consumed_p) +{ + int32_t code; + int consumed; + + if (len == 0) + return EOF; + + if ((in[0] & 0x80) == 0) + { + /* 1 byte - 0xxxxxxx */ + code = in[0]; + consumed = 1; + } + else if ((in[0] & 0xE0) == 0xC0) + { + /* 2 byte - 110xxxxx 10xxxxxx */ + if (len < 2) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("truncated UTF8 byte sequence starting with 0x%02x", + in[0]))); + if ((in[1] & 0xC0) != 0x80) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + code = ((in[0] & 0x1F) << 6) | (in[1] & 0x3F); + consumed = 2; + } + else if ((in[0] & 0xF0) == 0xE0) + { + /* 3 byte - 1110xxxx 10xxxxxx 10xxxxxx */ + if (len < 3) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("truncated UTF8 byte sequence starting with 0x%02x", + in[0]))); + if ((in[1] & 0xC0) != 0x80 || (in[2] & 0xC0) != 0x80) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + code = ((in[0] & 0x0F) << 12) | ((in[1] & 0x3F) << 6) | (in[2] & 0x3F); + consumed = 3; + } + else if ((in[0] & 0xF8) == 0xF0) + { + /* 4 byte - 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (len < 4) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("truncated UTF8 byte sequence starting with 0x%02x", + in[0]))); + if ((in[1] & 0xC0) != 0x80 || (in[2] & 0xC0) != 0x80 || + (in[3] & 0xC0) != 0x80) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + code = ((in[0] & 0x07) << 18) | ((in[1] & 0x3F) << 12) | + ((in[2] & 0x3F) << 6) | (in[3] & 0x3F); + consumed = 4; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + } + + if (code > 0x10FFFF || (code >= 0xD800 && code < 0xE000)) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 code point 0x%x", code))); + + if (consumed_p) + *consumed_p = consumed; + + return code; +} + +/* + * TsqlUTF8LengthInUTF16 - compute the length of a UTF8 string in number of + * 16-bit units if we were to convert it into + * UTF16 with TdsUTF8toUTF16StringInfo() + */ +int +TsqlUTF8LengthInUTF16(const void *vin, int len) +{ + const unsigned char *in = vin; + int result = 0; + int i; + int consumed; + int32_t code; + + for (i = 0; i < len;) + { + code = GetUTF8CodePoint(&in[i], len - i, &consumed); + + /* Check that this is a valid code point */ + if ((code > 0xD800 && code < 0xE000) || code < 0x0001 || code > 0x10FFFF) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid Unicode code point 0x%x", code))); + + if (code <= 0xFFFF) + /* This code point would result in a single 16-bit output */ + result += 1; + else + /* This code point would result in a 16-bit surrogate pair */ + result += 2; + + i += consumed; + } + + return result; +} + +static inline void +TsqlCheckUTF16Length(const char *utf8_str, size_t len, size_t maxlen, + char *varstr) +{ + int i; + for (i = len; i > 0; i--) + if (utf8_str[i - 1] != ' ') + break; + if (TsqlUTF8LengthInUTF16(utf8_str, i) > maxlen) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character%s(%d) " + "as UTF16 output", + varstr, (int)maxlen))); +} + +/* + * Check for T-SQL varchar function + */ +void +TsqlCheckUTF16Length_varchar(const char *s_data, int32 len, int32 maxlen, bool isExplicit) +{ + int i; + size_t maxmblen; + if (maxlen < 0) + return ; + + if (len <= maxlen) + { + TsqlCheckUTF16Length(s_data, len, maxlen, " varying"); + return ; + } + + /* truncate multibyte string preserving multibyte boundary */ + maxmblen = pg_mbcharcliplen(s_data, len, maxlen); + + if (!isExplicit && + !(suppress_string_truncation_error_hook && (*suppress_string_truncation_error_hook)())) + { + for (i = maxmblen; i < len; i++) + if (s_data[i] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character varying(%d)", + maxlen))); + TsqlCheckUTF16Length(s_data, len, maxlen, " varying"); + } + else + TsqlCheckUTF16Length(s_data, maxmblen, maxlen, " varying"); +} + +/* + * Check for T-SQL bpchar function + */ +void +TsqlCheckUTF16Length_bpchar(const char *s, int32 len, int32 maxlen, int charlen, bool isExplicit) +{ + int i; + if (charlen == maxlen) + { + TsqlCheckUTF16Length(s, len, maxlen, ""); + } + else if (charlen > maxlen) + { + /* Verify that extra characters are spaces, and clip them off */ + size_t maxmblen; + + maxmblen = pg_mbcharcliplen(s, len, maxlen); + + if (!isExplicit && + !(suppress_string_truncation_error_hook && (*suppress_string_truncation_error_hook)())) + { + for (i = maxmblen; i < len; i++) + if (s[i] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character(%d)", + maxlen))); + } + + len = maxmblen; + TsqlCheckUTF16Length(s, len, maxlen, ""); + } + else + { + TsqlCheckUTF16Length(s, len, maxlen, ""); + } +} + +/* + * Check for T-SQL varchar common input function, varchar_input() + */ +void TsqlCheckUTF16Length_varchar_input(const char *s, int32 len, int32 maxlen) +{ + TsqlCheckUTF16Length(s, len, maxlen, " varying"); +} + +/* + * Check for T-SQL bpchar function + */ +void TsqlCheckUTF16Length_bpchar_input(const char *s, int32 len, int32 maxlen, int charlen) +{ + if (charlen > maxlen) + { + /* Verify that extra characters are spaces, and clip them off */ + size_t mbmaxlen = pg_mbcharcliplen(s, len, maxlen); + size_t j; + + /* + * at this point, len is the actual BYTE length of the input + * string, maxlen is the max number of CHARACTERS allowed for this + * bpchar type, mbmaxlen is the length in BYTES of those chars. + */ + for (j = mbmaxlen; j < len; j++) + { + if (s[j] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character(%d)", + (int) maxlen))); + } + + TsqlCheckUTF16Length(s, len, maxlen, ""); + } + else + { + TsqlCheckUTF16Length(s, len, maxlen, ""); + } +} + +/* Function Registeration */ +PG_FUNCTION_INFO_V1(bpcharin); +PG_FUNCTION_INFO_V1(bpchar); +PG_FUNCTION_INFO_V1(bpcharrecv); + +PG_FUNCTION_INFO_V1(varcharin); +PG_FUNCTION_INFO_V1(varchar); +PG_FUNCTION_INFO_V1(varcharrecv); +PG_FUNCTION_INFO_V1(varchareq); +PG_FUNCTION_INFO_V1(varcharne); +PG_FUNCTION_INFO_V1(varcharlt); +PG_FUNCTION_INFO_V1(varcharle); +PG_FUNCTION_INFO_V1(varchargt); +PG_FUNCTION_INFO_V1(varcharge); +PG_FUNCTION_INFO_V1(varcharcmp); +PG_FUNCTION_INFO_V1(hashvarchar); + +/***************************************************************************** + * varchar - varchar(n) + * + * Note: varchar piggybacks on type text for most operations, and so has no + * C-coded functions except for I/O and typmod checking. + *****************************************************************************/ + +/* + * varchar_input -- common guts of varcharin and varcharrecv + * + * s is the input text of length len (may not be null-terminated) + * atttypmod is the typmod value to apply + * + * Note that atttypmod is measured in characters, which + * is not necessarily the same as the number of bytes. + * + * If the input string is too long, raise an error, unless the extra + * characters are spaces, in which case they're truncated. (per SQL) + * + * Uses the C string to text conversion function, which is only appropriate + * if VarChar and text are equivalent types. + */ +static VarChar * +varchar_input(const char *s, size_t len, int32 atttypmod) +{ + VarChar *result; + size_t maxlen; + + maxlen = atttypmod - VARHDRSZ; + + if (atttypmod >= (int32) VARHDRSZ && len > maxlen) + { + /* Verify that extra characters are spaces, and clip them off */ + size_t mbmaxlen = pg_mbcharcliplen(s, len, maxlen); + size_t j; + + for (j = mbmaxlen; j < len; j++) + { + if (s[j] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character varying(%d)", + (int) maxlen))); + } + + len = mbmaxlen; + } + + if (atttypmod >= (int32) VARHDRSZ) + TsqlCheckUTF16Length_varchar_input(s, len, maxlen); + + result = (VarChar *) cstring_to_text_with_len(s, len); + return result; +} + +void * +tsql_varchar_input(const char *s, size_t len, int32 atttypmod) +{ + return varchar_input(s, len, atttypmod); +} + +/* + * Convert a C string to VARCHAR internal representation. atttypmod + * is the declared length of the type plus VARHDRSZ. + */ +Datum +varcharin(PG_FUNCTION_ARGS) +{ + char *s = PG_GETARG_CSTRING(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); + VarChar *result; + + result = varchar_input(s, strlen(s), atttypmod); + PG_RETURN_VARCHAR_P(result); +} + +/* + * varcharrecv - converts external binary format to varchar + */ +Datum +varcharrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); + VarChar *result; + char *str; + int nbytes; + + str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); + result = varchar_input(str, nbytes, atttypmod); + pfree(str); + PG_RETURN_VARCHAR_P(result); +} + +/* + * Converts a VARCHAR type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to varchar(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error unless extra characters are + * all spaces. (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) + */ +Datum +varchar(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + int32 typmod = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + int32 len, + maxlen; + size_t maxmblen; + int i; + char *s_data; + + len = VARSIZE_ANY_EXHDR(source); + s_data = VARDATA_ANY(source); + maxlen = typmod - VARHDRSZ; + + TsqlCheckUTF16Length_varchar(s_data, len, maxlen, isExplicit); + + /* No work if typmod is invalid or supplied data fits it already */ + if (maxlen < 0 || len <= maxlen) + PG_RETURN_VARCHAR_P(source); + + /* only reach here if string is too long... */ + + /* truncate multibyte string preserving multibyte boundary */ + maxmblen = pg_mbcharcliplen(s_data, len, maxlen); + + if (!isExplicit && + !(suppress_string_truncation_error_hook && (*suppress_string_truncation_error_hook)())) + { + for (i = maxmblen; i < len; i++) + if (s_data[i] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character varying(%d)", + maxlen))); + } + + PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text_with_len(s_data, + maxmblen)); +} + +/***************************************************************************** + * bpchar - char() * + *****************************************************************************/ + +/* + * bpchar_input -- common guts of bpcharin and bpcharrecv + * + * s is the input text of length len (may not be null-terminated) + * atttypmod is the typmod value to apply + * + * Note that atttypmod is measured in characters, which + * is not necessarily the same as the number of bytes. + * + * If the input string is too long, raise an error, unless the extra + * characters are spaces, in which case they're truncated. (per SQL) + */ +static BpChar * +bpchar_input(const char *s, size_t len, int32 atttypmod) +{ + BpChar *result; + char *r; + size_t maxlen; + + /* If typmod is -1 (or invalid), use the actual string length */ + if (atttypmod < (int32) VARHDRSZ) + maxlen = len; + else + { + size_t charlen; /* number of CHARACTERS in the input */ + + maxlen = atttypmod - VARHDRSZ; + charlen = pg_mbstrlen_with_len(s, len); + + TsqlCheckUTF16Length_bpchar_input(s, len, maxlen, charlen); + + if (charlen > maxlen) + { + /* Verify that extra characters are spaces, and clip them off */ + size_t mbmaxlen = pg_mbcharcliplen(s, len, maxlen); + size_t j; + + /* + * at this point, len is the actual BYTE length of the input + * string, maxlen is the max number of CHARACTERS allowed for this + * bpchar type, mbmaxlen is the length in BYTES of those chars. + */ + for (j = mbmaxlen; j < len; j++) + { + if (s[j] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character(%d)", + (int) maxlen))); + } + + /* + * Now we set maxlen to the necessary byte length, not the number + * of CHARACTERS! + */ + maxlen = len = mbmaxlen; + } + else + { + /* + * Now we set maxlen to the necessary byte length, not the number + * of CHARACTERS! + */ + maxlen = len + (maxlen - charlen); + } + } + + result = (BpChar *) palloc(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + r = VARDATA(result); + memcpy(r, s, len); + + /* blank pad the string if necessary */ + if (maxlen > len) + memset(r + len, ' ', maxlen - len); + + return result; +} + +/* + * Convert a C string to CHARACTER internal representation. atttypmod + * is the declared length of the type plus VARHDRSZ. + */ +Datum +bpcharin(PG_FUNCTION_ARGS) +{ + char *s = PG_GETARG_CSTRING(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); + BpChar *result; + + result = bpchar_input(s, strlen(s), atttypmod); + PG_RETURN_BPCHAR_P(result); +} + +/* + * bpcharrecv - converts external binary format to bpchar + */ +Datum +bpcharrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + +#ifdef NOT_USED + Oid typelem = PG_GETARG_OID(1); +#endif + int32 atttypmod = PG_GETARG_INT32(2); + BpChar *result; + char *str; + int nbytes; + + str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes); + result = bpchar_input(str, nbytes, atttypmod); + pfree(str); + PG_RETURN_BPCHAR_P(result); +} + +/* + * Converts a CHARACTER type to the specified size. + * + * maxlen is the typmod, ie, declared length plus VARHDRSZ bytes. + * isExplicit is true if this is for an explicit cast to char(N). + * + * Truncation rules: for an explicit cast, silently truncate to the given + * length; for an implicit cast, raise error unless extra characters are + * all spaces. (This is sort-of per SQL: the spec would actually have us + * raise a "completion condition" for the explicit cast case, but Postgres + * hasn't got such a concept.) + */ +Datum +bpchar(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + int32 maxlen = PG_GETARG_INT32(1); + bool isExplicit = PG_GETARG_BOOL(2); + BpChar *result; + int32 len; + char *r; + char *s; + int i; + int charlen; /* number of characters in the input string + + * VARHDRSZ */ + + /* No work if typmod is invalid */ + if (maxlen < (int32) VARHDRSZ) + PG_RETURN_BPCHAR_P(source); + + maxlen -= VARHDRSZ; + + len = VARSIZE_ANY_EXHDR(source); + s = VARDATA_ANY(source); + + charlen = pg_mbstrlen_with_len(s, len); + + TsqlCheckUTF16Length_bpchar(s, len, maxlen, charlen, isExplicit); + + /* No work if supplied data matches typmod already */ + if (charlen == maxlen) + PG_RETURN_BPCHAR_P(source); + + if (charlen > maxlen) + { + /* Verify that extra characters are spaces, and clip them off */ + size_t maxmblen; + + maxmblen = pg_mbcharcliplen(s, len, maxlen); + + if (!isExplicit && + !(suppress_string_truncation_error_hook && (*suppress_string_truncation_error_hook)())) + { + for (i = maxmblen; i < len; i++) + if (s[i] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character(%d)", + maxlen))); + } + + len = maxmblen; + + /* + * At this point, maxlen is the necessary byte length, not the number + * of CHARACTERS! + */ + maxlen = len; + } + else + { + + /* + * At this point, maxlen is the necessary byte length, not the number + * of CHARACTERS! + */ + maxlen = len + (maxlen - charlen); + } + + Assert(maxlen >= len); + + result = palloc(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + r = VARDATA(result); + + memcpy(r, s, len); + + /* blank pad the string if necessary */ + if (maxlen > len) + memset(r + len, ' ', maxlen - len); + + PG_RETURN_BPCHAR_P(result); +} + +static inline int +varcharTruelen(VarChar *arg) +{ + char *s = VARDATA_ANY(arg); + int len = VARSIZE_ANY_EXHDR(arg); + + int i; + + /* + * Note that we rely on the assumption that ' ' is a singleton unit on + * every supported multibyte server encoding. + */ + for (i = len - 1; i >= 0; i--) + { + if (s[i] != ' ') + break; + } + return i + 1; +} + +static inline void +check_collation_set(Oid collid) +{ + if (!OidIsValid(collid)) + { + /* + * This typically means that the parser could not resolve a conflict + * of implicit collations, so report it that way. + */ + ereport(ERROR, + (errcode(ERRCODE_INDETERMINATE_COLLATION), + errmsg("could not determine which collation to use for string comparison"), + errhint("Use the COLLATE clause to set the collation explicitly."))); + } +} + +Datum +varchareq(PG_FUNCTION_ARGS) +{ + VarChar *arg1 = PG_GETARG_VARCHAR_PP(0); + VarChar *arg2 = PG_GETARG_VARCHAR_PP(1); + int len1, + len2; + bool result; + Oid collid = PG_GET_COLLATION(); + + check_collation_set(collid); + + len1 = varcharTruelen(arg1); + len2 = varcharTruelen(arg2); + + if (lc_collate_is_c(collid) || + collid == DEFAULT_COLLATION_OID || + pg_newlocale_from_collation(collid)->deterministic) + { + /* + * Since we only care about equality or not-equality, we can avoid all + * the expense of strcoll() here, and just do bitwise comparison. + */ + if (len1 != len2) + result = false; + else + result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) == 0); + } + else + { + result = (varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + collid) == 0); + } + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +varcharne(PG_FUNCTION_ARGS) +{ + VarChar *arg1 = PG_GETARG_VARCHAR_PP(0); + VarChar *arg2 = PG_GETARG_VARCHAR_PP(1); + int len1, + len2; + bool result; + Oid collid = PG_GET_COLLATION(); + + check_collation_set(collid); + + len1 = varcharTruelen(arg1); + len2 = varcharTruelen(arg2); + + if (lc_collate_is_c(collid) || + collid == DEFAULT_COLLATION_OID || + pg_newlocale_from_collation(collid)->deterministic) + { + /* + * Since we only care about equality or not-equality, we can avoid all + * the expense of strcoll() here, and just do bitwise comparison. + */ + if (len1 != len2) + result = true; + else + result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) != 0); + } + else + { + result = (varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + collid) != 0); + } + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +varcharlt(PG_FUNCTION_ARGS) +{ + VarChar *arg1 = PG_GETARG_VARCHAR_PP(0); + VarChar *arg2 = PG_GETARG_VARCHAR_PP(1); + int len1, + len2; + int cmp; + + len1 = varcharTruelen(arg1); + len2 = varcharTruelen(arg2); + + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(cmp < 0); +} + +Datum +varcharle(PG_FUNCTION_ARGS) +{ + VarChar *arg1 = PG_GETARG_VARCHAR_PP(0); + VarChar *arg2 = PG_GETARG_VARCHAR_PP(1); + int len1, + len2; + int cmp; + + len1 = varcharTruelen(arg1); + len2 = varcharTruelen(arg2); + + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(cmp <= 0); +} + +Datum +varchargt(PG_FUNCTION_ARGS) +{ + VarChar *arg1 = PG_GETARG_VARCHAR_PP(0); + VarChar *arg2 = PG_GETARG_VARCHAR_PP(1); + int len1, + len2; + int cmp; + + len1 = varcharTruelen(arg1); + len2 = varcharTruelen(arg2); + + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(cmp > 0); +} + +Datum +varcharge(PG_FUNCTION_ARGS) +{ + VarChar *arg1 = PG_GETARG_VARCHAR_PP(0); + VarChar *arg2 = PG_GETARG_VARCHAR_PP(1); + int len1, + len2; + int cmp; + + len1 = varcharTruelen(arg1); + len2 = varcharTruelen(arg2); + + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_BOOL(cmp >= 0); +} + +Datum +varcharcmp(PG_FUNCTION_ARGS) +{ + VarChar *arg1 = PG_GETARG_VARCHAR_PP(0); + VarChar *arg2 = PG_GETARG_VARCHAR_PP(1); + int len1, + len2; + int cmp; + + len1 = varcharTruelen(arg1); + len2 = varcharTruelen(arg2); + + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); + + PG_FREE_IF_COPY(arg1, 0); + PG_FREE_IF_COPY(arg2, 1); + + PG_RETURN_INT32(cmp); +} + +/* + * varchar needs a specialized hash function because we want to ignore + * trailing blanks in comparisons. + */ +Datum +hashvarchar(PG_FUNCTION_ARGS) +{ + VarChar *key = PG_GETARG_VARCHAR_PP(0); + Oid collid = PG_GET_COLLATION(); + char *keydata; + int keylen; + pg_locale_t mylocale = 0; + Datum result; + + if (!collid) + ereport(ERROR, + (errcode(ERRCODE_INDETERMINATE_COLLATION), + errmsg("could not determine which collation to use for string hashing"), + errhint("Use the COLLATE clause to set the collation explicitly."))); + + keydata = VARDATA_ANY(key); + keylen = varcharTruelen(key); + + if (!lc_collate_is_c(collid) && collid != DEFAULT_COLLATION_OID) + mylocale = pg_newlocale_from_collation(collid); + + if (!mylocale || mylocale->deterministic) + { + result = hash_any((unsigned char *) keydata, keylen); + } + else + { +#ifdef USE_ICU + if (mylocale->provider == COLLPROVIDER_ICU) + { + int32_t ulen = -1; + UChar *uchar = NULL; + Size bsize; + uint8_t *buf; + + ulen = icu_to_uchar(&uchar, keydata, keylen); + + bsize = ucol_getSortKey(mylocale->info.icu.ucol, + uchar, ulen, NULL, 0); + buf = palloc(bsize); + ucol_getSortKey(mylocale->info.icu.ucol, + uchar, ulen, buf, bsize); + + result = hash_any(buf, bsize); + + pfree(buf); + } + else +#endif + /* shouldn't happen */ + elog(ERROR, "unsupported collprovider: %c", mylocale->provider); + } + + /* Avoid leaking memory for toasted inputs */ + PG_FREE_IF_COPY(key, 0); + + return result; +} diff --git a/contrib/babelfishpg_money/Makefile b/contrib/babelfishpg_money/Makefile new file mode 100755 index 00000000000..f57bddcdd92 --- /dev/null +++ b/contrib/babelfishpg_money/Makefile @@ -0,0 +1,49 @@ +MODULE_big = babelfishpg_money +OBJS = fixeddecimal.o + +EXTENSION = babelfishpg_money + +#subdir = contrib/babelfishpg_money + +DATA = fixeddecimal--1.0.0--1.1.0.sql +DATA_built = babelfishpg_money--1.1.0.sql + +#include ../Makefile.common + +CFLAGS = `$(PG_CONFIG) --includedir-server` + +TESTS = $(wildcard test/sql/*.sql) + +REGRESS_BRIN := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "XL 9\.[5-9]| 10\.0| 11\.[0-9]| 12\.[0-9]" && echo brin-xl) +REGRESS_BRIN += $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -E "9\.[5-9]| 10\.0| 11\.[0-9]| 12\.[0-9]" | grep -qEv "XL" && echo brin) +REGRESS_VERSION_SPECIFIC := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "XL" && echo index-xl || echo index) +REGRESS = $(shell echo aggregate cast comparison overflow $(REGRESS_BRIN) $(REGRESS_VERSION_SPECIFIC)) + +REGRESS_OPTS = --inputdir=test --outputdir=test --load-extension=babelfishpg_money + +#PG_CONFIG = pg_config +ifdef USE_PGXS +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/babelfishpg_money +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + + +AGGSTATESQL := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "XL" && echo fixeddecimalaggstate.sql) +AGGFUNCSSQL := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "XL" && echo fixeddecimal--xlaggs.sql) + +AGGFUNCSSQL := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "9\.[6-9]| 10\.[0-9]| 11\.[0-9]| 12\.[0-9]" && echo fixeddecimal--parallelaggs.sql || echo fixeddecimal--aggs.sql) + +BRINSQL := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "9\.[5-9]| 10\.[0-9]| 11\.[0-9]| 12\.[0-9]" && echo fixeddecimal--brin.sql) + +# 9.6 was the dawn of parallel query, so we'll use the parallel enabled .sql file from then on. +BASESQL := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "9\.[6-9]| 10\.[0-9]| 11\.[0-9]| 12\.[0-9]" && echo fixeddecimal--1.1.0_base_parallel.sql || echo fixeddecimal--1.1.0_base.sql) + +OBJECTS := $(addprefix $(srcdir)/, $(AGGSTATESQL) $(BASESQL) $(AGGFUNCSSQL) $(BRINSQL)) + +babelfishpg_money--1.1.0.sql: $(OBJECTS) + cat $^ > $@ diff --git a/contrib/babelfishpg_money/README.md b/contrib/babelfishpg_money/README.md new file mode 100755 index 00000000000..8199dc6e707 --- /dev/null +++ b/contrib/babelfishpg_money/README.md @@ -0,0 +1,162 @@ +FIXEDDECIMAL +============ + +Works with PostgreSQL 9.5 or higher. +The latest test was executed on version 12. + +Overview +-------- + +FixedDecimal is a fixed precision decimal type which provides a subset of the +features of PostgreSQL's builtin NUMERIC type, but with vastly increased +performance. Fixeddecimal is targeted to cases where performance and disk space +are a critical. + +Just use FIXEDDECIMAL(n, 2) rather than NUMERIC(n, 2) for n=3..17 + +Often there are data storage requirements where the built in REAL and +DOUBLE PRECISION types cannot be used due to the non-exact representation of +numbers using these types, e.g. where monetary values need to be stored. In many +of these cases NUMERIC is an almost perfect type, although with NUMERIC +performance is no match for the performance of REAL or DOUBLE PRECISION, as +these use CPU native processor types. + +FixedDecimal delivers performance advantages over NUMERIC with full precision for +addition and subtraction. Just as occurs with REAL and DOUBLE PRECISION, there +are some caveats for multiplication and division. + +Behavioural differences between FIXEDDECIMAL and NUMERIC +-------------------------------------------------------- + +It should be noted that there are cases were FIXEDDECIMAL behaves differently +from NUMERIC. + +1. FIXEDDECIMAL has a much more limited range of values than NUMERIC. By + default this type can represent a maximum range of FIXEDDECIMAL(17,2), + although the underlying type is unable to represent the full range of + of the 17th significant digit. + +2. FIXEDDECIMAL always rounds towards zero. + +3. FIXEDDECIMAL does not support NaN. + +4. Any attempt to use a numerical scale other than the default fixed scale + will result in an error. e.g. SELECT '123.223'::FIXEDDECIMAL(4,1) will fail + by default, as the default scale is 2, not 1. + +Internals +--------- + +FIXEDDECIMAL internally uses a 64bit integer type for its underlying storage. +This is what gives the type the performance advantage over NUMERIC, as most +calculations are performed as native processor operations rather than software +implementations as in the case with NUMERIC. + +FIXEDDECIMAL has a fixed scale value, which by default is 2. Internally numbers +are stores as the actual value multiplied by 100. e.g. 50 would be stored as +5000, and 1.23 would be stored as 123. This internal representation allows very +fast and accurate addition and subtraction between two fixeddecimal types. + +Multiplication between two fixeddecimal types is slightly more complex. If we +perform 2.00 * 3.00 in fixeddecimal, internally these numbers would be 200 and +300 respectively, so internally 200 * 300 becomes 60000, which must be divided +by 100 in order to obtain the correct internal result of 600, which of course +externally is 6.00. This method of multiplication is hazard to overflowing the +internal 64bit integer type, for this reason all multiplication and division is +performed using 128bit integer types. + +Internally, by default, FIXEDDECIMAL is limited to a maximum value of +92233720368547758.07 and a minimum value of -92233720368547758.08. If any of +these limits are exceeded the query will fail with an error. + +By default the scale of FIXEDDECIMAL is 2 decimal digits after the decimal +point. This value may be changed only by recompiling FIXEDDECIMAL from source, +which is done by altering the FIXEDDECIMAL_MULTIPLIER and FIXEDDECIMAL_SCALE +constants. If the FIXEDDECIMAL_SCALE was set to 4, then the +FIXEDDECIMAL_MULTIPLIER should be set to 10000. Doing this will mean that the +absolute limits of the type decrease to a range of -922337203685477.5808 to +922337203685477.5807. + +Caution +------- + +FIXEDDECIMAL is mainly intended as a fast and efficient data type which will +suit a limited set numerical data storage and retrieval needs. Complex +arithmetic could be said to be one of fixeddecimal's limits. As stated above +division always rounds towards zero. Please observe the following example: + +``` +test=# select '2.00'::fixeddecimal / '3.00'::fixeddecimal; + ?column? +---------- + 0.66 +(1 row) +``` + +A workaround of this would be to perform all calculations in NUMERIC, and +ROUND() the result into the maximum scale of FIXEDDECIMAL: + +``` +test=# select round('2.00'::numeric / '3.00'::numeric, 2)::fixeddecimal; + ?column? +---------- + 0.67 +(1 row) +``` + +It should also be noted that excess precision is ignored by fixeddecimal. +With a FIXEDDECIMAL_PRECISION of 2, any value after the 2nd digit following +the decimal point is completely ignored rather than rounded. The following +example demonstrates this: + +``` +test=# select '1.239'::fixeddecimal; + fixeddecimal +-------------- + 1.23 +(1 row) +``` + +It is especially important to remember that this truncation also occurs during +arithmetic. Notice in the following example the result is 1120 rather than +1129, since 1.129 is immediately rounded to 1.12 on input. + +``` +test=# select '1000'::fixeddecimal * '1.129'::fixeddecimal; + ?column? +---------- + 1120.00 +(1 row) +``` + +Installation +------------ + +To install fixeddecimal you must build the extension from source code. + +First ensure that your PATH environment variable is setup to find the correct +PostgreSQL installation first. You can check this by typing running the +pg_config command and checking the paths listed. + +Once you are confident your PATH variable is set correctly + +``` +make +make install +make installcheck +``` + +From psql, in order to create the extension you must type: + +``` +CREATE EXTENSION fixeddecimal; +``` + +Credits +------- + +fixeddecimal is open source using The PostgreSQL Licence, copyright is novated to the PostgreSQL Global Development Group. + +Source code developed by 2ndQuadrant, as part of the AXLE project (http://axleproject.eu) which received funding from the European Union’s Seventh Framework Programme (FP7/2007-2015) under grant agreement n° 318633 + +Lead Developer - David Rowley diff --git a/contrib/babelfishpg_money/babelfishpg_money.control b/contrib/babelfishpg_money/babelfishpg_money.control new file mode 100755 index 00000000000..d4ddb8a067b --- /dev/null +++ b/contrib/babelfishpg_money/babelfishpg_money.control @@ -0,0 +1,4 @@ +comment = 'babelfishpg_money' +default_version = '1.1.0' +relocatable = false +module_pathname = '$libdir/babelfishpg_money' diff --git a/contrib/babelfishpg_money/fixeddecimal--1.0.0--1.1.0.sql b/contrib/babelfishpg_money/fixeddecimal--1.0.0--1.1.0.sql new file mode 100644 index 00000000000..2eb08ae4d54 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--1.0.0--1.1.0.sql @@ -0,0 +1,800 @@ +CREATE FUNCTION fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_numeric_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'numeric_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_eq(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_ne(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_lt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_le(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_gt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_ge(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_numeric_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_numeric_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_numeric_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_numeric_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_numeric_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_numeric_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, NUMERIC), + OPERATOR 2 <= (FIXEDDECIMAL, NUMERIC), + OPERATOR 3 = (FIXEDDECIMAL, NUMERIC), + OPERATOR 4 >= (FIXEDDECIMAL, NUMERIC), + OPERATOR 5 > (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC); + +CREATE OPERATOR CLASS fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- NUMERIC, FIXEDDECIMAL +CREATE FUNCTION numeric_fixeddecimal_eq(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_ne(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_lt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_le(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_gt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_ge(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = numeric_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = numeric_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = numeric_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = numeric_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = numeric_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = numeric_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 2 <= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 3 = (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 4 >= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 5 > (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + FUNCTION 1 numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL); + +CREATE OPERATOR CLASS numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (NUMERIC, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- FIXEDDECIMAL, INT4 +CREATE FUNCTION fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int4_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_eq(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_ne(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_lt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_le(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_gt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_ge(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int4_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int4_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int4_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int4_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int4_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int4_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT4), + OPERATOR 2 <= (FIXEDDECIMAL, INT4), + OPERATOR 3 = (FIXEDDECIMAL, INT4), + OPERATOR 4 >= (FIXEDDECIMAL, INT4), + OPERATOR 5 > (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4); + +CREATE OPERATOR CLASS fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT4, FIXEDDECIMAL +CREATE FUNCTION int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int4_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_eq(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_ne(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_lt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_le(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_gt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_ge(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT; +CREATE OPERATOR = ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int4_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int4_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int4_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int4_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int4_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int4_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT4, FIXEDDECIMAL), + OPERATOR 2 <= (INT4, FIXEDDECIMAL), + OPERATOR 3 = (INT4, FIXEDDECIMAL), + OPERATOR 4 >= (INT4, FIXEDDECIMAL), + OPERATOR 5 > (INT4, FIXEDDECIMAL), + FUNCTION 1 int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL); + +CREATE OPERATOR CLASS int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT4, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- FIXEDDECIMAL, INT2 +CREATE FUNCTION fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int2_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_eq(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_ne(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_lt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_le(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_gt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_ge(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ge' +LANGUAGE C IMMUTABLE STRICT; +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int2_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int2_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int2_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int2_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int2_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int2_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT2), + OPERATOR 2 <= (FIXEDDECIMAL, INT2), + OPERATOR 3 = (FIXEDDECIMAL, INT2), + OPERATOR 4 >= (FIXEDDECIMAL, INT2), + OPERATOR 5 > (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2); + +CREATE OPERATOR CLASS fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT2, FIXEDDECIMAL +CREATE FUNCTION int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int2_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_eq(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_ne(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_lt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_le(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_gt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_ge(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int2_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int2_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int2_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int2_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int2_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int2_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT2, FIXEDDECIMAL), + OPERATOR 2 <= (INT2, FIXEDDECIMAL), + OPERATOR 3 = (INT2, FIXEDDECIMAL), + OPERATOR 4 >= (INT2, FIXEDDECIMAL), + OPERATOR 5 > (INT2, FIXEDDECIMAL), + FUNCTION 1 int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL); + +CREATE OPERATOR CLASS int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT2, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + + +-- 9.6+ Parallel function changes. +ALTER FUNCTION fixeddecimalin(cstring, oid, int4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalout(fixeddecimal) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalrecv(internal) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalsend(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimaltypmodin(_cstring) PARALLEL SAFE; +ALTER FUNCTION fixeddecimaltypmodout(INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimaleq(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalne(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimallt(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalle(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalgt(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalge(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalum(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalpl(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalmi(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalmul(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimaldiv(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION abs(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimallarger(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalsmaller(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_hash(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric_eq(FIXEDDECIMAL, NUMERIC) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric_ne(FIXEDDECIMAL, NUMERIC) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric_lt(FIXEDDECIMAL, NUMERIC) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric_le(FIXEDDECIMAL, NUMERIC) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric_gt(FIXEDDECIMAL, NUMERIC) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric_ge(FIXEDDECIMAL, NUMERIC) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal_eq(NUMERIC, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal_ne(NUMERIC, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal_lt(NUMERIC, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal_le(NUMERIC, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal_gt(NUMERIC, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal_ge(NUMERIC, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int4_eq(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int4_ne(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int4_lt(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int4_le(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int4_gt(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int4_ge(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint4pl(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint4mi(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint4mul(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint4div(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4_fixeddecimal_eq(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4_fixeddecimal_ne(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4_fixeddecimal_lt(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4_fixeddecimal_le(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4_fixeddecimal_gt(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4_fixeddecimal_ge(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4fixeddecimalpl(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4fixeddecimalmi(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4fixeddecimalmul(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int4fixeddecimaldiv(INT4, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int2_eq(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int2_ne(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int2_lt(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int2_le(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int2_gt(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_int2_ge(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint2pl(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint2mi(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint2mul(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint2div(FIXEDDECIMAL, INT2) PARALLEL SAFE; +ALTER FUNCTION int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2_fixeddecimal_eq(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2_fixeddecimal_ne(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2_fixeddecimal_lt(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2_fixeddecimal_le(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2_fixeddecimal_gt(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2_fixeddecimal_ge(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2fixeddecimalpl(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2fixeddecimalmi(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2fixeddecimalmul(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2fixeddecimaldiv(INT2, FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal(FIXEDDECIMAL, INT4) PARALLEL SAFE; +ALTER FUNCTION int4fixeddecimal(INT4) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint4(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION int2fixeddecimal(INT2) PARALLEL SAFE; +ALTER FUNCTION fixeddecimalint2(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimaltod(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION dtofixeddecimal(DOUBLE PRECISION) PARALLEL SAFE; +ALTER FUNCTION fixeddecimaltof(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION ftofixeddecimal(REAL) PARALLEL SAFE; +ALTER FUNCTION fixeddecimal_numeric(FIXEDDECIMAL) PARALLEL SAFE; +ALTER FUNCTION numeric_fixeddecimal(NUMERIC) PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalaggstatecombine(INTERNAL, INTERNAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimalaggstatecombine' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalaggstateserialize(INTERNAL) +RETURNS BYTEA +AS 'babelfishpg_money', 'fixeddecimalaggstateserialize' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalaggstatedeserialize(BYTEA, INTERNAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimalaggstatedeserialize' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + + +UPDATE pg_proc SET proparallel = 's' +WHERE oid = 'min(FIXEDDECIMAL)'::pg_catalog.regprocedure; + +UPDATE pg_proc SET proparallel = 's' +WHERE oid = 'max(FIXEDDECIMAL)'::pg_catalog.regprocedure; + +UPDATE pg_proc SET proparallel = 's' +WHERE oid = 'sum(FIXEDDECIMAL)'::pg_catalog.regprocedure; + +UPDATE pg_proc SET proparallel = 's' +WHERE oid = 'avg(FIXEDDECIMAL)'::pg_catalog.regprocedure; + +UPDATE pg_aggregate SET aggcombinefn = 'fixeddecimalsmaller' +WHERE aggfnoid = 'min(FIXEDDECIMAL)'::pg_catalog.regprocedure; + +UPDATE pg_aggregate SET aggcombinefn = 'fixeddecimallarger' +WHERE aggfnoid = 'max(FIXEDDECIMAL)'::pg_catalog.regprocedure; + +UPDATE pg_aggregate SET aggcombinefn = 'fixeddecimalaggstatecombine', + aggserialfn = 'fixeddecimalaggstateserialize', + aggdeserialfn = 'fixeddecimalaggstatedeserialize' +WHERE aggfnoid = 'sum(FIXEDDECIMAL)'::pg_catalog.regprocedure; + +UPDATE pg_aggregate SET aggcombinefn = 'fixeddecimalaggstatecombine', + aggserialfn = 'fixeddecimalaggstateserialize', + aggdeserialfn = 'fixeddecimalaggstatedeserialize' +WHERE aggfnoid = 'avg(FIXEDDECIMAL)'::pg_catalog.regprocedure; diff --git a/contrib/babelfishpg_money/fixeddecimal--1.0.0_base.sql b/contrib/babelfishpg_money/fixeddecimal--1.0.0_base.sql new file mode 100755 index 00000000000..f991e9a5ac0 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--1.0.0_base.sql @@ -0,0 +1,527 @@ +------------------ +-- FIXEDDECIMAL -- +------------------ + +CREATE TYPE FIXEDDECIMAL; + +CREATE FUNCTION fixeddecimalin(cstring, oid, int4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalin' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalout(fixeddecimal) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimalout' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalrecv(internal) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalrecv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalsend(FIXEDDECIMAL) +RETURNS bytea +AS 'babelfishpg_money', 'fixeddecimalsend' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltypmodin(_cstring) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimaltypmodin' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltypmodout(INT4) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimaltypmodout' +LANGUAGE C IMMUTABLE STRICT; + + +CREATE TYPE FIXEDDECIMAL ( + INPUT = fixeddecimalin, + OUTPUT = fixeddecimalout, + RECEIVE = fixeddecimalrecv, + SEND = fixeddecimalsend, + TYPMOD_IN = fixeddecimaltypmodin, + TYPMOD_OUT = fixeddecimaltypmodout, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = plain, + CATEGORY = 'N', + PREFERRED = true, + COLLATABLE = false, + PASSEDBYVALUE -- But not always.. XXX fix that. +); + +CREATE FUNCTION fixeddecimaleq(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimaleq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalne(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimallt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimallt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalle(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalle' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalgt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalgt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalge(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalum(FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalum' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalpl(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalmi(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalmul(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaldiv(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION abs(FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalabs' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimallarger(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimallarger' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalsmaller(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalsmaller' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_hash(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_hash' +LANGUAGE C IMMUTABLE STRICT; + +-- +-- Operators. +-- + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimaleq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimalne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimallt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimalle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimalge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimalgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalmi +); + +CREATE OPERATOR - ( + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalum +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimaldiv +); + +CREATE OPERATOR CLASS fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 2 <= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 3 = (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 4 >= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 5 > (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL); + +CREATE OPERATOR CLASS fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int4 +-- + +CREATE FUNCTION fixeddecimalint4pl(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4pl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4mi(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4mi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4mul(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4mul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4div(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4div' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint4pl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4mi +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint4mul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4div +); + + +CREATE FUNCTION int4fixeddecimalpl(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimalmi(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimalmul(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimaldiv(INT4, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int4fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR + ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int4fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimalmi +); + +CREATE OPERATOR * ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int4fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimaldiv +); + +-- +-- Cross type operators with int2 +-- + +CREATE FUNCTION fixeddecimalint2pl(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2pl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2mi(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2mi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2mul(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2mul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2div(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2div' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint2pl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2mi +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint2mul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2div +); + +CREATE FUNCTION int2fixeddecimalpl(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimalmi(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimalmul(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimaldiv(INT2, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int2fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR + ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int2fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimalmi +); + +CREATE OPERATOR * ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int2fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimaldiv +); + +-- +-- Casts +-- + +CREATE FUNCTION fixeddecimal(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimal(INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimalint4' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimal(INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2(FIXEDDECIMAL) +RETURNS INT2 +AS 'babelfishpg_money', 'fixeddecimalint2' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltod(FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'fixeddecimaltod' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION dtofixeddecimal(DOUBLE PRECISION) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'dtofixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltof(FIXEDDECIMAL) +RETURNS REAL +AS 'babelfishpg_money', 'fixeddecimaltof' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION ftofixeddecimal(REAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'ftofixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric(FIXEDDECIMAL) +RETURNS NUMERIC +AS 'babelfishpg_money', 'fixeddecimal_numeric' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal(NUMERIC) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'numeric_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (FIXEDDECIMAL AS FIXEDDECIMAL) + WITH FUNCTION fixeddecimal (FIXEDDECIMAL, INT4) AS ASSIGNMENT; + +CREATE CAST (INT4 AS FIXEDDECIMAL) + WITH FUNCTION int4fixeddecimal (INT4) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT4) + WITH FUNCTION fixeddecimalint4 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (INT2 AS FIXEDDECIMAL) + WITH FUNCTION int2fixeddecimal (INT2) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT2) + WITH FUNCTION fixeddecimalint2 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (FIXEDDECIMAL AS DOUBLE PRECISION) + WITH FUNCTION fixeddecimaltod (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (DOUBLE PRECISION AS FIXEDDECIMAL) + WITH FUNCTION dtofixeddecimal (DOUBLE PRECISION) AS ASSIGNMENT; -- XXX? or Implicit? + +CREATE CAST (FIXEDDECIMAL AS REAL) + WITH FUNCTION fixeddecimaltof (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (REAL AS FIXEDDECIMAL) + WITH FUNCTION ftofixeddecimal (REAL) AS ASSIGNMENT; -- XXX or Implicit? + +CREATE CAST (FIXEDDECIMAL AS NUMERIC) + WITH FUNCTION fixeddecimal_numeric (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (NUMERIC AS FIXEDDECIMAL) + WITH FUNCTION numeric_fixeddecimal (NUMERIC) AS ASSIGNMENT; diff --git a/contrib/babelfishpg_money/fixeddecimal--1.1.0_base.sql b/contrib/babelfishpg_money/fixeddecimal--1.1.0_base.sql new file mode 100755 index 00000000000..c9ff33d5ad0 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--1.1.0_base.sql @@ -0,0 +1,1193 @@ +------------------ +-- FIXEDDECIMAL -- +------------------ + +CREATE TYPE FIXEDDECIMAL; + +CREATE FUNCTION fixeddecimalin(cstring, oid, int4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalin' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalout(fixeddecimal) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimalout' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalrecv(internal) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalrecv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalsend(FIXEDDECIMAL) +RETURNS bytea +AS 'babelfishpg_money', 'fixeddecimalsend' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltypmodin(_cstring) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimaltypmodin' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltypmodout(INT4) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimaltypmodout' +LANGUAGE C IMMUTABLE STRICT; + + +CREATE TYPE FIXEDDECIMAL ( + INPUT = fixeddecimalin, + OUTPUT = fixeddecimalout, + RECEIVE = fixeddecimalrecv, + SEND = fixeddecimalsend, + TYPMOD_IN = fixeddecimaltypmodin, + TYPMOD_OUT = fixeddecimaltypmodout, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = plain, + CATEGORY = 'N', + PREFERRED = true, + COLLATABLE = false, + PASSEDBYVALUE -- But not always.. XXX fix that. +); + +-- FIXEDDECIMAL, NUMERIC +CREATE FUNCTION fixeddecimaleq(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimaleq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalne(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimallt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimallt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalle(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalle' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalgt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalgt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalge(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalum(FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalum' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalpl(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalmi(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalmul(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaldiv(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION abs(FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalabs' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimallarger(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimallarger' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalsmaller(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalsmaller' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_hash(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_hash' +LANGUAGE C IMMUTABLE STRICT; + +-- +-- Operators. +-- + +-- FIXEDDECIMAL op FIXEDDECIMAL +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimaleq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimalne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimallt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimalle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimalge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimalgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalmi +); + +CREATE OPERATOR - ( + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalum +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimaldiv +); + +CREATE OPERATOR CLASS fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 2 <= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 3 = (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 4 >= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 5 > (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL); + +CREATE OPERATOR CLASS fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- FIXEDDECIMAL, NUMERIC +CREATE FUNCTION fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_numeric_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'numeric_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_eq(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_ne(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_lt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_le(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_gt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric_ge(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_numeric_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_numeric_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_numeric_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_numeric_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_numeric_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_numeric_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, NUMERIC), + OPERATOR 2 <= (FIXEDDECIMAL, NUMERIC), + OPERATOR 3 = (FIXEDDECIMAL, NUMERIC), + OPERATOR 4 >= (FIXEDDECIMAL, NUMERIC), + OPERATOR 5 > (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC); + +CREATE OPERATOR CLASS fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- NUMERIC, FIXEDDECIMAL +CREATE FUNCTION numeric_fixeddecimal_eq(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_ne(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_lt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_le(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_gt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal_ge(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = numeric_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = numeric_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = numeric_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = numeric_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = numeric_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = numeric_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 2 <= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 3 = (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 4 >= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 5 > (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + FUNCTION 1 numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL); + +CREATE OPERATOR CLASS numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (NUMERIC, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int4 +-- + +-- FIXEDDECIMAL, INT4 +CREATE FUNCTION fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int4_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_eq(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_ne(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_lt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_le(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_gt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int4_ge(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4pl(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4pl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4mi(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4mi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4mul(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4mul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4div(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4div' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int4_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int4_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int4_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int4_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int4_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int4_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint4pl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4mi +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint4mul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4div +); + +CREATE OPERATOR CLASS fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT4), + OPERATOR 2 <= (FIXEDDECIMAL, INT4), + OPERATOR 3 = (FIXEDDECIMAL, INT4), + OPERATOR 4 >= (FIXEDDECIMAL, INT4), + OPERATOR 5 > (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4); + +CREATE OPERATOR CLASS fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT4, FIXEDDECIMAL +CREATE FUNCTION int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int4_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_eq(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_ne(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_lt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_le(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_gt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4_fixeddecimal_ge(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimalpl(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimalmi(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimalmul(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimaldiv(INT4, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int4fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int4_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int4_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int4_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int4_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int4_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int4_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int4fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimalmi +); + +CREATE OPERATOR * ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int4fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimaldiv +); + +CREATE OPERATOR CLASS int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT4, FIXEDDECIMAL), + OPERATOR 2 <= (INT4, FIXEDDECIMAL), + OPERATOR 3 = (INT4, FIXEDDECIMAL), + OPERATOR 4 >= (INT4, FIXEDDECIMAL), + OPERATOR 5 > (INT4, FIXEDDECIMAL), + FUNCTION 1 int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL); + +CREATE OPERATOR CLASS int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT4, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int2 +-- +-- FIXEDDECIMAL, INT2 +CREATE FUNCTION fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int2_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_eq(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_ne(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_lt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_le(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_gt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_int2_ge(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2pl(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2pl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2mi(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2mi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2mul(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2mul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2div(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2div' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int2_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int2_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int2_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int2_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int2_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int2_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint2pl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2mi +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint2mul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2div +); + +CREATE OPERATOR CLASS fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT2), + OPERATOR 2 <= (FIXEDDECIMAL, INT2), + OPERATOR 3 = (FIXEDDECIMAL, INT2), + OPERATOR 4 >= (FIXEDDECIMAL, INT2), + OPERATOR 5 > (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2); + +CREATE OPERATOR CLASS fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT2, FIXEDDECIMAL +CREATE FUNCTION int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int2_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_eq(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_ne(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_lt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_le(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_gt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2_fixeddecimal_ge(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimalpl(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimalmi(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimalmul(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimaldiv(INT2, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int2fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OPERATOR = ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int2_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int2_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int2_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int2_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int2_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int2_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int2fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimalmi +); + +CREATE OPERATOR * ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int2fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimaldiv +); + +CREATE OPERATOR CLASS int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT2, FIXEDDECIMAL), + OPERATOR 2 <= (INT2, FIXEDDECIMAL), + OPERATOR 3 = (INT2, FIXEDDECIMAL), + OPERATOR 4 >= (INT2, FIXEDDECIMAL), + OPERATOR 5 > (INT2, FIXEDDECIMAL), + FUNCTION 1 int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL); + +CREATE OPERATOR CLASS int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT2, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Casts +-- + +CREATE FUNCTION fixeddecimal(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int4fixeddecimal(INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint4(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimalint4' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int2fixeddecimal(INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalint2(FIXEDDECIMAL) +RETURNS INT2 +AS 'babelfishpg_money', 'fixeddecimalint2' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltod(FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'fixeddecimaltod' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION dtofixeddecimal(DOUBLE PRECISION) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'dtofixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimaltof(FIXEDDECIMAL) +RETURNS REAL +AS 'babelfishpg_money', 'fixeddecimaltof' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION ftofixeddecimal(REAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'ftofixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimal_numeric(FIXEDDECIMAL) +RETURNS NUMERIC +AS 'babelfishpg_money', 'fixeddecimal_numeric' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION numeric_fixeddecimal(NUMERIC) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'numeric_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE CAST (FIXEDDECIMAL AS FIXEDDECIMAL) + WITH FUNCTION fixeddecimal (FIXEDDECIMAL, INT4) AS ASSIGNMENT; + +CREATE CAST (INT4 AS FIXEDDECIMAL) + WITH FUNCTION int4fixeddecimal (INT4) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT4) + WITH FUNCTION fixeddecimalint4 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (INT2 AS FIXEDDECIMAL) + WITH FUNCTION int2fixeddecimal (INT2) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT2) + WITH FUNCTION fixeddecimalint2 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (FIXEDDECIMAL AS DOUBLE PRECISION) + WITH FUNCTION fixeddecimaltod (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (DOUBLE PRECISION AS FIXEDDECIMAL) + WITH FUNCTION dtofixeddecimal (DOUBLE PRECISION) AS ASSIGNMENT; -- XXX? or Implicit? + +CREATE CAST (FIXEDDECIMAL AS REAL) + WITH FUNCTION fixeddecimaltof (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (REAL AS FIXEDDECIMAL) + WITH FUNCTION ftofixeddecimal (REAL) AS ASSIGNMENT; -- XXX or Implicit? + +CREATE CAST (FIXEDDECIMAL AS NUMERIC) + WITH FUNCTION fixeddecimal_numeric (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (NUMERIC AS FIXEDDECIMAL) + WITH FUNCTION numeric_fixeddecimal (NUMERIC) AS ASSIGNMENT; diff --git a/contrib/babelfishpg_money/fixeddecimal--1.1.0_base_parallel.sql b/contrib/babelfishpg_money/fixeddecimal--1.1.0_base_parallel.sql new file mode 100755 index 00000000000..97880d6e114 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--1.1.0_base_parallel.sql @@ -0,0 +1,1419 @@ +------------------ +-- FIXEDDECIMAL -- +------------------ + +CREATE TYPE FIXEDDECIMAL; + +CREATE FUNCTION fixeddecimalin(cstring, oid, int4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalout(fixeddecimal) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimalout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalrecv(internal) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalsend(FIXEDDECIMAL) +RETURNS bytea +AS 'babelfishpg_money', 'fixeddecimalsend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimaltypmodin(_cstring) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimaltypmodin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimaltypmodout(INT4) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimaltypmodout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + + +CREATE TYPE FIXEDDECIMAL ( + INPUT = fixeddecimalin, + OUTPUT = fixeddecimalout, + RECEIVE = fixeddecimalrecv, + SEND = fixeddecimalsend, + TYPMOD_IN = fixeddecimaltypmodin, + TYPMOD_OUT = fixeddecimaltypmodout, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = plain, + CATEGORY = 'N', + PREFERRED = true, + COLLATABLE = false, + PASSEDBYVALUE -- But not always.. XXX fix that. +); + +-- FIXEDDECIMAL, NUMERIC +CREATE FUNCTION fixeddecimaleq(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimaleq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalne(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimallt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimallt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalle(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalle' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalgt(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalgt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalge(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimalge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalum(FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalum' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalpl(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalmi(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalmul(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimaldiv(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION abs(FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalabs' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimallarger(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimallarger' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalsmaller(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalsmaller' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_hash(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_hash' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- +-- Operators. +-- + +-- FIXEDDECIMAL op FIXEDDECIMAL +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimaleq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimalne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimallt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimalle, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimalge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimalgt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalmi +); + +CREATE OPERATOR - ( + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimalum +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = fixeddecimaldiv +); + +CREATE OPERATOR CLASS fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 2 <= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 3 = (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 4 >= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 5 > (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_cmp(FIXEDDECIMAL, FIXEDDECIMAL); + +CREATE OPERATOR CLASS fixeddecimal_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- FIXEDDECIMAL, NUMERIC +CREATE FUNCTION fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_numeric_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'numeric_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_numeric_eq(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_numeric_ne(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_numeric_lt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_numeric_le(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_numeric_gt(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_numeric_ge(FIXEDDECIMAL, NUMERIC) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_numeric_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_numeric_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_numeric_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_numeric_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_numeric_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_numeric_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = NUMERIC, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_numeric_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, NUMERIC), + OPERATOR 2 <= (FIXEDDECIMAL, NUMERIC), + OPERATOR 3 = (FIXEDDECIMAL, NUMERIC), + OPERATOR 4 >= (FIXEDDECIMAL, NUMERIC), + OPERATOR 5 > (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_numeric_cmp(FIXEDDECIMAL, NUMERIC); + +CREATE OPERATOR CLASS fixeddecimal_numeric_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, NUMERIC), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- NUMERIC, FIXEDDECIMAL +CREATE FUNCTION numeric_fixeddecimal_eq(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION numeric_fixeddecimal_ne(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION numeric_fixeddecimal_lt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION numeric_fixeddecimal_le(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION numeric_fixeddecimal_gt(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION numeric_fixeddecimal_ge(NUMERIC, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'numeric_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = numeric_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = numeric_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = numeric_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = numeric_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = numeric_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = NUMERIC, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = numeric_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 2 <= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 3 = (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 4 >= (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + OPERATOR 5 > (NUMERIC, FIXEDDECIMAL) FOR SEARCH, + FUNCTION 1 numeric_fixeddecimal_cmp(NUMERIC, FIXEDDECIMAL); + +CREATE OPERATOR CLASS numeric_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (NUMERIC, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int8 +-- + +-- FIXEDDECIMAL, INT8 +CREATE FUNCTION fixeddecimal_int8_cmp(FIXEDDECIMAL, INT8) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int8_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int8_eq(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int8_ne(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int8_lt(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int8_le(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int8_gt(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int8_ge(FIXEDDECIMAL, INT8) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int8_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int8_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int8_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int8_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int8_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int8_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT8, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int8_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS fixeddecimal_int8_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT8), + OPERATOR 2 <= (FIXEDDECIMAL, INT8), + OPERATOR 3 = (FIXEDDECIMAL, INT8), + OPERATOR 4 >= (FIXEDDECIMAL, INT8), + OPERATOR 5 > (FIXEDDECIMAL, INT8), + FUNCTION 1 fixeddecimal_int8_cmp(FIXEDDECIMAL, INT8); + +CREATE OPERATOR CLASS fixeddecimal_int8_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT8), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT8, FIXEDDECIMAL +CREATE FUNCTION int8_fixeddecimal_cmp(INT8, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int8_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int8_fixeddecimal_eq(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int8_fixeddecimal_ne(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int8_fixeddecimal_lt(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int8_fixeddecimal_le(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int8_fixeddecimal_gt(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int8_fixeddecimal_ge(INT8, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int8_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int8_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int8_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int8_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int8_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int8_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = INT8, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int8_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR CLASS int8_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT8, FIXEDDECIMAL), + OPERATOR 2 <= (INT8, FIXEDDECIMAL), + OPERATOR 3 = (INT8, FIXEDDECIMAL), + OPERATOR 4 >= (INT8, FIXEDDECIMAL), + OPERATOR 5 > (INT8, FIXEDDECIMAL), + FUNCTION 1 int8_fixeddecimal_cmp(INT8, FIXEDDECIMAL); + +CREATE OPERATOR CLASS int8_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT8, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int4 +-- + +-- FIXEDDECIMAL, INT4 +CREATE FUNCTION fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int4_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int4_eq(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int4_ne(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int4_lt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int4_le(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int4_gt(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int4_ge(FIXEDDECIMAL, INT4) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int4_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint4pl(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint4mi(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint4mul(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint4div(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint4div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int4_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int4_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int4_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int4_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int4_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int4_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint4pl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4mi +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint4mul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4div +); + +CREATE OPERATOR CLASS fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT4), + OPERATOR 2 <= (FIXEDDECIMAL, INT4), + OPERATOR 3 = (FIXEDDECIMAL, INT4), + OPERATOR 4 >= (FIXEDDECIMAL, INT4), + OPERATOR 5 > (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_int4_cmp(FIXEDDECIMAL, INT4); + +CREATE OPERATOR CLASS fixeddecimal_int4_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT4), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT4, FIXEDDECIMAL +CREATE FUNCTION int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int4_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4_fixeddecimal_eq(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4_fixeddecimal_ne(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4_fixeddecimal_lt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4_fixeddecimal_le(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4_fixeddecimal_gt(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4_fixeddecimal_ge(INT4, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int4_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4fixeddecimalpl(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4fixeddecimalmi(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4fixeddecimalmul(INT4, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4fixeddecimaldiv(INT4, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int4fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int4_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int4_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int4_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int4_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int4_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int4_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int4fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimalmi +); + +CREATE OPERATOR * ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int4fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = INT4, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int4fixeddecimaldiv +); + +CREATE OPERATOR CLASS int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT4, FIXEDDECIMAL), + OPERATOR 2 <= (INT4, FIXEDDECIMAL), + OPERATOR 3 = (INT4, FIXEDDECIMAL), + OPERATOR 4 >= (INT4, FIXEDDECIMAL), + OPERATOR 5 > (INT4, FIXEDDECIMAL), + FUNCTION 1 int4_fixeddecimal_cmp(INT4, FIXEDDECIMAL); + +CREATE OPERATOR CLASS int4_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT4, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Cross type operators with int2 +-- +-- FIXEDDECIMAL, INT2 +CREATE FUNCTION fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimal_int2_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int2_eq(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int2_ne(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int2_lt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int2_le(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int2_gt(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_int2_ge(FIXEDDECIMAL, INT2) +RETURNS bool +AS 'babelfishpg_money', 'fixeddecimal_int2_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint2pl(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint2mi(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint2mul(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint2div(FIXEDDECIMAL, INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimalint2div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = fixeddecimal_int2_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = fixeddecimal_int2_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = fixeddecimal_int2_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = fixeddecimal_int2_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = fixeddecimal_int2_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = fixeddecimal_int2_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint2pl +); + +CREATE OPERATOR - ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2mi +); + +CREATE OPERATOR * ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint2mul +); + +CREATE OPERATOR / ( + LEFTARG = FIXEDDECIMAL, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2div +); + +CREATE OPERATOR CLASS fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (FIXEDDECIMAL, INT2), + OPERATOR 2 <= (FIXEDDECIMAL, INT2), + OPERATOR 3 = (FIXEDDECIMAL, INT2), + OPERATOR 4 >= (FIXEDDECIMAL, INT2), + OPERATOR 5 > (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_int2_cmp(FIXEDDECIMAL, INT2); + +CREATE OPERATOR CLASS fixeddecimal_int2_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (FIXEDDECIMAL, INT2), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- INT2, FIXEDDECIMAL +CREATE FUNCTION int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'int2_fixeddecimal_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2_fixeddecimal_eq(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2_fixeddecimal_ne(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ne' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2_fixeddecimal_lt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2_fixeddecimal_le(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_le' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2_fixeddecimal_gt(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2_fixeddecimal_ge(INT2, FIXEDDECIMAL) +RETURNS bool +AS 'babelfishpg_money', 'int2_fixeddecimal_ge' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2fixeddecimalpl(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2fixeddecimalmi(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2fixeddecimalmul(INT2, FIXEDDECIMAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2fixeddecimaldiv(INT2, FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int2fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR = ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = =, + NEGATOR = <>, + PROCEDURE = int2_fixeddecimal_eq, + RESTRICT = eqsel, + JOIN = eqjoinsel, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = =, + COMMUTATOR = <>, + PROCEDURE = int2_fixeddecimal_ne, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >=, + COMMUTATOR = >, + PROCEDURE = int2_fixeddecimal_lt, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = >, + COMMUTATOR = >=, + PROCEDURE = int2_fixeddecimal_le, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <, + COMMUTATOR = <=, + PROCEDURE = int2_fixeddecimal_ge, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + NEGATOR = <=, + COMMUTATOR = <, + PROCEDURE = int2_fixeddecimal_gt, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR + ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = +, + PROCEDURE = int2fixeddecimalpl +); + +CREATE OPERATOR - ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimalmi +); + +CREATE OPERATOR * ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + COMMUTATOR = *, + PROCEDURE = int2fixeddecimalmul +); + +CREATE OPERATOR / ( + LEFTARG = INT2, + RIGHTARG = FIXEDDECIMAL, + PROCEDURE = int2fixeddecimaldiv +); + +CREATE OPERATOR CLASS int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING btree AS + OPERATOR 1 < (INT2, FIXEDDECIMAL), + OPERATOR 2 <= (INT2, FIXEDDECIMAL), + OPERATOR 3 = (INT2, FIXEDDECIMAL), + OPERATOR 4 >= (INT2, FIXEDDECIMAL), + OPERATOR 5 > (INT2, FIXEDDECIMAL), + FUNCTION 1 int2_fixeddecimal_cmp(INT2, FIXEDDECIMAL); + +CREATE OPERATOR CLASS int2_fixeddecimal_ops +FOR TYPE FIXEDDECIMAL USING hash AS + OPERATOR 1 = (INT2, FIXEDDECIMAL), + FUNCTION 1 fixeddecimal_hash(FIXEDDECIMAL); + +-- +-- Casts +-- + +CREATE FUNCTION fixeddecimal(FIXEDDECIMAL, INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int4fixeddecimal(INT4) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int4fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint4(FIXEDDECIMAL) +RETURNS INT4 +AS 'babelfishpg_money', 'fixeddecimalint4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION int2fixeddecimal(INT2) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'int2fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalint2(FIXEDDECIMAL) +RETURNS INT2 +AS 'babelfishpg_money', 'fixeddecimalint2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimaltod(FIXEDDECIMAL) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'fixeddecimaltod' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION dtofixeddecimal(DOUBLE PRECISION) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'dtofixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimaltof(FIXEDDECIMAL) +RETURNS REAL +AS 'babelfishpg_money', 'fixeddecimaltof' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION ftofixeddecimal(REAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'ftofixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_numeric(FIXEDDECIMAL) +RETURNS NUMERIC +AS 'babelfishpg_money', 'fixeddecimal_numeric' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION numeric_fixeddecimal(NUMERIC) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'numeric_fixeddecimal' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (FIXEDDECIMAL AS FIXEDDECIMAL) + WITH FUNCTION fixeddecimal (FIXEDDECIMAL, INT4) AS ASSIGNMENT; + +CREATE CAST (INT4 AS FIXEDDECIMAL) + WITH FUNCTION int4fixeddecimal (INT4) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT4) + WITH FUNCTION fixeddecimalint4 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (INT2 AS FIXEDDECIMAL) + WITH FUNCTION int2fixeddecimal (INT2) AS IMPLICIT; + +CREATE CAST (FIXEDDECIMAL AS INT2) + WITH FUNCTION fixeddecimalint2 (FIXEDDECIMAL) AS ASSIGNMENT; + +CREATE CAST (FIXEDDECIMAL AS DOUBLE PRECISION) + WITH FUNCTION fixeddecimaltod (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (DOUBLE PRECISION AS FIXEDDECIMAL) + WITH FUNCTION dtofixeddecimal (DOUBLE PRECISION) AS ASSIGNMENT; -- XXX? or Implicit? + +CREATE CAST (FIXEDDECIMAL AS REAL) + WITH FUNCTION fixeddecimaltof (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (REAL AS FIXEDDECIMAL) + WITH FUNCTION ftofixeddecimal (REAL) AS ASSIGNMENT; -- XXX or Implicit? + +CREATE CAST (FIXEDDECIMAL AS NUMERIC) + WITH FUNCTION fixeddecimal_numeric (FIXEDDECIMAL) AS IMPLICIT; + +CREATE CAST (NUMERIC AS FIXEDDECIMAL) + WITH FUNCTION numeric_fixeddecimal (NUMERIC) AS ASSIGNMENT; diff --git a/contrib/babelfishpg_money/fixeddecimal--aggs.sql b/contrib/babelfishpg_money/fixeddecimal--aggs.sql new file mode 100644 index 00000000000..baf02bf3428 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--aggs.sql @@ -0,0 +1,44 @@ + +-- Aggregate Support + + +CREATE FUNCTION fixeddecimal_avg_accum(INTERNAL, FIXEDDECIMAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimal_avg_accum' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION fixeddecimal_sum(INTERNAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal_sum' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION fixeddecimal_avg(INTERNAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal_avg' +LANGUAGE C IMMUTABLE; + +CREATE AGGREGATE min(FIXEDDECIMAL) ( + SFUNC = fixeddecimalsmaller, + STYPE = FIXEDDECIMAL, + SORTOP = < +); + +CREATE AGGREGATE max(FIXEDDECIMAL) ( + SFUNC = fixeddecimallarger, + STYPE = FIXEDDECIMAL, + SORTOP = > +); + +CREATE AGGREGATE sum(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + FINALFUNC = fixeddecimal_sum, + STYPE = INTERNAL +); + +CREATE AGGREGATE avg(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + FINALFUNC = fixeddecimal_avg, + STYPE = INTERNAL +); + + diff --git a/contrib/babelfishpg_money/fixeddecimal--brin.sql b/contrib/babelfishpg_money/fixeddecimal--brin.sql new file mode 100644 index 00000000000..ce98da4be7d --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--brin.sql @@ -0,0 +1,12 @@ +CREATE OPERATOR CLASS fixeddecimal_minmax_ops +DEFAULT FOR TYPE FIXEDDECIMAL USING brin AS + OPERATOR 1 < (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 2 <= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 3 = (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 4 >= (FIXEDDECIMAL, FIXEDDECIMAL), + OPERATOR 5 > (FIXEDDECIMAL, FIXEDDECIMAL), + FUNCTION 1 brin_minmax_opcinfo(INTERNAL), + FUNCTION 2 brin_minmax_add_value(INTERNAL, INTERNAL, INTERNAL, INTERNAL), + FUNCTION 3 brin_minmax_consistent(INTERNAL, INTERNAL, INTERNAL), + FUNCTION 4 brin_minmax_union(INTERNAL, INTERNAL, INTERNAL); + diff --git a/contrib/babelfishpg_money/fixeddecimal--parallelaggs.sql b/contrib/babelfishpg_money/fixeddecimal--parallelaggs.sql new file mode 100644 index 00000000000..4e3b51e520a --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--parallelaggs.sql @@ -0,0 +1,70 @@ + +-- Aggregate Support + +CREATE FUNCTION fixeddecimalaggstatecombine(INTERNAL, INTERNAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimalaggstatecombine' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalaggstateserialize(INTERNAL) +RETURNS BYTEA +AS 'babelfishpg_money', 'fixeddecimalaggstateserialize' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION fixeddecimalaggstatedeserialize(BYTEA, INTERNAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimalaggstatedeserialize' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_avg_accum(INTERNAL, FIXEDDECIMAL) +RETURNS INTERNAL +AS 'babelfishpg_money', 'fixeddecimal_avg_accum' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_sum(INTERNAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal_sum' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE FUNCTION fixeddecimal_avg(INTERNAL) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal_avg' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE AGGREGATE min(FIXEDDECIMAL) ( + SFUNC = fixeddecimalsmaller, + STYPE = FIXEDDECIMAL, + SORTOP = <, + COMBINEFUNC = fixeddecimalsmaller, + PARALLEL = SAFE +); + +CREATE AGGREGATE max(FIXEDDECIMAL) ( + SFUNC = fixeddecimallarger, + STYPE = FIXEDDECIMAL, + SORTOP = >, + COMBINEFUNC = fixeddecimallarger, + PARALLEL = SAFE +); + +CREATE AGGREGATE sum(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + FINALFUNC = fixeddecimal_sum, + STYPE = INTERNAL, + COMBINEFUNC = fixeddecimalaggstatecombine, + SERIALFUNC = fixeddecimalaggstateserialize, + DESERIALFUNC = fixeddecimalaggstatedeserialize, + PARALLEL = SAFE +); + +CREATE AGGREGATE avg(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + FINALFUNC = fixeddecimal_avg, + STYPE = INTERNAL, + COMBINEFUNC = fixeddecimalaggstatecombine, + SERIALFUNC = fixeddecimalaggstateserialize, + DESERIALFUNC = fixeddecimalaggstatedeserialize, + PARALLEL = SAFE +); + + diff --git a/contrib/babelfishpg_money/fixeddecimal--xlaggs.sql b/contrib/babelfishpg_money/fixeddecimal--xlaggs.sql new file mode 100644 index 00000000000..0e3d0f87291 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal--xlaggs.sql @@ -0,0 +1,57 @@ + +-- Aggregate Support + + +CREATE FUNCTION fixeddecimalaggstatecombine(FIXEDDECIMALAGGSTATE, FIXEDDECIMALAGGSTATE) +RETURNS FIXEDDECIMALAGGSTATE +AS 'babelfishpg_money', 'fixeddecimalaggstatecombine' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION fixeddecimal_avg_accum(FIXEDDECIMALAGGSTATE, FIXEDDECIMAL) +RETURNS FIXEDDECIMALAGGSTATE +AS 'babelfishpg_money', 'fixeddecimal_avg_accum' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION fixeddecimal_sum(FIXEDDECIMALAGGSTATE) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal_sum' +LANGUAGE C IMMUTABLE; + +CREATE FUNCTION fixeddecimal_avg(FIXEDDECIMALAGGSTATE) +RETURNS FIXEDDECIMAL +AS 'babelfishpg_money', 'fixeddecimal_avg' +LANGUAGE C IMMUTABLE; + +CREATE AGGREGATE min(FIXEDDECIMAL) ( + SFUNC = fixeddecimalsmaller, + CFUNC = fixeddecimalsmaller, + CTYPE = FIXEDDECIMAL, + STYPE = FIXEDDECIMAL, + SORTOP = < +); + +CREATE AGGREGATE max(FIXEDDECIMAL) ( + SFUNC = fixeddecimallarger, + CFUNC = fixeddecimallarger, + CTYPE = FIXEDDECIMAL, + STYPE = FIXEDDECIMAL, + SORTOP = > +); + +CREATE AGGREGATE sum(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + CFUNC = fixeddecimalaggstatecombine, + CTYPE = FIXEDDECIMALAGGSTATE, + FINALFUNC = fixeddecimal_sum, + STYPE = FIXEDDECIMALAGGSTATE +); + +CREATE AGGREGATE avg(FIXEDDECIMAL) ( + SFUNC = fixeddecimal_avg_accum, + CFUNC = fixeddecimalaggstatecombine, + CTYPE = FIXEDDECIMALAGGSTATE, + FINALFUNC = fixeddecimal_avg, + STYPE = FIXEDDECIMALAGGSTATE +); + + diff --git a/contrib/babelfishpg_money/fixeddecimal.c b/contrib/babelfishpg_money/fixeddecimal.c new file mode 100755 index 00000000000..e835b3e0145 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal.c @@ -0,0 +1,3032 @@ +/*------------------------------------------------------------------------- + * + * fixeddecimal.c + * Fixed Decimal numeric type extension + * + * Copyright (c) 2015, PostgreSQL Global Development Group + * + * IDENTIFICATION + * fixeddecimal.c + * + * The research leading to these results has received funding from the European + * Union’s Seventh Framework Programme (FP7/2007-2015) under grant agreement + * n° 318633 + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include +#include +#include + +#include "funcapi.h" +#include "libpq/pqformat.h" +#include "access/hash.h" +#include "utils/array.h" +#include "utils/builtins.h" + +#include "utils/int8.h" + +#include "utils/numeric.h" + +/* + * The scale which the number is actually stored. + * For example: 100 will allow 2 decimal places of precision + * This must always be a '1' followed by a number of '0's. + */ +#define FIXEDDECIMAL_MULTIPLIER 10000LL + +/* + * Number of decimal places to store. + * This number should be the number of decimal digits that it takes to + * represent FIXEDDECIMAL_MULTIPLIER - 1 + */ +#define FIXEDDECIMAL_SCALE 4 + +/* Sanity checks */ +#if FIXEDDECIMAL_SCALE == 0 +#error "FIXEDDECIMAL_SCALE cannot be zero. Just use a BIGINT if that's what you really want" +#endif + +#if FIXEDDECIMAL_SCALE > 19 +#error "FIXEDDECIMAL_SCALE cannot be greater than 19" +#endif + +/* + * This is bounded by the maximum and minimum values of int64. + * 9223372036854775807 is 19 decimal digits long, but we we can only represent + * this number / FIXEDDECIMAL_MULTIPLIER, so we must subtract + * FIXEDDECIMAL_SCALE + */ +#define FIXEDDECIMAL_MAX_PRECISION 19 - FIXEDDECIMAL_SCALE + +/* Define this if your compiler has _builtin_add_overflow() */ +/* #define HAVE_BUILTIN_OVERFLOW */ + +#ifndef HAVE_BUILTIN_OVERFLOW +#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0)) +#endif /* HAVE_BUILTIN_OVERFLOW */ + +#define FIXEDDECIMAL_MAX (INT64_MAX/FIXEDDECIMAL_MULTIPLIER) +#define FIXEDDECIMAL_MIN (INT64_MIN/FIXEDDECIMAL_MULTIPLIER) + +/* Compiler must have a working 128 int type */ +typedef __int128 int128; + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +PG_FUNCTION_INFO_V1(fixeddecimalin); +PG_FUNCTION_INFO_V1(fixeddecimaltypmodin); +PG_FUNCTION_INFO_V1(fixeddecimaltypmodout); +PG_FUNCTION_INFO_V1(fixeddecimalout); +PG_FUNCTION_INFO_V1(fixeddecimalrecv); +PG_FUNCTION_INFO_V1(fixeddecimalsend); + +PG_FUNCTION_INFO_V1(fixeddecimaleq); +PG_FUNCTION_INFO_V1(fixeddecimalne); +PG_FUNCTION_INFO_V1(fixeddecimallt); +PG_FUNCTION_INFO_V1(fixeddecimalgt); +PG_FUNCTION_INFO_V1(fixeddecimalle); +PG_FUNCTION_INFO_V1(fixeddecimalge); +PG_FUNCTION_INFO_V1(fixeddecimal_cmp); + +PG_FUNCTION_INFO_V1(fixeddecimal_int2_eq); +PG_FUNCTION_INFO_V1(fixeddecimal_int2_ne); +PG_FUNCTION_INFO_V1(fixeddecimal_int2_lt); +PG_FUNCTION_INFO_V1(fixeddecimal_int2_gt); +PG_FUNCTION_INFO_V1(fixeddecimal_int2_le); +PG_FUNCTION_INFO_V1(fixeddecimal_int2_ge); +PG_FUNCTION_INFO_V1(fixeddecimal_int2_cmp); + +PG_FUNCTION_INFO_V1(int2_fixeddecimal_eq); +PG_FUNCTION_INFO_V1(int2_fixeddecimal_ne); +PG_FUNCTION_INFO_V1(int2_fixeddecimal_lt); +PG_FUNCTION_INFO_V1(int2_fixeddecimal_gt); +PG_FUNCTION_INFO_V1(int2_fixeddecimal_le); +PG_FUNCTION_INFO_V1(int2_fixeddecimal_ge); +PG_FUNCTION_INFO_V1(int2_fixeddecimal_cmp); + +PG_FUNCTION_INFO_V1(fixeddecimal_int4_eq); +PG_FUNCTION_INFO_V1(fixeddecimal_int4_ne); +PG_FUNCTION_INFO_V1(fixeddecimal_int4_lt); +PG_FUNCTION_INFO_V1(fixeddecimal_int4_gt); +PG_FUNCTION_INFO_V1(fixeddecimal_int4_le); +PG_FUNCTION_INFO_V1(fixeddecimal_int4_ge); +PG_FUNCTION_INFO_V1(fixeddecimal_int4_cmp); + +PG_FUNCTION_INFO_V1(int4_fixeddecimal_eq); +PG_FUNCTION_INFO_V1(int4_fixeddecimal_ne); +PG_FUNCTION_INFO_V1(int4_fixeddecimal_lt); +PG_FUNCTION_INFO_V1(int4_fixeddecimal_gt); +PG_FUNCTION_INFO_V1(int4_fixeddecimal_le); +PG_FUNCTION_INFO_V1(int4_fixeddecimal_ge); +PG_FUNCTION_INFO_V1(int4_fixeddecimal_cmp); + + +PG_FUNCTION_INFO_V1(fixeddecimal_int8_eq); +PG_FUNCTION_INFO_V1(fixeddecimal_int8_ne); +PG_FUNCTION_INFO_V1(fixeddecimal_int8_lt); +PG_FUNCTION_INFO_V1(fixeddecimal_int8_gt); +PG_FUNCTION_INFO_V1(fixeddecimal_int8_le); +PG_FUNCTION_INFO_V1(fixeddecimal_int8_ge); +PG_FUNCTION_INFO_V1(fixeddecimal_int8_cmp); + +PG_FUNCTION_INFO_V1(int8_fixeddecimal_eq); +PG_FUNCTION_INFO_V1(int8_fixeddecimal_ne); +PG_FUNCTION_INFO_V1(int8_fixeddecimal_lt); +PG_FUNCTION_INFO_V1(int8_fixeddecimal_gt); +PG_FUNCTION_INFO_V1(int8_fixeddecimal_le); +PG_FUNCTION_INFO_V1(int8_fixeddecimal_ge); +PG_FUNCTION_INFO_V1(int8_fixeddecimal_cmp); + +PG_FUNCTION_INFO_V1(fixeddecimal_numeric_cmp); +PG_FUNCTION_INFO_V1(fixeddecimal_numeric_eq); +PG_FUNCTION_INFO_V1(fixeddecimal_numeric_ne); +PG_FUNCTION_INFO_V1(fixeddecimal_numeric_lt); +PG_FUNCTION_INFO_V1(fixeddecimal_numeric_gt); +PG_FUNCTION_INFO_V1(fixeddecimal_numeric_le); +PG_FUNCTION_INFO_V1(fixeddecimal_numeric_ge); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal_cmp); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal_eq); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal_ne); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal_lt); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal_gt); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal_le); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal_ge); +PG_FUNCTION_INFO_V1(fixeddecimal_hash); +PG_FUNCTION_INFO_V1(fixeddecimalum); +PG_FUNCTION_INFO_V1(fixeddecimalup); +PG_FUNCTION_INFO_V1(fixeddecimalpl); +PG_FUNCTION_INFO_V1(fixeddecimalmi); +PG_FUNCTION_INFO_V1(fixeddecimalmul); +PG_FUNCTION_INFO_V1(fixeddecimaldiv); +PG_FUNCTION_INFO_V1(fixeddecimalabs); +PG_FUNCTION_INFO_V1(fixeddecimallarger); +PG_FUNCTION_INFO_V1(fixeddecimalsmaller); +PG_FUNCTION_INFO_V1(fixeddecimalint8pl); +PG_FUNCTION_INFO_V1(fixeddecimalint8mi); +PG_FUNCTION_INFO_V1(fixeddecimalint8mul); +PG_FUNCTION_INFO_V1(fixeddecimalint8div); +PG_FUNCTION_INFO_V1(int8fixeddecimalpl); +PG_FUNCTION_INFO_V1(int8fixeddecimalmi); +PG_FUNCTION_INFO_V1(int8fixeddecimalmul); +PG_FUNCTION_INFO_V1(int8fixeddecimaldiv); +PG_FUNCTION_INFO_V1(fixeddecimalint4pl); +PG_FUNCTION_INFO_V1(fixeddecimalint4mi); +PG_FUNCTION_INFO_V1(fixeddecimalint4mul); +PG_FUNCTION_INFO_V1(fixeddecimalint4div); +PG_FUNCTION_INFO_V1(fixeddecimal); +PG_FUNCTION_INFO_V1(int4fixeddecimalpl); +PG_FUNCTION_INFO_V1(int4fixeddecimalmi); +PG_FUNCTION_INFO_V1(int4fixeddecimalmul); +PG_FUNCTION_INFO_V1(int4fixeddecimaldiv); +PG_FUNCTION_INFO_V1(fixeddecimalint2pl); +PG_FUNCTION_INFO_V1(fixeddecimalint2mi); +PG_FUNCTION_INFO_V1(fixeddecimalint2mul); +PG_FUNCTION_INFO_V1(fixeddecimalint2div); +PG_FUNCTION_INFO_V1(int2fixeddecimalpl); +PG_FUNCTION_INFO_V1(int2fixeddecimalmi); +PG_FUNCTION_INFO_V1(int2fixeddecimalmul); +PG_FUNCTION_INFO_V1(int2fixeddecimaldiv); +PG_FUNCTION_INFO_V1(int8fixeddecimal); +PG_FUNCTION_INFO_V1(fixeddecimalint8); +PG_FUNCTION_INFO_V1(int4fixeddecimal); +PG_FUNCTION_INFO_V1(fixeddecimalint4); +PG_FUNCTION_INFO_V1(int2fixeddecimal); +PG_FUNCTION_INFO_V1(fixeddecimalint2); +PG_FUNCTION_INFO_V1(fixeddecimaltod); +PG_FUNCTION_INFO_V1(dtofixeddecimal); +PG_FUNCTION_INFO_V1(fixeddecimaltof); +PG_FUNCTION_INFO_V1(ftofixeddecimal); +PG_FUNCTION_INFO_V1(numeric_fixeddecimal); +PG_FUNCTION_INFO_V1(fixeddecimal_numeric); +PG_FUNCTION_INFO_V1(fixeddecimal_avg_accum); +PG_FUNCTION_INFO_V1(fixeddecimal_avg); +PG_FUNCTION_INFO_V1(fixeddecimal_sum); +PG_FUNCTION_INFO_V1(fixeddecimalaggstatecombine); +PG_FUNCTION_INFO_V1(fixeddecimalaggstateserialize); +PG_FUNCTION_INFO_V1(fixeddecimalaggstatedeserialize); + +PG_FUNCTION_INFO_V1(fixeddecimalaggstatein); +PG_FUNCTION_INFO_V1(fixeddecimalaggstateout); +PG_FUNCTION_INFO_V1(fixeddecimalaggstatesend); +PG_FUNCTION_INFO_V1(fixeddecimalaggstaterecv); + +PG_FUNCTION_INFO_V1(char_to_fixeddecimal); + +/* Aggregate Internal State */ +typedef struct FixedDecimalAggState +{ + MemoryContext agg_context; /* context we're calculating in */ + int64 N; /* count of processed numbers */ + int64 sumX; /* sum of processed numbers */ +} FixedDecimalAggState; + +static char *pg_int64tostr(char *str, int64 value); +static char *pg_int64tostr_zeropad(char *str, int64 value, int64 padding); +static void apply_typmod(int64 value, int32 typmod, int precision, int scale); +static int64 scanfixeddecimal(const char *str, int *precision, int *scale); +static FixedDecimalAggState *makeFixedDecimalAggState(FunctionCallInfo fcinfo); +static void fixeddecimal_accum(FixedDecimalAggState *state, int64 newval); + +/*********************************************************************** + ** + ** Routines for fixeddecimal + ** + ***********************************************************************/ + +/*---------------------------------------------------------- + * Formatting and conversion routines. + *---------------------------------------------------------*/ + + /* + * pg_int64tostr + * Converts 'value' into a decimal string representation of the number. + * + * Caller must ensure that 'str' points to enough memory to hold the result + * (at least 21 bytes, counting a leading sign and trailing NUL). + * Return value is a pointer to the new NUL terminated end of string. + */ +static char * +pg_int64tostr(char *str, int64 value) +{ + char *start; + char *end; + + /* + * Handle negative numbers in a special way. We can't just append a '-' + * prefix and reverse the sign as on two's complement machines negative + * numbers can be 1 further from 0 than positive numbers, we do it this way + * so we properly handle the smallest possible value. + */ + if (value < 0) + { + *str++ = '-'; + + /* mark the position we must reverse the string from. */ + start = str; + + /* Compute the result string backwards. */ + do + { + int64 remainder; + int64 oldval = value; + + value /= 10; + remainder = oldval - value * 10; + *str++ = '0' + -remainder; + } while (value != 0); + } + else + { + /* mark the position we must reverse the string from. */ + start = str; + do + { + int64 remainder; + int64 oldval = value; + + value /= 10; + remainder = oldval - value * 10; + *str++ = '0' + remainder; + } while (value != 0); + } + + /* Add trailing NUL byte, and back up 'str' to the last character. */ + end = str; + *str-- = '\0'; + + /* Reverse string. */ + while (start < str) + { + char swap = *start; + *start++ = *str; + *str-- = swap; + } + return end; +} + +/* + * pg_int64tostr_zeropad + * Converts 'value' into a decimal string representation of the number. + * 'padding' specifies the minimum width of the number. Any extra space + * is filled up by prefixing the number with zeros. The return value is a + * pointer to the NUL terminated end of the string. + * + * Note: Callers should ensure that 'padding' is above zero. + * Note: This function is optimized for the case where the number is not too + * big to fit inside of the specified padding. + * Note: Caller must ensure that 'str' points to enough memory to hold the + result (at least 21 bytes, counting a leading sign and trailing NUL, + or padding + 1 bytes, whichever is larger). + */ +static char * +pg_int64tostr_zeropad(char *str, int64 value, int64 padding) +{ + char *start = str; + char *end = &str[padding]; + int64 num = value; + + Assert(padding > 0); + + /* + * Handle negative numbers in a special way. We can't just append a '-' + * prefix and reverse the sign as on two's complement machines negative + * numbers can be 1 further from 0 than positive numbers, we do it this way + * so we properly handle the smallest possible value. + */ + if (num < 0) + { + *start++ = '-'; + padding--; + + /* + * Build the number starting at the end. Here remainder will be a + * negative number, we must reverse this sign on this before adding + * '0' in order to get the correct ASCII digit + */ + while (padding--) + { + int64 remainder; + int64 oldval = num; + + num /= 10; + remainder = oldval - num * 10; + start[padding] = '0' + -remainder; + } + } + else + { + /* build the number starting at the end */ + while (padding--) + { + int64 remainder; + int64 oldval = num; + + num /= 10; + remainder = oldval - num * 10; + start[padding] = '0' + remainder; + } + } + + /* + * If padding was not high enough to fit this number then num won't have + * been divided down to zero. We'd better have another go, this time we + * know there won't be any zero padding required so we can just enlist the + * help of pg_int64tostr() + */ + if (num != 0) + return pg_int64tostr(str, value); + + *end = '\0'; + return end; +} + +/* + * fixeddecimal2str + * Prints the fixeddecimal 'val' to buffer as a string. + * Returns a pointer to the end of the written string. + */ +static char * +fixeddecimal2str(int64 val, char *buffer) +{ + char *ptr = buffer; + int64 integralpart = val / FIXEDDECIMAL_MULTIPLIER; + int64 fractionalpart = val % FIXEDDECIMAL_MULTIPLIER; + + if (val < 0) + { + fractionalpart = -fractionalpart; + + /* + * Handle special case for negative numbers where the intergral part + * is zero. pg_int64tostr() won't prefix with "-0" in this case, so + * we'll do it manually + */ + if (integralpart == 0) + *ptr++ = '-'; + } + ptr = pg_int64tostr(ptr, integralpart); + *ptr++ = '.'; + ptr = pg_int64tostr_zeropad(ptr, fractionalpart, FIXEDDECIMAL_SCALE); + return ptr; +} + +/* + * scanfixeddecimal --- try to parse a string into a fixeddecimal. + */ +static int64 +scanfixeddecimal(const char *str, int *precision, int *scale) +{ + const char *ptr = str; + int64 integralpart = 0; + int64 fractionalpart = 0; + bool negative; + int vprecision = 0; + int vscale = 0; + bool has_seen_sign = false; + + /* + * Do our own scan, rather than relying on sscanf which might be broken + * for long long. + */ + + /* skip leading spaces */ + while (isspace((unsigned char) *ptr)) + ptr++; + + /* handle sign */ + if (*ptr == '-') + { + has_seen_sign = true; + negative = true; + ptr++; + } + else + { + negative = false; + + if (*ptr == '+') + { + has_seen_sign = true; + ptr++; + } + } + + /* skip leading spaces */ + while (isspace((unsigned char) *ptr)) + ptr++; + + /* skip currency symbol bytes */ + while (!isdigit((unsigned char) *ptr) && + (unsigned int) *ptr != '.' && + (unsigned int) *ptr != '-' && + (unsigned int) *ptr != '+' && + (unsigned int) *ptr != ' ' && + (unsigned int) *ptr != '\0') + { + /* Current workaround for BABEL-704 - this will accept multiple currency symbols + * until BABEL-704 is fixed */ + if ((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z')) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("invalid characters found: cannot cast value \"%s\" to money", + str))); + } + ptr++; + } + + /* skip leading spaces */ + while (isspace((unsigned char) *ptr)) + ptr++; + + /* Handle sign again. This is needed so that a sign after the currency symbol + * can be recognized */ + if (*ptr == '-') + { + if (has_seen_sign) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("invalid characters found: cannot cast value \"%s\" to money", + str))); + } + negative = true; + ptr++; + + /* skip leading spaces */ + while (isspace((unsigned char) *ptr)) + ptr++; + + while (isdigit((unsigned char) *ptr)) + { + int64 tmp = integralpart * 10 - (*ptr++ - '0'); + + vprecision++; + if ((tmp / 10) != integralpart) /* underflow? */ + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + } + integralpart = tmp; + /* skip thousand separator */ + if(*ptr == ',') + ptr++; + } + } + else + { + if (!has_seen_sign) + negative = false; + + if (*ptr == '+') + { + if (has_seen_sign) + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("invalid characters found: cannot cast value \"%s\" to money", + str))); + } + ptr++; + } + + /* skip leading spaces */ + while (isspace((unsigned char) *ptr)) + ptr++; + + while (isdigit((unsigned char) *ptr)) + { + int64 tmp; + + if (!negative) + tmp = integralpart * 10 + (*ptr++ - '0'); + else + tmp = integralpart * 10 - (*ptr++ - '0'); + + vprecision++; + if ((tmp / 10) != integralpart) /* overflow? */ + { + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + } + integralpart = tmp; + /* skip thousand separator */ + if(*ptr == ',') + ptr++; + } + } + /* process the part after the decimal point */ + if (*ptr == '.') + { + int64 multiplier = FIXEDDECIMAL_MULTIPLIER; + ptr++; + + while (isdigit((unsigned char) *ptr) && multiplier > 1) + { + multiplier /= 10; + fractionalpart += (*ptr++ - '0') * multiplier; + vscale++; + } + + /* + * Eat into any excess precision digits. + * XXX These are ignored, should we error instead? + */ + while (isdigit((unsigned char) *ptr)) + ptr++, vscale++; + } + + /* consume any remaining space chars */ + while (isspace((unsigned char) *ptr)) + ptr++; + + if (*ptr != '\0') + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", str))); + + *precision = vprecision; + *scale = vscale; + + if (negative) + { + + int64 value; + +#ifdef HAVE_BUILTIN_OVERFLOW + int64 multiplier = FIXEDDECIMAL_MULTIPLIER; + if (__builtin_mul_overflow(integralpart, multiplier, &value)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + + if (__builtin_sub_overflow(value, fractionalpart, &value)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + return value; + +#else + value = integralpart * FIXEDDECIMAL_MULTIPLIER; + if (value != 0 && (!SAMESIGN(value, integralpart) || + !SAMESIGN(value - fractionalpart, value) || + !SAMESIGN(value - fractionalpart, value))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + + return value - fractionalpart; +#endif /* HAVE_BUILTIN_OVERFLOW */ + + } + else + { + int64 value; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_mul_overflow(integralpart, FIXEDDECIMAL_MULTIPLIER, &value)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + + if (__builtin_add_overflow(value, fractionalpart, &value)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + return value; +#else + value = integralpart * FIXEDDECIMAL_MULTIPLIER; + if (value != 0 && (!SAMESIGN(value, integralpart) || + !SAMESIGN(value - fractionalpart, value) || + !SAMESIGN(value + fractionalpart, value))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value \"%s\" is out of range for type fixeddecimal", + str))); + + return value + fractionalpart; +#endif /* HAVE_BUILTIN_OVERFLOW */ + + } +} + +/* + * fixeddecimalin() + */ +Datum +fixeddecimalin(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + int32 typmod = PG_GETARG_INT32(2); + int precision; + int scale; + int64 result = scanfixeddecimal(str, &precision, &scale); + + apply_typmod(result, typmod, precision, scale); + + PG_RETURN_INT64(result); +} + +static void +apply_typmod(int64 value, int32 typmod, int precision, int scale) +{ + int precisionlimit; + int scalelimit; + int maxdigits; + + /* Do nothing if we have a default typmod (-1) */ + if (typmod < (int32) (VARHDRSZ)) + return; + + typmod -= VARHDRSZ; + precisionlimit = (typmod >> 16) & 0xffff; + scalelimit = typmod & 0xffff; + maxdigits = precisionlimit - scalelimit; + + if (scale > scalelimit) + + if (scale != FIXEDDECIMAL_SCALE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("FIXEDDECIMAL scale must be %d", + FIXEDDECIMAL_SCALE))); + + if (precision > maxdigits) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("FIXEDDECIMAL field overflow"), + errdetail("A field with precision %d, scale %d must round to an absolute value less than %s%d.", + precision, scale, + /* Display 10^0 as 1 */ + maxdigits ? "10^" : "", + maxdigits ? maxdigits : 1 + ))); + +} + +Datum +fixeddecimaltypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + int32 *tl; + int n; + int32 typmod; + + tl = ArrayGetIntegerTypmods(ta, &n); + + if (n == 2) + { + /* + * we demand that the precision is at least the scale, since later we + * enforce that the scale is exactly FIXEDDECIMAL_SCALE + */ + if (tl[0] < FIXEDDECIMAL_SCALE || tl[0] > FIXEDDECIMAL_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("FIXEDDECIMAL precision %d must be between %d and %d", + tl[0], FIXEDDECIMAL_SCALE, FIXEDDECIMAL_MAX_PRECISION))); + + if (tl[1] != FIXEDDECIMAL_SCALE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("FIXEDDECIMAL scale must be %d", + FIXEDDECIMAL_SCALE))); + + typmod = ((tl[0] << 16) | tl[1]) + VARHDRSZ; + } + else if (n == 1) + { + if (tl[0] < FIXEDDECIMAL_SCALE || tl[0] > FIXEDDECIMAL_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("FIXEDDECIMAL precision %d must be between %d and %d", + tl[0], FIXEDDECIMAL_SCALE, FIXEDDECIMAL_MAX_PRECISION))); + + /* scale defaults to FIXEDDECIMAL_SCALE */ + typmod = ((tl[0] << 16) | FIXEDDECIMAL_SCALE) + VARHDRSZ; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid FIXEDDECIMAL type modifier"))); + typmod = 0; /* keep compiler quiet */ + } + + PG_RETURN_INT32(typmod); +} + +Datum +fixeddecimaltypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + char *res = (char *) palloc(64); + + if (typmod >= 0) + snprintf(res, 64, "(%d,%d)", + ((typmod - VARHDRSZ) >> 16) & 0xffff, + (typmod - VARHDRSZ) & 0xffff); + else + *res = '\0'; + + PG_RETURN_CSTRING(res); +} + + +/* + * fixeddecimalout() + */ +Datum +fixeddecimalout(PG_FUNCTION_ARGS) +{ + int64 val = PG_GETARG_INT64(0); + char buf[MAXINT8LEN + 1]; + char *end = fixeddecimal2str(val, buf); + PG_RETURN_CSTRING(pnstrdup(buf, end - buf)); +} + +/* + * fixeddecimalrecv - converts external binary format to int8 + */ +Datum +fixeddecimalrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + + PG_RETURN_INT64(pq_getmsgint64(buf)); +} + +/* + * fixeddecimalsend - converts int8 to binary format + */ +Datum +fixeddecimalsend(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + StringInfoData buf; + + pq_begintypsend(&buf); + pq_sendint64(&buf, arg1); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + + +/*---------------------------------------------------------- + * Relational operators for fixeddecimals, including cross-data-type comparisons. + *---------------------------------------------------------*/ + +Datum +fixeddecimaleq(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 == val2); +} + +Datum +fixeddecimalne(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 != val2); +} + +Datum +fixeddecimallt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 < val2); +} + +Datum +fixeddecimalgt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 > val2); +} + +Datum +fixeddecimalle(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 <= val2); +} + +Datum +fixeddecimalge(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 >= val2); +} + +Datum +fixeddecimal_cmp(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 == val2) + PG_RETURN_INT32(0); + else if (val1 < val2) + PG_RETURN_INT32(-1); + else + PG_RETURN_INT32(1); +} + +/* int2, fixeddecimal */ +Datum +fixeddecimal_int2_eq(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 == val2); +} + +Datum +fixeddecimal_int2_ne(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 != val2); +} + +Datum +fixeddecimal_int2_lt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 < val2); +} + +Datum +fixeddecimal_int2_gt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 > val2); +} + +Datum +fixeddecimal_int2_le(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 <= val2); +} + +Datum +fixeddecimal_int2_ge(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 >= val2); +} + +Datum +fixeddecimal_int2_cmp(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + + if (val1 == val2) + PG_RETURN_INT32(0); + else if (val1 < val2) + PG_RETURN_INT32(-1); + else + PG_RETURN_INT32(1); +} + +Datum +int2_fixeddecimal_eq(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 == val2); +} + +Datum +int2_fixeddecimal_ne(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 != val2); +} + +Datum +int2_fixeddecimal_lt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 < val2); +} + +Datum +int2_fixeddecimal_gt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 > val2); +} + +Datum +int2_fixeddecimal_le(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 <= val2); +} + +Datum +int2_fixeddecimal_ge(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 >= val2); +} + +Datum +int2_fixeddecimal_cmp(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + if (val1 == val2) + PG_RETURN_INT32(0); + else if (val1 < val2) + PG_RETURN_INT32(-1); + else + PG_RETURN_INT32(1); +} + +/* fixeddecimal, int4 */ +Datum +fixeddecimal_int4_eq(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 == val2); +} + +Datum +fixeddecimal_int4_ne(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 != val2); +} + +Datum +fixeddecimal_int4_lt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 < val2); +} + +Datum +fixeddecimal_int4_gt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 > val2); +} + +Datum +fixeddecimal_int4_le(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 <= val2); +} + +Datum +fixeddecimal_int4_ge(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_BOOL(val1 >= val2); +} + +Datum +fixeddecimal_int4_cmp(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + + if (val1 == val2) + PG_RETURN_INT32(0); + else if (val1 < val2) + PG_RETURN_INT32(-1); + else + PG_RETURN_INT32(1); +} + +Datum +int4_fixeddecimal_eq(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 == val2); +} + +Datum +int4_fixeddecimal_ne(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 != val2); +} + +Datum +int4_fixeddecimal_lt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 < val2); +} + +Datum +int4_fixeddecimal_gt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 > val2); +} + +Datum +int4_fixeddecimal_le(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 <= val2); +} + +Datum +int4_fixeddecimal_ge(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + PG_RETURN_BOOL(val1 >= val2); +} + +Datum +int4_fixeddecimal_cmp(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 val2 = PG_GETARG_INT64(1); + + if (val1 == val2) + PG_RETURN_INT32(0); + else if (val1 < val2) + PG_RETURN_INT32(-1); + else + PG_RETURN_INT32(1); +} + +/* fixeddecimal, int8 */ +Datum +fixeddecimal_int8_eq(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val2 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(false); + else if (val2 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(false); + + val2 = val2 * FIXEDDECIMAL_MULTIPLIER; + return val1 == val2; +} + +Datum +fixeddecimal_int8_ne(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val2 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(true); + else if (val2 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(true); + + val2 = val2 * FIXEDDECIMAL_MULTIPLIER; + return val1 != val2; +} + +Datum +fixeddecimal_int8_lt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val2 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(true); + else if (val2 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(false); + + val2 = val2 * FIXEDDECIMAL_MULTIPLIER; + return val1 < val2; +} + +Datum +fixeddecimal_int8_gt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val2 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(false); + else if (val2 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(true); + + val2 = val2 * FIXEDDECIMAL_MULTIPLIER; + return val1 > val2; +} + +Datum +fixeddecimal_int8_le(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val2 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(true); + else if (val2 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(false); + + val2 = val2 * FIXEDDECIMAL_MULTIPLIER; + return val1 <= val2; +} + +Datum +fixeddecimal_int8_ge(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val2 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(false); + else if (val2 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(true); + + val2 = val2 * FIXEDDECIMAL_MULTIPLIER; + return val1 >= val2; +} + +Datum +fixeddecimal_int8_cmp(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val2 > FIXEDDECIMAL_MAX) + PG_RETURN_INT32(-1); + else if (val2 < FIXEDDECIMAL_MIN) + PG_RETURN_INT32(1); + + val2 = val2 * FIXEDDECIMAL_MULTIPLIER; + if (val1 == val2) + PG_RETURN_INT32(0); + else if (val1 < val2) + PG_RETURN_INT32(-1); + else + PG_RETURN_INT32(1); +} + +Datum +int8_fixeddecimal_eq(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(false); + else if (val1 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(false); + + val1 = val1 * FIXEDDECIMAL_MULTIPLIER; + return val1 == val2; +} + +Datum +int8_fixeddecimal_ne(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(true); + else if (val1 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(true); + + val1 = val1 * FIXEDDECIMAL_MULTIPLIER; + return val1 != val2; +} + +Datum +int8_fixeddecimal_lt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(false); + else if (val1 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(true); + + val1 = val1 * FIXEDDECIMAL_MULTIPLIER; + return val1 < val2; +} + +Datum +int8_fixeddecimal_gt(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(true); + else if (val1 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(false); + + val1 = val1 * FIXEDDECIMAL_MULTIPLIER; + return val1 > val2; +} + +Datum +int8_fixeddecimal_le(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(false); + else if (val1 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(true); + + val1 = val1 * FIXEDDECIMAL_MULTIPLIER; + return val1 <= val2; +} + +Datum +int8_fixeddecimal_ge(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 > FIXEDDECIMAL_MAX) + PG_RETURN_BOOL(true); + else if (val1 < FIXEDDECIMAL_MIN) + PG_RETURN_BOOL(false); + + val1 = val1 * FIXEDDECIMAL_MULTIPLIER; + return val1 >= val2; +} + +Datum +int8_fixeddecimal_cmp(PG_FUNCTION_ARGS) +{ + int64 val1 = PG_GETARG_INT64(0); + int64 val2 = PG_GETARG_INT64(1); + + if (val1 > FIXEDDECIMAL_MAX) + PG_RETURN_INT32(1); + else if (val1 < FIXEDDECIMAL_MIN) + PG_RETURN_INT32(-1); + + val1 = val1 * FIXEDDECIMAL_MULTIPLIER; + if (val1 == val2) + PG_RETURN_INT32(0); + else if (val1 < val2) + PG_RETURN_INT32(-1); + else + PG_RETURN_INT32(1); +} + +Datum +fixeddecimal_numeric_cmp(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + Datum val2 = PG_GETARG_DATUM(1); + Datum val1; + + val1 = DirectFunctionCall1(fixeddecimal_numeric, Int64GetDatum(arg1)); + + PG_RETURN_INT32(DirectFunctionCall2(numeric_cmp, val1, val2)); +} + +Datum +fixeddecimal_numeric_eq(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(fixeddecimal_numeric_cmp, val1, + val2)); + + PG_RETURN_BOOL(result == 0); +} + +Datum +fixeddecimal_numeric_ne(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(fixeddecimal_numeric_cmp, val1, + val2)); + + PG_RETURN_BOOL(result != 0); +} + +Datum +fixeddecimal_numeric_lt(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(fixeddecimal_numeric_cmp, val1, + val2)); + + PG_RETURN_BOOL(result < 0); +} + +Datum +fixeddecimal_numeric_gt(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(fixeddecimal_numeric_cmp, val1, + val2)); + + PG_RETURN_BOOL(result > 0); +} + +Datum +fixeddecimal_numeric_le(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(fixeddecimal_numeric_cmp, val1, + val2)); + + PG_RETURN_BOOL(result <= 0); +} + +Datum +fixeddecimal_numeric_ge(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(fixeddecimal_numeric_cmp, val1, + val2)); + + PG_RETURN_BOOL(result >= 0); +} + +Datum +numeric_fixeddecimal_cmp(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + int64 arg2 = PG_GETARG_INT64(1); + Datum val2; + + val2 = DirectFunctionCall1(fixeddecimal_numeric, Int64GetDatum(arg2)); + + PG_RETURN_INT32(DirectFunctionCall2(numeric_cmp, val1, val2)); +} + +Datum +numeric_fixeddecimal_eq(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(numeric_fixeddecimal_cmp, val1, + val2)); + + PG_RETURN_BOOL(result == 0); +} + +Datum +numeric_fixeddecimal_ne(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(numeric_fixeddecimal_cmp, val1, + val2)); + + PG_RETURN_BOOL(result != 0); +} + +Datum +numeric_fixeddecimal_lt(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(numeric_fixeddecimal_cmp, val1, + val2)); + + PG_RETURN_BOOL(result < 0); +} + +Datum +numeric_fixeddecimal_gt(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(numeric_fixeddecimal_cmp, val1, + val2)); + + PG_RETURN_BOOL(result > 0); +} + +Datum +numeric_fixeddecimal_le(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(numeric_fixeddecimal_cmp, val1, + val2)); + + PG_RETURN_BOOL(result <= 0); +} + +Datum +numeric_fixeddecimal_ge(PG_FUNCTION_ARGS) +{ + Datum val1 = PG_GETARG_DATUM(0); + Datum val2 = PG_GETARG_DATUM(1); + int32 result; + + result = DatumGetInt32(DirectFunctionCall2(numeric_fixeddecimal_cmp, val1, + val2)); + + PG_RETURN_BOOL(result >= 0); +} + +Datum +fixeddecimal_hash(PG_FUNCTION_ARGS) +{ + int64 val = PG_GETARG_INT64(0); + Datum result; + + result = hash_any((unsigned char *) &val, sizeof(int64)); + PG_RETURN_DATUM(result); +} + +/*---------------------------------------------------------- + * Arithmetic operators on fixeddecimal. + *---------------------------------------------------------*/ + +Datum +fixeddecimalum(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + int64 zero = 0; + + if (__builtin_sub_overflow(zero, arg, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = -arg; + /* overflow check (needed for INT64_MIN) */ + if (arg != 0 && SAMESIGN(result, arg)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalup(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0); + + PG_RETURN_INT64(arg); +} + +Datum +fixeddecimalpl(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(arg1, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 + arg2; + + /* + * Overflow check. If the inputs are of different signs then their sum + * cannot overflow. If the inputs are of the same sign, their sum had + * better be that sign too. + */ + if (SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalmi(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_sub_overflow(arg1, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 - arg2; + + /* + * Overflow check. If the inputs are of the same sign then their + * difference cannot overflow. If they are of different signs then the + * result should be of the same sign as the first input. + */ + if (!SAMESIGN(arg1, arg2) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalmul(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int128 result; + + /* We need to promote this to 128bit as we may overflow int64 here. + * Remember that arg2 is the number multiplied by + * FIXEDDECIMAL_MULTIPLIER, we must divide the result by this to get + * the correct result. + */ + result = (int128) arg1 * arg2 / FIXEDDECIMAL_MULTIPLIER; + + if (result != ((int64) result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); + + PG_RETURN_INT64((int64) result); +} + +Datum +fixeddecimaldiv(PG_FUNCTION_ARGS) +{ + int64 dividend = PG_GETARG_INT64(0); + int64 divisor = PG_GETARG_INT64(1); + int128 result; + + if (divisor == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + if (divisor == 0) + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + + /* + * this can't overflow, but we can end up with a number that's too big for + * int64 + */ + result = (int128) dividend * FIXEDDECIMAL_MULTIPLIER / divisor; + + if (result != ((int64) result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); + + PG_RETURN_INT64((int64) result); +} + +/* fixeddecimalabs() + * Absolute value + */ +Datum +fixeddecimalabs(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 result; + + result = (arg1 < 0) ? -arg1 : arg1; + /* overflow check (needed for INT64_MIN) */ + if (result < 0) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); + PG_RETURN_INT64(result); +} + + +Datum +fixeddecimallarger(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + + result = ((arg1 > arg2) ? arg1 : arg2); + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalsmaller(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + + result = ((arg1 < arg2) ? arg1 : arg2); + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint8pl(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 adder = PG_GETARG_INT64(1) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(arg1, adder, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 + adder; + + /* + * Overflow check. If the inputs are of different signs then their sum + * cannot overflow. If the inputs are of the same sign, their sum had + * better be that sign too. + */ + if (SAMESIGN(arg1, adder) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint8mi(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 subtractor = PG_GETARG_INT64(1) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_sub_overflow(arg1, subtractor, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 - subtractor; + + /* + * Overflow check. If the inputs are of the same sign then their + * difference cannot overflow. If they are of different signs then the + * result should be of the same sign as the first input. + */ + if (!SAMESIGN(arg1, subtractor) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint8mul(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_mul_overflow(arg1, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 * arg2; + + /* + * Overflow check. We basically check to see if result / arg1 gives arg2 + * again. There is one case where this fails: arg1 = 0 (which cannot + * overflow). + * + * Since the division is likely much more expensive than the actual + * multiplication, we'd like to skip it where possible. The best bang for + * the buck seems to be to check whether both inputs are in the int32 + * range; if so, no overflow is possible. + */ + if (arg1 != (int64) ((int32) arg1) && + result / arg1 != arg2) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint8div(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* + * INT64_MIN / -1 is problematic, since the result can't be represented on + * a two's-complement machine. Some machines produce INT64_MIN, some + * produce zero, some throw an exception. We can dodge the problem by + * recognizing that division by -1 is the same as negation. + */ + if (arg2 == -1) + { +#ifdef HAVE_BUILTIN_OVERFLOW + int64 zero = 0; + if (__builtin_sub_overflow(zero, arg1, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = -arg1; + /* overflow check (needed for INT64_MIN) */ + if (arg1 != 0 && SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); + } + + /* No overflow is possible */ + + result = arg1 / arg2; + + PG_RETURN_INT64(result); +} + +Datum +int8fixeddecimalpl(PG_FUNCTION_ARGS) +{ + int64 adder = PG_GETARG_INT64(0) * FIXEDDECIMAL_MULTIPLIER; + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(adder, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = adder + arg2; + + /* + * Overflow check. If the inputs are of different signs then their sum + * cannot overflow. If the inputs are of the same sign, their sum had + * better be that sign too. + */ + if (SAMESIGN(adder, arg2) && !SAMESIGN(result, adder)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int8fixeddecimalmi(PG_FUNCTION_ARGS) +{ + int64 subtractor = PG_GETARG_INT64(0) * FIXEDDECIMAL_MULTIPLIER; + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_sub_overflow(subtractor, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = subtractor - arg2; + + /* + * Overflow check. If the inputs are of the same sign then their + * difference cannot overflow. If they are of different signs then the + * result should be of the same sign as the first input. + */ + if (!SAMESIGN(subtractor, arg2) && !SAMESIGN(result, subtractor)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int8fixeddecimalmul(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_mul_overflow(arg1, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 * arg2; + + /* + * Overflow check. We basically check to see if result / arg2 gives arg1 + * again. There is one case where this fails: arg2 = 0 (which cannot + * overflow). + * + * Since the division is likely much more expensive than the actual + * multiplication, we'd like to skip it where possible. The best bang for + * the buck seems to be to check whether both inputs are in the int32 + * range; if so, no overflow is possible. + */ + if (arg2 != (int64) ((int32) arg2) && + result / arg2 != arg1) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int8fixeddecimaldiv(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + float8 arg2 = (float8) PG_GETARG_INT64(1) / (float8) FIXEDDECIMAL_MULTIPLIER; + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* No overflow is possible */ + PG_RETURN_FLOAT8((float8) arg1 / arg2); +} + +Datum +fixeddecimalint4pl(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 adder = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(arg1, adder, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 + adder; + + /* + * Overflow check. If the inputs are of different signs then their sum + * cannot overflow. If the inputs are of the same sign, their sum had + * better be that sign too. + */ + if (SAMESIGN(arg1, adder) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint4mi(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 subtractor = PG_GETARG_INT32(1) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_sub_overflow(arg1, subtractor, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 - subtractor; + + /* + * Overflow check. If the inputs are of the same sign then their + * difference cannot overflow. If they are of different signs then the + * result should be of the same sign as the first input. + */ + if (!SAMESIGN(arg1, subtractor) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint4mul(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int32 arg2 = PG_GETARG_INT32(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_mul_overflow(arg1, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 * arg2; + + /* + * Overflow check. We basically check to see if result / arg1 gives arg2 + * again. There is one case where this fails: arg1 = 0 (which cannot + * overflow). + * + * Since the division is likely much more expensive than the actual + * multiplication, we'd like to skip it where possible. The best bang for + * the buck seems to be to check whether both inputs are in the int32 + * range; if so, no overflow is possible. + */ + if (arg1 != (int64) ((int32) arg1) && + result / arg1 != arg2) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint4div(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int32 arg2 = PG_GETARG_INT32(1); + int64 result; + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* + * INT64_MIN / -1 is problematic, since the result can't be represented on + * a two's-complement machine. Some machines produce INT64_MIN, some + * produce zero, some throw an exception. We can dodge the problem by + * recognizing that division by -1 is the same as negation. + */ + if (arg2 == -1) + { +#ifdef HAVE_BUILTIN_OVERFLOW + int64 zero = 0; + if (__builtin_sub_overflow(zero, arg1, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = -arg1; + /* overflow check (needed for INT64_MIN) */ + if (arg1 != 0 && SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); + } + + /* No overflow is possible */ + + result = arg1 / arg2; + + PG_RETURN_INT64(result); +} + +Datum +int4fixeddecimalpl(PG_FUNCTION_ARGS) +{ + int64 adder = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(adder, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = adder + arg2; + + /* + * Overflow check. If the inputs are of different signs then their sum + * cannot overflow. If the inputs are of the same sign, their sum had + * better be that sign too. + */ + if (SAMESIGN(adder, arg2) && !SAMESIGN(result, adder)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int4fixeddecimalmi(PG_FUNCTION_ARGS) +{ + int64 subtractor = PG_GETARG_INT32(0) * FIXEDDECIMAL_MULTIPLIER; + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_sub_overflow(subtractor, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = subtractor - arg2; + + /* + * Overflow check. If the inputs are of the same sign then their + * difference cannot overflow. If they are of different signs then the + * result should be of the same sign as the first input. + */ + if (!SAMESIGN(subtractor, arg2) && !SAMESIGN(result, subtractor)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int4fixeddecimalmul(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_mul_overflow(arg1, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 * arg2; + + /* + * Overflow check. We basically check to see if result / arg2 gives arg1 + * again. There is one case where this fails: arg2 = 0 (which cannot + * overflow). + * + * Since the division is likely much more expensive than the actual + * multiplication, we'd like to skip it where possible. The best bang for + * the buck seems to be to check whether both inputs are in the int32 + * range; if so, no overflow is possible. + */ + if (arg2 != (int64) ((int32) arg2) && + result / arg2 != arg1) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int4fixeddecimaldiv(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + float8 arg2 = (float8) PG_GETARG_INT64(1) / (float8) FIXEDDECIMAL_MULTIPLIER; + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* No overflow is possible */ + PG_RETURN_FLOAT8((float8) arg1 / arg2); +} + +Datum +fixeddecimalint2pl(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 adder = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(arg1, adder, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 + adder; + + /* + * Overflow check. If the inputs are of different signs then their sum + * cannot overflow. If the inputs are of the same sign, their sum had + * better be that sign too. + */ + if (SAMESIGN(arg1, adder) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint2mi(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int64 subtractor = PG_GETARG_INT16(1) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_sub_overflow(arg1, subtractor, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 - subtractor; + + /* + * Overflow check. If the inputs are of the same sign then their + * difference cannot overflow. If they are of different signs then the + * result should be of the same sign as the first input. + */ + if (!SAMESIGN(arg1, subtractor) && !SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint2mul(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int16 arg2 = PG_GETARG_INT16(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_mul_overflow(arg1, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 * arg2; + + /* + * Overflow check. We basically check to see if result / arg1 gives arg2 + * again. There is one case where this fails: arg1 = 0 (which cannot + * overflow). + * + * Since the division is likely much more expensive than the actual + * multiplication, we'd like to skip it where possible. The best bang for + * the buck seems to be to check whether both inputs are in the int32 + * range; if so, no overflow is possible. + */ + if (arg1 != (int64) ((int32) arg1) && + result / arg1 != arg2) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimalint2div(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + int16 arg2 = PG_GETARG_INT16(1); + int64 result; + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* + * INT64_MIN / -1 is problematic, since the result can't be represented on + * a two's-complement machine. Some machines produce INT64_MIN, some + * produce zero, some throw an exception. We can dodge the problem by + * recognizing that division by -1 is the same as negation. + */ + if (arg2 == -1) + { +#ifdef HAVE_BUILTIN_OVERFLOW + int64 zero = 0; + if (__builtin_sub_overflow(zero, arg1, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = -arg1; + /* overflow check (needed for INT64_MIN) */ + if (arg1 != 0 && SAMESIGN(result, arg1)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); + } + + /* No overflow is possible */ + result = arg1 / arg2; + + PG_RETURN_INT64(result); +} + +Datum +int2fixeddecimalpl(PG_FUNCTION_ARGS) +{ + int64 adder = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(adder, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = adder + arg2; + + /* + * Overflow check. If the inputs are of different signs then their sum + * cannot overflow. If the inputs are of the same sign, their sum had + * better be that sign too. + */ + if (SAMESIGN(adder, arg2) && !SAMESIGN(result, adder)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int2fixeddecimalmi(PG_FUNCTION_ARGS) +{ + int64 subtractor = PG_GETARG_INT16(0) * FIXEDDECIMAL_MULTIPLIER; + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_sub_overflow(subtractor, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = subtractor - arg2; + + /* + * Overflow check. If the inputs are of the same sign then their + * difference cannot overflow. If they are of different signs then the + * result should be of the same sign as the first input. + */ + if (!SAMESIGN(subtractor, arg2) && !SAMESIGN(result, subtractor)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int2fixeddecimalmul(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT16(0); + int64 arg2 = PG_GETARG_INT64(1); + int64 result; + +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_mul_overflow(multiplier, arg2, &result)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#else + result = arg1 * arg2; + + /* + * Overflow check. We basically check to see if result / arg2 gives + * arg1 again. There is one case where this fails: arg2 = 0 (which + * cannot overflow). + * + * Since the division is likely much more expensive than the actual + * multiplication, we'd like to skip it where possible. The best bang for + * the buck seems to be to check whether both inputs are in the int32 + * range; if so, no overflow is possible. + */ + if (arg2 != (int64) ((int32) arg2) && + result / arg2 != arg1) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); +#endif /* HAVE_BUILTIN_OVERFLOW */ + + PG_RETURN_INT64(result); +} + +Datum +int2fixeddecimaldiv(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + float8 arg2 = PG_GETARG_INT64(1) / (float8) FIXEDDECIMAL_MULTIPLIER; + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* No overflow is possible */ + PG_RETURN_FLOAT8((float8) arg1 / arg2); +} + +/*---------------------------------------------------------- + * Conversion operators. + *---------------------------------------------------------*/ + +/* + * fixeddecimal serves as casting function for fixeddecimal to fixeddecimal. + * The only serves to generate an error if the fixedecimal is too big for the + * specified typmod. + */ +Datum +fixeddecimal(PG_FUNCTION_ARGS) +{ + int64 num = PG_GETARG_INT64(0); + int32 typmod = PG_GETARG_INT32(1); + Datum result; + + /* no need to check typmod if it's -1 */ + if (typmod != -1) + { + result = DirectFunctionCall1(fixeddecimalout, num); + result = DirectFunctionCall3(fixeddecimalin, result, 0, typmod); + } + PG_RETURN_INT64(num); +} + +Datum +int8fixeddecimal(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0); + + PG_RETURN_INT64(arg * FIXEDDECIMAL_MULTIPLIER); +} + +Datum +fixeddecimalint8(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0) / FIXEDDECIMAL_MULTIPLIER; + + if ((int64) arg != arg) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT64((int64) arg); +} + +Datum +int4fixeddecimal(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT32(0); + + PG_RETURN_INT64(arg * FIXEDDECIMAL_MULTIPLIER); +} + +Datum +fixeddecimalint4(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0) / FIXEDDECIMAL_MULTIPLIER; + + if ((int32) arg != arg) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("integer out of range"))); + + PG_RETURN_INT32((int32) arg); +} + +Datum +int2fixeddecimal(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT16(0); + + PG_RETURN_INT64(arg * FIXEDDECIMAL_MULTIPLIER); +} + +Datum +fixeddecimalint2(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0) / FIXEDDECIMAL_MULTIPLIER; + + if ((int16) arg != arg) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("smallint out of range"))); + + PG_RETURN_INT16((int16) arg); +} + +Datum +fixeddecimaltod(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0); + float8 result; + + result = (float8) arg / FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_FLOAT8(result); +} + +/* dtofixeddecimal() + * Convert float8 to fixeddecimal + */ +Datum +dtofixeddecimal(PG_FUNCTION_ARGS) +{ + float8 arg = PG_GETARG_FLOAT8(0) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + + /* Round arg to nearest integer (but it's still in float form) */ + arg = rint(arg); + + /* + * Does it fit in an int64? Avoid assuming that we have handy constants + * defined for the range boundaries, instead test for overflow by + * reverse-conversion. + */ + result = (int64) arg; + + if ((float8) result != arg) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); + + PG_RETURN_INT64(result); +} + +Datum +fixeddecimaltof(PG_FUNCTION_ARGS) +{ + int64 arg = PG_GETARG_INT64(0); + float4 result; + + result = (float4) arg / FIXEDDECIMAL_MULTIPLIER; + + PG_RETURN_FLOAT4(result); +} + +/* ftofixeddecimal() + * Convert float4 to fixeddecimal. + */ +Datum +ftofixeddecimal(PG_FUNCTION_ARGS) +{ + float4 arg = PG_GETARG_FLOAT4(0) * FIXEDDECIMAL_MULTIPLIER; + int64 result; + float8 darg; + + /* Round arg to nearest integer (but it's still in float form) */ + darg = rint(arg); + + /* + * Does it fit in an int64? Avoid assuming that we have handy constants + * defined for the range boundaries, instead test for overflow by + * reverse-conversion. + */ + result = (int64) darg; + + if ((float8) result != darg) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); + + PG_RETURN_INT64(result); +} + + +Datum +fixeddecimal_numeric(PG_FUNCTION_ARGS) +{ + int64 num = PG_GETARG_INT64(0); + char *tmp; + Datum result; + + tmp = DatumGetCString(DirectFunctionCall1(fixeddecimalout, + Int64GetDatum(num))); + + result = DirectFunctionCall3(numeric_in, CStringGetDatum(tmp), 0, -1); + + pfree(tmp); + + PG_RETURN_DATUM(result); +} + +Datum +numeric_fixeddecimal(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + char *tmp; + Datum result; + + if (numeric_is_nan(num)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert NaN to fixeddecimal"))); + + tmp = DatumGetCString(DirectFunctionCall1(numeric_out, + NumericGetDatum(num))); + + result = DirectFunctionCall3(fixeddecimalin, CStringGetDatum(tmp), 0, -1); + + pfree(tmp); + + PG_RETURN_DATUM(result); +} + + +/* Aggregate Support */ + +static FixedDecimalAggState * +makeFixedDecimalAggState(FunctionCallInfo fcinfo) +{ + FixedDecimalAggState *state; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + old_context = MemoryContextSwitchTo(agg_context); + + state = (FixedDecimalAggState *) palloc0(sizeof(FixedDecimalAggState)); + state->agg_context = agg_context; + + MemoryContextSwitchTo(old_context); + + return state; +} + +/* + * Accumulate a new input value for fixeddecimal aggregate functions. + */ +static void +fixeddecimal_accum(FixedDecimalAggState *state, int64 newval) +{ +#ifdef HAVE_BUILTIN_OVERFLOW + if (__builtin_add_overflow(state->sumX, newval, &state->sumX)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); + state->N++; +#else + if (state->N++ > 0) + { + int64 result = state->sumX + newval; + + if (SAMESIGN(state->sumX, newval) && !SAMESIGN(result, state->sumX)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("fixeddecimal out of range"))); + + state->sumX = result; + } + else + state->sumX = newval; +#endif /* HAVE_BUILTIN_OVERFLOW */ +} + +Datum +fixeddecimal_avg_accum(PG_FUNCTION_ARGS) +{ + FixedDecimalAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (FixedDecimalAggState *) PG_GETARG_POINTER(0); + + /* Create the state data on the first call */ + if (state == NULL) + state = makeFixedDecimalAggState(fcinfo); + + if (!PG_ARGISNULL(1)) + fixeddecimal_accum(state, PG_GETARG_INT64(1)); + + PG_RETURN_POINTER(state); +} + +Datum +fixeddecimal_avg(PG_FUNCTION_ARGS) +{ + FixedDecimalAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (FixedDecimalAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || state->N == 0) + PG_RETURN_NULL(); + + PG_RETURN_INT64(state->sumX / state->N); +} + + +Datum +fixeddecimal_sum(PG_FUNCTION_ARGS) +{ + FixedDecimalAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (FixedDecimalAggState *) PG_GETARG_POINTER(0); + + /* If there were no non-null inputs, return NULL */ + if (state == NULL || state->N == 0) + PG_RETURN_NULL(); + + PG_RETURN_INT64(state->sumX); +} + + +/* + * Input / Output / Send / Receive functions for aggrgate states + * Currently for XL only + */ + +Datum +fixeddecimalaggstatein(PG_FUNCTION_ARGS) +{ + char *str = pstrdup(PG_GETARG_CSTRING(0)); + FixedDecimalAggState *state; + char *token; + + state = (FixedDecimalAggState *) palloc(sizeof(FixedDecimalAggState)); + + token = strtok(str, ":"); + state->sumX = DatumGetInt64(DirectFunctionCall3(fixeddecimalin, CStringGetDatum(token), 0, -1)); + token = strtok(NULL, ":"); + state->N = DatumGetInt64(DirectFunctionCall1(int8in, CStringGetDatum(token))); + pfree(str); + + PG_RETURN_POINTER(state); +} + + +/* + * fixeddecimalaggstateout() + */ +Datum +fixeddecimalaggstateout(PG_FUNCTION_ARGS) +{ + FixedDecimalAggState *state = (FixedDecimalAggState *) PG_GETARG_POINTER(0); + char buf[MAXINT8LEN + 1 + MAXINT8LEN + 1]; + char *p; + + p = fixeddecimal2str(state->sumX, buf); + *p++ = ':'; + p = pg_int64tostr(p, state->N); + + PG_RETURN_CSTRING(pnstrdup(buf, p - buf)); +} + +/* + * fixeddecimalaggstaterecv + */ +Datum +fixeddecimalaggstaterecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + FixedDecimalAggState *state; + state = (FixedDecimalAggState *) palloc(sizeof(FixedDecimalAggState)); + + state->sumX = pq_getmsgint(buf, sizeof(int64)); + state->N = pq_getmsgint(buf, sizeof(int64)); + + PG_RETURN_POINTER(state); +} + +/* + * fixeddecimalaggstatesend + */ +Datum +fixeddecimalaggstatesend(PG_FUNCTION_ARGS) +{ + FixedDecimalAggState *state = (FixedDecimalAggState *) PG_GETARG_POINTER(0); + StringInfoData buf; + + pq_begintypsend(&buf); + + pq_sendint(&buf, state->sumX, sizeof (int64)); + pq_sendint(&buf, state->N, sizeof (int64)); + + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + +Datum +fixeddecimalaggstateserialize(PG_FUNCTION_ARGS) +{ + FixedDecimalAggState *state; + StringInfoData buf; + bytea *result; + + /* Ensure we disallow calling when not in aggregate context */ + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + state = (FixedDecimalAggState *) PG_GETARG_POINTER(0); + + pq_begintypsend(&buf); + + /* N */ + pq_sendint64(&buf, state->N); + + /* sumX */ + pq_sendint64(&buf, state->sumX); + + result = pq_endtypsend(&buf); + + PG_RETURN_BYTEA_P(result); +} + +Datum +fixeddecimalaggstatedeserialize(PG_FUNCTION_ARGS) +{ + bytea *sstate; + FixedDecimalAggState *result; + StringInfoData buf; + + if (!AggCheckCallContext(fcinfo, NULL)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + sstate = PG_GETARG_BYTEA_P(0); + + /* + * Copy the bytea into a StringInfo so that we can "receive" it using the + * standard recv-function infrastructure. + */ + initStringInfo(&buf); + appendBinaryStringInfo(&buf, VARDATA(sstate), VARSIZE(sstate) - VARHDRSZ); + + result = (FixedDecimalAggState *) palloc(sizeof(FixedDecimalAggState)); + + /* N */ + result->N = pq_getmsgint64(&buf); + + /* sumX */ + result->sumX = pq_getmsgint64(&buf); + + pq_getmsgend(&buf); + pfree(buf.data); + + PG_RETURN_POINTER(result); +} + + +Datum +fixeddecimalaggstatecombine(PG_FUNCTION_ARGS) +{ + FixedDecimalAggState *collectstate; + FixedDecimalAggState *transstate; + MemoryContext agg_context; + MemoryContext old_context; + + if (!AggCheckCallContext(fcinfo, &agg_context)) + elog(ERROR, "aggregate function called in non-aggregate context"); + + old_context = MemoryContextSwitchTo(agg_context); + + collectstate = PG_ARGISNULL(0) ? NULL : (FixedDecimalAggState *) + PG_GETARG_POINTER(0); + + if (collectstate == NULL) + { + collectstate = (FixedDecimalAggState *) palloc(sizeof + (FixedDecimalAggState)); + collectstate->sumX = 0; + collectstate->N = 0; + } + + transstate = PG_ARGISNULL(1) ? NULL : (FixedDecimalAggState *) + PG_GETARG_POINTER(1); + + if (transstate == NULL) + PG_RETURN_POINTER(collectstate); + + collectstate->sumX = DatumGetInt64(DirectFunctionCall2(fixeddecimalpl, + Int64GetDatum(collectstate->sumX), Int64GetDatum(transstate->sumX))); + collectstate->N = DatumGetInt64(DirectFunctionCall2(int8pl, + Int64GetDatum(collectstate->N), Int64GetDatum(transstate->N))); + + MemoryContextSwitchTo(old_context); + + PG_RETURN_POINTER(collectstate); +} + + +/* + * Function to support implicit casting from Char/Varchar/Text to fixeddecimal + */ +Datum +char_to_fixeddecimal(PG_FUNCTION_ARGS) +{ + char *str = TextDatumGetCString(PG_GETARG_DATUM(0)); + int precision; + int scale; + int64 result = scanfixeddecimal(str, &precision, &scale); + + PG_RETURN_INT64(result); +} diff --git a/contrib/babelfishpg_money/fixeddecimalaggstate.sql b/contrib/babelfishpg_money/fixeddecimalaggstate.sql new file mode 100644 index 00000000000..564f50ab7c2 --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimalaggstate.sql @@ -0,0 +1,41 @@ +-------------------------- +-- FIXEDDECIMALAGGSTATE -- +------------------------- + +CREATE TYPE FIXEDDECIMALAGGSTATE; + +CREATE FUNCTION fixeddecimalaggstatein(cstring, oid, int4) +RETURNS FIXEDDECIMALAGGSTATE +AS 'babelfishpg_money', 'fixeddecimalaggstatein' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalaggstateout(fixeddecimalaggstate) +RETURNS cstring +AS 'babelfishpg_money', 'fixeddecimalaggstateout' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalaggstaterecv(internal) +RETURNS FIXEDDECIMALAGGSTATE +AS 'babelfishpg_money', 'fixeddecimalaggstaterecv' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION fixeddecimalaggstatesend(FIXEDDECIMALAGGSTATE) +RETURNS bytea +AS 'babelfishpg_money', 'fixeddecimalaggstatesend' +LANGUAGE C IMMUTABLE STRICT; + + +CREATE TYPE FIXEDDECIMALAGGSTATE ( + INPUT = fixeddecimalaggstatein, + OUTPUT = fixeddecimalaggstateout, + RECEIVE = fixeddecimalaggstaterecv, + SEND = fixeddecimalaggstatesend, + INTERNALLENGTH = 8, + ALIGNMENT = 'double', + STORAGE = plain, + CATEGORY = 'N', + PREFERRED = false, + COLLATABLE = false, + PASSEDBYVALUE +); + diff --git a/contrib/babelfishpg_money/test/expected/aggregate.out b/contrib/babelfishpg_money/test/expected/aggregate.out new file mode 100755 index 00000000000..934c51b9b13 --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/aggregate.out @@ -0,0 +1,33 @@ +CREATE TABLE fixed_decimal(a FIXEDDECIMAL NOT NULL); +INSERT INTO fixed_decimal VALUES('92233720368547758.07'),('0.01'),('-92233720368547758.08'),('-0.01'); +SELECT SUM(a) FROM fixed_decimal WHERE a > 0; +ERROR: fixeddecimal out of range +SELECT SUM(a) FROM fixed_decimal WHERE a < 0; +ERROR: fixeddecimal out of range +TRUNCATE TABLE fixed_decimal; +INSERT INTO fixed_decimal VALUES('11.11'),('22.22'),('33.33'); +SELECT SUM(a) FROM fixed_decimal; + sum +------- + 66.66 +(1 row) + +SELECT MAX(a) FROM fixed_decimal; + max +------- + 33.33 +(1 row) + +SELECT MIN(a) FROM fixed_decimal; + min +------- + 11.11 +(1 row) + +SELECT AVG(a) FROM fixed_decimal; + avg +------- + 22.22 +(1 row) + +DROP TABLE fixed_decimal; diff --git a/contrib/babelfishpg_money/test/expected/brin-xl.out b/contrib/babelfishpg_money/test/expected/brin-xl.out new file mode 100644 index 00000000000..a66f7cf515e --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/brin-xl.out @@ -0,0 +1,23 @@ +-- Test BRIN indexes +SET enable_seqscan = off; +CREATE TABLE fixdec (d FIXEDDECIMAL, txt TEXT); +INSERT INTO fixdec SELECT s.i,REPEAT('0',64) FROM generate_series(1,10000) s(i); +CREATE INDEX fixdec_d_idx ON fixdec USING BRIN (d); +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + QUERY PLAN +--------------------------------------------------------- + Remote Subquery Scan on all (datanode_1,datanode_2) + -> Bitmap Heap Scan on fixdec + Recheck Cond: (d > '9999.00'::fixeddecimal) + -> Bitmap Index Scan on fixdec_d_idx + Index Cond: (d > '9999.00'::fixeddecimal) +(5 rows) + +SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + d | txt +----------+------------------------------------------------------------------ + 10000.00 | 0000000000000000000000000000000000000000000000000000000000000000 +(1 row) + +DROP TABLE fixdec; +RESET enable_seqscan; diff --git a/contrib/babelfishpg_money/test/expected/brin.out b/contrib/babelfishpg_money/test/expected/brin.out new file mode 100644 index 00000000000..e3ecea1c12b --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/brin.out @@ -0,0 +1,22 @@ +-- Test BRIN indexes +SET enable_seqscan = off; +CREATE TABLE fixdec (d FIXEDDECIMAL, txt TEXT); +INSERT INTO fixdec SELECT s.i,REPEAT('0',64) FROM generate_series(1,10000) s(i); +CREATE INDEX fixdec_d_idx ON fixdec USING BRIN (d); +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + QUERY PLAN +--------------------------------------------------- + Bitmap Heap Scan on fixdec + Recheck Cond: (d > '9999.00'::fixeddecimal) + -> Bitmap Index Scan on fixdec_d_idx + Index Cond: (d > '9999.00'::fixeddecimal) +(4 rows) + +SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + d | txt +----------+------------------------------------------------------------------ + 10000.00 | 0000000000000000000000000000000000000000000000000000000000000000 +(1 row) + +DROP TABLE fixdec; +RESET enable_seqscan; diff --git a/contrib/babelfishpg_money/test/expected/cast.out b/contrib/babelfishpg_money/test/expected/cast.out new file mode 100755 index 00000000000..b230ed59787 --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/cast.out @@ -0,0 +1,48 @@ +SELECT CAST('2147483647'::FIXEDDECIMAL AS INT); + int4 +------------ + 2147483647 +(1 row) + +-- Ensure overflow is detected +SELECT CAST('2147483648'::FIXEDDECIMAL AS INT); +ERROR: integer out of range +SELECT CAST('-2147483648'::FIXEDDECIMAL AS INT); + int4 +------------- + -2147483648 +(1 row) + +-- Ensure underflow is detected +SELECT CAST('-2147483649'::FIXEDDECIMAL AS INT); +ERROR: integer out of range +SELECT CAST('32767'::FIXEDDECIMAL AS SMALLINT); + int2 +------- + 32767 +(1 row) + +-- Ensure overflow is detected +SELECT CAST('32768'::FIXEDDECIMAL AS SMALLINT); +ERROR: smallint out of range +SELECT CAST('-32768'::FIXEDDECIMAL AS SMALLINT); + int2 +-------- + -32768 +(1 row) + +-- Ensure underflow is detected +SELECT CAST('-32769'::FIXEDDECIMAL AS SMALLINT); +ERROR: smallint out of range +SELECT CAST('1234321.23'::FIXEDDECIMAL AS FLOAT); + float8 +------------ + 1234321.23 +(1 row) + +SELECT CAST('1234321.23'::FIXEDDECIMAL AS DOUBLE PRECISION); + float8 +------------ + 1234321.23 +(1 row) + diff --git a/contrib/babelfishpg_money/test/expected/comparison.out b/contrib/babelfishpg_money/test/expected/comparison.out new file mode 100755 index 00000000000..9ba9abe9ae5 --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/comparison.out @@ -0,0 +1,310 @@ +-- True comparisons +SELECT '123'::FIXEDDECIMAL < '123.01'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::FIXEDDECIMAL <= '123.01'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::FIXEDDECIMAL > '122.99'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::FIXEDDECIMAL >= '122.99'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123.00'::FIXEDDECIMAL = '123'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +-- Compare to int4 +SELECT '123'::INT < '123.01'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::INT <= '123.01'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::INT > '122.99'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::INT >= '122.99'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::INT = '123.00'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +-- Compare to int4 reversed +SELECT '123.01'::FIXEDDECIMAL > '123'::INT; + ?column? +---------- + t +(1 row) + +SELECT '123.01'::FIXEDDECIMAL >= '123'::INT; + ?column? +---------- + t +(1 row) + +SELECT '122.99'::FIXEDDECIMAL < '123'::INT; + ?column? +---------- + t +(1 row) + +SELECT '122.99'::FIXEDDECIMAL <= '123'::INT; + ?column? +---------- + t +(1 row) + +SELECT '123.00'::FIXEDDECIMAL = '123'::INT; + ?column? +---------- + t +(1 row) + +-- Compare to int2 +SELECT '123'::SMALLINT < '123.01'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::SMALLINT <= '123.01'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::SMALLINT > '122.99'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::SMALLINT >= '122.99'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +SELECT '123'::SMALLINT = '123.00'::FIXEDDECIMAL; + ?column? +---------- + t +(1 row) + +-- Compare to int4 reversed +SELECT '123.01'::FIXEDDECIMAL > '123'::SMALLINT; + ?column? +---------- + t +(1 row) + +SELECT '123.01'::FIXEDDECIMAL >= '123'::SMALLINT; + ?column? +---------- + t +(1 row) + +SELECT '122.99'::FIXEDDECIMAL < '123'::SMALLINT; + ?column? +---------- + t +(1 row) + +SELECT '122.99'::FIXEDDECIMAL <= '123'::SMALLINT; + ?column? +---------- + t +(1 row) + +SELECT '123.00'::FIXEDDECIMAL = '123'::SMALLINT; + ?column? +---------- + t +(1 row) + +-- False comparisons +SELECT '123'::FIXEDDECIMAL >= '123.01'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::FIXEDDECIMAL > '123.01'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::FIXEDDECIMAL <= '122.99'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::FIXEDDECIMAL < '122.99'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123.00'::FIXEDDECIMAL <> '123'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +-- Compare to int4 +SELECT '123'::INT >= '123.01'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::INT > '123.01'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::INT <= '122.99'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::INT < '122.99'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::INT <> '123.00'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +-- Compare to int4 reversed +SELECT '123.01'::FIXEDDECIMAL <= '123'::INT; + ?column? +---------- + f +(1 row) + +SELECT '123.01'::FIXEDDECIMAL < '123'::INT; + ?column? +---------- + f +(1 row) + +SELECT '122.99'::FIXEDDECIMAL >= '123'::INT; + ?column? +---------- + f +(1 row) + +SELECT '122.99'::FIXEDDECIMAL > '123'::INT; + ?column? +---------- + f +(1 row) + +SELECT '123.00'::FIXEDDECIMAL <> '123'::INT; + ?column? +---------- + f +(1 row) + +-- Compare to int2 +SELECT '123'::SMALLINT >= '123.01'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::SMALLINT > '123.01'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::SMALLINT <= '122.99'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::SMALLINT < '122.99'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +SELECT '123'::SMALLINT <> '123.00'::FIXEDDECIMAL; + ?column? +---------- + f +(1 row) + +-- Compare to int4 reversed +SELECT '123.01'::FIXEDDECIMAL <= '123'::SMALLINT; + ?column? +---------- + f +(1 row) + +SELECT '123.01'::FIXEDDECIMAL < '123'::SMALLINT; + ?column? +---------- + f +(1 row) + +SELECT '122.99'::FIXEDDECIMAL >= '123'::SMALLINT; + ?column? +---------- + f +(1 row) + +SELECT '122.99'::FIXEDDECIMAL > '123'::SMALLINT; + ?column? +---------- + f +(1 row) + +SELECT '123.00'::FIXEDDECIMAL <> '123'::SMALLINT; + ?column? +---------- + f +(1 row) + diff --git a/contrib/babelfishpg_money/test/expected/index-xl.out b/contrib/babelfishpg_money/test/expected/index-xl.out new file mode 100644 index 00000000000..43c34566432 --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/index-xl.out @@ -0,0 +1,95 @@ +CREATE TABLE fixdec (id INT, d FIXEDDECIMAL(5,2)); +INSERT INTO fixdec (id,d) VALUES(1,-123.45); +INSERT INTO fixdec (id,d) VALUES(2,-123); +INSERT INTO fixdec (id,d) VALUES(3,-12.34); +INSERT INTO fixdec (id,d) VALUES(4,-1.34); +INSERT INTO fixdec (id,d) VALUES(5, 0.12); +INSERT INTO fixdec (id,d) VALUES(6, 1.23); +INSERT INTO fixdec (id,d) VALUES(7, 12.34); +INSERT INTO fixdec (id,d) VALUES(8, 123.45); +INSERT INTO fixdec (id,d) VALUES(9, 123.456); +CREATE INDEX fixdec_d_idx ON fixdec (d); +DELETE FROM fixdec WHERE id = 9; +SET enable_seqscan = off; +EXPLAIN (COSTS OFF) SELECT * FROM fixdec ORDER BY d; + QUERY PLAN +----------------------------------------------------- + Remote Subquery Scan on all (datanode_1,datanode_2) + -> Index Scan using fixdec_d_idx on fixdec +(2 rows) + +SELECT * FROM fixdec ORDER BY d; + id | d +----+--------- + 1 | -123.45 + 2 | -123.00 + 3 | -12.34 + 4 | -1.34 + 5 | 0.12 + 6 | 1.23 + 7 | 12.34 + 8 | 123.45 +(8 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + QUERY PLAN +------------------------------------------------------- + Remote Subquery Scan on all (datanode_1,datanode_2) + -> Bitmap Heap Scan on fixdec + Recheck Cond: (d = '12.34'::fixeddecimal) + -> Bitmap Index Scan on fixdec_d_idx + Index Cond: (d = '12.34'::fixeddecimal) +(5 rows) + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + id | d +----+------- + 7 | 12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + id | d +----+-------- + 3 | -12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + id | d +----+-------- + 8 | 123.45 +(1 row) + +DROP INDEX fixdec_d_idx; +SET client_min_messages = ERROR; +CREATE INDEX fixdec_d_idx ON fixdec USING hash (d); +RESET client_min_messages; +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + QUERY PLAN +------------------------------------------------------- + Remote Subquery Scan on all (datanode_1,datanode_2) + -> Bitmap Heap Scan on fixdec + Recheck Cond: (d = '12.34'::fixeddecimal) + -> Bitmap Index Scan on fixdec_d_idx + Index Cond: (d = '12.34'::fixeddecimal) +(5 rows) + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + id | d +----+------- + 7 | 12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + id | d +----+-------- + 3 | -12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + id | d +----+-------- + 8 | 123.45 +(1 row) + +DROP TABLE fixdec; +SET enable_seqscan = on; diff --git a/contrib/babelfishpg_money/test/expected/index.out b/contrib/babelfishpg_money/test/expected/index.out new file mode 100644 index 00000000000..f02aaf91065 --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/index.out @@ -0,0 +1,92 @@ +CREATE TABLE fixdec (id INT, d FIXEDDECIMAL(5,2)); +INSERT INTO fixdec (id,d) VALUES(1,-123.45); +INSERT INTO fixdec (id,d) VALUES(2,-123); +INSERT INTO fixdec (id,d) VALUES(3,-12.34); +INSERT INTO fixdec (id,d) VALUES(4,-1.34); +INSERT INTO fixdec (id,d) VALUES(5, 0.12); +INSERT INTO fixdec (id,d) VALUES(6, 1.23); +INSERT INTO fixdec (id,d) VALUES(7, 12.34); +INSERT INTO fixdec (id,d) VALUES(8, 123.45); +INSERT INTO fixdec (id,d) VALUES(9, 123.456); +-- Should fail +CREATE UNIQUE INDEX fixdec_d_idx ON fixdec (d); +ERROR: could not create unique index "fixdec_d_idx" +DETAIL: Key (d)=(123.45) is duplicated. +DELETE FROM fixdec WHERE id = 9; +CREATE UNIQUE INDEX fixdec_d_idx ON fixdec (d); +SET enable_seqscan = off; +EXPLAIN (COSTS OFF) SELECT * FROM fixdec ORDER BY d; + QUERY PLAN +----------------------------------------- + Index Scan using fixdec_d_idx on fixdec +(1 row) + +SELECT * FROM fixdec ORDER BY d; + id | d +----+--------- + 1 | -123.45 + 2 | -123.00 + 3 | -12.34 + 4 | -1.34 + 5 | 0.12 + 6 | 1.23 + 7 | 12.34 + 8 | 123.45 +(8 rows) + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + QUERY PLAN +------------------------------------------- + Index Scan using fixdec_d_idx on fixdec + Index Cond: (d = '12.34'::fixeddecimal) +(2 rows) + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + id | d +----+------- + 7 | 12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + id | d +----+-------- + 3 | -12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + id | d +----+-------- + 8 | 123.45 +(1 row) + +DROP INDEX fixdec_d_idx; +SET client_min_messages = ERROR; +CREATE INDEX fixdec_d_idx ON fixdec USING hash (d); +RESET client_min_messages; +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + QUERY PLAN +------------------------------------------- + Index Scan using fixdec_d_idx on fixdec + Index Cond: (d = '12.34'::fixeddecimal) +(2 rows) + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + id | d +----+------- + 7 | 12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + id | d +----+-------- + 3 | -12.34 +(1 row) + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + id | d +----+-------- + 8 | 123.45 +(1 row) + +DROP TABLE fixdec; +SET enable_seqscan = on; diff --git a/contrib/babelfishpg_money/test/expected/overflow.out b/contrib/babelfishpg_money/test/expected/overflow.out new file mode 100755 index 00000000000..ee0010080d4 --- /dev/null +++ b/contrib/babelfishpg_money/test/expected/overflow.out @@ -0,0 +1,123 @@ +-- Ensure the expected extreme values can be represented +SELECT '-92233720368547758.08'::FIXEDDECIMAL as minvalue,'92233720368547758.07'::FIXEDDECIMAL as maxvalue; + minvalue | maxvalue +-----------------------+---------------------- + -92233720368547758.08 | 92233720368547758.07 +(1 row) + +SELECT '-92233720368547758.09'::FIXEDDECIMAL; +ERROR: value "-92233720368547758.09" is out of range for type fixeddecimal +LINE 1: SELECT '-92233720368547758.09'::FIXEDDECIMAL; + ^ +SELECT '92233720368547758.08'::FIXEDDECIMAL; +ERROR: value "92233720368547758.08" is out of range for type fixeddecimal +LINE 1: SELECT '92233720368547758.08'::FIXEDDECIMAL; + ^ +-- Ensure casts from numeric to fixeddecimal work +SELECT '92233720368547758.07'::numeric::FIXEDDECIMAL; + fixeddecimal +---------------------- + 92233720368547758.07 +(1 row) + +-- The literal below must be quoted as the parser seems to read the literal as +-- a positive number first and then us the - unary operator to make it negaive. +-- This would overflow without the quotes as this number cannot be represented +-- in a positive fixeddecimal. +SELECT '-92233720368547758.08'::numeric::FIXEDDECIMAL; + fixeddecimal +----------------------- + -92233720368547758.08 +(1 row) + +-- Ensure casts from numeric to fixed decimal detect overflow +SELECT '92233720368547758.08'::numeric::FIXEDDECIMAL; +ERROR: value "92233720368547758.08" is out of range for type fixeddecimal +SELECT '-92233720368547758.09'::numeric::FIXEDDECIMAL; +ERROR: value "-92233720368547758.09" is out of range for type fixeddecimal +SELECT '-92233720368547758.08'::FIXEDDECIMAL - '0.01'::FIXEDDECIMAL; +ERROR: fixeddecimal out of range +SELECT '92233720368547758.07'::FIXEDDECIMAL + '0.01'::FIXEDDECIMAL; +ERROR: fixeddecimal out of range +-- Should not overflow +SELECT '46116860184273879.03'::FIXEDDECIMAL * '2.00'::FIXEDDECIMAL; + ?column? +---------------------- + 92233720368547758.06 +(1 row) + +-- Ensure this overflows +SELECT '46116860184273879.04'::FIXEDDECIMAL * '2.00'::FIXEDDECIMAL; +ERROR: fixeddecimal out of range +-- Should not overflow +SELECT '46116860184273879.03'::FIXEDDECIMAL / '0.50'::FIXEDDECIMAL; + ?column? +---------------------- + 92233720368547758.06 +(1 row) + +-- Ensure this overflows +SELECT '46116860184273879.04'::FIXEDDECIMAL / '0.50'::FIXEDDECIMAL; +ERROR: fixeddecimal out of range +-- Ensure limits of int2 can be represented +SELECT '32767'::FIXEDDECIMAL::INT2,'-32768'::FIXEDDECIMAL::INT2; + int2 | int2 +-------+-------- + 32767 | -32768 +(1 row) + +-- Ensure overflow of int2 is detected +SELECT '32768'::FIXEDDECIMAL::INT2; +ERROR: smallint out of range +-- Ensure underflow of int2 is detected +SELECT '-32769'::FIXEDDECIMAL::INT2; +ERROR: smallint out of range +-- Ensure limits of int4 can be represented +SELECT '2147483647'::FIXEDDECIMAL::INT4,'-2147483648'::FIXEDDECIMAL::INT4; + int4 | int4 +------------+------------- + 2147483647 | -2147483648 +(1 row) + +-- Ensure overflow of int4 is detected +SELECT '2147483648'::FIXEDDECIMAL::INT4; +ERROR: integer out of range +-- Ensure underflow of int4 is detected +SELECT '-2147483649'::FIXEDDECIMAL::INT4; +ERROR: integer out of range +-- Ensure overflow is detected +SELECT SUM(a) FROM (VALUES('92233720368547758.07'::FIXEDDECIMAL),('0.01'::FIXEDDECIMAL)) a(a); +ERROR: fixeddecimal out of range +-- Ensure underflow is detected +SELECT SUM(a) FROM (VALUES('-92233720368547758.08'::FIXEDDECIMAL),('-0.01'::FIXEDDECIMAL)) a(a); +ERROR: fixeddecimal out of range +-- Test typmods +SELECT 12345.33::FIXEDDECIMAL(3,2); -- Fail +ERROR: FIXEDDECIMAL field overflow +DETAIL: A field with precision 5, scale 2 must round to an absolute value less than 10^1. +SELECT 12345.33::FIXEDDECIMAL(5,2); -- Fail +ERROR: FIXEDDECIMAL field overflow +DETAIL: A field with precision 5, scale 2 must round to an absolute value less than 10^3. +-- scale of 2 should be enforced. +SELECT 12345.44::FIXEDDECIMAL(7,0); +ERROR: FIXEDDECIMAL scale must be 2 +LINE 1: SELECT 12345.44::FIXEDDECIMAL(7,0); + ^ +-- should work. +SELECT 12345.33::FIXEDDECIMAL(7,2); + fixeddecimal +-------------- + 12345.33 +(1 row) + +-- error, precision limit should be 17 +SELECT 12345.33::FIXEDDECIMAL(18,2); +ERROR: FIXEDDECIMAL precision 18 must be between 2 and 17 +LINE 1: SELECT 12345.33::FIXEDDECIMAL(18,2); + ^ +CREATE TABLE fixdec (d FIXEDDECIMAL(3,2)); +INSERT INTO fixdec VALUES(12.34); -- Fail +ERROR: FIXEDDECIMAL field overflow +DETAIL: A field with precision 2, scale 2 must round to an absolute value less than 10^1. +INSERT INTO fixdec VALUES(1.23); -- Pass +DROP TABLE fixdec; diff --git a/contrib/babelfishpg_money/test/sql/aggregate.sql b/contrib/babelfishpg_money/test/sql/aggregate.sql new file mode 100755 index 00000000000..6c00a7205aa --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/aggregate.sql @@ -0,0 +1,21 @@ +CREATE TABLE fixed_decimal(a FIXEDDECIMAL NOT NULL); + +INSERT INTO fixed_decimal VALUES('92233720368547758.07'),('0.01'),('-92233720368547758.08'),('-0.01'); + +SELECT SUM(a) FROM fixed_decimal WHERE a > 0; + +SELECT SUM(a) FROM fixed_decimal WHERE a < 0; + +TRUNCATE TABLE fixed_decimal; + +INSERT INTO fixed_decimal VALUES('11.11'),('22.22'),('33.33'); + +SELECT SUM(a) FROM fixed_decimal; + +SELECT MAX(a) FROM fixed_decimal; + +SELECT MIN(a) FROM fixed_decimal; + +SELECT AVG(a) FROM fixed_decimal; + +DROP TABLE fixed_decimal; \ No newline at end of file diff --git a/contrib/babelfishpg_money/test/sql/brin-xl.sql b/contrib/babelfishpg_money/test/sql/brin-xl.sql new file mode 100644 index 00000000000..802328517d5 --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/brin-xl.sql @@ -0,0 +1,14 @@ +-- Test BRIN indexes +SET enable_seqscan = off; +CREATE TABLE fixdec (d FIXEDDECIMAL, txt TEXT); +INSERT INTO fixdec SELECT s.i,REPEAT('0',64) FROM generate_series(1,10000) s(i); + +CREATE INDEX fixdec_d_idx ON fixdec USING BRIN (d); + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + +DROP TABLE fixdec; + +RESET enable_seqscan; diff --git a/contrib/babelfishpg_money/test/sql/brin.sql b/contrib/babelfishpg_money/test/sql/brin.sql new file mode 100644 index 00000000000..802328517d5 --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/brin.sql @@ -0,0 +1,14 @@ +-- Test BRIN indexes +SET enable_seqscan = off; +CREATE TABLE fixdec (d FIXEDDECIMAL, txt TEXT); +INSERT INTO fixdec SELECT s.i,REPEAT('0',64) FROM generate_series(1,10000) s(i); + +CREATE INDEX fixdec_d_idx ON fixdec USING BRIN (d); + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d > '9999'::FIXEDDECIMAL; + +DROP TABLE fixdec; + +RESET enable_seqscan; diff --git a/contrib/babelfishpg_money/test/sql/cast.sql b/contrib/babelfishpg_money/test/sql/cast.sql new file mode 100755 index 00000000000..3b7b1c8bbc6 --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/cast.sql @@ -0,0 +1,23 @@ +SELECT CAST('2147483647'::FIXEDDECIMAL AS INT); + +-- Ensure overflow is detected +SELECT CAST('2147483648'::FIXEDDECIMAL AS INT); + +SELECT CAST('-2147483648'::FIXEDDECIMAL AS INT); + +-- Ensure underflow is detected +SELECT CAST('-2147483649'::FIXEDDECIMAL AS INT); + +SELECT CAST('32767'::FIXEDDECIMAL AS SMALLINT); + +-- Ensure overflow is detected +SELECT CAST('32768'::FIXEDDECIMAL AS SMALLINT); + +SELECT CAST('-32768'::FIXEDDECIMAL AS SMALLINT); + +-- Ensure underflow is detected +SELECT CAST('-32769'::FIXEDDECIMAL AS SMALLINT); + +SELECT CAST('1234321.23'::FIXEDDECIMAL AS FLOAT); + +SELECT CAST('1234321.23'::FIXEDDECIMAL AS DOUBLE PRECISION); \ No newline at end of file diff --git a/contrib/babelfishpg_money/test/sql/comparison.sql b/contrib/babelfishpg_money/test/sql/comparison.sql new file mode 100755 index 00000000000..ed3e6ad0c2f --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/comparison.sql @@ -0,0 +1,118 @@ +-- True comparisons + +SELECT '123'::FIXEDDECIMAL < '123.01'::FIXEDDECIMAL; + +SELECT '123'::FIXEDDECIMAL <= '123.01'::FIXEDDECIMAL; + +SELECT '123'::FIXEDDECIMAL > '122.99'::FIXEDDECIMAL; + +SELECT '123'::FIXEDDECIMAL >= '122.99'::FIXEDDECIMAL; + +SELECT '123.00'::FIXEDDECIMAL = '123'::FIXEDDECIMAL; + +-- Compare to int4 + +SELECT '123'::INT < '123.01'::FIXEDDECIMAL; + +SELECT '123'::INT <= '123.01'::FIXEDDECIMAL; + +SELECT '123'::INT > '122.99'::FIXEDDECIMAL; + +SELECT '123'::INT >= '122.99'::FIXEDDECIMAL; + +SELECT '123'::INT = '123.00'::FIXEDDECIMAL; + +-- Compare to int4 reversed + +SELECT '123.01'::FIXEDDECIMAL > '123'::INT; + +SELECT '123.01'::FIXEDDECIMAL >= '123'::INT; + +SELECT '122.99'::FIXEDDECIMAL < '123'::INT; + +SELECT '122.99'::FIXEDDECIMAL <= '123'::INT; + +SELECT '123.00'::FIXEDDECIMAL = '123'::INT; + +-- Compare to int2 + +SELECT '123'::SMALLINT < '123.01'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT <= '123.01'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT > '122.99'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT >= '122.99'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT = '123.00'::FIXEDDECIMAL; + +-- Compare to int4 reversed + +SELECT '123.01'::FIXEDDECIMAL > '123'::SMALLINT; + +SELECT '123.01'::FIXEDDECIMAL >= '123'::SMALLINT; + +SELECT '122.99'::FIXEDDECIMAL < '123'::SMALLINT; + +SELECT '122.99'::FIXEDDECIMAL <= '123'::SMALLINT; + +SELECT '123.00'::FIXEDDECIMAL = '123'::SMALLINT; + +-- False comparisons +SELECT '123'::FIXEDDECIMAL >= '123.01'::FIXEDDECIMAL; + +SELECT '123'::FIXEDDECIMAL > '123.01'::FIXEDDECIMAL; + +SELECT '123'::FIXEDDECIMAL <= '122.99'::FIXEDDECIMAL; + +SELECT '123'::FIXEDDECIMAL < '122.99'::FIXEDDECIMAL; + +SELECT '123.00'::FIXEDDECIMAL <> '123'::FIXEDDECIMAL; + +-- Compare to int4 + +SELECT '123'::INT >= '123.01'::FIXEDDECIMAL; + +SELECT '123'::INT > '123.01'::FIXEDDECIMAL; + +SELECT '123'::INT <= '122.99'::FIXEDDECIMAL; + +SELECT '123'::INT < '122.99'::FIXEDDECIMAL; + +SELECT '123'::INT <> '123.00'::FIXEDDECIMAL; + +-- Compare to int4 reversed + +SELECT '123.01'::FIXEDDECIMAL <= '123'::INT; + +SELECT '123.01'::FIXEDDECIMAL < '123'::INT; + +SELECT '122.99'::FIXEDDECIMAL >= '123'::INT; + +SELECT '122.99'::FIXEDDECIMAL > '123'::INT; + +SELECT '123.00'::FIXEDDECIMAL <> '123'::INT; + +-- Compare to int2 + +SELECT '123'::SMALLINT >= '123.01'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT > '123.01'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT <= '122.99'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT < '122.99'::FIXEDDECIMAL; + +SELECT '123'::SMALLINT <> '123.00'::FIXEDDECIMAL; + +-- Compare to int4 reversed + +SELECT '123.01'::FIXEDDECIMAL <= '123'::SMALLINT; + +SELECT '123.01'::FIXEDDECIMAL < '123'::SMALLINT; + +SELECT '122.99'::FIXEDDECIMAL >= '123'::SMALLINT; + +SELECT '122.99'::FIXEDDECIMAL > '123'::SMALLINT; + +SELECT '123.00'::FIXEDDECIMAL <> '123'::SMALLINT; diff --git a/contrib/babelfishpg_money/test/sql/index-xl.sql b/contrib/babelfishpg_money/test/sql/index-xl.sql new file mode 100644 index 00000000000..c963ba1f888 --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/index-xl.sql @@ -0,0 +1,47 @@ +CREATE TABLE fixdec (id INT, d FIXEDDECIMAL(5,2)); + +INSERT INTO fixdec (id,d) VALUES(1,-123.45); +INSERT INTO fixdec (id,d) VALUES(2,-123); +INSERT INTO fixdec (id,d) VALUES(3,-12.34); +INSERT INTO fixdec (id,d) VALUES(4,-1.34); +INSERT INTO fixdec (id,d) VALUES(5, 0.12); +INSERT INTO fixdec (id,d) VALUES(6, 1.23); +INSERT INTO fixdec (id,d) VALUES(7, 12.34); +INSERT INTO fixdec (id,d) VALUES(8, 123.45); +INSERT INTO fixdec (id,d) VALUES(9, 123.456); + +CREATE INDEX fixdec_d_idx ON fixdec (d); + +DELETE FROM fixdec WHERE id = 9; + +SET enable_seqscan = off; + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec ORDER BY d; + +SELECT * FROM fixdec ORDER BY d; + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + +DROP INDEX fixdec_d_idx; + +SET client_min_messages = ERROR; +CREATE INDEX fixdec_d_idx ON fixdec USING hash (d); +RESET client_min_messages; + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + +DROP TABLE fixdec; + +SET enable_seqscan = on; diff --git a/contrib/babelfishpg_money/test/sql/index.sql b/contrib/babelfishpg_money/test/sql/index.sql new file mode 100644 index 00000000000..3782593fb8c --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/index.sql @@ -0,0 +1,50 @@ +CREATE TABLE fixdec (id INT, d FIXEDDECIMAL(5,2)); + +INSERT INTO fixdec (id,d) VALUES(1,-123.45); +INSERT INTO fixdec (id,d) VALUES(2,-123); +INSERT INTO fixdec (id,d) VALUES(3,-12.34); +INSERT INTO fixdec (id,d) VALUES(4,-1.34); +INSERT INTO fixdec (id,d) VALUES(5, 0.12); +INSERT INTO fixdec (id,d) VALUES(6, 1.23); +INSERT INTO fixdec (id,d) VALUES(7, 12.34); +INSERT INTO fixdec (id,d) VALUES(8, 123.45); +INSERT INTO fixdec (id,d) VALUES(9, 123.456); + +-- Should fail +CREATE UNIQUE INDEX fixdec_d_idx ON fixdec (d); + +DELETE FROM fixdec WHERE id = 9; + +CREATE UNIQUE INDEX fixdec_d_idx ON fixdec (d); + +SET enable_seqscan = off; + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec ORDER BY d; + +SELECT * FROM fixdec ORDER BY d; + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + +DROP INDEX fixdec_d_idx; + +SET client_min_messages = ERROR; +CREATE INDEX fixdec_d_idx ON fixdec USING hash (d); +RESET client_min_messages; + +EXPLAIN (COSTS OFF) SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '-12.34'::FIXEDDECIMAL; + +SELECT * FROM fixdec WHERE d = '123.45'::FIXEDDECIMAL; + +DROP TABLE fixdec; + +SET enable_seqscan = on; diff --git a/contrib/babelfishpg_money/test/sql/overflow.sql b/contrib/babelfishpg_money/test/sql/overflow.sql new file mode 100755 index 00000000000..06408cda2b9 --- /dev/null +++ b/contrib/babelfishpg_money/test/sql/overflow.sql @@ -0,0 +1,79 @@ +-- Ensure the expected extreme values can be represented +SELECT '-92233720368547758.08'::FIXEDDECIMAL as minvalue,'92233720368547758.07'::FIXEDDECIMAL as maxvalue; + +SELECT '-92233720368547758.09'::FIXEDDECIMAL; + +SELECT '92233720368547758.08'::FIXEDDECIMAL; + +-- Ensure casts from numeric to fixeddecimal work +SELECT '92233720368547758.07'::numeric::FIXEDDECIMAL; + +-- The literal below must be quoted as the parser seems to read the literal as +-- a positive number first and then us the - unary operator to make it negaive. +-- This would overflow without the quotes as this number cannot be represented +-- in a positive fixeddecimal. +SELECT '-92233720368547758.08'::numeric::FIXEDDECIMAL; + +-- Ensure casts from numeric to fixed decimal detect overflow +SELECT '92233720368547758.08'::numeric::FIXEDDECIMAL; + +SELECT '-92233720368547758.09'::numeric::FIXEDDECIMAL; + +SELECT '-92233720368547758.08'::FIXEDDECIMAL - '0.01'::FIXEDDECIMAL; + +SELECT '92233720368547758.07'::FIXEDDECIMAL + '0.01'::FIXEDDECIMAL; + +-- Should not overflow +SELECT '46116860184273879.03'::FIXEDDECIMAL * '2.00'::FIXEDDECIMAL; + +-- Ensure this overflows +SELECT '46116860184273879.04'::FIXEDDECIMAL * '2.00'::FIXEDDECIMAL; + +-- Should not overflow +SELECT '46116860184273879.03'::FIXEDDECIMAL / '0.50'::FIXEDDECIMAL; + +-- Ensure this overflows +SELECT '46116860184273879.04'::FIXEDDECIMAL / '0.50'::FIXEDDECIMAL; + +-- Ensure limits of int2 can be represented +SELECT '32767'::FIXEDDECIMAL::INT2,'-32768'::FIXEDDECIMAL::INT2; + +-- Ensure overflow of int2 is detected +SELECT '32768'::FIXEDDECIMAL::INT2; + +-- Ensure underflow of int2 is detected +SELECT '-32769'::FIXEDDECIMAL::INT2; + +-- Ensure limits of int4 can be represented +SELECT '2147483647'::FIXEDDECIMAL::INT4,'-2147483648'::FIXEDDECIMAL::INT4; + +-- Ensure overflow of int4 is detected +SELECT '2147483648'::FIXEDDECIMAL::INT4; + +-- Ensure underflow of int4 is detected +SELECT '-2147483649'::FIXEDDECIMAL::INT4; + +-- Ensure overflow is detected +SELECT SUM(a) FROM (VALUES('92233720368547758.07'::FIXEDDECIMAL),('0.01'::FIXEDDECIMAL)) a(a); + +-- Ensure underflow is detected +SELECT SUM(a) FROM (VALUES('-92233720368547758.08'::FIXEDDECIMAL),('-0.01'::FIXEDDECIMAL)) a(a); + +-- Test typmods +SELECT 12345.33::FIXEDDECIMAL(3,2); -- Fail + +SELECT 12345.33::FIXEDDECIMAL(5,2); -- Fail + +-- scale of 2 should be enforced. +SELECT 12345.44::FIXEDDECIMAL(7,0); + +-- should work. +SELECT 12345.33::FIXEDDECIMAL(7,2); + +-- error, precision limit should be 17 +SELECT 12345.33::FIXEDDECIMAL(18,2); + +CREATE TABLE fixdec (d FIXEDDECIMAL(3,2)); +INSERT INTO fixdec VALUES(12.34); -- Fail +INSERT INTO fixdec VALUES(1.23); -- Pass +DROP TABLE fixdec; diff --git a/contrib/babelfishpg_tds/Makefile b/contrib/babelfishpg_tds/Makefile new file mode 100644 index 00000000000..e36f1b99fe8 --- /dev/null +++ b/contrib/babelfishpg_tds/Makefile @@ -0,0 +1,47 @@ +# contrib/babelfishpg_tds/Makefile +MODULE_big = babelfishpg_tds +EXTENSION = babelfishpg_tds +DATA = babelfishpg_tds--1.0.0.sql +PGFILEDESC = "babelfishpg_tds - TDS Listener Extension" +#REGRESS = babelfishpg_tds + +tds_top_dir = . +tds_backend = $(tds_top_dir)/src/backend +tds_include = $(tds_top_dir)/src/include +TSQL_SRC = ../babelfishpg_tsql + +PG_CPPFLAGS += -I$(TSQL_SRC) -I$(PG_SRC) -I$(tds_top_dir) -DFAULT_INJECTOR + +# Exclude the following files from the build (sometimes these +# files are included in another c file) +tds_exclude_files = $(tds_backend)/tds/support_funcs.c \ + $(tds_backend)/tds/tds_data_map.c \ + $(tds_backend)/tds/tdsprinttup.c + +tds_temp_srcs = $(shell find $(tds_top_dir) -name "*.c") +tds_srcs = $(filter-out $(tds_exclude_files), $(tds_temp_srcs)) + +OBJS = $(patsubst %.c, %.o, $(tds_srcs)) +OBJS += $(WIN32RES) + +$(tds_include)/error_mapping.h: error_mapping.txt generate_error_mapping.pl + $(PERL) generate_error_mapping.pl $< > $@ +$(tds_backend)/tds/err_handler.o: $(tds_include)/error_mapping.h + +# Disable for now +#NO_PGXS = 1 + +ifdef USE_PGXS +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/babelfishpg_tds +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + + +#include ../Makefile.common + +.DEFAULT_GOAL := all diff --git a/contrib/babelfishpg_tds/README b/contrib/babelfishpg_tds/README new file mode 100644 index 00000000000..e0c072c12f8 --- /dev/null +++ b/contrib/babelfishpg_tds/README @@ -0,0 +1,8 @@ +TDS Extension +---------------------- + +1. Introduction +2. Installation +3. Design and implementation details + + diff --git a/contrib/babelfishpg_tds/README.err b/contrib/babelfishpg_tds/README.err new file mode 100644 index 00000000000..6aa529b5872 --- /dev/null +++ b/contrib/babelfishpg_tds/README.err @@ -0,0 +1,4 @@ +This documentation should explain the PG error code to SQL Server error code +implementation that we've done. + + diff --git a/contrib/babelfishpg_tds/babelfishpg_tds--1.0.0.sql b/contrib/babelfishpg_tds/babelfishpg_tds--1.0.0.sql new file mode 100644 index 00000000000..56b3a4940fb --- /dev/null +++ b/contrib/babelfishpg_tds/babelfishpg_tds--1.0.0.sql @@ -0,0 +1,50 @@ +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION babelfishpg_tds" to load this file. \quit + +CREATE FUNCTION sys.inject_fault( + faultname text, + num_occurrences int4) +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION sys.inject_fault( + faultname text, + num_occurrences int4, + tamper_byte int4) +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION sys.inject_fault_status( + faultname text) +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION trigger_test_fault() +RETURNS text +AS 'MODULE_PATHNAME' +LANGUAGE C VOLATILE STRICT; + +CREATE FUNCTION sys.inject_fault( + faultname text) +RETURNS text +AS $$ SELECT sys.inject_fault(faultname, 1) $$ +LANGUAGE SQL; + +CREATE FUNCTION sys.disable_injected_fault( + faultname text) +RETURNS text +AS $$ SELECT sys.inject_fault(faultname, 0) $$ +LANGUAGE SQL; + +CREATE FUNCTION sys.inject_fault_all() +RETURNS text +AS $$ SELECT sys.inject_fault('all', 1) $$ +LANGUAGE SQL; + +CREATE FUNCTION sys.disable_injected_fault_all() +RETURNS text +AS $$ SELECT sys.inject_fault('all', 0) $$ +LANGUAGE SQL; diff --git a/contrib/babelfishpg_tds/babelfishpg_tds.control b/contrib/babelfishpg_tds/babelfishpg_tds.control new file mode 100644 index 00000000000..1cf3a8e1688 --- /dev/null +++ b/contrib/babelfishpg_tds/babelfishpg_tds.control @@ -0,0 +1,7 @@ +# TDS extension +comment = 'TDS protocol extension' +default_version = '1.0.0' +module_pathname = '$libdir/babelfishpg_tds' +relocatable = true +superuser = true +requires = 'babelfishpg_tsql' diff --git a/contrib/babelfishpg_tds/error_mapping.txt b/contrib/babelfishpg_tds/error_mapping.txt new file mode 100644 index 00000000000..db3a3d37146 --- /dev/null +++ b/contrib/babelfishpg_tds/error_mapping.txt @@ -0,0 +1,169 @@ +# +# error_mapping.txt +# +# This file contains all possible error messages with error code +# which can be sent to end user along with their corresponding +# TSQL error code. +# Any newly added error message should be reflected here as well. +# +# File generated from this file one is: error_mapping.h +# +# Format of this file is, one error per line with following structure: +# sqlstate errcode_macro_name error_message tsql_error_code tsql_severity_level error_msg_keywords\n + +# error_msg_keywords can contain more than 1 key words concatenated by "#". +# If correct tsql error details could be identified by PG error code + error message_id (untranslated error message) +# then error_msg_keywords can be empty. + +# don't forget new line at the end. +# +# Empty line and line starts with hash are treated as comment. +# + +0A000 ERRCODE_FEATURE_NOT_SUPPORTED "mixture of ISO syntax and T-SQL extended syntax" SQL_ERROR_1049 15 +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_1051 16 "VARYING" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_142 15 "REFERENCES" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_10727 15 "INSERT" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_10727 15 "DELETE" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_10727 15 "UPDATE" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_10733 15 "MERGE" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_11717 15 "OVER" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_487 15 "BEGIN" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_11709 15 "WITH" +42601 ERRCODE_SYNTAX_ERROR "syntax error near %s at line %lu and character position %lu" SQL_ERROR_153 15 "FILEGROWTH" +42601 ERRCODE_SYNTAX_ERROR "Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters." SQL_ERROR_2747 16 +P0001 ERRCODE_RAISE_EXCEPTION "%s" SQL_ERROR_289 16 "Cannot construct data type datetime, some of the arguments have values which are not valid." +42601 ERRCODE_SYNTAX_ERROR "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" SQL_ERROR_141 15 +09000 ERRCODE_TRIGGERED_ACTION_EXCEPTION "An error was raised during trigger execution. The batch has been aborted and the user transaction, if any, has been rolled back." SQL_ERROR_3616 16 +42601 ERRCODE_SYNTAX_ERROR "duplicate declaration" SQL_ERROR_134 15 +42804 ERRCODE_DATATYPE_MISMATCH "variable \"%s\" must be of type cursor or refcursor" SQL_ERROR_16948 16 +2200N ERRCODE_INVALID_XML_CONTENT "%s" SQL_ERROR_9451 16 "invalid XML content" +2200N ERRCODE_INVALID_XML_CONTENT "%s" SQL_ERROR_9441 16 "invalid XML content#invalid XML declaration" +P0001 ERRCODE_RAISE_EXCEPTION "%s" SQL_ERROR_9809 16 "is not supported for conversions from" +22003 ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE "\"%s\" is out of range for type real" SQL_ERROR_232 16 +42809 ERRCODE_WRONG_OBJECT_TYPE "\"%s\" is not a table" SQL_ERROR_4708 16 +42601 ERRCODE_SYNTAX_ERROR "%s at or near \"%s\"" SQL_ERROR_1051 15 "VARYING" +42601 ERRCODE_SYNTAX_ERROR "%s at or near \"%s\"" SQL_ERROR_3914 16 "SAVE" +42601 ERRCODE_SYNTAX_ERROR "%s at or near \"%s\"" SQL_ERROR_1034 15 "duplicate trigger events specified" +42601 ERRCODE_SYNTAX_ERROR "%s at or near \"%s\"" SQL_ERROR_10793 15 "INDEX" +42601 ERRCODE_SYNTAX_ERROR "%s at or near \"%s\"" SQL_ERROR_11555 15 "NOT" +25P01 ERRCODE_NO_ACTIVE_SQL_TRANSACTION "%s can only be used in transaction blocks" SQL_ERROR_3902 16 "COMMIT" +25P01 ERRCODE_NO_ACTIVE_SQL_TRANSACTION "%s can only be used in transaction blocks" SQL_ERROR_3903 16 "ROLLBACK" +25P01 ERRCODE_NO_ACTIVE_SQL_TRANSACTION "%s can only be used in transaction blocks" SQL_ERROR_628 16 "SAVEPOINT" +25P01 ERRCODE_NO_ACTIVE_SQL_TRANSACTION "%s can only be used in transaction blocks" SQL_ERROR_3903 16 "ROLLBACK TO SAVEPOINT" +25001 ERRCODE_ACTIVE_SQL_TRANSACTION "%s cannot run inside a transaction block" SQL_ERROR_574 16 +42809 ERRCODE_WRONG_OBJECT_TYPE "\"%s\" is not a table or materialized view" SQL_ERROR_10610 16 +42809 ERRCODE_WRONG_OBJECT_TYPE "%s parameter should be of %s type" SQL_ERROR_16902 16 +22004 ERRCODE_NULL_VALUE_NOT_ALLOWED "%s parameter should not be null" SQL_ERROR_16902 16 +42601 ERRCODE_SYNTAX_ERROR "argument name \"%s\" used more than once" SQL_ERROR_8143 16 +22003 ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE "bigint out of range" SQL_ERROR_8115 16 +55006 ERRCODE_OBJECT_IN_USE "cannot %s \"%s\" because it is being used by active queries in this session" SQL_ERROR_556 16 +2BP01 ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST "cannot drop %s because %s requires it" SQL_ERROR_3723 16 +2BP01 ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST "cannot drop %s because other objects depend on it" SQL_ERROR_3732 16 "type" +2BP01 ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST "cannot drop %s because other objects depend on it" SQL_ERROR_3729 16 "function" +2BP01 ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST "cannot drop %s because other objects depend on it" SQL_ERROR_3726 16 "table" +2201E ERRCODE_INVALID_ARGUMENT_FOR_LOG "cannot take logarithm of a negative number" SQL_ERROR_3623 16 +2201F ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION "cannot take square root of a negative number" SQL_ERROR_3623 16 +0A000 ERRCODE_FEATURE_NOT_SUPPORTED "cannot truncate a table referenced in a foreign key constraint" SQL_ERROR_4712 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "CACHE (%s) must be greater than zero" SQL_ERROR_11706 16 +23514 ERRCODE_CHECK_VIOLATION "check constraint \"%s\" of relation \"%s\" is violated by some row" SQL_ERROR_547 16 +2F005 ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT "The transaction ended in the trigger. The batch has been aborted." SQL_ERROR_3609 16 +23502 ERRCODE_NOT_NULL_VIOLATION "column \"%s\" of relation \"%s\" contains null values" SQL_ERROR_4901 16 +42601 ERRCODE_SYNTAX_ERROR "column \"%s\" of relation \"%s\" is a generated column" SQL_ERROR_1752 16 +XX000 ERRCODE_INTERNAL_ERROR "CREATE FUNCTION failed because a column name is not specified for column %d" SQL_ERROR_4514 16 +42601 ERRCODE_SYNTAX_ERROR "CREATE VIEW specifies more column names than columns" SQL_ERROR_8159 16 +42704 ERRCODE_UNDEFINED_OBJECT "constraint \"%s\" of relation \"%s\" does not exist" SQL_ERROR_3728 16 +23505 ERRCODE_UNIQUE_VIOLATION "could not create unique index \"%s\"" SQL_ERROR_1505 16 +42883 ERRCODE_UNDEFINED_FUNCTION "could not find a function named \"%s\"" SQL_ERROR_3701 11 +42883 ERRCODE_UNDEFINED_FUNCTION "could not find a procedure named \"%s\"" SQL_ERROR_3701 11 +42883 ERRCODE_UNDEFINED_FUNCTION "could not identify an equality operator for type %s" SQL_ERROR_306 16 +42883 ERRCODE_UNDEFINED_FUNCTION "could not identify an ordering operator for type %s" SQL_ERROR_306 16 +42P03 ERRCODE_DUPLICATE_CURSOR "cursor \"%s\" already exists" SQL_ERROR_16915 16 +42704 ERRCODE_UNDEFINED_OBJECT "cursor %d doesn't exist" SQL_ERROR_16916 16 +22004 ERRCODE_NULL_VALUE_NOT_ALLOWED "cursor variable \"%s\" is null" SQL_ERROR_16950 16 +42P04 ERRCODE_DUPLICATE_DATABASE "database \"%s\" already exists" SQL_ERROR_1801 16 +3D000 ERRCODE_UNDEFINED_DATABASE "database \"%s\" does not exist" SQL_ERROR_3701 11 +22008 ERRCODE_DATETIME_VALUE_OUT_OF_RANGE "data out of range for datetime" SQL_ERROR_517 16 +40P01 ERRCODE_T_R_DEADLOCK_DETECTED "deadlock detected" SQL_ERROR_1205 13 +22012 ERRCODE_DIVISION_BY_ZERO "division by zero" SQL_ERROR_8134 16 +0A000 ERRCODE_FEATURE_NOT_SUPPORTED "Do not support BREAK outside of a WHILE loop, line %d" SQL_ERROR_135 15 +0A000 ERRCODE_FEATURE_NOT_SUPPORTED "Do not support CONTINUE outside of a WHILE loop, line %d" SQL_ERROR_136 15 +23505 ERRCODE_UNIQUE_VIOLATION "duplicate key value violates unique constraint \"%s\"" SQL_ERROR_2627 14 +42703 ERRCODE_UNDEFINED_COLUMN "Explicit value must be specified for identity column in table '%s' when IDENTITY_INSERT is set to ON" SQL_ERROR_545 16 +42804 ERRCODE_DATATYPE_MISMATCH "foreign key constraint \"%s\" cannot be implemented" SQL_ERROR_1778 16 +22013 ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE "frame ending offset must not be negative" SQL_ERROR_102 15 +22013 ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE "frame starting offset must not be negative" SQL_ERROR_102 15 +42723 ERRCODE_DUPLICATE_FUNCTION "function \"%s\" already exists with same argument types" SQL_ERROR_2714 16 +54023 ERRCODE_TOO_MANY_ARGUMENTS "functions cannot have more than %d argument" SQL_ERROR_180 15 +0A000 ERRCODE_FEATURE_NOT_SUPPORTED "GOTO target Label %s not defined" SQL_ERROR_133 15 +23001 ERRCODE_RESTRICT_VIOLATION "IDENTITY_INSERT is already ON for table \'%s.%s.%s\'" SQL_ERROR_8107 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "INCREMENT must not be zero" SQL_ERROR_11700 16 +22003 ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE "input is out of range" SQL_ERROR_3623 16 +54000 ERRCODE_PROGRAM_LIMIT_EXCEEDED "index row size %zu exceeds btree version %u maximum %zu for index \"%s\"" SQL_ERROR_1946 16 +23503 ERRCODE_FOREIGN_KEY_VIOLATION "insert or update on table \"%s\" violates foreign key constraint \"%s\"" SQL_ERROR_547 16 +22003 ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE "integer out of range" SQL_ERROR_8115 16 +42601 ERRCODE_SYNTAX_ERROR "invalid %s action for foreign key constraint containing generated column" SQL_ERROR_1715 16 "ON UPDATE" +42601 ERRCODE_SYNTAX_ERROR "invalid %s action for foreign key constraint containing generated column" SQL_ERROR_1765 16 "ON DELETE" +22003 ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE "invalid characters found: cannot cast value \"%s\" to money" SQL_ERROR_293 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "invalid cursor fetch type %X" SQL_ERROR_155 15 +22023 ERRCODE_INVALID_PARAMETER_VALUE "Invalid format specification: %s" SQL_ERROR_2787 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "invalid nrow value 0 for cursor type %X" SQL_ERROR_16902 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "Invalid parameter %d (\"%s\"): Data type 0x%02X is a deprecated large object, or LOB, but is marked as output parameter. Deprecated types are not supported as output parameters. Use current large object types instead." SQL_ERROR_8018 16 +22025 ERRCODE_INVALID_ESCAPE_SEQUENCE "invalid escape string" SQL_ERROR_506 16 +0A000 ERRCODE_FEATURE_NOT_SUPPORTED "Label %s not unique wihtin one procedure in line %d, previous defined in line %d" SQL_ERROR_132 15 +08004 ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION "Login failed for user \"%s\"" SQL_ERROR_18456 14 +22023 ERRCODE_INVALID_PARAMETER_VALUE "MAXVALUE (%s) is out of range for sequence data type tinyint" SQL_ERROR_11708 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "MAXVALUE (%s) is out of range for sequence data type %s" SQL_ERROR_11708 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "MINVALUE (%s) must be less than MAXVALUE (%s)" SQL_ERROR_11705 16 +21000 ERRCODE_CARDINALITY_VIOLATION "more than one row returned by a subquery used as an expression" SQL_ERROR_512 16 +23514 ERRCODE_CHECK_VIOLATION "new row for relation \"%s\" violates check constraint \"%s\"" SQL_ERROR_547 16 +44000 ERRCODE_WITH_CHECK_OPTION_VIOLATION "new row violates check option for view \"%s\"" SQL_ERROR_550 16 +23502 ERRCODE_NOT_NULL_VIOLATION "null value in column \"%s\" of relation \"%s\" violates not-null constraint" SQL_ERROR_515 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "msg_id argument of RAISERROR should be no less than 50000" SQL_ERROR_2732 16 +42704 ERRCODE_UNDEFINED_OBJECT "Prepared statement not found: %d" SQL_ERROR_8179 16 +22004 ERRCODE_NULL_VALUE_NOT_ALLOWED "Prepared statement not found: %d" SQL_ERROR_8179 16 +42809 ERRCODE_WRONG_OBJECT_TYPE "referenced relation \"%s\" is not a table" SQL_ERROR_1768 16 +42P07 ERRCODE_DUPLICATE_TABLE "relation \"%s\" already exists" SQL_ERROR_2714 16 +3B001 ERRCODE_S_E_INVALID_SPECIFICATION "savepoint \"%s\" does not exist" SQL_ERROR_6401 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "sequence type must be smallint, integer, or bigint" SQL_ERROR_11702 16 +22003 ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE "smallint out of range" SQL_ERROR_220 16 +54001 ERRCODE_STATEMENT_TOO_COMPLEX "stack depth limit exceeded" SQL_ERROR_217 16 +0A000 ERRCODE_FEATURE_NOT_SUPPORTED "SP_PREPEXECRPC not supported yet" SQL_ERROR_16901 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "START value (%s) cannot be greater than MAXVALUE (%s)" SQL_ERROR_11703 16 +22023 ERRCODE_INVALID_PARAMETER_VALUE "START value (%s) cannot be less than MINVALUE (%s)" SQL_ERROR_11703 16 +42703 ERRCODE_UNDEFINED_COLUMN "Table '%s.%s' does not have the identity property. Cannot perform SET operation." SQL_ERROR_8106 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type). The specified column is computed or default and has ordering or uniqueness set. Ordering and uniqueness can only be set on columns that have client supplied data." SQL_ERROR_8057 16 +2202H ERRCODE_INVALID_TABLESAMPLE_ARGUMENT "TABLESAMPLE parameter cannot be null" SQL_ERROR_477 15 +2202G ERRCODE_INVALID_TABLESAMPLE_REPEAT "TABLESAMPLE REPEATABLE parameter cannot be null" SQL_ERROR_477 15 +22023 ERRCODE_INVALID_PARAMETER_VALUE "The absolute value of the increment must be less than or equal to the difference between the minimum and maximum value of the sequence object." SQL_ERROR_11701 16 +40000 ERRCODE_TRANSACTION_ROLLBACK "The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction." SQL_ERROR_3930 16 +XX000 ERRCODE_INTERNAL_ERROR "The parameter \"%s\" can not be declared READONLY since it is not a table-valued parameter." SQL_ERROR_346 15 +XX000 ERRCODE_INTERNAL_ERROR "The table-valued parameter \"%s\" must be declared with the READONLY option." SQL_ERROR_352 15 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter %d (\"%s\"): Data type 0x%02X has an invalid data length or metadata length." SQL_ERROR_8016 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter %d (\"%s\"): Data type 0x%02X (sql_variant) has an invalid length for type-specific metadata." SQL_ERROR_8011 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter %d (\"%s\"): Data type 0x%02X is unknown." SQL_ERROR_8009 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter %d (\"%s\"): The chunking format is incorrect for a large object parameter of type 0x%02X." SQL_ERROR_8007 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter %d (\"%s\"): The supplied length is not valid for data type CHAR/NCHAR/VARCHAR/NVARCHAR. Check the source data for invalid lengths. An example of an invalid length is data of nchar type with an odd length in bytes." SQL_ERROR_8028 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter %d (\"%s\"): The supplied value is not a valid instance of data type Numeric/Decimal. Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision" SQL_ERROR_8023 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. The RPC name is invalid." SQL_ERROR_8004 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Too many parameters were provided in this RPC request. The maximum is %d" SQL_ERROR_8003 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type) has a non-zero length database name specified. Database name is not allowed with a table-valued parameter, only schema name and type name are valid." SQL_ERROR_8047 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type) has an invalid column count specified." SQL_ERROR_8050 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type) unexpected token encountered processing a table-valued parameter." SQL_ERROR_8029 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X has an invalid data length or metadata length." SQL_ERROR_8037 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X is unknown." SQL_ERROR_8032 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d (\"%s\"), row %d, column %d: The chunking format is incorrect for a large object parameter of data type 0x%02X." SQL_ERROR_8031 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d (\"%s\"), row %d, column %d: The supplied value is not a valid instance of data type Numeric/Decimal. Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision." SQL_ERROR_8043 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter %d, to a parameterized string has no table type defined." SQL_ERROR_8058 16 +08P01 ERRCODE_PROTOCOL_VIOLATION "The minimum number of parameters should be %d" SQL_ERROR_16903 16 +42830 ERRCODE_INVALID_FOREIGN_KEY "there is no unique constraint matching given keys for referenced table \"%s\"" SQL_ERROR_1776 16 +40002 ERRCODE_T_R_INTEGRITY_CONSTRAINT_VIOLATION "Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count %u current count %u" SQL_ERROR_266 16 +42704 ERRCODE_UNDEFINED_OBJECT "trigger \"%s\" does not exist" SQL_ERROR_3701 11 +42704 ERRCODE_UNDEFINED_OBJECT "trigger \"%s\" for table \"%s\" does not exist" SQL_ERROR_4920 16 +42710 ERRCODE_DUPLICATE_OBJECT "type \"%s\" already exists" SQL_ERROR_219 16 +23503 ERRCODE_FOREIGN_KEY_VIOLATION "update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"" SQL_ERROR_547 16 +22001 ERRCODE_STRING_DATA_RIGHT_TRUNCATION "value too long for type character(%d)" SQL_ERROR_8152 16 +22001 ERRCODE_STRING_DATA_RIGHT_TRUNCATION "value too long for type character varying(%d)" SQL_ERROR_8152 16 +23514 ERRCODE_CHECK_VIOLATION "value for domain %s violates check constraint \"%s\"" SQL_ERROR_220 16 +42P01 ERRCODE_UNDEFINED_TABLE "view \"%s\" does not exist" SQL_ERROR_3701 16 +42P01 ERRCODE_UNDEFINED_TABLE "table \"%s\" does not exist" SQL_ERROR_3701 16 diff --git a/contrib/babelfishpg_tds/generate_error_mapping.pl b/contrib/babelfishpg_tds/generate_error_mapping.pl new file mode 100644 index 00000000000..1c9c75fdbc9 --- /dev/null +++ b/contrib/babelfishpg_tds/generate_error_mapping.pl @@ -0,0 +1,35 @@ +#!/usr/bin/perl +# +# Generate the error_mapping.h header from error_mapping.txt + +use warnings; +use strict; + +print "/* autogenerated from error_mapping.txt, do not edit */\n"; + +open my $error_map_details, '<', $ARGV[0] or die; + +while (<$error_map_details>) +{ + chomp; + + # Skip comments + next if /^#/; + next if /^\s*$/; + + die unless /^([^\s]{5})\s+([^\s]+)\s(.*)(\sSQL_ERROR_)(\d{1,5})\s(\d{2})\s?(.*)?\s?/; + + (my $sqlstate, my $errcode_macro, my $error_msg, my $tsql_error_code, my $tsql_error_sev, my $error_msg_keywords) = + ($1, $2, $3, $5, $6, $7); + + next unless defined($error_msg); + + if ($error_msg_keywords eq "") + { + $error_msg_keywords="\"\""; + } + + print "{\n\t\"$sqlstate\",$error_msg, $tsql_error_code, $tsql_error_sev, $error_msg_keywords\n},\n\n"; +} + +close $error_map_details; diff --git a/contrib/babelfishpg_tds/src/backend/encoding/encoding_utils.c b/contrib/babelfishpg_tds/src/backend/encoding/encoding_utils.c new file mode 100644 index 00000000000..ec78aba183f --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/encoding/encoding_utils.c @@ -0,0 +1,121 @@ +#include "postgres.h" + +#include "access/xact.h" +#include "catalog/namespace.h" +#include "mb/pg_wchar.h" +#include "utils/builtins.h" +#include "utils/memutils.h" +#include "utils/syscache.h" + +#include "src/include/tds_int.h" + +static unsigned char *do_encoding_conversion(unsigned char *src, int len, int src_encoding, int dest_encoding); + +/* + * Convert server encoding to any encoding. + * + * See the notes about string conversion functions at the top of this file. + */ +char * +server_to_any(const char *s, int len, int encoding) +{ + if (len <= 0) + return (char *) s; /* empty string is always valid */ + + if (encoding == GetDatabaseEncoding() || + encoding == PG_SQL_ASCII) + return (char *) s; /* assume data is valid */ + + if (GetDatabaseEncoding() == PG_SQL_ASCII) + { + /* No conversion is possible, but we must validate the result */ + (void) pg_verify_mbstr(encoding, s, len, false); + return (char *) s; + } + return (char *) do_encoding_conversion((unsigned char *) s, + len, + GetDatabaseEncoding(), + encoding); +} + +/* + * Convert src string to another encoding (general case). + * + * See the notes about string conversion functions at the top of this file. + */ +static unsigned char * +do_encoding_conversion(unsigned char *src, int len, + int src_encoding, int dest_encoding) +{ + unsigned char *result; + + if (len <= 0) + return src; /* empty string is always valid */ + + if (src_encoding == dest_encoding) + return src; /* no conversion required, assume valid */ + + if (dest_encoding == PG_SQL_ASCII) + return src; /* any string is valid in SQL_ASCII */ + + if (src_encoding == PG_SQL_ASCII) + { + /* No conversion is possible, but we must validate the result */ + (void) pg_verify_mbstr(dest_encoding, (const char *) src, len, false); + return src; + } + + if (!IsTransactionState()) /* shouldn't happen */ + elog(ERROR, "cannot perform encoding conversion outside a transaction"); + /* + * Allocate space for conversion result, being wary of integer overflow. + * + * len * MAX_CONVERSION_GROWTH is typically a vast overestimate of the + * required space, so it might exceed MaxAllocSize even though the result + * would actually fit. We do not want to hand back a result string that + * exceeds MaxAllocSize, because callers might not cope gracefully --- but + * if we just allocate more than that, and don't use it, that's fine. + */ + if ((Size) len >= (MaxAllocHugeSize / (Size) MAX_CONVERSION_GROWTH)) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("out of memory"), + errdetail("String of %d bytes is too long for encoding conversion.", + len))); + + result = (unsigned char *) + MemoryContextAllocHuge(CurrentMemoryContext, + (Size) len * MAX_CONVERSION_GROWTH + 1); + + if (dest_encoding == PG_BIG5) + utf8_to_big5(src_encoding, dest_encoding, src, result, len); + else if (dest_encoding == PG_GBK) + utf8_to_gbk(src_encoding, dest_encoding, src, result, len); + else if (dest_encoding == PG_UHC) + utf8_to_uhc(src_encoding, dest_encoding, src, result, len); + else if (dest_encoding == PG_SJIS) + utf8_to_sjis(src_encoding, dest_encoding, src, result, len); + else + utf8_to_win(src_encoding, dest_encoding, src, result, len); + + /* + * If the result is large, it's worth repalloc'ing to release any extra + * space we asked for. The cutoff here is somewhat arbitrary, but we + * *must* check when len * MAX_CONVERSION_GROWTH exceeds MaxAllocSize. + */ + if (len > 1000000) + { + Size resultlen = strlen((char *) result); + + if (resultlen >= MaxAllocSize) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("out of memory"), + errdetail("String of %d bytes is too long for encoding conversion.", + len))); + + result = (unsigned char *) repalloc(result, resultlen + 1); + } + + return result; +} diff --git a/contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection.c b/contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection.c new file mode 100644 index 00000000000..2e4e1798b7f --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection.c @@ -0,0 +1,403 @@ +/*------------------------------------------------------------------------- + * + * fault_injection.c + * Fault Injection Framework + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "fmgr.h" +#include "lib/stringinfo.h" +#include "miscadmin.h" +#include "utils/builtins.h" +#include "utils/hsearch.h" +#include "utils/memutils.h" + +#include "src/include/faultinjection.h" +#include "src/include/tds_int.h" + +extern Datum inject_fault(PG_FUNCTION_ARGS); +extern Datum inject_fault_status(PG_FUNCTION_ARGS); +extern Datum trigger_test_fault(PG_FUNCTION_ARGS); +extern Datum inject_fault_status_all(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(inject_fault); +PG_FUNCTION_INFO_V1(inject_fault_status); +PG_FUNCTION_INFO_V1(trigger_test_fault); +PG_FUNCTION_INFO_V1(inject_fault_status_all); + +bool trigger_fault_injection = true; +static HTAB *faultInjectorHash = NULL; + +int tamperByte = INVALID_TAMPER_BYTE; + +/* + * FaultInjectionHashInit - initialize the hash + */ +static void +FaultInjectionHashInit() +{ + HASHCTL hash_ctl; + MemoryContext oldContext; + + oldContext = MemoryContextSwitchTo(TopMemoryContext); + /* Local cache */ + MemSet(&hash_ctl, 0, sizeof(hash_ctl)); + hash_ctl.keysize = FAULT_NAME_MAX_LENGTH; + hash_ctl.entrysize = sizeof(FaultInjectorEntry_s); + faultInjectorHash = hash_create("Fault Injection Cache", + 16, + &hash_ctl, + HASH_ELEM); + MemoryContextSwitchTo(oldContext); +} + +/* + * FaultInjectionInitialize - initializa the fault injection hash with enrties + */ +static void +FaultInjectionInitialize() +{ + int i = 0; + bool foundPtr; + + if (faultInjectorHash == NULL) + FaultInjectionHashInit(); + + do + { + const FaultInjectorEntry_s *entry = &Faults[i]; + FaultInjectorEntry_s *new_entry; + + if (entry->type == InvalidType) + break; + new_entry = (FaultInjectorEntry_s *) hash_search( + faultInjectorHash, + (void *) entry->faultName, //key + HASH_ENTER, + &foundPtr); + /* should not try to insert same entry multiple times */ + Assert(foundPtr == false); + + if (entry == NULL) + { + ereport(DEBUG5, + (errmsg("FaultInjectionLookupHashEntry() could not insert fault injection hash entry:'%s' ", + entry->faultName))); + } + + new_entry->type = entry->type; + new_entry->num_occurrences = 0; + new_entry->fault_callback = entry->fault_callback; + + i++; + } while(true); +} + +/* + * FaultInjectionLookupHashEntry - look for the entry + */ +static FaultInjectorEntry_s * +FaultInjectionLookupHashEntry(const char *faultName) +{ + FaultInjectorEntry_s *entry; + + if (faultInjectorHash == NULL) + FaultInjectionInitialize(); + + entry = (FaultInjectorEntry_s *) hash_search( + faultInjectorHash, + (void *) faultName, //key + HASH_FIND, + NULL); + + if (entry == NULL) + { + ereport(ERROR, + (errmsg("FaultInjectionLookupHashEntry() could not find fault injection hash entry:'%s' ", + faultName))); + } + + return entry; +} + +static void +FaultInjectionEnableTest(FaultInjectorEntry_s *entry) +{ + List *list = FaultInjectionTypes[entry->type].injected_entries; + ListCell *lc; + MemoryContext oldContext; + + + foreach(lc, list) + { + if (entry == (FaultInjectorEntry_s *) lfirst(lc)) + return; + } + + oldContext = MemoryContextSwitchTo(TopMemoryContext); + list = lappend(list, entry); + MemoryContextSwitchTo(oldContext); + + FaultInjectionTypes[entry->type].injected_entries = list; +} + +static inline void +FaultInjectionDisableTest(FaultInjectorEntry_s *entry) +{ + List *list = FaultInjectionTypes[entry->type].injected_entries; + + if (list_length(list) == 1) + { + list_free(list); + list = NIL; + } + else + list = list_delete(list, entry); + + tamperByte = INVALID_TAMPER_BYTE; + FaultInjectionTypes[entry->type].injected_entries = list; +} + +static char* +FetchFaultStatus(char *faultName) +{ + StringInfo buf = makeStringInfo(); + FaultInjectorEntry_s *entry; + + entry = FaultInjectionLookupHashEntry(faultName); + + if (entry->num_occurrences == 0) + appendStringInfo(buf, "disabled, Type: %s", + FaultInjectionTypes[entry->type].faultTypeName); + else + appendStringInfo(buf, "enabled, Type: %s, pending occurrences: %d", + FaultInjectionTypes[entry->type].faultTypeName, + entry->num_occurrences); + + return buf->data; +} + +static char * +InjectFault(const char *faultName, int num_occurrences, int tamper_byte) +{ + StringInfo buf = makeStringInfo(); + FaultInjectorEntry_s *entry; + + entry = FaultInjectionLookupHashEntry(faultName); + if (entry->num_occurrences == 0 && num_occurrences > 0) + FaultInjectionEnableTest(entry); + else if (entry->num_occurrences > 0 && num_occurrences == 0) + FaultInjectionDisableTest(entry); + + entry->num_occurrences = num_occurrences; + tamperByte = tamper_byte; + + if (entry->num_occurrences == 0) + appendStringInfo(buf, "disabled"); + else if (tamperByte != INVALID_TAMPER_BYTE) + appendStringInfo(buf, "enabled, pending occurrences: %d, tamper byte value: %d", + entry->num_occurrences, tamperByte); + else + appendStringInfo(buf, "enabled, pending occurrences: %d", entry->num_occurrences); + + return buf->data; +} + +void +TriggerFault(FaultInjectorType_e type, void *arg) +{ + List *list = FaultInjectionTypes[type].injected_entries; + List *tmp_list = NIL; + ListCell *lc; + + /* if triggering is disabled, return */ + if (!trigger_fault_injection || list_length(list) == 0) + return; + + TDS_DEBUG(TDS_DEBUG1, "Triggering fault type: %s", FaultInjectionTypes[type].faultTypeName); + + /* Fast Path when entry is just 1 */ + if (list_length(list) == 1) + { + FaultInjectorEntry_s *entry; + + lc = list_head(list); + entry = (FaultInjectorEntry_s *) lfirst(lc); + + /* otherwise it should have been removed */ + Assert(entry->num_occurrences > 0); + + entry->num_occurrences--; + + PG_TRY(); + { + TDS_DEBUG(TDS_DEBUG2, "Triggering fault: %s", entry->faultName); + (*(entry->fault_callback)) (arg); + } + PG_CATCH(); + { + if (entry->num_occurrences == 0) + FaultInjectionDisableTest(entry); + + PG_RE_THROW(); + } + PG_END_TRY(); + + if (entry->num_occurrences == 0) + FaultInjectionDisableTest(entry); + + return; + } + + /* + * If there is more than one entry, we've to be careful while removing + * entries from the list while traversing the same. + */ + foreach(lc, list) + { + FaultInjectorEntry_s *entry = (FaultInjectorEntry_s *) lfirst(lc); + + /* otherwise it should have been removed */ + Assert(entry->num_occurrences > 0); + + entry->num_occurrences--; + + PG_TRY(); + { + TDS_DEBUG(TDS_DEBUG2, "Triggering fault: %s", entry->faultName); + (*(entry->fault_callback)) (arg); + } + PG_CATCH(); + { + if (entry->num_occurrences == 0) + tmp_list = lappend(tmp_list, entry); + + foreach(lc, tmp_list) + { + FaultInjectorEntry_s *entry = (FaultInjectorEntry_s *) lfirst(lc); + + FaultInjectionDisableTest(entry); + } + + list_free(tmp_list); + + PG_RE_THROW(); + } + PG_END_TRY(); + + if (entry->num_occurrences == 0) + tmp_list = lappend(tmp_list, entry); + } + + foreach(lc, tmp_list) + { + FaultInjectorEntry_s *entry = (FaultInjectorEntry_s *) lfirst(lc); + + FaultInjectionDisableTest(entry); + } + + list_free(tmp_list); +} + +/* + * InjectFaultAll - inject all the faults if enable = true, disable otherwise + * + * It enables the faults with occurences as 1 + */ +static char* +InjectFaultAll(bool enable) +{ + int i = 0; + StringInfo response = makeStringInfo(); + + do + { + char *ret; + const FaultInjectorEntry_s *entry = &Faults[i]; + + if (entry->type == InvalidType) + break; + ret = InjectFault(entry->faultName, (enable) ? 1 : 0, INVALID_TAMPER_BYTE); + + if (!ret) + elog(ERROR, "failed to inject fault"); + + pfree(ret); + + i++; + } while(true); + + appendStringInfo(response, "success"); + + return response->data; +} + +Datum +inject_fault(PG_FUNCTION_ARGS) +{ + char *faultName = TextDatumGetCString(PG_GETARG_DATUM(0)); + int num_occurrences = PG_GETARG_INT32(1); + char *response; + int nargs = PG_NARGS(); + int tamper_byte = INVALID_TAMPER_BYTE; + + if (nargs > 2) + tamper_byte = PG_GETARG_INT32(2); + + if (num_occurrences < 0) + elog(ERROR, "number of occurrences cannot be negative"); + + /* check if we need to enable/disable all the tests */ + if (strcmp(faultName, "all") == 0 && num_occurrences > 0) + response = InjectFaultAll(true); + else if (strcmp(faultName, "all") == 0 && num_occurrences == 0) + response = InjectFaultAll(false); + else + response = InjectFault(faultName, num_occurrences, tamper_byte); + if (!response) + elog(ERROR, "failed to inject fault"); + + PG_RETURN_TEXT_P(cstring_to_text(response)); +} + +Datum +inject_fault_status(PG_FUNCTION_ARGS) +{ + char *faultName = TextDatumGetCString(PG_GETARG_DATUM(0)); + char *response; + + response = FetchFaultStatus(faultName); + if (!response) + elog(ERROR, "failed to fetch injected fault status"); + + PG_RETURN_TEXT_P(cstring_to_text(response)); +} + +Datum +inject_fault_status_all(PG_FUNCTION_ARGS) +{ + /* TODO */ + PG_RETURN_VOID(); +} + +Datum +trigger_test_fault(PG_FUNCTION_ARGS) +{ + StringInfo buf = makeStringInfo(); + + TriggerFault(TestType, (void *) buf); + if (!buf) + elog(ERROR, "failed to trigger fault"); + + PG_RETURN_TEXT_P(cstring_to_text(buf->data)); +} diff --git a/contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection_tests.c b/contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection_tests.c new file mode 100644 index 00000000000..ec793e398c4 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection_tests.c @@ -0,0 +1,239 @@ +/*------------------------------------------------------------------------- + * + * fault_injection_tests.c + * TDS test cases for Fault Injection Framework + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection_tests.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "lib/stringinfo.h" +#include "miscadmin.h" + +#include "src/include/tds_int.h" +#include "src/include/tds_request.h" +#include "src/include/faultinjection.h" + +/* test cases */ +static void +test_fault1(void *arg) +{ + StringInfo buf = (StringInfo) arg; + + if (buf->len > 0) + appendStringInfo(buf, ", "); + + appendStringInfo(buf, "test_fault1"); +} + +static void +test_fault2(void *arg) +{ + StringInfo buf = (StringInfo) arg; + + if (buf->len > 0) + appendStringInfo(buf, ", "); + + appendStringInfo(buf, "test_fault2"); +} + +/* + * In this function, we tamper bytes of the input argument sequentially and + * call TDS parser function. The expectation is the parser code can throw and + * error, but it should not crash. + */ +static void +tamper_request_sequential(void *arg, char tamper_byte) +{ + struct TdsMessageWrapper *wrapper = (struct TdsMessageWrapper *) arg; + StringInfo buf = wrapper->message; + StringInfo tmp = makeStringInfo(); + MemoryContext oldcontext; + uint64_t offset = 0; + int i; + uint32_t tdsVersion = GetClientTDSVersion(); + + /* Skip if its an Attention Request. */ + if (wrapper->messageType == TDS_ATTENTION) + return; + oldcontext = MemoryContextSwitchTo(MessageContext); + + /* + * Skip the offset part, otherwise, we'll throw FATAL error and terminate + * the connection + * + * Note: In the ALL_HEADERS rule, the Query Notifications header and the Transaction + * Descriptor header were introduced in TDS 7.2. We need to to Process them only + * for TDS versions more than or equal to 7.2, otherwise we do not increment the offset. + */ + if (tdsVersion > TDS_VERSION_7_1_1) + offset = ProcessStreamHeaders(buf);; + for (i = offset; i < buf->len; i++) + { + PG_TRY(); + { + appendBinaryStringInfoNT(tmp, buf->data, buf->len); + + tmp->data[i] = tamper_byte; + + switch (wrapper->messageType) + { + case TDS_QUERY: /* Simple SQL BATCH */ + { + (void) GetSQLBatchRequest(tmp); + } + break; + case TDS_RPC: /* Remote procedure call */ + { + (void) GetRPCRequest(tmp); + } + break; + case TDS_TXN: /* Transaction management request */ + { + (void) GetTxnMgmtRequest(tmp); + } + break; + } + } + PG_CATCH(); + { + FlushErrorState(); + } + PG_END_TRY(); + + resetStringInfo(tmp); + MemoryContextReset(MessageContext); + } + + MemoryContextSwitchTo(oldcontext); + + pfree(tmp->data); + pfree(tmp); +} + +static void +pre_parsing_tamper_request(void *arg) +{ + /* tamper byte with all 0s */ + tamper_request_sequential(arg, 0x00); + /* tamper byte with all Fs */ + tamper_request_sequential(arg, 0xFF); + /* tamper byte with a random byte value */ + tamper_request_sequential(arg, (10 * rand() % 0xFF)); +} + + +/* + * In this function, we tamper bytes at particular offset and call + * call TDS RPC parser function. The expectation is the parser code can throw and + * error, but it should not crash. + */ +static void +tamper_rpc_request(void *arg, uint64_t offset, int tamper_byte) +{ + struct TdsMessageWrapper *wrapper = (struct TdsMessageWrapper *) arg; + StringInfo buf = wrapper->message; + StringInfo tmp = makeStringInfo(); + + MemoryContext oldcontext = MemoryContextSwitchTo(MessageContext); + + PG_TRY(); + { + appendBinaryStringInfoNT(tmp, buf->data, buf->len); + + tmp->data[offset] = tamper_byte; + + (void) GetRPCRequest(tmp); + } + PG_CATCH(); + { + FlushErrorState(); + } + PG_END_TRY(); + + resetStringInfo(tmp); + MemoryContextReset(MessageContext); + + MemoryContextSwitchTo(oldcontext); + + pfree(tmp->data); + pfree(tmp); +} + +static void +pre_parsing_tamper_rpc_request_sptype(void *arg) +{ + uint64_t offset = 0; + + if (GetClientTDSVersion() > TDS_VERSION_7_1_1) + offset = ProcessStreamHeaders(((struct TdsMessageWrapper *) arg)->message); + + offset += 2; /* Skip length. */ + + if (tamperByte != INVALID_TAMPER_BYTE) + tamper_rpc_request(arg, offset, tamperByte); + else + tamper_rpc_request(arg, offset, rand() % 0xFF); + +} + +static void +parsing_tamper_rpc_parameter_datatype(void *arg) +{ + if (tamperByte != INVALID_TAMPER_BYTE) + tamper_rpc_request(arg, ((struct TdsMessageWrapper *) arg)->offset, tamperByte); + else + tamper_rpc_request(arg, ((struct TdsMessageWrapper *) arg)->offset, rand() % 0xFF); +} + +static void +throw_error(void *arg) +{ + elog(ERROR, "error triggered from fault injection"); +} + +/* + * Type declarations + * + * Format: {Enum type, Type name, Callback list} + * + * Enum type: type from FaultInjectorType_e + * Type name: user visible name for this type + * Callback list: fault callback list for this type; set it to NIL + */ +TEST_TYPE_LIST = { + {TestType, "Test", NIL}, + {ParseHeaderType, "TDS request header", NIL}, + {PreParsingType, "TDS pre-parsing", NIL}, + {PostParsingType, "TDS post-parsing", NIL}, + {ParseRpcType, "TDS RPC Parsing", NIL} +}; + +/* + * Test declarations + * + * Format: {Test name, Type name, 0, Callback function} + * + * Test name: name of the test used to trigger this fault + * Type name: type of the test + * Callback function: callback function executed when this test is triggered + */ +TEST_LIST = { + {"test_fault1", TestType, 0, &test_fault1}, + {"test_fault2", TestType, 0, &test_fault2}, + {"tds_comm_throw_error", ParseHeaderType, 0, &throw_error}, + {"pre_parsing_tamper_request", PreParsingType, 0, &pre_parsing_tamper_request}, + {"pre_parsing_tamper_rpc_request_sptype", PreParsingType, 0, &pre_parsing_tamper_rpc_request_sptype}, + {"parsing_tamper_rpc_parameter_datatype", ParseRpcType, 0, &parsing_tamper_rpc_parameter_datatype}, + {"pre_parsing_throw_error", PreParsingType, 0, &throw_error}, + {"post_parsing_throw_error", PostParsingType, 0, &throw_error}, + {"", InvalidType, 0, NULL} /* keep this as last */ +}; diff --git a/contrib/babelfishpg_tds/src/backend/tds/err_handler.c b/contrib/babelfishpg_tds/src/backend/tds/err_handler.c new file mode 100644 index 00000000000..bd8d3bb4a0a --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/err_handler.c @@ -0,0 +1,319 @@ +#include + +#include "postgres.h" + +#include "common/hashfn.h" +#include "miscadmin.h" +#include "nodes/bitmapset.h" +#include "utils/elog.h" +#include "utils/hsearch.h" +#include "utils/palloc.h" /* Needed for pstrdup() */ + +#include "src/include/tds_int.h" +#include "src/include/tds_response.h" +#include "src/include/err_handler.h" +#include "src/include/tds_instr.h" + +static bool is_user_defined_error(int pg_error_code); + +bool tds_disable_error_log_hook = false; +static HTAB *error_map_hash = NULL; + +extern bool GetTdsEstateErrorData(int *number, int *severity, int *state); + +error_map_details error_list[] = { + #include "src/include/error_mapping.h" + {"00000", NULL, 0, 0, NULL} +}; + +/* + * Returns list of sql error code for which Babel does have support for. + */ +int * +get_mapped_error_code_list() +{ + int i; + int *list; /* Temp list to store list of mapped sql error codes and its length. */ + Bitmapset *tmp = NULL; /* To store the unique sql error codes. */ + int tmp_len = 0; /* To store number of unique sql error codes. */ + int prev_idx = -1; /* To retrieve all members of set. */ + int len = sizeof(error_list)/sizeof(error_list[0]); + for (i = 0; i < len - 1; i++) + { + if (!bms_is_member(error_list[i].tsql_error_code, tmp)) + { + /* If given sql error code is not already present in set.*/ + tmp = bms_add_member(tmp, error_list[i].tsql_error_code); + tmp_len += 1; + } + } + + list = palloc0((tmp_len + 1) * sizeof(int)); + list[0] = tmp_len; + i = 1; + while ((prev_idx = bms_next_member(tmp, prev_idx)) >= 0) + { + list[i] = prev_idx; + i += 1; + } + + bms_free(tmp); + return list; +} + +/* + * load_err_code_mapping() - loads error code mapping details in HASH table. + */ +void +load_error_mapping() +{ + HASHCTL hashCtl; + int i, len = sizeof(error_list)/sizeof(error_list[0]); + + /* For now, we don't allow user to update the mapping. */ + if (error_map_hash != NULL) + return ; + + MemSet(&hashCtl, 0, sizeof(hashCtl)); + hashCtl.keysize = sizeof(error_map_key); + hashCtl.entrysize = sizeof(error_map); + hashCtl.hcxt = TdsMemoryContext; + error_map_hash = hash_create("Error code mapping cache", + len, + &hashCtl, + HASH_ELEM | HASH_CONTEXT | HASH_BLOBS); + + for (i = 0; i < len - 1; i++) + { + error_map_info map_info; + error_map_key key_info; + bool found; + key_info.sqlerrcode = MAKE_SQLSTATE(error_list[i].sql_state[0], + error_list[i].sql_state[1], + error_list[i].sql_state[2], + error_list[i].sql_state[3], + error_list[i].sql_state[4]); + key_info.message_hash = (uint32) hash_any((unsigned char *)error_list[i].error_message, strlen(error_list[i].error_message)); + map_info = (error_map_info) hash_search(error_map_hash, + &key_info, + HASH_ENTER, + &found); + if (found) + { + error_map_node *head = map_info->head; + error_map_node *tmp = (error_map_node *)palloc0(sizeof(error_map_node)); + tmp->error_msg_keywords = error_list[i].error_msg_keywords; + tmp->tsql_error_code = error_list[i].tsql_error_code; + tmp->tsql_error_severity = error_list[i].tsql_error_severity; + tmp->next = head; + map_info->head = tmp; + } + else + { + error_map_node *tmp = (error_map_node *)palloc0(sizeof(error_map_node)); + tmp->error_msg_keywords = error_list[i].error_msg_keywords; + tmp->tsql_error_code = error_list[i].tsql_error_code; + tmp->tsql_error_severity = error_list[i].tsql_error_severity; + tmp->next = NULL; + map_info->head = tmp; + } + } +} + +bool +get_tsql_error_details(ErrorData *edata, + int *tsql_error_code, + int *tsql_error_severity, + int *tsql_error_state, + char *error_context) +{ + error_map_info map_info; + error_map_key key_info; + bool found; + + /* Skip mapping if this is a user-defined error */ + if (is_user_defined_error(edata->sqlerrcode)) + { + if (GetTdsEstateErrorData(tsql_error_code, tsql_error_severity, tsql_error_state)) + return true; + + /* Failed to find reliable user-defined error data, use default values */ + *tsql_error_code = 50000; + *tsql_error_severity = 16; + *tsql_error_state = 1; + + return true; + } + + /* + * This condition is useful when error is thrown before + * initialising the hash table. In that case, load hash + * table immediately. + */ + if (error_map_hash == NULL) + { + MemoryContext oldContext = MemoryContextSwitchTo(TdsMemoryContext); + load_error_mapping(); + MemoryContextSwitchTo(oldContext); + } + + key_info.message_hash = (uint32) hash_any((unsigned char *)edata->message_id, strlen(edata->message_id)); + key_info.sqlerrcode = edata->sqlerrcode; + + map_info = (error_map_info) hash_search(error_map_hash, + &key_info, + HASH_FIND, + &found); + + /* For all system generated errors, error state is default to be 1 */ + *tsql_error_state = 1; + + /* TODO: Ideally we should have mapping for every error. */ + if (!found) + { + *tsql_error_code = ERRCODE_PLTSQL_ERROR_NOT_MAPPED; + *tsql_error_severity = 16; + + TDSInstrumentation(INSTR_TDS_UNMAPPED_ERROR); + + elog(LOG, "Unmapped error found. Code: %d, Message: %s, File: %s, Line: %d, Context: %s", + edata->sqlerrcode, edata->message, edata->filename, edata->lineno, error_context); + + return false; + } + else + { + bool flag = false; + error_map_node *tmp = map_info->head; + + while (tmp) + { + if (!tmp->error_msg_keywords) + elog(FATAL, "Error message keyword is NULL (internal error)"); + + if (strlen(tmp->error_msg_keywords) == 0) + { + flag = true; + *tsql_error_code = tmp->tsql_error_code; + *tsql_error_severity = tmp->tsql_error_severity; + } + else + { + /* All key words should be matched to qualify it as a correct tsql error details.*/ + char *key_word; + char *tmp_keywords = pstrdup(tmp->error_msg_keywords); + flag = true; + /* + * According to document of strtok(), passed string is modify + * by being broken into smaller strings (tokens). + * Certian platforms does not allow to modify the string + * literal. Attempting to do so will result in segmentation + * fault. So, here we are storing string literal into temp string + * and then passing it into strtok(). + */ + key_word = strtok(tmp_keywords, "#"); + while (key_word != NULL) + { + if (!strcasestr(edata->message, key_word)) + { + flag = false; + break; + } + key_word = strtok(NULL, "#"); + } + if (flag) + { + *tsql_error_code = tmp->tsql_error_code; + *tsql_error_severity = tmp->tsql_error_severity; + pfree(tmp_keywords); + return true; + } + pfree(tmp_keywords); + } + tmp = tmp->next; + } + /* + * If appropriate tsql error code could not be found then use PG error code as a default. + */ + if (!flag) + { + *tsql_error_code = ERRCODE_PLTSQL_ERROR_NOT_MAPPED; + *tsql_error_severity = 16; + return false; + } + } + return true; +} + +void +emit_tds_log(ErrorData *edata) +{ + int tsql_error_code, tsql_error_sev, tsql_error_state, error_lineno; + + /* + * We've already sent the error token to the TDS client. We don't have to + * send the error to a psql client. So, turn it off. + */ + edata->output_to_client = false; + + /* If disabled, return from here */ + if (tds_disable_error_log_hook) + return; + + /* disable further entry to this function to avoid recursion */ + tds_disable_error_log_hook = true; + + if (edata->elevel < ERROR) + { + elog(DEBUG5, "suppressing informational client message < ERROR"); + + /* reset the flag */ + tds_disable_error_log_hook = false; + return; + } + + get_tsql_error_details(edata, &tsql_error_code, &tsql_error_sev, &tsql_error_state, "TDS"); + error_lineno = 1; + if (pltsql_plugin_handler_ptr && pltsql_plugin_handler_ptr->pltsql_current_lineno && *(pltsql_plugin_handler_ptr->pltsql_current_lineno) > 0) + error_lineno = *(pltsql_plugin_handler_ptr->pltsql_current_lineno); + + TdsSendError(tsql_error_code, tsql_error_state, tsql_error_sev, + edata->message, error_lineno); + + /* + * If we've not reached the main query loop yet, flush the error message + * immediately. + */ + if (!IsNormalProcessingMode()) + { + /* + * As of now, we can only reach here if we get any error during + * prelogin and login phase. + */ + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_ERROR, 0, 0); + TdsFlush(); + } + + /* reset the flag */ + tds_disable_error_log_hook = false; +} + + +void +reset_error_mapping_cache() +{ + error_map_hash = NULL; +} + +/* + * Define whether this is a user-defined error + */ +static bool +is_user_defined_error(int pg_error_code) +{ + if (pg_error_code == ERRCODE_PLTSQL_RAISERROR || + pg_error_code == ERRCODE_PLTSQL_THROW) + return true; + + return false; +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/guc.c b/contrib/babelfishpg_tds/src/backend/tds/guc.c new file mode 100644 index 00000000000..d07c33dd247 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/guc.c @@ -0,0 +1,303 @@ +/*------------------------------------------------------------------------- + * + * guc.c + * TDS configuration variables + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/guc.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "miscadmin.h" +#include "utils/guc.h" + +#include "src/include/tds_int.h" +#include "src/include/tds_response.h" +#include "src/include/tds_secure.h" +#include "src/include/faultinjection.h" +#include "src/include/guc.h" + +/* Global variables */ +int pe_port; +char *pe_listen_addrs = NULL; +char *pe_unix_socket_directories = NULL; +int pe_unix_socket_permissions = 0; +char *pe_unix_socket_group = NULL; + +char *default_server_name = NULL; +int tds_default_numeric_precision = 38; +int tds_default_numeric_scale = 8; +bool tds_ssl_encrypt = false; +int tds_default_protocol_version = 0; +int32_t tds_default_packet_size = 4096; +int tds_debug_log_level = 1; +bool tds_enable_db_session_property = true; +#ifdef FAULT_INJECTOR +static bool TdsFaultInjectionEnabled = false; +#endif + +const struct config_enum_entry ssl_protocol_versions_info[] = { + {"", PG_TLS_ANY, false}, + {"TLSv1", PG_TLS1_VERSION, false}, + {"TLSv1.1", PG_TLS1_1_VERSION, false}, + {"TLSv1.2", PG_TLS1_2_VERSION, false}, + {NULL, 0, false} +}; + +const struct config_enum_entry tds_protocol_versions_info[] = { + {"TDSv7.0", TDS_VERSION_7_0, false}, + {"TDSv7.1", TDS_VERSION_7_1, false}, + {"TDSv7.1.1", TDS_VERSION_7_1_1, false}, + {"TDSv7.2", TDS_VERSION_7_2, false}, + {"TDSv7.3A", TDS_VERSION_7_3_A, false}, + {"TDSv7.3B", TDS_VERSION_7_3_B, false}, + {"TDSv7.4", TDS_VERSION_7_4, false}, + {"DEFAULT", TDS_DEFAULT_VERSION, false}, + {NULL, 0, false} +}; + +/* -------------------------------- + * TdsSslProtocolMinVersionCheck - check for Tds ssl min Protocol Vesion GUC + * ------------------------------- + */ +static bool +TdsSslProtocolMinVersionCheck(int *newvalue, void **extra, GucSource source) +{ + if (*newvalue <= tds_ssl_max_protocol_version) + return true; + else + { + GUC_check_errmsg("TDS SSL Min Protocol Version 0x%X more than TDS SSL Max Protocol Version 0x%x", + *newvalue, tds_ssl_max_protocol_version); + return false; + } +} + +/* -------------------------------- + * TdsSslProtocolMaxVersionCheck - check for Tds ssl max Protocol Vesion GUC + * ------------------------------- + */ +static bool +TdsSslProtocolMaxVersionCheck(int *newvalue, void **extra, GucSource source) +{ + if (*newvalue >= tds_ssl_min_protocol_version) + return true; + else + { + GUC_check_errmsg("TDS SSL Max Protocol Version 0x%X less than TDS SSL Min Protocol Version 0x%x", + *newvalue, tds_ssl_min_protocol_version); + return false; + } +} + +/* -------------------------------- + * TdsGucDefaultPacketSizeCheck - Using this function to Assign the + * appropriate value to the GUC. In TDS, the packet + * Size is rounded down to the nearest multiple of 4. + * ------------------------------- + */ +static bool +TdsGucDefaultPacketSizeCheck(int *newvalue, void **extra, GucSource source) +{ + *newvalue = (((int) *newvalue / 4) * 4); + return true; +} + +/* + * Define various GUCs which are part of TDS protocol + */ +void +TdsDefineGucs(void) +{ + /* Define TDS specific GUCs */ + DefineCustomIntVariable( + "babelfishpg_tds.port", + gettext_noop("Sets the TDS TCP port the server listens on."), + NULL, + &pe_port, + 1433, 1024, 65536, + PGC_POSTMASTER, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomStringVariable( + "babelfishpg_tds.listen_addresses", + gettext_noop("Sets the host name or IP address(es) to listen TDS to."), + NULL, + &pe_listen_addrs, + "*", + PGC_POSTMASTER, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomStringVariable( + "babelfishpg_tds.unix_socket_directories", + gettext_noop("TDS server unix socket directories."), + NULL, + &pe_unix_socket_directories, + NULL, + PGC_POSTMASTER, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomIntVariable( + "babelfishpg_tds.unix_socket_permissions", + gettext_noop("TDS server unix socket permissions."), + NULL, + &pe_unix_socket_permissions, + 0777, 0, 0777, + PGC_POSTMASTER, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomStringVariable( + "babelfishpg_tds.unix_socket_group", + gettext_noop("TDS server unix socket group."), + NULL, + &pe_unix_socket_group, + NULL, + PGC_POSTMASTER, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomStringVariable( + "babelfishpg_tds.default_server_name", + gettext_noop("Predefined Babelfish default server name"), + NULL, + &default_server_name, + TDS_DEFAULT_SERVER_NAME, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomIntVariable( + "babelfishpg_tds.tds_default_numeric_precision", + gettext_noop("Sets the default precision of numeric type to be sent in" + "the TDS column metadata if the engine does not specify one."), + NULL, + &tds_default_numeric_precision, + 38, 1, 38, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomIntVariable( + "babelfishpg_tds.tds_default_numeric_scale", + gettext_noop("Sets the default scale of numeric type to be sent in" + "the TDS column metadata if the engine does not specify one."), + NULL, + &tds_default_numeric_scale, + 8, 0, 38, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomBoolVariable( + "babelfishpg_tds.tds_ssl_encrypt", + gettext_noop("Sets the SSL Encryption option"), + NULL, + &tds_ssl_encrypt, + false, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + + DefineCustomEnumVariable( + "babelfishpg_tds.tds_default_protocol_version", + gettext_noop("Sets a default TDS protocol version for" + "all the clients being connected"), + NULL, + &tds_default_protocol_version, + TDS_DEFAULT_VERSION, tds_protocol_versions_info, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + DefineCustomEnumVariable( + "babelfishpg_tds.tds_ssl_max_protocol_version", + gettext_noop("Sets the minimum SSL/TLS protocol version to use" + "for tds session."), + NULL, + &tds_ssl_max_protocol_version, + PG_TLS1_2_VERSION, ssl_protocol_versions_info + 1, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + TdsSslProtocolMaxVersionCheck, + NULL, + NULL); + + DefineCustomEnumVariable( + "babelfishpg_tds.tds_ssl_min_protocol_version", + gettext_noop("Sets the minimum SSL/TLS protocol version to use" + "for tds session."), + NULL, + &tds_ssl_min_protocol_version, + PG_TLS1_VERSION, ssl_protocol_versions_info, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + TdsSslProtocolMinVersionCheck, + NULL, + NULL); + + DefineCustomIntVariable( + "babelfishpg_tds.tds_default_packet_size", + gettext_noop("Sets the default packet size for" + "all the clients being connected"), + NULL, + &tds_default_packet_size, + 4096, 512, 32767, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + TdsGucDefaultPacketSizeCheck, + NULL, + NULL); + + DefineCustomIntVariable( + "babelfishpg_tds.tds_debug_log_level", + gettext_noop("Sets the tds debug log level"), + NULL, + &tds_debug_log_level, + 1, 0, 3, + PGC_SIGHUP, + GUC_NOT_IN_SAMPLE, + NULL, + NULL, + NULL); + + DefineCustomBoolVariable( + "babelfishpg_tds.set_db_session_property", + gettext_noop("Set database session property on TDS connections"), + NULL, + &tds_enable_db_session_property, + true, + PGC_SUSET, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + +/* the guc is accessible only if it's compiled with fault injection flag */ +#ifdef FAULT_INJECTOR + if (!TdsFaultInjectionEnabled) + { + DefineCustomBoolVariable( + "babelfishpg_tds.trigger_fault_enabled", + gettext_noop("Enable fault injection triggers"), + NULL, + &trigger_fault_injection, + true, + PGC_SUSET, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + TdsFaultInjectionEnabled = true; + } +#endif +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/support_funcs.c b/contrib/babelfishpg_tds/src/backend/tds/support_funcs.c new file mode 100644 index 00000000000..a7a49f3aa19 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/support_funcs.c @@ -0,0 +1,632 @@ +/*------------------------------------------------------------------------- + * + * support_funcs.c + * Socket related support functions for loadable protocol extensions + * + * Copyright (c) 2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/support_funcs.c + * + *------------------------------------------------------------------------- + */ + +/*---- Function declarations ----*/ + +static void pe_create_server_ports(void); +static int pe_create_server_port(int family, const char *hostName, + unsigned short portNumber, + const char *unixSocketDir, + ProtocolExtensionConfig *protocol_config); +static int pe_create_connection(pgsocket server_fd, Port *port); + + +#ifdef HAVE_UNIX_SOCKETS +static int Lock_AF_UNIX(const char *unixSocketDir, const char *unixSocketPath); +static int Setup_AF_UNIX(const char *sock_path); +#endif /* HAVE_UNIX_SOCKETS */ + +/* + * pe_create_server_ports - create server ports as per config + */ +static void +pe_create_server_ports(void) +{ + int status; + bool listen_addr_saved = false; + + if (ListenAddresses) + { + char *rawstring; + List *elemlist; + ListCell *l; + int success = 0; + + /* Need a modifiable copy of ListenAddresses */ + //rawstring = pstrdup(pe_listen_addrs); + rawstring = pstrdup(ListenAddresses); + + /* Parse string into list of hostnames */ + if (!SplitGUCList(rawstring, ',', &elemlist)) + { + /* syntax error in list */ + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid list syntax in parameter \"%s\"", + "listen_addresses"))); + } + + foreach(l, elemlist) + { + char *curhost = (char *) lfirst(l); + + if (strcmp(curhost, "*") == 0) + status = pe_create_server_port(AF_UNSPEC, NULL, + (unsigned short) pe_port, + NULL, + &pe_config); + else + status = pe_create_server_port(AF_UNSPEC, curhost, + (unsigned short) pe_port, + NULL, + &pe_config); + + if (status == STATUS_OK) + { + success++; + /* record the first successful host addr in lockfile */ + if (!listen_addr_saved) + { + AddToDataDirLockFile(LOCK_FILE_LINE_LISTEN_ADDR, curhost); + listen_addr_saved = true; + } + } + else + ereport(WARNING, + (errmsg("could not create listen socket for \"%s\"", + curhost))); + } + + if (!success && elemlist != NIL) + ereport(WARNING, + (errmsg("could not create any TCP/IP sockets to accept TDS connections"))); + + list_free(elemlist); + pfree(rawstring); + } +} + + +/* + * pe_create_server_port -- open a "listening" port to accept connections. + * Implementation copied from StreamClose() in src/backend/libpq/pqcomm.c + * + * family should be AF_UNIX or AF_UNSPEC; portNumber is the port number. + * For AF_UNIX ports, hostName should be NULL and unixSocketDir must be + * specified. For TCP ports, hostName is either NULL for all interfaces or + * the interface to listen on, and unixSocketDir is ignored (can be NULL). + * + * Successfully opened sockets are added to the ListenSocket[] array (of + * length MaxListen), at the first position that isn't PGINVALID_SOCKET. + * + * RETURNS: STATUS_OK or STATUS_ERROR + * + * NOTE: This is mostly a copy of StreamServerPort() + */ + +static int +pe_create_server_port(int family, const char *hostName, + unsigned short portNumber, + const char *unixSocketDir, + ProtocolExtensionConfig *protocol_config) + +{ + pgsocket fd; + int err; + int maxconn; + int ret; + char portNumberStr[32]; + const char *familyDesc; + char familyDescBuf[64]; + const char *addrDesc; + char addrBuf[NI_MAXHOST]; + char *service; + struct addrinfo *addrs = NULL, + *addr; + struct addrinfo hint; + int added = 0; + +#ifdef HAVE_UNIX_SOCKETS + char unixSocketPath[MAXPGPATH]; +#endif +#if !defined(WIN32) || defined(IPV6_V6ONLY) + int one = 1; +#endif + + /* Initialize hint structure */ + MemSet(&hint, 0, sizeof(hint)); + hint.ai_family = family; + hint.ai_flags = AI_PASSIVE; + hint.ai_socktype = SOCK_STREAM; + +#ifdef HAVE_UNIX_SOCKETS + if (family == AF_UNIX) + { + /* + * Create unixSocketPath from portNumber and unixSocketDir and lock + * that file path + */ + UNIXSOCK_PATH(unixSocketPath, portNumber, unixSocketDir); + if (strlen(unixSocketPath) >= UNIXSOCK_PATH_BUFLEN) + { + ereport(LOG, + (errmsg("Unix-domain socket path \"%s\" is too long (maximum %d bytes)", + unixSocketPath, + (int) (UNIXSOCK_PATH_BUFLEN - 1)))); + return STATUS_ERROR; + } + if (Lock_AF_UNIX(unixSocketDir, unixSocketPath) != STATUS_OK) + return STATUS_ERROR; + service = unixSocketPath; + } + else +#endif /* HAVE_UNIX_SOCKETS */ + { + snprintf(portNumberStr, sizeof(portNumberStr), "%d", portNumber); + service = portNumberStr; + } + + ret = pg_getaddrinfo_all(hostName, service, &hint, &addrs); + if (ret || !addrs) + { + if (hostName) + ereport(LOG, + (errmsg("could not translate host name \"%s\", service \"%s\" to address: %s", + hostName, service, gai_strerror(ret)))); + else + ereport(LOG, + (errmsg("could not translate service \"%s\" to address: %s", + service, gai_strerror(ret)))); + if (addrs) + pg_freeaddrinfo_all(hint.ai_family, addrs); + return STATUS_ERROR; + } + + for (addr = addrs; addr; addr = addr->ai_next) + { + if (!IS_AF_UNIX(family) && IS_AF_UNIX(addr->ai_family)) + { + /* + * Only set up a unix domain socket when they really asked for it. + * The service/port is different in that case. + */ + continue; + } + + /* See if there is still room to add 1 more socket. */ + if (!listen_have_free_slot()) + break; + + /* set up address family name for log messages */ + switch (addr->ai_family) + { + case AF_INET: + familyDesc = _("IPv4"); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + familyDesc = _("IPv6"); + break; +#endif +#ifdef HAVE_UNIX_SOCKETS + case AF_UNIX: + familyDesc = _("Unix"); + break; +#endif + default: + snprintf(familyDescBuf, sizeof(familyDescBuf), + _("unrecognized address family %d"), + addr->ai_family); + familyDesc = familyDescBuf; + break; + } + + /* set up text form of address for log messages */ +#ifdef HAVE_UNIX_SOCKETS + if (addr->ai_family == AF_UNIX) + addrDesc = unixSocketPath; + else +#endif + { + pg_getnameinfo_all((const struct sockaddr_storage *) addr->ai_addr, + addr->ai_addrlen, + addrBuf, sizeof(addrBuf), + NULL, 0, + NI_NUMERICHOST); + addrDesc = addrBuf; + } + + if ((fd = socket(addr->ai_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET) + { + ereport(LOG, + (errcode_for_socket_access(), + /* translator: first %s is IPv4, IPv6, or Unix */ + errmsg("could not create %s socket for address \"%s\": %m", + familyDesc, addrDesc))); + continue; + } + +#ifndef WIN32 + + /* + * Without the SO_REUSEADDR flag, a new postmaster can't be started + * right away after a stop or crash, giving "address already in use" + * error on TCP ports. + * + * On win32, however, this behavior only happens if the + * SO_EXCLUSIVEADDRUSE is set. With SO_REUSEADDR, win32 allows + * multiple servers to listen on the same address, resulting in + * unpredictable behavior. With no flags at all, win32 behaves as Unix + * with SO_REUSEADDR. + */ + if (!IS_AF_UNIX(addr->ai_family)) + { + if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (char *) &one, sizeof(one))) == -1) + { + ereport(LOG, + (errcode_for_socket_access(), + /* translator: first %s is IPv4, IPv6, or Unix */ + errmsg("setsockopt(SO_REUSEADDR) failed for %s address \"%s\": %m", + familyDesc, addrDesc))); + closesocket(fd); + continue; + } + } +#endif + +#ifdef IPV6_V6ONLY + if (addr->ai_family == AF_INET6) + { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, + (char *) &one, sizeof(one)) == -1) + { + ereport(LOG, + (errcode_for_socket_access(), + /* translator: first %s is IPv4, IPv6, or Unix */ + errmsg("setsockopt(IPV6_V6ONLY) failed for %s address \"%s\": %m", + familyDesc, addrDesc))); + closesocket(fd); + continue; + } + } +#endif + + /* + * Note: This might fail on some OS's, like Linux older than + * 2.4.21-pre3, that don't have the IPV6_V6ONLY socket option, and map + * ipv4 addresses to ipv6. It will show ::ffff:ipv4 for all ipv4 + * connections. + */ + err = bind(fd, addr->ai_addr, addr->ai_addrlen); + if (err < 0) + { + int saved_errno = errno; + + ereport(LOG, + (errcode_for_socket_access(), + /* translator: first %s is IPv4, IPv6, or Unix */ + errmsg("could not bind %s address \"%s\": %m", + familyDesc, addrDesc), + saved_errno == EADDRINUSE ? + (IS_AF_UNIX(addr->ai_family) ? + errhint("Is another postmaster already running on port %d?", + (int) portNumber) : + errhint("Is another postmaster already running on port %d?" + " If not, wait a few seconds and retry.", + (int) portNumber)) : 0)); + closesocket(fd); + continue; + } + +#ifdef HAVE_UNIX_SOCKETS + if (addr->ai_family == AF_UNIX) + { + if (Setup_AF_UNIX(service) != STATUS_OK) + { + closesocket(fd); + break; + } + } +#endif + + /* + * Select appropriate accept-queue length limit. PG_SOMAXCONN is only + * intended to provide a clamp on the request on platforms where an + * overly large request provokes a kernel error (are there any?). + */ + maxconn = MaxBackends * 2; + if (maxconn > PG_SOMAXCONN) + maxconn = PG_SOMAXCONN; + + err = listen(fd, maxconn); + if (err < 0) + { + ereport(LOG, + (errcode_for_socket_access(), + /* translator: first %s is IPv4, IPv6, or Unix */ + errmsg("could not listen on %s address \"%s\": %m", + familyDesc, addrDesc))); + closesocket(fd); + continue; + } + +#ifdef HAVE_UNIX_SOCKETS + if (addr->ai_family == AF_UNIX) + ereport(LOG, + (errmsg("listening on Unix socket \"%s\"", + addrDesc))); + else +#endif + ereport(LOG, + /* translator: first %s is IPv4 or IPv6 */ + (errmsg("listening on %s address \"%s\", port %d", + familyDesc, addrDesc, (int) portNumber))); + + listen_add_socket(fd, protocol_config); + added++; + } + + pg_freeaddrinfo_all(hint.ai_family, addrs); + + if (!added) + return STATUS_ERROR; + + return STATUS_OK; +} + +#ifdef HAVE_UNIX_SOCKETS + +/* + * Lock_AF_UNIX -- configure unix socket file path + * Implementation copied from Lock_AF_UNIX() in + * src/backend/libpq/pqcomm.c + */ +static int +Lock_AF_UNIX(const char *unixSocketDir, const char *unixSocketPath) +{ + /* no lock file for abstract sockets */ + if (unixSocketPath[0] == '@') + return STATUS_OK; + + /* + * Grab an interlock file associated with the socket file. + * + * Note: there are two reasons for using a socket lock file, rather than + * trying to interlock directly on the socket itself. First, it's a lot + * more portable, and second, it lets us remove any pre-existing socket + * file without race conditions. + */ + CreateSocketLockFile(unixSocketPath, true, unixSocketDir); + + /* + * Once we have the interlock, we can safely delete any pre-existing + * socket file to avoid failure at bind() time. + */ + (void) unlink(unixSocketPath); + + /* + * Remember socket file pathnames for later maintenance. + */ + sock_paths = lappend(sock_paths, pstrdup(unixSocketPath)); + + return STATUS_OK; +} + + +/* + * Setup_AF_UNIX -- configure unix socket permissions + * Implementation copied from Setup_AF_UNIX() in + * src/backend/libpq/pqcomm.c + */ +static int +Setup_AF_UNIX(const char *sock_path) +{ + /* no file system permissions for abstract sockets */ + if (sock_path[0] == '@') + return STATUS_OK; + + /* + * Fix socket ownership/permission if requested. Note we must do this + * before we listen() to avoid a window where unwanted connections could + * get accepted. + */ + Assert(pe_unix_socket_group); + if (pe_unix_socket_group[0] != '\0') + { +#ifdef WIN32 + elog(WARNING, "configuration item unix_socket_group is not supported on this platform"); +#else + char *endptr; + unsigned long val; + gid_t gid; + + val = strtoul(pe_unix_socket_group, &endptr, 10); + if (*endptr == '\0') + { /* numeric group id */ + gid = val; + } + else + { /* convert group name to id */ + struct group *gr; + + gr = getgrnam(pe_unix_socket_group); + if (!gr) + { + ereport(LOG, + (errmsg("group \"%s\" does not exist", + pe_unix_socket_group))); + return STATUS_ERROR; + } + gid = gr->gr_gid; + } + if (chown(sock_path, -1, gid) == -1) + { + ereport(LOG, + (errcode_for_file_access(), + errmsg("could not set group of file \"%s\": %m", + sock_path))); + return STATUS_ERROR; + } +#endif + } + + if (chmod(sock_path, pe_unix_socket_permissions) == -1) + { + ereport(LOG, + (errcode_for_file_access(), + errmsg("could not set permissions of file \"%s\": %m", + sock_path))); + return STATUS_ERROR; + } + return STATUS_OK; +} +#endif /* HAVE_UNIX_SOCKETS */ + +/* + * pe_create_connection -- create a new connection with client using + * server port. Set port->sock to the FD of the new connection. + * Implementation copied from StreamConnection() in + * src/backend/libpq/pqcomm.c + * + * ASSUME: that this doesn't need to be non-blocking because + * the Postmaster uses select() to tell when the socket is ready for + * accept(). + * + * RETURNS: STATUS_OK or STATUS_ERROR + * + * NOTE: this is mostly a copy of StreamConnection + */ +int +pe_create_connection(pgsocket server_fd, Port *port) +{ + /* accept connection and fill in the client (remote) address */ + port->raddr.salen = sizeof(port->raddr.addr); + if ((port->sock = accept(server_fd, + (struct sockaddr *) &port->raddr.addr, + &port->raddr.salen)) == PGINVALID_SOCKET) + { + ereport(LOG, + (errcode_for_socket_access(), + errmsg("could not accept new connection: %m"))); + + /* + * If accept() fails then postmaster.c will still see the server + * socket as read-ready, and will immediately try again. To avoid + * uselessly sucking lots of CPU, delay a bit before trying again. + * (The most likely reason for failure is being out of kernel file + * table slots; we can do little except hope some will get freed up.) + */ + pg_usleep(100000L); /* wait 0.1 sec */ + return STATUS_ERROR; + } + + /* fill in the server (local) address */ + port->laddr.salen = sizeof(port->laddr.addr); + if (getsockname(port->sock, + (struct sockaddr *) &port->laddr.addr, + &port->laddr.salen) < 0) + { + ereport(LOG, + (errmsg("getsockname() failed: %m"))); + return STATUS_ERROR; + } + + /* select NODELAY and KEEPALIVE options if it's a TCP connection */ + if (!IS_AF_UNIX(port->laddr.addr.ss_family)) + { + int on; +#ifdef WIN32 + int oldopt; + int optlen; + int newopt; +#endif + +#ifdef TCP_NODELAY + on = 1; + if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, sizeof(on)) < 0) + { + ereport(LOG, + (errmsg("setsockopt(%s) failed: %m", "TCP_NODELAY"))); + return STATUS_ERROR; + } +#endif + on = 1; + if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof(on)) < 0) + { + ereport(LOG, + (errmsg("setsockopt(%s) failed: %m", "SO_KEEPALIVE"))); + return STATUS_ERROR; + } + +#ifdef WIN32 + + /* + * This is a Win32 socket optimization. The OS send buffer should be + * large enough to send the whole Postgres send buffer in one go, or + * performance suffers. The Postgres send buffer can be enlarged if a + * very large message needs to be sent, but we won't attempt to + * enlarge the OS buffer if that happens, so somewhat arbitrarily + * ensure that the OS buffer is at least PQ_SEND_BUFFER_SIZE * 4. + * (That's 32kB with the current default). + * + * The default OS buffer size used to be 8kB in earlier Windows + * versions, but was raised to 64kB in Windows 2012. So it shouldn't + * be necessary to change it in later versions anymore. Changing it + * unnecessarily can even reduce performance, because setting + * SO_SNDBUF in the application disables the "dynamic send buffering" + * feature that was introduced in Windows 7. So before fiddling with + * SO_SNDBUF, check if the current buffer size is already large enough + * and only increase it if necessary. + * + * See https://support.microsoft.com/kb/823764/EN-US/ and + * https://msdn.microsoft.com/en-us/library/bb736549%28v=vs.85%29.aspx + */ + optlen = sizeof(oldopt); + if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt, + &optlen) < 0) + { + ereport(LOG, + (errmsg("getsockopt(%s) failed: %m", "SO_SNDBUF"))); + return STATUS_ERROR; + } + newopt = PQ_SEND_BUFFER_SIZE * 4; + if (oldopt < newopt) + { + if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt, + sizeof(newopt)) < 0) + { + ereport(LOG, + (errmsg("setsockopt(%s) failed: %m", "SO_SNDBUF"))); + return STATUS_ERROR; + } + } +#endif + + /* + * Also apply the current keepalive parameters. If we fail to set a + * parameter, don't error out, because these aren't universally + * supported. (Note: you might think we need to reset the GUC + * variables to 0 in such a case, but it's not necessary because the + * show hooks for these variables report the truth anyway.) + */ + (void) pq_setkeepalivesidle(tcp_keepalives_idle, port); + (void) pq_setkeepalivesinterval(tcp_keepalives_interval, port); + (void) pq_setkeepalivescount(tcp_keepalives_count, port); + (void) pq_settcpusertimeout(tcp_user_timeout, port); + } + + return STATUS_OK; +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tds-secure-openssl.c b/contrib/babelfishpg_tds/src/backend/tds/tds-secure-openssl.c new file mode 100644 index 00000000000..89de9daaaf9 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds-secure-openssl.c @@ -0,0 +1,1442 @@ +/*------------------------------------------------------------------------- + * + * tds-secure-openssl.c + * functions for OpenSSL support in the backend. + * + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tds-be-secure-openssl.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#include +#endif + +#ifdef USE_OPENSSL +#include +#include +#include +#endif +#ifndef OPENSSL_NO_ECDH +#include +#endif + +#include "libpq/libpq.h" +#include "miscadmin.h" +#include "pgstat.h" +#include "storage/fd.h" +#include "storage/latch.h" +#include "tcop/tcopprot.h" +#include "utils/memutils.h" + +#include "src/include/tds_secure.h" + +#ifdef USE_SSL +static int my_sock_read(BIO *h, char *buf, int size); +static int my_sock_write(BIO *h, const char *buf, int size); +#if 0 // Register tds specific function +static BIO_METHOD *my_BIO_s_socket(void); +#endif +static int my_SSL_set_fd(Port *port, int fd); + +static DH *load_dh_file(char *filename, bool isServerStart); +static DH *load_dh_buffer(const char *, size_t); +static int ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdata); +static int dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata); +#if 0 +static int verify_cb(int, X509_STORE_CTX *); +#endif +static void info_cb(const SSL *ssl, int type, int args); +static bool initialize_dh(SSL_CTX *context, bool isServerStart); +static bool initialize_ecdh(SSL_CTX *context, bool isServerStart); +static const char *SSLerrmessage(unsigned long ecode); + + +static SSL_CTX *SSL_context = NULL; +static bool SSL_initialized = false; +static bool dummy_ssl_passwd_cb_called = false; +static bool ssl_is_server_start; +static BIO_METHOD *my_bio_methods = NULL; + +static int ssl_protocol_version_to_openssl(int v, const char *guc_name, + int loglevel); +#ifndef SSL_CTX_set_min_proto_version +static int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version); +static int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version); +#endif + +/* ------------------------------------------------------------ */ +/* Public interface */ +/* ------------------------------------------------------------ */ +int +Tds_be_tls_init(bool isServerStart) +{ + STACK_OF(X509_NAME) *root_cert_list = NULL; + SSL_CTX *context; + + /* This stuff need be done only once. */ + if (!SSL_initialized) + { +#ifdef HAVE_OPENSSL_INIT_SSL + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL); +#else + OPENSSL_config(NULL); + SSL_library_init(); + SSL_load_error_strings(); +#endif + SSL_initialized = true; + } + + /* + * We use SSLv23_method() because it can negotiate use of the highest + * mutually supported protocol version, while alternatives like + * TLSv1_2_method() permit only one specific version. Note that we don't + * actually allow SSL v2 or v3, only TLS protocols (see below). + */ + context = SSL_CTX_new(SSLv23_method()); + if (!context) + { + ereport(isServerStart ? FATAL : LOG, + (errmsg("could not create SSL context: %s", + SSLerrmessage(ERR_get_error())))); + goto error; + } + + /* + * Disable OpenSSL's moving-write-buffer sanity check, because it causes + * unnecessary failures in nonblocking send cases. + */ + SSL_CTX_set_mode(context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + /* + * Set password callback + */ + if (isServerStart) + { + if (ssl_passphrase_command[0]) + SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb); + } + else + { + if (ssl_passphrase_command[0] && ssl_passphrase_command_supports_reload) + SSL_CTX_set_default_passwd_cb(context, ssl_external_passwd_cb); + else + + /* + * If reloading and no external command is configured, override + * OpenSSL's default handling of passphrase-protected files, + * because we don't want to prompt for a passphrase in an + * already-running server. + */ + SSL_CTX_set_default_passwd_cb(context, dummy_ssl_passwd_cb); + } + /* used by the callback */ + ssl_is_server_start = isServerStart; + + /* + * Load and verify server's certificate and private key + */ + if (SSL_CTX_use_certificate_chain_file(context, ssl_cert_file) != 1) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not load server certificate file \"%s\": %s", + ssl_cert_file, SSLerrmessage(ERR_get_error())))); + goto error; + } + + if (!check_ssl_key_file_permissions(ssl_key_file, isServerStart)) + goto error; + + /* + * OK, try to load the private key file. + */ + dummy_ssl_passwd_cb_called = false; + + if (SSL_CTX_use_PrivateKey_file(context, + ssl_key_file, + SSL_FILETYPE_PEM) != 1) + { + if (dummy_ssl_passwd_cb_called) + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("private key file \"%s\" cannot be reloaded because it requires a passphrase", + ssl_key_file))); + else + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not load private key file \"%s\": %s", + ssl_key_file, SSLerrmessage(ERR_get_error())))); + goto error; + } + + if (SSL_CTX_check_private_key(context) != 1) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("check of private key failed: %s", + SSLerrmessage(ERR_get_error())))); + goto error; + } + + if (tds_ssl_min_protocol_version) + { + int ssl_ver = ssl_protocol_version_to_openssl(tds_ssl_min_protocol_version, + "ssl_min_protocol_version", + isServerStart ? FATAL : LOG); + if (ssl_ver == -1) + goto error; + if (!SSL_CTX_set_min_proto_version(context, ssl_ver)) + { + ereport(isServerStart ? FATAL : LOG, + (errmsg("could not set minimum SSL protocol version"))); + goto error; + } + } + + if (tds_ssl_max_protocol_version) + { + int ssl_ver = ssl_protocol_version_to_openssl(tds_ssl_max_protocol_version, + "tds_ssl_max_protocol_version", + isServerStart ? FATAL : LOG); + if (ssl_ver == -1) + goto error; + if (!SSL_CTX_set_max_proto_version(context, ssl_ver)) + { + ereport(isServerStart ? FATAL : LOG, + (errmsg("could not set maximum SSL protocol version"))); + goto error; + } + } + + /* disallow SSL session tickets */ +#ifdef SSL_OP_NO_TICKET /* added in OpenSSL 0.9.8f */ + SSL_CTX_set_options(context, SSL_OP_NO_TICKET); +#endif + + /* disallow SSL session caching, too */ + SSL_CTX_set_session_cache_mode(context, SSL_SESS_CACHE_OFF); + + /* set up ephemeral DH and ECDH keys */ + if (!initialize_dh(context, isServerStart)) + goto error; + if (!initialize_ecdh(context, isServerStart)) + goto error; + + /* set up the allowed cipher list */ + if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not set the cipher list (no valid ciphers available)"))); + goto error; + } + + /* Let server choose order */ + if (SSLPreferServerCiphers) + SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE); + + /* + * Load CA store, so we can verify client certificates if needed. + */ + if (ssl_ca_file[0]) + { + if (SSL_CTX_load_verify_locations(context, ssl_ca_file, NULL) != 1 || + (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not load root certificate file \"%s\": %s", + ssl_ca_file, SSLerrmessage(ERR_get_error())))); + goto error; + } + } + + /*---------- + * Load the Certificate Revocation List (CRL). + * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html + *---------- + */ + if (ssl_crl_file[0]) + { + X509_STORE *cvstore = SSL_CTX_get_cert_store(context); + + if (cvstore) + { + /* Set the flags to check against the complete CRL chain */ + if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1) + { + /* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */ +#ifdef X509_V_FLAG_CRL_CHECK + X509_STORE_set_flags(cvstore, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); +#else + ereport(LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("SSL certificate revocation list file \"%s\" ignored", + ssl_crl_file), + errdetail("SSL library does not support certificate revocation lists."))); +#endif + } + else + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not load SSL certificate revocation list file \"%s\": %s", + ssl_crl_file, SSLerrmessage(ERR_get_error())))); + goto error; + } + } + } +#if 0 // TDS specific + /* TDS specific - TSQL doesn't support multual certificate authentication */ + if (ssl_ca_file[0]) + { + /* + * Always ask for SSL client cert, but don't fail if it's not + * presented. We might fail such connections later, depending on what + * we find in pg_hba.conf. + */ + SSL_CTX_set_verify(context, + (SSL_VERIFY_PEER | + SSL_VERIFY_CLIENT_ONCE), + verify_cb); + + /* + * Tell OpenSSL to send the list of root certs we trust to clients in + * CertificateRequests. This lets a client with a keystore select the + * appropriate client certificate to send to us. + */ + SSL_CTX_set_client_CA_list(context, root_cert_list); + } +#endif + /* + * Success! Replace any existing SSL_context. + */ + if (SSL_context) + SSL_CTX_free(SSL_context); + + SSL_context = context; + + ssl_loaded_verify_locations = false; +#if 0 // TDS specific + /* + * Set flag to remember whether CA store has been loaded into SSL_context. + */ + if (ssl_ca_file[0]) + ssl_loaded_verify_locations = true; + else + ssl_loaded_verify_locations = false; +#endif + return 0; + +error: + if (context) + SSL_CTX_free(context); + return -1; +} + +void +Tds_be_tls_destroy(void) +{ + if (SSL_context) + SSL_CTX_free(SSL_context); + SSL_context = NULL; + ssl_loaded_verify_locations = false; +} + +int +Tds_be_tls_open_server(Port *port) +{ + int r; + int err; + int waitfor; + unsigned long ecode; + + Assert(!port->ssl); + Assert(!port->peer); + + if (!SSL_context) + { + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("could not initialize SSL connection: SSL context not set up"))); + return -1; + } + + if (!(port->ssl = SSL_new(SSL_context))) + { + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("could not initialize SSL connection: %s", + SSLerrmessage(ERR_get_error())))); + return -1; + } + if (!my_SSL_set_fd(port, port->sock)) + { + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("could not set SSL socket: %s", + SSLerrmessage(ERR_get_error())))); + return -1; + } + port->ssl_in_use = true; + +aloop: + + /* + * Prepare to call SSL_get_error() by clearing thread's OpenSSL error + * queue. In general, the current thread's error queue must be empty + * before the TLS/SSL I/O operation is attempted, or SSL_get_error() will + * not work reliably. An extension may have failed to clear the + * per-thread error queue following another call to an OpenSSL I/O + * routine. + */ + ERR_clear_error(); + r = SSL_accept(port->ssl); + if (r <= 0) + { + err = SSL_get_error(port->ssl, r); + + /* + * Other clients of OpenSSL in the backend may fail to call + * ERR_get_error(), but we always do, so as to not cause problems for + * OpenSSL clients that don't call ERR_clear_error() defensively. Be + * sure that this happens by calling now. SSL_get_error() relies on + * the OpenSSL per-thread error queue being intact, so this is the + * earliest possible point ERR_get_error() may be called. + */ + ecode = ERR_get_error(); + switch (err) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* not allowed during connection establishment */ + Assert(!port->noblock); + + /* + * No need to care about timeouts/interrupts here. At this + * point authentication_timeout still employs + * StartupPacketTimeoutHandler() which directly exits. + */ + if (err == SSL_ERROR_WANT_READ) + waitfor = WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH; + else + waitfor = WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH; + + (void) WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0, + WAIT_EVENT_SSL_OPEN_SERVER); + goto aloop; + case SSL_ERROR_SYSCALL: + if (r < 0) + ereport(COMMERROR, + (errcode_for_socket_access(), + errmsg("could not accept SSL connection: %m"))); + else + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("could not accept SSL connection: EOF detected"))); + break; + case SSL_ERROR_SSL: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("could not accept SSL connection: %s", + SSLerrmessage(ecode)))); + break; + case SSL_ERROR_ZERO_RETURN: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("could not accept SSL connection: EOF detected"))); + break; + default: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unrecognized SSL error code: %d", + err))); + break; + } + return -1; + } + + /* TDS specific post SSL function register */ +#ifdef HAVE_BIO_METH_NEW + BIO_meth_set_read(my_bio_methods, my_sock_read); + BIO_meth_set_write(my_bio_methods, my_sock_write); +#else + my_bio_methods->bread = my_sock_read; + my_bio_methods->bwrite = my_sock_write; +#endif + + + /* Get client certificate, if available. */ + port->peer = SSL_get_peer_certificate(port->ssl); + + /* and extract the Common Name from it. */ + port->peer_cn = NULL; + port->peer_cert_valid = false; + if (port->peer != NULL) + { + int len; + + len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer), + NID_commonName, NULL, 0); + if (len != -1) + { + char *peer_cn; + + peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1); + r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer), + NID_commonName, peer_cn, len + 1); + peer_cn[len] = '\0'; + if (r != len) + { + /* shouldn't happen */ + pfree(peer_cn); + return -1; + } + + /* + * Reject embedded NULLs in certificate common name to prevent + * attacks like CVE-2009-4034. + */ + if (len != strlen(peer_cn)) + { + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("SSL certificate's common name contains embedded null"))); + pfree(peer_cn); + return -1; + } + + port->peer_cn = peer_cn; + } + port->peer_cert_valid = true; + } + + /* set up debugging/info callback */ + SSL_CTX_set_info_callback(SSL_context, info_cb); + + return 0; +} + +void +Tds_be_tls_close(Port *port) +{ + if (port->ssl) + { + SSL_shutdown(port->ssl); + SSL_free(port->ssl); + port->ssl = NULL; + port->ssl_in_use = false; + } + + if (port->peer) + { + X509_free(port->peer); + port->peer = NULL; + } + + if (port->peer_cn) + { + pfree(port->peer_cn); + port->peer_cn = NULL; + } +} + +ssize_t +Tds_be_tls_read(Port *port, void *ptr, size_t len, int *waitfor) +{ + ssize_t n; + int err; + unsigned long ecode; + + errno = 0; + ERR_clear_error(); + n = SSL_read(port->ssl, ptr, len); + err = SSL_get_error(port->ssl, n); + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; + switch (err) + { + case SSL_ERROR_NONE: + /* a-ok */ + break; + case SSL_ERROR_WANT_READ: + *waitfor = WL_SOCKET_READABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_WANT_WRITE: + *waitfor = WL_SOCKET_WRITEABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_SYSCALL: + /* leave it to caller to ereport the value of errno */ + if (n != -1) + { + errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("SSL error: %s", SSLerrmessage(ecode)))); + errno = ECONNRESET; + n = -1; + break; + case SSL_ERROR_ZERO_RETURN: + /* connection was cleanly shut down by peer */ + n = 0; + break; + default: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unrecognized SSL error code: %d", + err))); + errno = ECONNRESET; + n = -1; + break; + } + + return n; +} + +ssize_t +Tds_be_tls_write(Port *port, void *ptr, size_t len, int *waitfor) +{ + ssize_t n; + int err; + unsigned long ecode; + + errno = 0; + ERR_clear_error(); + n = SSL_write(port->ssl, ptr, len); + err = SSL_get_error(port->ssl, n); + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; + switch (err) + { + case SSL_ERROR_NONE: + /* a-ok */ + break; + case SSL_ERROR_WANT_READ: + *waitfor = WL_SOCKET_READABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_WANT_WRITE: + *waitfor = WL_SOCKET_WRITEABLE; + errno = EWOULDBLOCK; + n = -1; + break; + case SSL_ERROR_SYSCALL: + /* leave it to caller to ereport the value of errno */ + if (n != -1) + { + errno = ECONNRESET; + n = -1; + } + break; + case SSL_ERROR_SSL: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("SSL error: %s", SSLerrmessage(ecode)))); + errno = ECONNRESET; + n = -1; + break; + case SSL_ERROR_ZERO_RETURN: + + /* + * the SSL connection was closed, leave it to the caller to + * ereport it + */ + errno = ECONNRESET; + n = -1; + break; + default: + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unrecognized SSL error code: %d", + err))); + errno = ECONNRESET; + n = -1; + break; + } + + return n; +} + +/* ------------------------------------------------------------ */ +/* Internal functions */ +/* ------------------------------------------------------------ */ + +/* + * Private substitute BIO: this does the sending and receiving using send() and + * recv() instead. This is so that we can enable and disable interrupts + * just while calling recv(). We cannot have interrupts occurring while + * the bulk of OpenSSL runs, because it uses malloc() and possibly other + * non-reentrant libc facilities. We also need to call send() and recv() + * directly so it gets passed through the socket/signals layer on Win32. + * + * These functions are closely modelled on the standard socket BIO in OpenSSL; + * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c. + * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons + * to retry; do we need to adopt their logic for that? + */ + +#ifndef HAVE_BIO_GET_DATA +#define BIO_get_data(bio) (bio->ptr) +#define BIO_set_data(bio, data) (bio->ptr = data) +#endif + +static int +my_sock_read(BIO *h, char *buf, int size) +{ + int res = 0; + + if (buf != NULL) + { + res = secure_raw_read(((Port *) BIO_get_data(h)), buf, size); + BIO_clear_retry_flags(h); + if (res <= 0) + { + /* If we were interrupted, tell caller to retry */ + if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) + { + BIO_set_retry_read(h); + } + } + } + + return res; +} + +static int +my_sock_write(BIO *h, const char *buf, int size) +{ + int res = 0; + + res = secure_raw_write(((Port *) BIO_get_data(h)), buf, size); + BIO_clear_retry_flags(h); + if (res <= 0) + { + /* If we were interrupted, tell caller to retry */ + if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) + { + BIO_set_retry_write(h); + } + } + + return res; +} + +#if 0 +static BIO_METHOD * +my_BIO_s_socket(void) +{ + if (!my_bio_methods) + { + BIO_METHOD *biom = (BIO_METHOD *) BIO_s_socket(); +#ifdef HAVE_BIO_METH_NEW + int my_bio_index; + + my_bio_index = BIO_get_new_index(); + if (my_bio_index == -1) + return NULL; + my_bio_methods = BIO_meth_new(my_bio_index, "PostgreSQL backend socket"); + if (!my_bio_methods) + return NULL; + if (!BIO_meth_set_write(my_bio_methods, my_sock_write) || + !BIO_meth_set_read(my_bio_methods, my_sock_read) || + !BIO_meth_set_gets(my_bio_methods, BIO_meth_get_gets(biom)) || + !BIO_meth_set_puts(my_bio_methods, BIO_meth_get_puts(biom)) || + !BIO_meth_set_ctrl(my_bio_methods, BIO_meth_get_ctrl(biom)) || + !BIO_meth_set_create(my_bio_methods, BIO_meth_get_create(biom)) || + !BIO_meth_set_destroy(my_bio_methods, BIO_meth_get_destroy(biom)) || + !BIO_meth_set_callback_ctrl(my_bio_methods, BIO_meth_get_callback_ctrl(biom))) + { + BIO_meth_free(my_bio_methods); + my_bio_methods = NULL; + return NULL; + } +#else + my_bio_methods = malloc(sizeof(BIO_METHOD)); + if (!my_bio_methods) + return NULL; + memcpy(my_bio_methods, biom, sizeof(BIO_METHOD)); + my_bio_methods->bread = my_sock_read; + my_bio_methods->bwrite = my_sock_write; +#endif + } + return my_bio_methods; +} +#endif + +/* This should exactly match OpenSSL's SSL_set_fd except for using my BIO */ +static int +my_SSL_set_fd(Port *port, int fd) +{ + int ret = 0; + BIO *bio; + BIO_METHOD *bio_method; + + /* Tds specific SSL function register */ + bio_method = TdsBioSecureSocket(my_bio_methods); + my_bio_methods = bio_method; + + if (bio_method == NULL) + { + SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB); + goto err; + } + bio = BIO_new(bio_method); + + if (bio == NULL) + { + SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB); + goto err; + } + BIO_set_data(bio, port); + + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set_bio(port->ssl, bio, bio); + ret = 1; +err: + return ret; +} + +/* + * Load precomputed DH parameters. + * + * To prevent "downgrade" attacks, we perform a number of checks + * to verify that the DBA-generated DH parameters file contains + * what we expect it to contain. + */ +static DH * +load_dh_file(char *filename, bool isServerStart) +{ + FILE *fp; + DH *dh = NULL; + int codes; + + /* attempt to open file. It's not an error if it doesn't exist. */ + if ((fp = AllocateFile(filename, "r")) == NULL) + { + ereport(isServerStart ? FATAL : LOG, + (errcode_for_file_access(), + errmsg("could not open DH parameters file \"%s\": %m", + filename))); + return NULL; + } + + dh = PEM_read_DHparams(fp, NULL, NULL, NULL); + FreeFile(fp); + + if (dh == NULL) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("could not load DH parameters file: %s", + SSLerrmessage(ERR_get_error())))); + return NULL; + } + + /* make sure the DH parameters are usable */ + if (DH_check(dh, &codes) == 0) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("invalid DH parameters: %s", + SSLerrmessage(ERR_get_error())))); + return NULL; + } + if (codes & DH_CHECK_P_NOT_PRIME) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("invalid DH parameters: p is not prime"))); + return NULL; + } + if ((codes & DH_NOT_SUITABLE_GENERATOR) && + (codes & DH_CHECK_P_NOT_SAFE_PRIME)) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("invalid DH parameters: neither suitable generator or safe prime"))); + return NULL; + } + + return dh; +} + +/* + * Load hardcoded DH parameters. + * + * To prevent problems if the DH parameters files don't even + * exist, we can load DH parameters hardcoded into this file. + */ +static DH * +load_dh_buffer(const char *buffer, size_t len) +{ + BIO *bio; + DH *dh = NULL; + + bio = BIO_new_mem_buf(unconstify(char *, buffer), len); + if (bio == NULL) + return NULL; + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + if (dh == NULL) + ereport(DEBUG2, + (errmsg_internal("DH load buffer: %s", + SSLerrmessage(ERR_get_error())))); + BIO_free(bio); + + return dh; +} + +/* + * Passphrase collection callback using ssl_passphrase_command + */ +static int +ssl_external_passwd_cb(char *buf, int size, int rwflag, void *userdata) +{ + /* same prompt as OpenSSL uses internally */ + const char *prompt = "Enter PEM pass phrase:"; + + Assert(rwflag == 0); + + return run_ssl_passphrase_command(prompt, ssl_is_server_start, buf, size); +} + +/* + * Dummy passphrase callback + * + * If OpenSSL is told to use a passphrase-protected server key, by default + * it will issue a prompt on /dev/tty and try to read a key from there. + * That's no good during a postmaster SIGHUP cycle, not to mention SSL context + * reload in an EXEC_BACKEND postmaster child. So override it with this dummy + * function that just returns an empty passphrase, guaranteeing failure. + */ +static int +dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void *userdata) +{ + /* Set flag to change the error message we'll report */ + dummy_ssl_passwd_cb_called = true; + /* And return empty string */ + Assert(size > 0); + buf[0] = '\0'; + return 0; +} + +#if 0 +/* + * Certificate verification callback + * + * This callback allows us to log intermediate problems during + * verification, but for now we'll see if the final error message + * contains enough information. + * + * This callback also allows us to override the default acceptance + * criteria (e.g., accepting self-signed or expired certs), but + * for now we accept the default checks. + */ +static int +verify_cb(int ok, X509_STORE_CTX *ctx) +{ + return ok; +} +#endif + +/* + * This callback is used to copy SSL information messages + * into the PostgreSQL log. + */ +static void +info_cb(const SSL *ssl, int type, int args) +{ + switch (type) + { + case SSL_CB_HANDSHAKE_START: + ereport(DEBUG4, + (errmsg_internal("SSL: handshake start"))); + break; + case SSL_CB_HANDSHAKE_DONE: + ereport(DEBUG4, + (errmsg_internal("SSL: handshake done"))); + break; + case SSL_CB_ACCEPT_LOOP: + ereport(DEBUG4, + (errmsg_internal("SSL: accept loop"))); + break; + case SSL_CB_ACCEPT_EXIT: + ereport(DEBUG4, + (errmsg_internal("SSL: accept exit (%d)", args))); + break; + case SSL_CB_CONNECT_LOOP: + ereport(DEBUG4, + (errmsg_internal("SSL: connect loop"))); + break; + case SSL_CB_CONNECT_EXIT: + ereport(DEBUG4, + (errmsg_internal("SSL: connect exit (%d)", args))); + break; + case SSL_CB_READ_ALERT: + ereport(DEBUG4, + (errmsg_internal("SSL: read alert (0x%04x)", args))); + break; + case SSL_CB_WRITE_ALERT: + ereport(DEBUG4, + (errmsg_internal("SSL: write alert (0x%04x)", args))); + break; + } +} + +/* + * Set DH parameters for generating ephemeral DH keys. The + * DH parameters can take a long time to compute, so they must be + * precomputed. + * + * Since few sites will bother to create a parameter file, we also + * provide a fallback to the parameters provided by the OpenSSL + * project. + * + * These values can be static (once loaded or computed) since the + * OpenSSL library can efficiently generate random keys from the + * information provided. + */ +static bool +initialize_dh(SSL_CTX *context, bool isServerStart) +{ + DH *dh = NULL; + + SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE); + + if (ssl_dh_params_file[0]) + dh = load_dh_file(ssl_dh_params_file, isServerStart); + if (!dh) + dh = load_dh_buffer(FILE_DH2048, sizeof(FILE_DH2048)); + if (!dh) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + (errmsg("DH: could not load DH parameters")))); + return false; + } + + if (SSL_CTX_set_tmp_dh(context, dh) != 1) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + (errmsg("DH: could not set DH parameters: %s", + SSLerrmessage(ERR_get_error()))))); + return false; + } + return true; +} + +/* + * Set ECDH parameters for generating ephemeral Elliptic Curve DH + * keys. This is much simpler than the DH parameters, as we just + * need to provide the name of the curve to OpenSSL. + */ +static bool +initialize_ecdh(SSL_CTX *context, bool isServerStart) +{ +#ifndef OPENSSL_NO_ECDH + EC_KEY *ecdh; + int nid; + + nid = OBJ_sn2nid(SSLECDHCurve); + if (!nid) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve))); + return false; + } + + ecdh = EC_KEY_new_by_curve_name(nid); + if (!ecdh) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("ECDH: could not create key"))); + return false; + } + + SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); + SSL_CTX_set_tmp_ecdh(context, ecdh); + EC_KEY_free(ecdh); +#endif + + return true; +} + +/* + * Obtain reason string for passed SSL errcode + * + * ERR_get_error() is used by caller to get errcode to pass here. + * + * Some caution is needed here since ERR_reason_error_string will + * return NULL if it doesn't recognize the error code. We don't + * want to return NULL ever. + */ +static const char * +SSLerrmessage(unsigned long ecode) +{ + const char *errreason; + static char errbuf[36]; + + if (ecode == 0) + return _("no SSL error reported"); + errreason = ERR_reason_error_string(ecode); + if (errreason != NULL) + return errreason; + snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), ecode); + return errbuf; +} + +#if 0 +int +be_tls_get_cipher_bits(Port *port) +{ + int bits; + + if (port->ssl) + { + SSL_get_cipher_bits(port->ssl, &bits); + return bits; + } + else + return 0; +} + +bool +be_tls_get_compression(Port *port) +{ + if (port->ssl) + return (SSL_get_current_compression(port->ssl) != NULL); + else + return false; +} + +const char * +be_tls_get_version(Port *port) +{ + if (port->ssl) + return SSL_get_version(port->ssl); + else + return NULL; +} + +const char * +be_tls_get_cipher(Port *port) +{ + if (port->ssl) + return SSL_get_cipher(port->ssl); + else + return NULL; +} + +void +be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len) +{ + if (port->peer) + strlcpy(ptr, X509_NAME_to_cstring(X509_get_subject_name(port->peer)), len); + else + ptr[0] = '\0'; +} + +void +be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len) +{ + if (port->peer) + strlcpy(ptr, X509_NAME_to_cstring(X509_get_issuer_name(port->peer)), len); + else + ptr[0] = '\0'; +} + +void +be_tls_get_peer_serial(Port *port, char *ptr, size_t len) +{ + if (port->peer) + { + ASN1_INTEGER *serial; + BIGNUM *b; + char *decimal; + + serial = X509_get_serialNumber(port->peer); + b = ASN1_INTEGER_to_BN(serial, NULL); + decimal = BN_bn2dec(b); + + BN_free(b); + strlcpy(ptr, decimal, len); + OPENSSL_free(decimal); + } + else + ptr[0] = '\0'; +} + +#ifdef HAVE_X509_GET_SIGNATURE_NID +char * +be_tls_get_certificate_hash(Port *port, size_t *len) +{ + X509 *server_cert; + char *cert_hash; + const EVP_MD *algo_type = NULL; + unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */ + unsigned int hash_size; + int algo_nid; + + *len = 0; + server_cert = SSL_get_certificate(port->ssl); + if (server_cert == NULL) + return NULL; + + /* + * Get the signature algorithm of the certificate to determine the hash + * algorithm to use for the result. + */ + if (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert), + &algo_nid, NULL)) + elog(ERROR, "could not determine server certificate signature algorithm"); + + /* + * The TLS server's certificate bytes need to be hashed with SHA-256 if + * its signature algorithm is MD5 or SHA-1 as per RFC 5929 + * (https://tools.ietf.org/html/rfc5929#section-4.1). If something else + * is used, the same hash as the signature algorithm is used. + */ + switch (algo_nid) + { + case NID_md5: + case NID_sha1: + algo_type = EVP_sha256(); + break; + default: + algo_type = EVP_get_digestbynid(algo_nid); + if (algo_type == NULL) + elog(ERROR, "could not find digest for NID %s", + OBJ_nid2sn(algo_nid)); + break; + } + + /* generate and save the certificate hash */ + if (!X509_digest(server_cert, algo_type, hash, &hash_size)) + elog(ERROR, "could not generate server certificate hash"); + + cert_hash = palloc(hash_size); + memcpy(cert_hash, hash, hash_size); + *len = hash_size; + + return cert_hash; +} +#endif + +/* + * Convert an X509 subject name to a cstring. + * + */ +static char * +X509_NAME_to_cstring(X509_NAME *name) +{ + BIO *membuf = BIO_new(BIO_s_mem()); + int i, + nid, + count = X509_NAME_entry_count(name); + X509_NAME_ENTRY *e; + ASN1_STRING *v; + const char *field_name; + size_t size; + char nullterm; + char *sp; + char *dp; + char *result; + + (void) BIO_set_close(membuf, BIO_CLOSE); + for (i = 0; i < count; i++) + { + e = X509_NAME_get_entry(name, i); + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e)); + v = X509_NAME_ENTRY_get_data(e); + field_name = OBJ_nid2sn(nid); + if (!field_name) + field_name = OBJ_nid2ln(nid); + BIO_printf(membuf, "/%s=", field_name); + ASN1_STRING_print_ex(membuf, v, + ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB) + | ASN1_STRFLGS_UTF8_CONVERT)); + } + + /* ensure null termination of the BIO's content */ + nullterm = '\0'; + BIO_write(membuf, &nullterm, 1); + size = BIO_get_mem_data(membuf, &sp); + dp = pg_any_to_server(sp, size - 1, PG_UTF8); + + result = pstrdup(dp); + if (dp != sp) + pfree(dp); + BIO_free(membuf); + + return result; +} +#endif + +/* + * Convert TLS protocol version GUC enum to OpenSSL values + * + * This is a straightforward one-to-one mapping, but doing it this way makes + * guc.c independent of OpenSSL availability and version. + * + * If a version is passed that is not supported by the current OpenSSL + * version, then we log with the given loglevel and return (if we return) -1. + * If a nonnegative value is returned, subsequent code can assume it's working + * with a supported version. + */ +static int +ssl_protocol_version_to_openssl(int v, const char *guc_name, int loglevel) +{ + switch (v) + { + case PG_TLS_ANY: + return 0; + case PG_TLS1_VERSION: + return TLS1_VERSION; + case PG_TLS1_1_VERSION: +#ifdef TLS1_1_VERSION + return TLS1_1_VERSION; +#else + break; +#endif + case PG_TLS1_2_VERSION: +#ifdef TLS1_2_VERSION + return TLS1_2_VERSION; +#else + break; +#endif + case PG_TLS1_3_VERSION: +#ifdef TLS1_3_VERSION + return TLS1_3_VERSION; +#else + break; +#endif + } + + ereport(loglevel, + (errmsg("%s setting %s not supported by this build", + guc_name, + GetConfigOption(guc_name, false, false)))); + return -1; +} + +/* + * Replacements for APIs present in newer versions of OpenSSL + */ +#ifndef SSL_CTX_set_min_proto_version + +/* + * OpenSSL versions that support TLS 1.3 shouldn't get here because they + * already have these functions. So we don't have to keep updating the below + * code for every new TLS version, and eventually it can go away. But let's + * just check this to make sure ... + */ +#ifdef TLS1_3_VERSION +#error OpenSSL version mismatch +#endif + +static int +SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version) +{ + int ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + + if (version > TLS1_VERSION) + ssl_options |= SSL_OP_NO_TLSv1; + /* + * Some OpenSSL versions define TLS*_VERSION macros but not the + * corresponding SSL_OP_NO_* macro, so in those cases we have to return + * unsuccessfully here. + */ +#ifdef TLS1_1_VERSION + if (version > TLS1_1_VERSION) + { +#ifdef SSL_OP_NO_TLSv1_1 + ssl_options |= SSL_OP_NO_TLSv1_1; +#else + return 0; +#endif + } +#endif +#ifdef TLS1_2_VERSION + if (version > TLS1_2_VERSION) + { +#ifdef SSL_OP_NO_TLSv1_2 + ssl_options |= SSL_OP_NO_TLSv1_2; +#else + return 0; +#endif + } +#endif + + SSL_CTX_set_options(ctx, ssl_options); + + return 1; /* success */ +} + +static int +SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version) +{ + int ssl_options = 0; + + AssertArg(version != 0); + + /* + * Some OpenSSL versions define TLS*_VERSION macros but not the + * corresponding SSL_OP_NO_* macro, so in those cases we have to return + * unsuccessfully here. + */ +#ifdef TLS1_1_VERSION + if (version < TLS1_1_VERSION) + { +#ifdef SSL_OP_NO_TLSv1_1 + ssl_options |= SSL_OP_NO_TLSv1_1; +#else + return 0; +#endif + } +#endif +#ifdef TLS1_2_VERSION + if (version < TLS1_2_VERSION) + { +#ifdef SSL_OP_NO_TLSv1_2 + ssl_options |= SSL_OP_NO_TLSv1_2; +#else + return 0; +#endif + } +#endif + + SSL_CTX_set_options(ctx, ssl_options); + + return 1; /* success */ +} + +#endif /* !SSL_CTX_set_min_proto_version */ +#endif diff --git a/contrib/babelfishpg_tds/src/backend/tds/tds.c b/contrib/babelfishpg_tds/src/backend/tds/tds.c new file mode 100644 index 00000000000..53a162e3928 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds.c @@ -0,0 +1,124 @@ + +/*------------------------------------------------------------------------- + * + * tds.c + * TDS Listener extension entrypoint + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tds.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/printtup.h" +#include "src/include/tds_int.h" +#include "src/include/tds_secure.h" +#include "src/include/tds_instr.h" +#include "commands/defrem.h" +#include "fmgr.h" +#include "miscadmin.h" +#include "libpq/libpq.h" +#include "libpq/libpq-be.h" +#include "miscadmin.h" +#include "parser/parse_expr.h" +#include "postmaster/postmaster.h" +#include "utils/elog.h" +#include "utils/pidfile.h" +#include "utils/lsyscache.h" + +#include "src/include/err_handler.h" + +PG_MODULE_MAGIC; + +extern void _PG_init(void); +extern void _PG_fini(void); + +TdsInstrPlugin **tds_instr_plugin_ptr = NULL; + +/* Hook for plugins */ +static struct PLtsql_protocol_plugin pltsql_plugin_handler; +PLtsql_protocol_plugin *pltsql_plugin_handler_ptr = &pltsql_plugin_handler; + +static Oid tvp_lookup(const char *relname, Oid relnamespace); +static relname_lookup_hook_type prev_relname_lookup_hook = NULL; + +/* + * Module initialization function + */ +void +_PG_init(void) +{ + /* Be sure we do initialization only once */ + static bool inited = false; + + if (inited) + return; + + /* Must be loaded with shared_preload_libaries */ + if (!process_shared_preload_libraries_in_progress) + ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("babelfishpg_tds must be loaded via shared_preload_libraries"))); + + TdsDefineGucs(); + + tds_instr_plugin_ptr = (TdsInstrPlugin **) find_rendezvous_variable("TdsInstrPlugin"); + + pe_init(); + + prev_relname_lookup_hook = relname_lookup_hook; + relname_lookup_hook = tvp_lookup; + + inited = true; +} + +/* + * Module unload function + */ +void +_PG_fini(void) +{ + pe_fin(); + relname_lookup_hook = prev_relname_lookup_hook; +} + +/* + * For table-valued parameter that's not handled by pltsql, we set up a hook so + * that we can look up a TVP's underlying table. + */ +static Oid +tvp_lookup(const char *relname, Oid relnamespace) +{ + Oid relid; + ListCell *lc; + + if (prev_relname_lookup_hook) + relid = (*prev_relname_lookup_hook) (relname, relnamespace); + else + relid = get_relname_relid(relname, relnamespace); + + /* + * If we find a TVP whose name matches relname, return its + * underlying table's relid. Otherwise, just return relname's relid. + */ + foreach (lc, tvp_lookup_list) + { + TvpLookupItem *item = (TvpLookupItem *) lfirst(lc); + + if (strcmp(relname, item->name) == 0) + { + if (OidIsValid(item->tableRelid)) + return item->tableRelid; + else + return get_relname_relid(item->tableName, relnamespace); + } + } + + return relid; +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tds_data_map.c b/contrib/babelfishpg_tds/src/backend/tds/tds_data_map.c new file mode 100644 index 00000000000..2573b24695b --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds_data_map.c @@ -0,0 +1,219 @@ +#include "postgres.h" + +#include "mb/pg_wchar.h" +#include "access/attnum.h" +#include "lib/stringinfo.h" +#include "src/include/tds_typeio.h" +#include "src/include/tds_iofuncmap.h" + + +TdsLCIDToEncodingMap TdsLCIDToEncodingMap_data[] = +{ + {0x0436, PG_WIN1252}, // Afrikaans: South Africa + {0x041c, PG_WIN1250}, // Albanian: Albania + {0x1401, PG_WIN1256}, // Arabic: Algeria + {0x3c01, PG_WIN1256}, // Arabic: Bahrain + {0x0c01, PG_WIN1256}, // Arabic: Egypt + {0x0801, PG_WIN1256}, // Arabic: Iraq + {0x2c01, PG_WIN1256}, // Arabic: Jordan + {0x3401, PG_WIN1256}, // Arabic: Kuwait + {0x3001, PG_WIN1256}, // Arabic: Lebanon + {0x1001, PG_WIN1256}, // Arabic: Libya + {0x1801, PG_WIN1256}, // Arabic: Morocco + {0x2001, PG_WIN1256}, // Arabic: Oman + {0x4001, PG_WIN1256}, // Arabic: Qatar + {0x0401, PG_WIN1256}, // Arabic: Saudi Arabia + {0x2801, PG_WIN1256}, //Arabic: Syria + {0x1c01, PG_WIN1256}, // Arabic: Tunisia + {0x3801, PG_WIN1256}, // Arabic: U.A.E. + {0x2401, PG_WIN1256}, // Arabic: Yemen + // {0x042b, 0},// Armenian: Armenia + {0x082c, PG_WIN1251},// Azeri: Azerbaijan (Cyrillic) + {0x042c, PG_WIN1250},// Azeri: Azerbaijan (Latin) + {0x042d, PG_WIN1252},// Basque: Spain + {0x0423, PG_WIN1251},// Belarusian: Belarus + {0x0402, PG_WIN1251},// Bulgarian: Bulgaria + {0x0403, PG_WIN1252},// Catalan: Spain + {0x0c04, PG_BIG5}, + {0x1404, PG_BIG5},// Chinese: Macao SAR (Traditional) + {0x0804, PG_GBK},// Chinese: PRC (Simplified) + {0x1004, PG_GBK},// Chinese: Singapore (Simplified) + {0x0404, PG_BIG5},// Chinese: Taiwan (Traditional) + // {0x0827, PG_WIN1257}, + {0x041a, PG_WIN1250},// Croatian: Croatia + {0x0405, PG_WIN1250},// Czech: Czech Republic + {0x0406, PG_WIN1252},// Danish: Denmark + {0x0813, PG_WIN1252},// Dutch: Belgium + {0x0413, PG_WIN1252},// Dutch: Netherlands + {0x0c09, PG_WIN1252},// English: Australia + {0x2809, PG_WIN1252},// English: Belize + {0x1009, PG_WIN1252},// English: Canada + // {0x2409, PG_WIN1252}, + {0x1809, PG_WIN1252},// English: Ireland + {0x2009, PG_WIN1252},// English: Jamaica + {0x1409, PG_WIN1252},// English: New Zealand + {0x3409, PG_WIN1252},// English: Philippines + {0x1c09, PG_WIN1252},// English: South Africa + {0x2c09, PG_WIN1252},// English: Trinidad + {0x0809, PG_WIN1252},// English: United Kingdom + {0x0409, PG_WIN1252},// English: United States + {0x3009, PG_WIN1252},// English: Zimbabwe + {0x0425, PG_WIN1257},// Estonian: Estonia + {0x0438, PG_WIN1252},// Faeroese: Faeroe Islands + {0x0429, PG_WIN1256},// Farsi: Iran + {0x040b, PG_WIN1252},// Finnish: Finland + {0x080c, PG_WIN1252},// French: Belgium + {0x0c0c, PG_WIN1252},// French: Canada + {0x040c, PG_WIN1252},// French: France + {0x140c, PG_WIN1252},// French: Luxembourg + {0x180c, PG_WIN1252},// French: Monaco + {0x100c, PG_WIN1252},// French: Switzerland + {0x042f, PG_WIN1251},// Macedonian (FYROM) + // {0x0437, 0},// Georgian: Georgia + {0x0c07, PG_WIN1252},// German: Austria + {0x0407, PG_WIN1252},// German: Germany + {0x1407, PG_WIN1252},// German: Liechtenstein + {0x1007, PG_WIN1252},// German: Luxembourg + {0x0807, PG_WIN1252},// German: Switzerland + {0x0408, PG_WIN1253},// Greek: Greece + // {0x0447, 0},// Gujarati: India + {0x040d, PG_WIN1255},// Hebrew: Israel + // {0x0439, 0},// Hindi: India + {0x040e, PG_WIN1250},// Hungarian: Hungary + {0x040f, PG_WIN1252},// Icelandic: Iceland + {0x0421, PG_WIN1252},// Indonesian: Indonesia + {0x0410, PG_WIN1252},// Italian: Italy + {0x0810, PG_WIN1252},// Italian: Switzerland + {0x0411, PG_SJIS},// Japanese: Japan + // {0x044b, 0},// Kannada: India + // {0x0457, 0},// Konkani: India + {0x0412, PG_UHC},// Korean (Extended Wansung): Korea + {0x0440, PG_WIN1251},// Kyrgyz: Kyrgyzstan + {0x0426, PG_WIN1257},// Latvian: Latvia + {0x0427, PG_WIN1257},// Lithuanian: Lithuania + {0x083e, PG_WIN1252},// Malay: Brunei Darussalam + {0x043e, PG_WIN1252},// Malay: Malaysia + // {0x044e, 0},// Marathi: India + {0x0450, PG_WIN1251},// Mongolian: Mongolia + {0x0414, PG_WIN1252},// Norwegian: Norway (Bokmål) + {0x0814, PG_WIN1252},// Norwegian: Norway (Nynorsk) + {0x0415, PG_WIN1250},// Polish: Poland + {0x0416, PG_WIN1252},// Portuguese: Brazil + {0x0816, PG_WIN1252},// Portuguese: Portugal + // {0x0446, 0},// Punjabi: India + {0x0418, PG_WIN1250},// Romanian: Romania + {0x0419, PG_WIN1251},// Russian: Russia + // {0x044f, 0},// Sanskrit: India + {0x0c1a, PG_WIN1251},// Serbian: Serbia (Cyrillic) + {0x081a, PG_WIN1250},// Serbian: Serbia (Latin) + {0x041b, PG_WIN1250},// Slovak: Slovakia + {0x0424, PG_WIN1250},// Slovenian: Slovenia + {0x2c0a, PG_WIN1252},// Spanish: Argentina + {0x400a, PG_WIN1252},// Spanish: Bolivia + {0x340a, PG_WIN1252},// Spanish: Chile + {0x240a, PG_WIN1252},// Spanish: Colombia + {0x140a, PG_WIN1252},// Spanish: Costa Rica + {0x1c0a, PG_WIN1252},// Spanish: Dominican Republic + {0x300a, PG_WIN1252},// Spanish: Ecuador + {0x440a, PG_WIN1252},// Spanish: El Salvador + {0x100a, PG_WIN1252},// Spanish: Guatemala + {0x480a, PG_WIN1252},// Spanish: Honduras + {0x080a, PG_WIN1252},// Spanish: Mexico + {0x4c0a, PG_WIN1252},// Spanish: Nicaragua + {0x180a, PG_WIN1252},// Spanish: Panama + {0x3c0a, PG_WIN1252},// Spanish: Paraguay + {0x280a, PG_WIN1252},// Spanish: Peru + {0x500a, PG_WIN1252},// Spanish: Puerto Rico + {0x0c0a, PG_WIN1252},// Spanish: Spain (Modern Sort) + {0x040a, PG_WIN1252},// Spanish: Spain (International Sort) + {0x380a, PG_WIN1252},// Spanish: Uruguay + {0x200a, PG_WIN1252},// Spanish: Venezuela + {0x0441, PG_WIN1252},// Swahili: Kenya + {0x081d, PG_WIN1252},// Swedish: Finland + {0x041d, PG_WIN1252},// Swedish: Sweden + {0x0444, PG_WIN1251},// Tatar: Tatarstan + // {0x044a, 0},// Telgu: India + {0x041e, PG_WIN874},// Thai: Thailand + {0x041f, PG_WIN1254},// Turkish: Turkey + {0x0422, PG_WIN1251},// Ukrainian: Ukraine + {0x0820, PG_WIN1256},// Urdu: India + {0x0420, PG_WIN1256},// Urdu: Pakistan + {0x0843, PG_WIN1251},// Uzbek: Uzbekistan (Cyrillic) + {0x0443, PG_WIN1250},// Uzbek: Uzbekistan (Latin) + {0x042a, PG_WIN1258}// Vietnamese: Vietnam +}; + +size_t TdsLCIDToEncodingMap_datasize = lengthof(TdsLCIDToEncodingMap_data); + +TdsIoFunctionRawData TdsIoFunctionRawData_data[] = +{ + {"sys", "bit", TDS_TYPE_BIT, 1, 1, TDS_SEND_BIT, TDS_RECV_BIT}, + {"sys", "tinyint", TDS_TYPE_INTEGER, 1, 1, TDS_SEND_TINYINT, TDS_RECV_TINYINT}, + {"pg_catalog", "int2", TDS_TYPE_INTEGER, 2, 1, TDS_SEND_SMALLINT, TDS_RECV_SMALLINT}, + {"pg_catalog", "int4", TDS_TYPE_INTEGER, 4, 1, TDS_SEND_INTEGER, TDS_RECV_INTEGER}, + {"pg_catalog", "int8", TDS_TYPE_INTEGER, 8, 1, TDS_SEND_BIGINT, TDS_RECV_BIGINT}, + {"pg_catalog", "float4", TDS_TYPE_FLOAT, 4, 1, TDS_SEND_FLOAT4, TDS_RECV_FLOAT4}, + {"pg_catalog", "float8", TDS_TYPE_FLOAT, 8, 1, TDS_SEND_FLOAT8, TDS_RECV_FLOAT8}, + {"pg_catalog", "bpchar", TDS_TYPE_CHAR, -1, 2, TDS_SEND_CHAR, TDS_RECV_CHAR}, + {"sys", "bpchar", TDS_TYPE_CHAR, -1, 2, TDS_SEND_CHAR, TDS_RECV_CHAR}, + {"sys", "nchar", TDS_TYPE_NCHAR, -1, 2, TDS_SEND_NCHAR, TDS_RECV_NCHAR}, + {"sys", "nvarchar", TDS_TYPE_NVARCHAR, -1, 2, TDS_SEND_NVARCHAR, TDS_RECV_NVARCHAR}, + {"sys", "varchar", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_VARCHAR}, + {"sys", "smallmoney", TDS_TYPE_MONEYN, 4, 1, TDS_SEND_SMALLMONEY, TDS_RECV_SMALLMONEY}, + {"sys", "money", TDS_TYPE_MONEYN, 8, 1, TDS_SEND_MONEY, TDS_RECV_MONEY}, + {"pg_catalog", "text", TDS_TYPE_TEXT, -1, 2, TDS_SEND_TEXT, TDS_RECV_TEXT}, + {"sys", "ntext", TDS_TYPE_NTEXT, -1, 2, TDS_SEND_NTEXT, TDS_RECV_NTEXT}, + {"pg_catalog", "date", TDS_TYPE_DATE, 3, 1, TDS_SEND_DATE, TDS_RECV_DATE}, + {"sys", "datetime", TDS_TYPE_DATETIMEN, 8, 1, TDS_SEND_DATETIME, TDS_RECV_DATETIME}, + {"pg_catalog", "numeric", TDS_TYPE_NUMERICN, 17, 1, TDS_SEND_NUMERIC, TDS_RECV_NUMERIC}, + {"sys", "smalldatetime", TDS_TYPE_DATETIMEN, 4, 1, TDS_SEND_SMALLDATETIME, TDS_RECV_SMALLDATETIME}, + {"sys", "binary", TDS_TYPE_BINARY, -1, 2, TDS_SEND_BINARY, TDS_RECV_BINARY}, + {"sys", "varbinary", TDS_TYPE_VARBINARY, -1, 2, TDS_SEND_VARBINARY, TDS_RECV_VARBINARY}, + {"sys", "bbf_varbinary", TDS_TYPE_VARBINARY, -1, 2, TDS_SEND_VARBINARY, TDS_RECV_VARBINARY}, + {"sys", "image", TDS_TYPE_IMAGE, -1, 2, TDS_SEND_IMAGE, TDS_RECV_IMAGE}, + {"sys", "uniqueidentifier", TDS_TYPE_UNIQUEIDENTIFIER, 16, 1, TDS_SEND_UNIQUEIDENTIFIER, TDS_RECV_UNIQUEIDENTIFIER}, + {"pg_catalog", "time", TDS_TYPE_TIME, 5, 1, TDS_SEND_TIME, TDS_RECV_TIME}, + {"sys", "datetime2", TDS_TYPE_DATETIME2, 8, 1, TDS_SEND_DATETIME2, TDS_RECV_DATETIME2}, + {"pg_catalog", "xml", TDS_TYPE_XML, -1, 1, TDS_SEND_XML, TDS_RECV_XML}, + {"sys", "sql_variant", TDS_TYPE_SQLVARIANT, -1, 4, TDS_SEND_SQLVARIANT, TDS_RECV_SQLVARIANT}, + {"sys", "datetimeoffset", TDS_TYPE_DATETIMEOFFSET, 10, 1, TDS_SEND_DATETIMEOFFSET, TDS_RECV_DATETIMEOFFSET}, + + /* Mapping TDS listener sender to basic Postgres datatypes. */ + {"pg_catalog", "oid", TDS_TYPE_INTEGER, 4, 1, TDS_SEND_INTEGER, TDS_RECV_INVALID}, + {"pg_catalog", "sql_identifier", TDS_TYPE_VARCHAR, -1, 1, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "name", TDS_TYPE_VARCHAR, -1, 1, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "character_data", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "bool", TDS_TYPE_BIT, 1, 1, TDS_SEND_BIT, TDS_RECV_INVALID}, + {"pg_catalog", "varchar", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "cardinal_number", TDS_TYPE_INTEGER, 4, 1, TDS_SEND_INTEGER, TDS_RECV_INVALID}, + {"pg_catalog", "yes_or_no", TDS_TYPE_VARCHAR, -1, 1, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "char", TDS_TYPE_VARCHAR, -1, 1, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "timestamp", TDS_TYPE_DATETIMEN, 8, 1, TDS_SEND_DATETIME, TDS_RECV_INVALID}, + {"pg_catalog", "timestamptz", TDS_TYPE_VARCHAR, -1, 1, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "regproc", TDS_TYPE_VARCHAR, -1, 1, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "cstring", TDS_TYPE_TEXT, -1, 2, TDS_SEND_TEXT, TDS_RECV_INVALID}, + {"pg_catalog", "real", TDS_TYPE_FLOAT, 4, 1, TDS_SEND_FLOAT4, TDS_RECV_INVALID}, + {"pg_catalog", "aclitem", TDS_TYPE_VARCHAR, -1, 1, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "int2vector", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "oidvector", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog","pg_node_tree", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog","pg_lsn", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_oid", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_text", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_aclitem", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_float4", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_float8", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_int2", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_real", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "_char", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog","pg_dependencies", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog","pg_ndistinct", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog","anyarray", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "xid", TDS_TYPE_INTEGER, 4, 1, TDS_SEND_INTEGER, TDS_RECV_INVALID}, + {"pg_catalog", "cid", TDS_TYPE_INTEGER, 4, 1, TDS_SEND_INTEGER, TDS_RECV_INVALID}, + {"pg_catalog", "tid", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "inet", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID}, + {"pg_catalog", "interval", TDS_TYPE_VARCHAR, -1, 2, TDS_SEND_VARCHAR, TDS_RECV_INVALID} +}; + +size_t TdsIoFunctionRawData_datasize = lengthof(TdsIoFunctionRawData_data); diff --git a/contrib/babelfishpg_tds/src/backend/tds/tds_srv.c b/contrib/babelfishpg_tds/src/backend/tds/tds_srv.c new file mode 100644 index 00000000000..a98803c713f --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds_srv.c @@ -0,0 +1,477 @@ +/*------------------------------------------------------------------------- + * + * tds_srv.c + * register wire protocol hooks for TDS + * + * Copyright (c) 2021, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tds_srv.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include +#include +#include + +#include "commands/defrem.h" +#include "common/ip.h" +#include "miscadmin.h" +#include "parser/parse_expr.h" +#include "postmaster/postmaster.h" +#include "postmaster/protocol_extension.h" +#include "pgstat.h" +#include "storage/ipc.h" +#include "tcop/pquery.h" +#include "tcop/tcopprot.h" +#include "utils/guc.h" +#include "utils/pidfile.h" +#include "utils/portal.h" +#include "utils/ps_status.h" +#include "utils/timeout.h" +#include "utils/varlena.h" + +#include "src/include/tds_int.h" +#include "src/include/tds_secure.h" +#include "src/include/tds_protocol.h" +#include "src/include/tds_response.h" +#include "src/include/err_handler.h" +#include "src/include/guc.h" + +static listen_init_hook_type prev_listen_init; + +static bool LoadedSSL = false; + +/* Where the Unix socket files are (list of palloc'd strings) */ +static List *sock_paths = NIL; + +/* Declare the TDS context callback only once */ +static ErrorContextCallback tdserrcontext; + +TdsErrorContextData *TdsErrorContext = NULL; + +static int pe_accept(pgsocket server_fd, Port *port); +static void pe_listen_init(void); +static void pe_close(pgsocket server_fd); +static void pe_tds_init(void); +static int pe_start(Port *port); +static void pe_authenticate(Port *port, const char **username); +static void pe_mainfunc(Port *port, + int argc, char *argv[]) pg_attribute_noreturn(); +static void pe_send_message(ErrorData *edata); +static void pe_send_ready_for_query(CommandDest dest); +static int pe_read_command(StringInfo inBuf); +static int pe_process_command(void); +static void pe_end_command(QueryCompletion *qc, CommandDest dest); +static void socket_close(int code, Datum arg); + +/* the dest reveiver support is kept in a separate file */ +#include "tdsprinttup.c" + +static ProtocolExtensionConfig pe_config = { + pe_accept, + pe_close, + pe_tds_init, + pe_start, + pe_authenticate, + pe_mainfunc, + pe_send_message, + NULL, /* not interested in cancel key */ + NULL, + NULL, + pe_send_ready_for_query, + pe_read_command, + pe_end_command, + TdsPrintTup, + TdsPrinttupStartup, + TdsShutdown, + TdsDestroy, + pe_process_command +}; + +/* + * The generic socket support is kept in a separate file + */ +#include "support_funcs.c" + +void +pe_init(void) +{ +#ifdef USE_SSL + /* Server will be started when this extension will be loaded */ + if (EnableSSL) + if (Tds_be_tls_init(true) == 0) + LoadedSSL = true; +#endif + + /* Install hooks */ + prev_listen_init = listen_init_hook; + listen_init_hook = pe_listen_init; +} + +void +pe_fin(void) +{ + /* Uninstall hooks. */ + listen_init_hook = prev_listen_init; +} + +/* + * pe_listen_init - Create the telnet server socket(s) + */ +static void +pe_listen_init(void) +{ + pe_create_server_ports(); +} + +/* + * pe_accept - Accept a new incoming client connection + */ +static int +pe_accept(pgsocket server_fd, Port *port) +{ + return pe_create_connection(server_fd, port); +} + +/* + * pe_close - called to close server sockets in new backend + */ +static void +pe_close(pgsocket server_fd) +{ + StreamClose(server_fd); +} + +/* + * pe_init - equivalent of pq_init + */ +static void +pe_tds_init(void) +{ + PLtsql_protocol_plugin **pltsql_plugin_handler_ptr_tmp; + + /* This is client backend */ + MyBackendType = B_BACKEND; + + TdsClientInit(); + + /* + * If this is a TDS client, we install the TDS specific protocol function + * hooks. + * XXX: All of them should be removed in future. + */ + lookup_param_hook = TdsFindParam; + + /* Set up a rendezvous point with pltsql plugin */ + pltsql_plugin_handler_ptr_tmp = (PLtsql_protocol_plugin **) + find_rendezvous_variable("PLtsql_protocol_plugin"); + + /* unlikely */ + if (!pltsql_plugin_handler_ptr_tmp) + elog(ERROR, "failed to setup rendezvous variable for pltsql plugin"); + + *pltsql_plugin_handler_ptr_tmp = pltsql_plugin_handler_ptr; + + memset(pltsql_plugin_handler_ptr, 0, sizeof(PLtsql_protocol_plugin)); + + pltsql_plugin_handler_ptr->send_info = &TdsSendInfo; + pltsql_plugin_handler_ptr->send_done = &TdsSendDone; + pltsql_plugin_handler_ptr->send_env_change = &TdsSendEnvChange; + pltsql_plugin_handler_ptr->get_tsql_error = &get_tsql_error_details; + pltsql_plugin_handler_ptr->stmt_beg = TDSStatementBeginCallback; + pltsql_plugin_handler_ptr->stmt_end = TDSStatementEndCallback; + pltsql_plugin_handler_ptr->stmt_exception = TDSStatementExceptionCallback; + pltsql_plugin_handler_ptr->send_column_metadata = SendColumnMetadata; + pltsql_plugin_handler_ptr->get_mapped_error_list = &get_mapped_error_code_list; + + pltsql_plugin_handler_ptr->get_login_domainname = &get_tds_login_domainname; + + /* set up process-exit hook to close the socket */ + on_proc_exit(socket_close, 0); +} + +/* + * pe_start - equivalent of ProcessStartupPacket() + * + * This function needs to communicate with the client + * at least to the point where it can fill in the + * database_name and user_name in the Port. It cannot + * do anything that would require actual database + * access. + */ +static int +pe_start(Port *port) +{ + int rc; + MemoryContext oldContext; + + /* we're ready to begin the communication with the TDS client */ + if((pltsql_plugin_handler_ptr)) + pltsql_plugin_handler_ptr->is_tds_client = true; + + /* + * Initialise The Global Variable TdsErrorContext, which is + * to be used throughout TDS. We could have allocated the same + * in TdsMemoryContext. But, during reset connection, we reset + * the same. We don't want to reset TdsErrorContext at that point + * of time. So, allocate the memory in TopMemoryContext. + */ + oldContext = MemoryContextSwitchTo(TopMemoryContext); + TdsErrorContext = palloc(sizeof(TdsErrorContextData)); + MemoryContextSwitchTo(oldContext); + + /* Push the error context */ + tdserrcontext.callback = TdsErrorContextCallback; + tdserrcontext.arg = TdsErrorContext; + tdserrcontext.previous = error_context_stack; + error_context_stack = &tdserrcontext; + + rc = TdsProcessLogin(port, LoadedSSL); + + /* Pop the error context stack */ + error_context_stack = tdserrcontext.previous; + + return rc; +} + +static void +pe_authenticate(Port *port, const char **username) +{ + /* This should be set already, but let's make sure */ + ClientAuthInProgress = true; /* limit visibility of log messages */ + + /* + * In EXEC_BACKEND case, we didn't inherit the contents of pg_hba.conf + * etcetera from the postmaster, and have to load them ourselves. + * + * FIXME: [fork/exec] Ugh. Is there a way around this overhead? + */ +#ifdef EXEC_BACKEND + + /* + * load_hba() and load_ident() want to work within the PostmasterContext, + * so create that if it doesn't exist (which it won't). We'll delete it + * again later, in PostgresMain. + */ + if (PostmasterContext == NULL) + PostmasterContext = AllocSetContextCreate(TopMemoryContext, + MC_Postmaster, + ALLOCSET_DEFAULT_SIZES); + + if (!load_hba()) + { + /* + * It makes no sense to continue if we fail to load the HBA file, + * since there is no way to connect to the database in this case. + */ + ereport(FATAL, + (errmsg("could not load pg_hba.conf"))); + } + + if (!load_ident()) + { + /* + * It is ok to continue if we fail to load the IDENT file, although it + * means that you cannot log in using any of the authentication + * methods that need a user name mapping. load_ident() already logged + * the details of error to the log. + */ + } +#endif + + /* + * Perform authentication exchange. + */ + set_ps_display("authentication"); + + /* + * Set up a timeout in case a buggy or malicious client fails to respond + * during authentication. Since we're inside a transaction and might do + * database access, we have to use the statement_timeout infrastructure. + */ + enable_timeout_after(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000); + + /* + * Now perform authentication exchange. + */ + TdsClientAuthentication(port); /* might not return, if failure */ + + /* + * Done with authentication. Disable the timeout, and log if needed. + */ + disable_timeout(STATEMENT_TIMEOUT, false); + + /* Log only if Log_connections is set. */ + if (Log_connections) + { + StringInfoData logmsg; + + initStringInfo(&logmsg); + appendStringInfo(&logmsg, _("connection authorized: user=%s,"), + port->user_name); + if (port->application_name) + appendStringInfo(&logmsg, _(" application=%s,"), + port->application_name); + + appendStringInfo(&logmsg, _(" Tds Version=0x%X."), GetClientTDSVersion()); + + ereport(LOG, errmsg_internal("%s", logmsg.data)); + pfree(logmsg.data); + } + + set_ps_display("startup"); + + ClientAuthInProgress = false; /* client_min_messages is active now */ + + *username = port->user_name; +} + +static void +pe_mainfunc(Port *port, int argc, char *argv[]) +{ + /* + * This protocol doesn't need anything other than the default + * behavior of PostgresMain(). Note that PostgresMain() will + * connect to the database and in turn will call our + * pe_authenticate() function. + */ + PostgresMain(argc, argv, port->database_name, + port->user_name); +} + +static void +pe_send_message(ErrorData *edata) +{ + if (edata->output_to_client) + emit_tds_log(edata); +} + +static void +pe_send_ready_for_query(CommandDest dest) +{ + /* + * If we've already sent the login ack and initialized the protocol, + * return from here. We're ready to process the next query. + */ + if (TdsRequestCtrl) + return; + + /* Push the error context */ + tdserrcontext.callback = TdsErrorContextCallback; + tdserrcontext.arg = TdsErrorContext; + tdserrcontext.previous = error_context_stack; + error_context_stack = &tdserrcontext; + + /* If first time, we should send the login ack */ + TdsSendLoginAck(MyProcPort); + + /* Pop the error context stack */ + error_context_stack = tdserrcontext.previous; +} + +static int +pe_read_command(StringInfo inBuf) +{ + int rc; + + /* Push the error context */ + tdserrcontext.callback = TdsErrorContextCallback; + tdserrcontext.arg = TdsErrorContext; + tdserrcontext.previous = error_context_stack; + error_context_stack = &tdserrcontext; + + rc = TdsSocketBackend(); + + /* Pop the error context stack */ + error_context_stack = tdserrcontext.previous; + return rc; +} + +static int +pe_process_command() +{ + int result; + + /* Push the error context */ + tdserrcontext.callback = TdsErrorContextCallback; + tdserrcontext.arg = TdsErrorContext; + tdserrcontext.previous = error_context_stack; + error_context_stack = &tdserrcontext; + + result = TdsSocketBackend(); + /* + * If no transaction is on-going, enforce transaction state cleanup before + * calling pgstat_report_stat function which requires a clean transaction state. + */ + if (!IsTransactionOrTransactionBlock()) + Cleanup_xact_PgStat(); + + /* Pop the error context stack */ + error_context_stack = tdserrcontext.previous; + return result; +} + +static void +pe_end_command(QueryCompletion *qc, CommandDest dest) +{ + /* no-op */ +} + +/* -------------------------------- + * socket_close - shutdown TDS at backend exit + * + * This is same as socket_close() in pqcomm.c, but for a TDS backend. + * -------------------------------- + */ +static void +socket_close(int code, Datum arg) +{ + /* Nothing to do in a standalone backend, where MyProcPort is NULL. */ + if (MyProcPort != NULL) + { +#ifdef ENABLE_GSS + /* + * Shutdown GSSAPI layer. This section does nothing when interrupting + * BackendInitialize(), because pg_GSS_recvauth() makes first use of + * "ctx" and "cred". + * + * Note that we don't bother to free MyProcPort->gss, since we're + * about to exit anyway. + */ + if (MyProcPort->gss) + { + OM_uint32 min_s; + + if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL); + + if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL) + gss_release_cred(&min_s, &MyProcPort->gss->cred); + } +#endif /* ENABLE_GSS */ + + /* + * Cleanly shut down SSL layer. Nowhere else does a postmaster child + * call this, so this is safe when interrupting BackendInitialize(). + */ +#ifdef USE_SSL + if (MyProcPort->ssl_in_use) + Tds_be_tls_close(MyProcPort); +#endif + + /* + * Formerly we did an explicit close() here, but it seems better to + * leave the socket open until the process dies. This allows clients + * to perform a "synchronous close" if they care --- wait till the + * transport layer reports connection closure, and you can be sure the + * backend has exited. + * + * We do set sock to PGINVALID_SOCKET to prevent any further I/O, + * though. + */ + MyProcPort->sock = PGINVALID_SOCKET; + } +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdscomm.c b/contrib/babelfishpg_tds/src/backend/tds/tdscomm.c new file mode 100644 index 00000000000..c3e92c0e3c5 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdscomm.c @@ -0,0 +1,953 @@ +/*------------------------------------------------------------------------- + * + * tdscomm.c + * TDS Listener communication support Postgres + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdscomm.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "libpq/libpq.h" + +#include "miscadmin.h" /* for MyProcPort */ +#include "access/xact.h" /* for IsTransactionOrTransactionBlock() */ +#include "utils/memutils.h" +#include "utils/guc.h" +#include "port/pg_bswap.h" +#include "utils/guc.h" +#include "utils/memutils.h" + +#include "src/include/tds_debug.h" +#include "src/include/tds_int.h" +#include "src/include/faultinjection.h" + +/* Globals */ +MemoryContext TdsMemoryContext = NULL; + + +static uint32_t TdsBufferSize; +static char *TdsSendBuffer; +static int TdsSendCur; /* Next index to store a byte in TdsSendBuffer */ +static int TdsSendStart; /* Next index to send a byte in TdsSendBuffer */ +static uint8_t TdsSendMessageType; /* Current TDS message in progress */ + +static bool TdsDoProcessHeader; /* Header is processed or not. */ +static char *TdsRecvBuffer; +static int TdsRecvStart; /* Next index to read a byte from TdsRecvBuffer */ +static int TdsRecvEnd; /* End of data available in TdsRecvBuffer */ +static uint8_t TdsRecvMessageType; /* Current TDS message in progress */ +static uint8_t TdsRecvPacketStatus; +static int TdsLeftInPacket; + +static TdsSecureSocketApi tds_secure_read; +static TdsSecureSocketApi tds_secure_write; + + +/* Internal functions */ +static void SocketSetNonblocking(bool nonblocking); +static int InternalFlush(bool); +static void TdsConsumedBytes(int bytes); + +/* Inline functions */ + +/* -------------------------------- + * InternalPutbytes - send bytes to connection (not flushed until TdsFlush) + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +static inline int +InternalPutbytes(void *bytes, size_t len) +{ + size_t amount; + unsigned char *s = bytes; + + while (len > 0) + { + /* If buffer is full, then flush it out */ + if (TdsSendCur >= TdsBufferSize) + { + SocketSetNonblocking(false); + if (InternalFlush(false)) + return EOF; + } + amount = TdsBufferSize - TdsSendCur; + if (amount > len) + amount = len; + memcpy(TdsSendBuffer + TdsSendCur, s, amount); + TdsSendCur += amount; + s += amount; + len -= amount; + } + return 0; +} + + +/* -------------------------------- + * TdsSetMessageType - Set current TDS message context + * -------------------------------- + */ +void +TdsSetMessageType(uint8_t msgType) +{ + TdsSendMessageType = msgType; +} + +/* -------------------------------- + * Low-level I/O routines begin here. + * + * These routines communicate with a frontend client across a connection. + * -------------------------------- + */ + +/* -------------------------------- + * SocketSetNonblocking - set socket blocking/non-blocking + * + * Sets the socket non-blocking if nonblocking is true, or sets it + * blocking otherwise. + * -------------------------------- + */ +static void +SocketSetNonblocking(bool nonblocking) +{ + if (MyProcPort == NULL) + ereport(ERROR, + (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), + errmsg("there is no client connection"))); + + MyProcPort->noblock = nonblocking; +} + +/* -------------------------------- + * TdsReadsocket - read data from socket + * + * Data is read in a fix size buffer. Read socket will + * issue network read for left capacity in receive buffer + * -------------------------------- + */ +static int +TdsReadsocket(void) +{ + TdsErrorContext->err_text = "Reading data from socket"; + if (TdsRecvStart > 0) + { + if (TdsRecvEnd > TdsRecvStart) + { + /* still some unread data, left-justify it in the buffer */ + memmove(TdsRecvBuffer, TdsRecvBuffer + TdsRecvStart, + TdsRecvEnd - TdsRecvStart); + TdsRecvEnd -= TdsRecvStart; + TdsRecvStart = 0; + } + else + TdsRecvStart = TdsRecvEnd = 0; + } + + /* Ensure that we're in blocking mode */ + SocketSetNonblocking(false); + + /* Can fill buffer from TdsRecvStart and upwards */ + for (;;) + { + int r; + + r = tds_secure_read(MyProcPort, TdsRecvBuffer + TdsRecvEnd, + TdsBufferSize - TdsRecvEnd); + + if (r < 0) + { + if (errno == EINTR) + continue; /* Ok if interrupted */ + + /* + * Careful: an ereport() that tries to write to the client would + * cause recursion to here, leading to stack overflow and core + * dump! This message must go *only* to the postmaster log. + */ + ereport(COMMERROR, + (errcode_for_socket_access(), + errmsg("could not receive data from client: %m"))); + return EOF; + } + if (r == 0) + { + /* + * EOF detected. We used to write a log message here, but it's + * better to expect the ultimate caller to do that. + */ + return EOF; + } + /* r contains number of bytes read, so just incr length */ + TdsRecvEnd += r; + return 0; + } + +} + +/* -------------------------------- + * TdsProcessHeader - Process TDS header + * + * TDS header is of 8 bytes and is prefixed before + * each packet in message + * -------------------------------- + */ +static int +TdsProcessHeader(void) +{ + uint16_t data16; + + FAULT_INJECT(ParseHeaderType, TdsRecvBuffer); + TdsErrorContext->err_text = "Processing TDS header"; + + if (TdsLeftInPacket != 0) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("New TDS packet read encountered while " + "last packet is not fully consumed"))); + /* Get atleast header worth of data */ + while (TdsRecvEnd - TdsRecvStart < TDS_PACKET_HEADER_SIZE) + { + if (TdsReadsocket()) + return EOF; + } + /* Message type */ + TdsRecvMessageType = TdsRecvBuffer[TdsRecvStart]; + /* Packet status */ + TdsRecvPacketStatus = TdsRecvBuffer[TdsRecvStart+1]; + + /* Packet length in network byte order (includes header size) */ + memcpy(&data16, TdsRecvBuffer + TdsRecvStart + 2, sizeof(data16)); + data16 = pg_ntoh16(data16); + if (data16 > TdsBufferSize) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("Packet length %u exceeds packet size %u", + data16, TdsBufferSize))); + + TdsLeftInPacket = data16 - TDS_PACKET_HEADER_SIZE; + TdsRecvStart += TDS_PACKET_HEADER_SIZE; + + /* [BABEL-648] TDS packet with no TDS data is valid packet.*/ + if (TdsLeftInPacket < 0) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("TDS packet with insufficient data"))); + + TdsDoProcessHeader = false; + TDS_DEBUG(TDS_DEBUG3, "TDS packet MessageType %d LeftInPacket %d Status %d", + TdsRecvMessageType, TdsLeftInPacket, TdsRecvPacketStatus); + TDS_DEBUG(TDS_DEBUG3, "TDS receive buffer start %d end %d", TdsRecvStart, TdsRecvEnd); + return 0; +} + +/* -------------------------------- + * TdsRecvbuf - load some bytes into the input buffer + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +static int +TdsRecvbuf(void) +{ + TdsErrorContext->err_text = "Loading data into input buffer"; + /* Need to process the packet header */ + if (TdsLeftInPacket == 0 && TdsRecvStart < TdsRecvEnd) + { + if (TdsProcessHeader()) + return EOF; + if (TdsLeftInPacket == 0) + return 0; + } + /* No more data in the buffer to read */ + if (TdsRecvStart == TdsRecvEnd) + { + if (TdsReadsocket()) + return EOF; + if (TdsLeftInPacket == 0) + { + if (TdsDoProcessHeader && TdsProcessHeader()) + { + return EOF; + } + /* + * Last socket read only got header worth of data and + * if something is left to read. + */ + if ((TdsRecvStart == TdsRecvEnd) && (TdsLeftInPacket > 0)) + { + if (TdsReadsocket()) + return EOF; + } + } + } + if (TdsRecvStart > TdsRecvEnd) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("TDS buffer start pointer %d beyond end pointer %d", + TdsRecvStart, TdsRecvEnd))); + return 0; +} + +#if 0 +/* -------------------------------- + * TdsGetbyte - get a single byte from connection, or return EOF + * -------------------------------- + */ +static int +TdsGetbyte(void) +{ + while (TdsLeftInPacket == 0 || TdsRecvStart >= TdsRecvEnd) + { + if (TdsRecvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + --TdsLeftInPacket; + return (unsigned char) TdsRecvBuffer[TdsRecvStart++]; +} +#endif + +/* -------------------------------- + * TdsFillHeader - Make TDS header for current message + * + * Header is of fix 8 byte + * -------------------------------- + */ +static void +TdsFillHeader(bool lastPacket) +{ + uint16_t net16; + /* Message type */ + TdsSendBuffer[0] = TdsSendMessageType; + /* Packet status */ + TdsSendBuffer[1] = (lastPacket) ? 0x1 : 0x0; + /* Packet length including header */ + net16 = pg_hton16(TdsSendCur - TdsSendStart); + memcpy(TdsSendBuffer + 2, &net16, sizeof(net16)); + net16 = 0; + memcpy(TdsSendBuffer + 4, &net16, sizeof(net16)); /* TODO get server pid */ + TdsSendBuffer[6] = 0; /* TODO generate packet id */ + TdsSendBuffer[7] = 0; /* unused */ +} + +/* -------------------------------- + * InternalFlush - flush pending output + * + * Returns 0 if OK (meaning everything was sent, or operation would block + * and the socket is in non-blocking mode), or EOF if trouble. + * -------------------------------- + */ +static int +InternalFlush(bool lastPacket) +{ + static int lastReportedSendErrno = 0; + + char *bufptr = TdsSendBuffer + TdsSendStart; + char *bufend = TdsSendBuffer + TdsSendCur; + + TdsErrorContext->err_text = "TDS InternalFlush - Sending data to the client"; + /* Writing the packet for the first time */ + if (TdsSendStart == 0) + { + TdsFillHeader(lastPacket); + } + + if (lastPacket) + TdsSendMessageType = 0; + + while (bufptr < bufend) + { + int r; + + DebugPrintBytes("TDS InternalFlush", bufptr, bufend - bufptr); + r = tds_secure_write(MyProcPort, bufptr, bufend - bufptr); + + if (r <= 0) + { + if (errno == EINTR) + continue; /* Ok if we were interrupted */ + + /* + * Ok if no data writable without blocking, and the socket is in + * non-blocking mode. + */ + if (errno == EAGAIN || + errno == EWOULDBLOCK) + { + return 0; + } + + /* + * Careful: an ereport() that tries to write to the client would + * cause recursion to here, leading to stack overflow and core + * dump! This message must go *only* to the postmaster log. + * + * If a client disconnects while we're in the midst of output, we + * might write quite a bit of data before we get to a safe query + * abort point. So, suppress duplicate log messages. + */ + if (errno != lastReportedSendErrno) + { + lastReportedSendErrno = errno; + ereport(COMMERROR, + (errcode_for_socket_access(), + errmsg("could not send data to client: %m"))); + } + + /* + * We drop the buffered data anyway so that processing can + * continue, even though we'll probably quit soon. We also set a + * flag that'll cause the next CHECK_FOR_INTERRUPTS to terminate + * the connection. + */ + TdsSendStart = 0; + TdsSendCur = TDS_PACKET_HEADER_SIZE; + ClientConnectionLost = 1; + InterruptPending = 1; + return EOF; + } + + lastReportedSendErrno = 0; /* reset after any successful send */ + bufptr += r; + TdsSendStart += r; + } + + TdsSendStart = 0; + TdsSendCur = TDS_PACKET_HEADER_SIZE; + return 0; +} + +/* -------------------------------- + * TdsCommInit - Setup TDS comm context + * -------------------------------- + */ +void +TdsCommInit(uint32_t bufferSize, + TdsSecureSocketApi secure_read, + TdsSecureSocketApi secure_write) +{ + tds_secure_read = secure_read; + tds_secure_write = secure_write; + TdsDoProcessHeader = true; + + /* + * Create our own long term memory context for things like the send + * and recieve buffers and caches. + */ + Assert(TdsMemoryContext == NULL); + TdsMemoryContext = AllocSetContextCreate(TopMemoryContext, + "TDS Listener", + ALLOCSET_DEFAULT_SIZES); + + TdsBufferSize = bufferSize; + + TdsCommReset(); +} + +/* -------------------------------- + * TdsCommReset - Reset TDS variables and allocate socket buffers + * -------------------------------- + */ +void +TdsCommReset(void) +{ + MemoryContext oldContext; + + TdsRecvMessageType = TdsSendMessageType = 0; + TdsRecvPacketStatus = 0; + TdsRecvStart = TdsRecvEnd = TdsLeftInPacket = 0; + TdsSendStart = 0; + TdsSendCur = TDS_PACKET_HEADER_SIZE; + + oldContext = MemoryContextSwitchTo(TdsMemoryContext); + TdsRecvBuffer = palloc(TdsBufferSize); + TdsSendBuffer = palloc(TdsBufferSize); + MemoryContextSwitchTo(oldContext); +} + +/* -------------------------------- + * TdsCommShutdown - Shutdown TDS comm context + * -------------------------------- + */ +void +TdsCommShutdown(void) +{ + Assert(TdsSendMessageType == 0); + /* + * Both send and receive buffers should not have any + * valid data at this point in time + */ + Assert(TdsSendStart == 0 && TdsSendCur == TDS_PACKET_HEADER_SIZE); + Assert(TdsRecvStart == TdsRecvEnd && TdsLeftInPacket == 0); + + pfree(TdsSendBuffer); + pfree(TdsRecvBuffer); + if (TdsMemoryContext != NULL) + { + MemoryContextDelete(TdsMemoryContext); + TdsMemoryContext = NULL; + } +} + +/* -------------------------------- + * TdsSetBufferSize - Change network buffer size + * + * During login handshake, client might ask for different + * packet size. Adjust buffer size accordingly + * -------------------------------- + */ +void +TdsSetBufferSize(uint32_t newSize) +{ + TDS_DEBUG(TDS_DEBUG3, "TdsSetBufferSize current size %u new size %u", + TdsBufferSize, newSize); + + if (newSize == TdsBufferSize) + return; + /* + * Both send and receive buffers should not have any + * valid data at this point in time + */ + if (TdsSendStart != 0 || + TdsSendCur != TDS_PACKET_HEADER_SIZE || + TdsRecvStart != TdsRecvEnd || + TdsLeftInPacket != 0) + { + TDS_DEBUG(TDS_DEBUG1, "TDS buffers in inconsistent state; " + "TdsSendStart: %d TdsSendCur: %d TdsRecvStart: %d " + "TdsRecvEnd: %d TdsLeftInPacket: %d", + TdsSendStart, TdsSendCur, TdsRecvStart, + TdsRecvEnd, TdsLeftInPacket); + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("TDS buffers in inconsistent state"))); + } + + TdsSendBuffer = repalloc(TdsSendBuffer, newSize); + TdsRecvBuffer = repalloc(TdsRecvBuffer, newSize); + + TdsBufferSize = newSize; + TdsRecvStart = TdsRecvEnd = TdsLeftInPacket = 0; +} + +/* -------------------------------- + * TdsCheckMessageType - Check current TDS message context + * -------------------------------- + */ +bool +TdsCheckMessageType(uint8_t msgType) +{ + return (TdsRecvMessageType == msgType); +} + +#if 0 +/* -------------------------------- + * TdsPeekbyte - peek at next byte from connection + * + * Same as TdsGetbyte() except we don't advance the pointer. + * -------------------------------- + */ +int +TdsPeekbyte(void) +{ + while (TdsLeftInPacket == 0 || TdsRecvStart >= TdsRecvEnd) + { + if (TdsRecvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + return (unsigned char) TdsRecvBuffer[TdsRecvStart]; +} +#endif + +/* -------------------------------- + * TdsReadNextBuffer - reads buffer from socket + * -------------------------------- + */ +int +TdsReadNextBuffer(void) +{ + TdsErrorContext->err_text = "Reading buffer from socket"; + while ((TdsLeftInPacket > 0 && TdsRecvStart >= TdsRecvEnd) || TdsDoProcessHeader) + { + if (TdsRecvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + return 0; +} + +/* --------------------------------- + * TdsConsumedBytes - reduce TdsLeftInPacket by number of bytes consumed/read + * --------------------------------- + */ +static void +TdsConsumedBytes(int bytes) +{ + TdsLeftInPacket -= bytes; + if (TdsLeftInPacket == 0) + TdsDoProcessHeader = true; +} + +/* -------------------------------- + * TdsGetbytes - get a known number of bytes from connection + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsGetbytes(char *s, size_t len) +{ + size_t amount; + + TDS_DEBUG(TDS_DEBUG3, "TdsGetbytes LeftInPacket %d RecvStart %d RecvEnd %d", + TdsLeftInPacket, TdsRecvStart, TdsRecvEnd); + while (len > 0) + { + while (TdsLeftInPacket == 0 || TdsRecvStart >= TdsRecvEnd) + { + if (TdsRecvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + TdsErrorContext->err_text = ""; + amount = Min(TdsLeftInPacket, TdsRecvEnd - TdsRecvStart); + if (amount > len) + amount = len; + memcpy(s, TdsRecvBuffer + TdsRecvStart, amount); + TdsRecvStart += amount; + TdsConsumedBytes(amount); + s += amount; + len -= amount; + } + return 0; +} + +/* -------------------------------- + * PAGTdsDiscardbytes - throw away a known number of bytes + * + * same as TdsGetbytes except we do not copy the data to anyplace. + * this is used for resynchronizing after read errors. + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsDiscardbytes(size_t len) +{ + size_t amount; + + while (len > 0) + { + while (TdsLeftInPacket == 0 || TdsRecvStart >= TdsRecvEnd) + { + if (TdsRecvbuf()) /* If nothing in buffer, then recv some */ + return EOF; /* Failed to recv data */ + } + TdsErrorContext->err_text = ""; + amount = Min(TdsLeftInPacket, TdsRecvEnd - TdsRecvStart); + if (amount > len) + amount = len; + TdsRecvStart += amount; + TdsConsumedBytes(amount); + len -= amount; + } + return 0; +} + +/* -------------------------------- + * TdsPutbytes - send bytes to connection (not flushed until TdsFlush) + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutbytes(void *s, size_t len) +{ + int res; + + res = InternalPutbytes(s, len); + return res; +} + +/* -------------------------------- + * TdsPutDate - send one 24-bit unsigned integer + * in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutDate(uint32_t value) +{ + uint32_t tmp = htoLE32(value); + + return InternalPutbytes(&tmp, 3); +} + +/* -------------------------------- + * TdsPutInt8 - send one byte + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutInt8(int8_t value) +{ + return InternalPutbytes(&value, sizeof(value)); +} + +/* -------------------------------- + * TdsPutInt64LE - send one 64-bit integer in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutInt64LE(int64_t value) +{ + int64_t tmp = htoLE64(value); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} + +/* -------------------------------- + * TdsPutUInt16LE - send one 16-bit unsigned integer in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutUInt16LE(uint16_t value) +{ + uint16_t tmp = htoLE16(value); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} + +/* -------------------------------- + * TdsPutUInt8 - send one byte + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutUInt8(uint8_t value) +{ + return InternalPutbytes(&value, sizeof(value)); +} + +/* -------------------------------- + * TdsPutInt16LE - send one 16-bit integer in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutInt16LE(int16_t value) +{ + int16_t tmp = htoLE16(value); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} +/* -------------------------------- + * TdsPutInt32LE - send one 32-bit integer in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutInt32LE(int32_t value) +{ + int32_t tmp = htoLE32(value); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} +/* -------------------------------- + * TdsPutUInt32LE - send one 32-bit unsigned integer in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutUInt32LE(uint32_t value) +{ + uint32_t tmp = htoLE32(value); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} +/* -------------------------------- + * TdsPutUInt64LE - send one unsigned 64-bit integer in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutUInt64LE(uint64_t value) +{ + uint64_t tmp = htoLE64(value); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} + +/* -------------------------------- + * TdsPutFloat4LE - send one 32-bit float in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutFloat4LE(float4 value) +{ + uint32 tmp; + union + { + float4 f; + int32 i; + } swap; + + swap.f = value; + tmp = htoLE32(swap.i); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} + +/* -------------------------------- + * TdsPutFloat8LE - send one 64-bit float in LITTLE_ENDIAN + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsPutFloat8LE(float8 value) +{ + uint64 tmp; + union + { + float8 f; + int64 i; + } swap; + + swap.f = value; + tmp = htoLE64(swap.i); + + return InternalPutbytes(&tmp, sizeof(tmp)); +} + +/* -------------------------------- + * TdsSocketFlush - flush pending output + * + * returns 0 if OK, EOF if trouble + * -------------------------------- + */ +int +TdsSocketFlush(void) +{ + SocketSetNonblocking(false); + return InternalFlush(true); +} + +/* -------------------------------- + * TdsReadNextRequest - Read new request + * + * Put message into input sting info and + * status out parameter - returns the status from first packet header + * message type in out parameter + * Return 0 for success and EOF for trouble + * -------------------------------- + */ +int +TdsReadNextRequest(StringInfo message, uint8_t *status, uint8_t *messageType) +{ + int readBytes = 0; + bool isFirst = true; + while(1) + { + if (TdsReadNextBuffer() == EOF) + return EOF; + TdsErrorContext->err_text = "Save the status from first packet header"; + /* + * If this is the first packet header for this TDS request, save the + * status. + */ + if (isFirst) + { + *messageType = TdsRecvMessageType; + *status = TdsRecvPacketStatus; + isFirst = false; + } + readBytes = TdsLeftInPacket; + enlargeStringInfo(message, readBytes); + if (TdsGetbytes(message->data + message->len, readBytes)) + return EOF; + message->len += readBytes; + /* if this is the last packet, break the loop */ + if (TdsRecvPacketStatus & TDS_PACKET_HEADER_STATUS_EOM) + { + if (TdsLeftInPacket == 0 && TdsRecvStart == TdsRecvEnd) + TdsDoProcessHeader = true; + return 0; + } + } + return 0; +} + +/* -------------------------------- + * TdsReadMessage - Read and verify given message type + * + * Put message into input sting info + * Return 0 for success and EOF for trouble + * -------------------------------- + */ +int +TdsReadMessage(StringInfo message, uint8_t messageType) +{ + uint8_t curMsgType; + uint8_t status; + + /* Make sure that last write is flushed */ + if (TdsSendStart != 0 || TdsSendCur != TDS_PACKET_HEADER_SIZE) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("TDS last write did not flush"))); + + if (TdsReadNextRequest(message, &status, &curMsgType)) + return EOF; + // TODO Map to proper error code for TDS client + if (messageType != curMsgType) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Invalid message type %u, expected %u", + curMsgType, messageType))); + return 0; +} + +/* -------------------------------- + * TdsWriteMessage - Write given message type + * + * Send given message over wire + * Return 0 for success and EOF for trouble + * -------------------------------- + */ +int +TdsWriteMessage(StringInfo message, uint8_t messageType) +{ + /* No write should be active */ + if (TdsSendMessageType != 0) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("TDS message write %u already in progress", + TdsSendMessageType))); + + TdsSetMessageType(messageType); + if (TdsPutbytes(message->data, message->len)) + return EOF; + if (TdsSocketFlush()) + return EOF; + return 0; +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdslogin.c b/contrib/babelfishpg_tds/src/backend/tds/tdslogin.c new file mode 100644 index 00000000000..3959f623c69 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdslogin.c @@ -0,0 +1,2330 @@ +/*------------------------------------------------------------------------- + * + * tdslogin.c + * TDS Listener connection handshake + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdslogin.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include "access/printtup.h" +#include "catalog/pg_type.h" /* For type translation */ +#include "commands/dbcommands.h" +#include "common/ip.h" +#include "common/md5.h" +#include "common/scram-common.h" +#include "common/string.h" +#include "commands/extension.h" +#include "commands/user.h" +#include "libpq/auth.h" +#include "libpq/crypt.h" +#include "libpq/libpq.h" +#include "libpq/pqformat.h" +#include "libpq/scram.h" +#include "miscadmin.h" +#include "replication/walsender.h" +#include "storage/ipc.h" +#include "utils/timestamp.h" + +#include "access/printtup.h" +#include "libpq/libpq.h" +#include "libpq/pqformat.h" +#include "tcop/pquery.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/memdebug.h" +#include "utils/memutils.h" +#include "utils/ps_status.h" +#include "utils/snapmgr.h" +#include "utils/timestamp.h" + +#include "src/include/tds_debug.h" +#include "src/include/tds_int.h" +#include "src/include/tds_request.h" +#include "src/include/tds_response.h" +#include "src/include/guc.h" + +#include "src/include/tds_secure.h" +#include "src/include/tds_instr.h" + +#include +#ifdef USE_OPENSSL +#include +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +#ifdef ENABLE_SSPI +#define SECURITY_WIN32 +#if defined(WIN32) && !defined(_MSC_VER) +#include +#endif +#include +#undef SECURITY_WIN32 + +#ifndef ENABLE_GSS +/* + * Define a fake structure compatible with GSSAPI on Unix. + */ +typedef struct +{ + void *value; + int length; +} gss_buffer_desc; +#endif +#endif /* ENABLE_SSPI */ + +/*---------------------------------------------------------------- + * GSSAPI Authentication + *---------------------------------------------------------------- + */ +#ifdef ENABLE_GSS +#if defined(HAVE_GSSAPI_H) +#include +#else +#include +#endif /* HAVE_GSSAPI_H */ +/* + * GSSAPI brings in headers that set a lot of things in the global namespace on win32, + * that doesn't match the msvc build. It gives a bunch of compiler warnings that we ignore, + * but also defines a symbol that simply does not exist. Undefine it again. + */ +#ifdef _MSC_VER +#undef HAVE_GETADDRINFO +#endif + +static int SecureOpenServer(Port *port); +static void SendGSSAuthError(int severity, const char *errmsg, OM_uint32 maj_stat, + OM_uint32 min_stat); +static void SendGSSAuthResponse(Port *port, char *extradata, uint16_t extralen); +static int CheckGSSAuth(Port *port); +#endif /* ENABLE_GSS */ + +/* Global to store default collation info */ +int TdsDefaultLcid; +int TdsDefaultCollationFlags; +uint8_t TdsDefaultSortid; + +static void TdsDefineDefaultCollationInfo(void); + +typedef struct LoginRequestData +{ + /* Fixed length attributes */ + uint32_t length; + uint32_t tdsVersion; + uint32_t packetSize; + uint32_t clientProVersion; + uint32_t clientPid; + uint32_t connectionId; + uint8_t optionFlags1; /* see above */ + uint8_t optionFlags2; /* see above */ + uint8_t typeFlags; /* see above */ + uint8_t optionFlags3; /* Reserved flags, see above */ + uint32_t clientTimezone; + uint32_t clientLcid; /* Language code identifier */ + + /* + * The variable length attributes are stored in the following order in the + * login packet. If a new entry has to be added in future, make sure to + * keep TDS_LOGIN_ATTR_MAX as the last index. For all fields, we always + * store null terminated strings. Hence, we don't store the lengths. + */ +#define TDS_LOGIN_ATTR_HOSTNAME 0 + char *hostname; +#define TDS_LOGIN_ATTR_USERNAME 1 + char *username; +#define TDS_LOGIN_ATTR_PASSWORD 2 + char *password; +#define TDS_LOGIN_ATTR_APPNAME 3 + char *appname; +#define TDS_LOGIN_ATTR_SERVERNAME 4 + char *servername; +#define TDS_LOGIN_ATTR_UNUSED 5 +#define TDS_LOGIN_ATTR_LIBRARY 6 + char *library; +#define TDS_LOGIN_ATTR_LANGUAGE 7 + char *language; +#define TDS_LOGIN_ATTR_DATABASE 8 + char *database; +#define TDS_LOGIN_ATTR_MAX 9 /* should be last */ + + /* the 6-byte client mac address */ + char clientId[6]; + + uint16_t sspiLen; + char *sspi; + + /* the Active Directory (AD) domain name */ + char *domainname; + + /* TODO: Feature data */ + +} LoginRequestData; + +typedef LoginRequestData *LoginRequest; + +#define SizeOfLoginRequestFixed (offsetof(LoginRequestData, clientLcid) + sizeof(uint32_t)) + +typedef struct PreLoginOption +{ + int8_t token; + uint16_t offset; + uint16_t length; + StringInfoData val; + struct PreLoginOption *next; +} PreLoginOption; + +PreLoginOption *TdsPreLoginRequest; +LoginRequest loginInfo = NULL; + +static const char *PreLoginTokenType(uint8_t token); +static void DebugPrintPreLoginStructure(PreLoginOption *request); +static int ParsePreLoginRequest(); +static void SetPreLoginResponseVal(Port *port, uint8_t token, + StringInfo val, StringInfo reqVal, + bool loadSsl, int *loadEncryption); +static int MakePreLoginResponse(Port *, bool); + +static void ValidateLoginRequest(LoginRequest request); +static int FetchLoginRequest(LoginRequest request); +static int ProcessLoginInternal(Port *port); +static int CheckAuthPassword(Port *port, char **logdetail); +static void SendLoginError(Port *port, char *logdetail); +static void GetLoginFlagsInstrumentation(LoginRequest loginInfo); +static void GetTDSVersionInstrumentation(uint32_t version); + +/* Macros for OptionFlags1. */ +#define LOGIN_OPTION_FLAGS1_BYTE_ORDER_68000 0x01 +#define LOGIN_OPTION_FLAGS1_CHAR_EBCDIC 0x02 +#define LOGIN_OPTION_FLAGS1_FLOAT_VAX 0x04 +#define LOGIN_OPTION_FLAGS1_FLOAT_ND5000 0x08 +#define LOGIN_OPTION_FLAGS1_DUMP_LOAD_OFF 0x10 +#define LOGIN_OPTION_FLAGS1_USE_DB_ON 0x20 +#define LOGIN_OPTION_FLAGS1_DATABASE_FATAL 0x40 +#define LOGIN_OPTION_FLAGS1_SET_LANG_ON 0x80 + +/* Macros for OptionFlags2. */ +#define LOGIN_OPTION_FLAGS2_LANGUAGE_FATAL 0x01 +#define LOGIN_OPTION_FLAGS2_ODBC 0x02 +#define LOGIN_OPTION_FLAGS2_TRAN_BOUNDARY 0x04 +#define LOGIN_OPTION_FLAGS2_CACHE_CONNECT 0x08 +#define LOGIN_OPTION_FLAGS2_USER_TYPE_SERVER 0x10 +#define LOGIN_OPTION_FLAGS2_USER_TYPE_REMUSER 0x20 +#define LOGIN_OPTION_FLAGS2_USER_TYPE_SQLREPL 0x30 +#define LOGIN_OPTION_FLAGS2_INT_SECURITY_ON 0x80 + +/* Macros for TypeFlags */ +#define LOGIN_TYPE_FLAGS_SQL_TSQL 0x01 +#define LOGIN_TYPE_FLAGS_OLEDB 0x10 +#define LOGIN_TYPE_FLAGS_READ_ONLY_INTENT 0x20 + +/* Macros for OptionFlags3. */ +#define LOGIN_OPTION_FLAGS3_CHANGE_PASSWORD 0x01 +#define LOGIN_OPTION_FLAGS3_USER_INSTANCE 0x02 +#define LOGIN_OPTION_FLAGS3_SEND_YUKON_BINARY_XML 0x04 +#define LOGIN_OPTION_FLAGS3_UNKNOWN_COLLATION_HANDLING 0x08 +#define LOGIN_OPTION_FLAGS3_EXTENSION 0x10 + +#define TEXT_SIZE_2GB 0x7FFFFFFF +#define TEXT_SIZE_INFINITE 0xFFFFFFFF + +static const char * +PreLoginTokenType(uint8_t token) +{ + const char *id = NULL; + + switch(token) + { + case TDS_PRELOGIN_VERSION: + id = "TDS_PRELOGIN_VERSION (0x00)"; + break; + case TDS_PRELOGIN_ENCRYPTION: + id = "TDS_PRELOGIN_ENCRYPTION (0x01)"; + break; + case TDS_PRELOGIN_INSTOPT: + id = "TDS_PRELOGIN_INSTOPT (0x02)"; + break; + case TDS_PRELOGIN_THREADID: + id = "TDS_PRELOGIN_THREADID (0x03)"; + break; + case TDS_PRELOGIN_MARS: + id = "TDS_PRELOGIN_MARS (0x04)"; + break; + case TDS_PRELOGIN_TRACEID: + id = "TDS_PRELOGIN_TRACEID (0x05)"; + break; + case TDS_PRELOGIN_FEDAUTHREQUIRED: + id = "TDS_PRELOGIN_FEDAUTHREQUIRED (0x06)"; + break; + case TDS_PRELOGIN_NONCEOPT: + id = "TDS_PRELOGIN_NONCEOPT (0x07)"; + break; + case TDS_PRELOGIN_TERMINATOR: + id = "TDS_PRELOGIN_TERMINATOR (0xFF)"; + break; + default: + id = "unknown"; + } + + return id; +} + +static void +DebugPrintPreLoginStructure(PreLoginOption *request) +{ + PreLoginOption *prev; + StringInfoData s; + int i = 0; + + initStringInfo(&s); + appendStringInfo(&s, "\nOption token: %s \n\t Option offset: %d \n\t Option Length: %d \n\t Version: %02x.%02x.%04x Subbuild: %04x ", + PreLoginTokenType(request->token), request->offset, request->length, + request->val.data[0], request->val.data[1], request->val.data[2], request->val.data[4]); + prev = request->next; + while(prev != NULL) + { + appendStringInfo(&s, "\nOption token: %s \n\t Option offset: %d \n\t Option Length: %d \n\t Data : ", + PreLoginTokenType(prev->token), prev->offset, prev->length); + + for(i = 0; i < prev->length; i++) + { + appendStringInfo(&s, "%02x", (unsigned char) prev->val.data[i]); + } + + prev = prev->next; + } + + if (!TDS_DEBUG_ENABLED(TDS_DEBUG3)) + return; + if (s.len > 0) + elog(LOG, "MESSAGE: \n %s", s.data); + else + elog(LOG, "MESSAGE: "); +} + + +static int +ParsePreLoginRequest() +{ + uint16_t data16; + PreLoginOption *temp; + PreLoginOption *prev = NULL; + + TdsErrorContext->reqType = TDS_PRELOGIN; + while (1) + { + temp = palloc0(sizeof(PreLoginOption)); + if (TdsGetbytes((char *)(&temp->token), sizeof(temp->token))) + return STATUS_ERROR; + + // Terminator token + if (temp->token == -1) + { + temp->offset = 0; + temp->length = 0; + temp->next = NULL; + initStringInfo(&temp->val); + prev->next = temp; + prev = prev->next; + break; + } + if (TdsGetbytes((char *)&data16, sizeof(data16))) + return STATUS_ERROR; + temp->offset = pg_ntoh16(data16); + if (TdsGetbytes((char *)&data16, sizeof(data16))) + return STATUS_ERROR; + temp->length = pg_ntoh16(data16); + initStringInfo(&temp->val); + + temp->next = NULL; + if (prev == NULL) + { + prev = temp; + TdsPreLoginRequest = temp; + } + else + { + prev->next = temp; + prev = prev->next; + } + } + prev = TdsPreLoginRequest; + while (prev->next != NULL) + { + if (TdsGetbytes(prev->val.data, prev->length)) + return STATUS_ERROR; + prev = prev->next; + } + if (!TdsCheckMessageType(TDS_PRELOGIN)) + return STATUS_ERROR; + + DebugPrintPreLoginStructure(TdsPreLoginRequest); + + TDS_DEBUG(TDS_DEBUG1, "message_type: TDS7 Prelogin Message"); + + return 0; +} + +static void +SetPreLoginResponseVal(Port *port, uint8_t token, StringInfo val, + StringInfo reqVal, bool loadSsl, int *loadEncryption) +{ + switch(token) + { + case TDS_PRELOGIN_VERSION: + /* Major Version 0x0C */ + appendStringInfoChar(val, 0x0C); + + /* Minor Version 0x00 */ + appendStringInfoChar(val, 0x00); + + /* Micro Version 0x07d0 */ + appendStringInfoChar(val, 0x07); + appendStringInfoChar(val, 0xd0); + + /* Subbuild Version 0x0000 */ + appendStringInfoChar(val, 0x00); + appendStringInfoChar(val, 0x00); + break; + case TDS_PRELOGIN_ENCRYPTION: + /* + * Support full encryption if server supports & + * client has requested ENCRYPT_ON or ENCRYPT_REQ, + * or Login7 request encryption if req = TDS_ENCRYPT_OFF + * or else TDS_ENCRYPT_OFF + * No SSL support - when disabled or on Unix sockets + */ + if (loadSsl && !IS_AF_UNIX(port->laddr.addr.ss_family)) + { + if ((reqVal->data[0] == TDS_ENCRYPT_ON) || + (reqVal->data[0] == TDS_ENCRYPT_REQ)) + { + appendStringInfoChar(val, TDS_ENCRYPT_ON); + *loadEncryption = TDS_ENCRYPT_ON; + } + else if (reqVal->data[0] == TDS_ENCRYPT_OFF) + { + if (tds_ssl_encrypt) + { + appendStringInfoChar(val, TDS_ENCRYPT_REQ); + *loadEncryption = TDS_ENCRYPT_REQ; + } + else + { + appendStringInfoChar(val, TDS_ENCRYPT_OFF); + *loadEncryption = TDS_ENCRYPT_OFF; + } + } + else if (reqVal->data[0] == TDS_ENCRYPT_NOT_SUP) + { + if (tds_ssl_encrypt) + { + appendStringInfoChar(val, TDS_ENCRYPT_REQ); + *loadEncryption = TDS_ENCRYPT_REQ; + } + else + { + appendStringInfoChar(val, TDS_ENCRYPT_NOT_SUP); + *loadEncryption = TDS_ENCRYPT_NOT_SUP; + } + } + else + elog(FATAL, "Certification 0x%02x not supported", (unsigned char) reqVal->data[0]); + } + else + { + appendStringInfoChar(val, TDS_ENCRYPT_NOT_SUP); + *loadEncryption = TDS_ENCRYPT_NOT_SUP; + } + break; + case TDS_PRELOGIN_INSTOPT: + /* + * Val 00 - To indicate client's val matches server expectation + * Val 01 - Otherwise 01 to indicate client should terminate + * TODO:- Instead of fixed value, add the logic + */ + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_PRELOGIN_INSTOPT); + appendStringInfoChar(val, 0x00); + break; + case TDS_PRELOGIN_THREADID: + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_PRELOGIN_THREADID); + break; + case TDS_PRELOGIN_MARS: + appendStringInfoChar(val, 0x00); + break; + case TDS_PRELOGIN_TRACEID: + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_PRELOGIN_TRACEID); + break; + case TDS_PRELOGIN_FEDAUTHREQUIRED: + /* + * Should only be set when SSPI or FedAuth is supported + * Val 00 - SSPI supported + * Val 01 - FedAuth Supported + */ + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_PRELOGIN_FEDAUTHREQUIRED); + break; + case TDS_PRELOGIN_NONCEOPT: + /* Only used with FedAuth - Noop in our case */ + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_PRELOGIN_NONCEOPT); + break; + case TDS_PRELOGIN_TERMINATOR: + break; + + } +} + +/* + * MakePreLoginResponse - Sends the PreLogin response to the client, also decides + * whether to load the encryption for the session + * + * Return Value: + * Encryption option which can be + * TDS_ENCRYPT_ON - Complete End to End Encryption + * TDS_ENCRYPT_OFF - Login7 Encryption + * TDS_ENCRYPT_NOT_SUP - No Encryption + */ +static int +MakePreLoginResponse(Port *port, bool loadSsl) +{ + uint16_t temp16; + PreLoginOption *preLoginResponse; + PreLoginOption *tempRequest, *temp, *prev = NULL; + int offset = 0; + int loadEncryption = 0; + + preLoginResponse = palloc0(sizeof(PreLoginOption)); + + /* Prepare the structure */ + tempRequest = TdsPreLoginRequest; + + while(tempRequest != NULL) + { + if (tempRequest->token != TDS_PRELOGIN_FEDAUTHREQUIRED) + { + temp = palloc0(sizeof(PreLoginOption)); + temp->token = tempRequest->token; + initStringInfo(&temp->val); + SetPreLoginResponseVal(port, temp->token, &temp->val, &tempRequest->val, + loadSsl, &loadEncryption); + temp->length = temp->val.len; + /* 1 - type, 2 - offsetlen, 2 - len */ + offset += 5; + temp->next = NULL; + if (prev == NULL) + { + preLoginResponse = temp; + prev = temp; + } + else + { + prev->next = temp; + prev = prev->next; + } + } + tempRequest = tempRequest->next; + } + /* Terminator token doesn't have offset & len */ + offset -= 4; + + /* Add all the offset val */ + prev = preLoginResponse; + while(prev != NULL) + { + prev->offset = offset; + offset += prev->length; + prev = prev->next; + } + /* Structure prepared, now print it */ + DebugPrintPreLoginStructure(preLoginResponse); + + /* Prepare the response message */ + TdsSetMessageType(TDS_RESPONSE); + prev = preLoginResponse; + while (prev->next != NULL) + { + TdsPutbytes(&(prev->token), sizeof(prev->token)); + temp16 = pg_hton16(prev->offset); + TdsPutbytes(&temp16, sizeof(temp16)); + temp16 = pg_hton16(prev->length); + TdsPutbytes(&temp16, sizeof(temp16)); + prev = prev->next; + } + // Terminator token + TdsPutbytes(&(prev->token), sizeof(prev->token)); + + prev = preLoginResponse; + while (prev != NULL) + { + TdsPutbytes(prev->val.data, prev->val.len); + prev = prev->next; + } + + // Free the PreLogin Structures + prev = TdsPreLoginRequest; + while (prev != NULL) + { + pfree(prev->val.data); + temp = prev->next; + pfree(prev); + prev = temp; + } + prev = preLoginResponse; + while (prev != NULL) + { + pfree(prev->val.data); + temp = prev->next; + pfree(prev); + prev = temp; + } + return loadEncryption; +} + +/* + * ValidateLoginRequest - Validate the login request according to the TDS + * specifications. + */ +static void +ValidateLoginRequest(LoginRequest request) +{ + /* TODO: do the sanity checks */ + + uint32_t version; + + /* Use the GUC's values, if set. */ + if (tds_default_protocol_version > 0) + request->tdsVersion = tds_default_protocol_version; + version = request->tdsVersion; + + /* TDS Version must be valid */ + if (!( version == TDS_VERSION_7_0 || + version == TDS_VERSION_7_1 || + version == TDS_VERSION_7_1_1 || + version == TDS_VERSION_7_2 || + version == TDS_VERSION_7_3_A || + version == TDS_VERSION_7_3_B || + version == TDS_VERSION_7_4)) + elog(FATAL, "invalid TDS Version: %X", version); + + GetTDSVersionInstrumentation(version); + + /* TDS Version 7.0 is unsupported */ + if(version == TDS_VERSION_7_0) + elog(FATAL, "unsupported TDS Version: %X", version); + + /* + * The packet size must be greater than or equal to 512 bytes and smaller + * than or equal to 32,767 bytes. Or, the packet size can be 0 in which + * case we should use the server default. + */ + if (request->packetSize != TDS_USE_SERVER_DEFAULT_PACKET_SIZE && + (request->packetSize < 512 || request->packetSize > 32767)) + elog(FATAL, "Invalid packet size: %u, Packet size has to be zero or " + "a number between 512 and 32767.", request->packetSize); +} + +/* + * FetchLoginRequest - Fetch and parse TDS login packet + * + * RETURNS: STATUS_OK or STATUS_ERROR + */ +static int +FetchLoginRequest(LoginRequest request) +{ + uint32_t attrs[TDS_LOGIN_ATTR_MAX]; + uint32_t sspiOffsetLen; + StringInfoData buf; + StringInfoData temp_utf8; + int i, read = 0; + + Assert(request != NULL); + + TdsErrorContext->reqType = TDS_LOGIN7; +#ifdef WORDS_BIGENDIAN + /* + * Are we going to support this? + */ + Assert(0); +#endif + + /* + * The client writes all other bytes except clientProVersion in + * little-endian. Hence, we can read everything at once. No endian + * conversion is needed. + */ + if (TdsGetbytes((char *) request, SizeOfLoginRequestFixed)) + return STATUS_ERROR; + + /* The length of a LOGIN7 stream MUST NOT be longer than 128K-1(byte) bytes */ + if (request->length > 128 * 1024) + return STATUS_ERROR; + + read += SizeOfLoginRequestFixed; + + /* At any point, read CANNOT be greater than length of login stream */ + if (read > request->length) + return STATUS_ERROR; + + /* Check we indeed got the correct packet */ + Assert(TdsCheckMessageType(TDS_LOGIN7)); + + /* fix the client version now */ + request->clientProVersion = pg_bswap32(request->clientProVersion); + + /* Let's read the {offset, length} array now. */ + if (TdsGetbytes((char *) attrs, TDS_LOGIN_ATTR_MAX * sizeof(uint32_t))) + return STATUS_ERROR; + + read += TDS_LOGIN_ATTR_MAX * sizeof(uint32_t); + + if (read > request->length) + return STATUS_ERROR; + + /* 6-bytes Client MAC Address */ + if (TdsGetbytes((char *) request->clientId, sizeof(request->clientId))) + return STATUS_ERROR; + + read += sizeof(request->clientId); + + if (read > request->length) + return STATUS_ERROR; + + /* SSPI data */ + if (TdsGetbytes((char *) &sspiOffsetLen, sizeof(sspiOffsetLen))) + return STATUS_ERROR; + + read += sizeof(sspiOffsetLen); + + if (read > request->length) + return STATUS_ERROR; + + /* + * It follows the following data that we're going to discard for now: + * 1. Database to attach during connection process + * 2. New password for the specified login. Introduced in TDS 7.2 + * 3. Used for large SSPI data when cbSSPI==USHORT_MAX. Introduced in TDS 7.2 + */ + + initStringInfo(&buf); + initStringInfo(&temp_utf8); + + /* Now, read from the offsets */ + for (i = 0; i < TDS_LOGIN_ATTR_MAX; i++) + { + uint16_t offset = (uint16_t) attrs[i]; + uint16_t length = (uint16_t) (attrs[i] >> 16); + + if (length > 0) + { + /* Skip bytes till the offset */ + if (TdsDiscardbytes(offset - read)) + { + pfree(temp_utf8.data); + pfree(buf.data); + return STATUS_ERROR; + } + + read = offset; + + /* + * The hostname, username, password, appname, servername, + * library name, language and database name MUST specify + * at most 128 characters + */ + if(length > 128) + return STATUS_ERROR; + + if (i == TDS_LOGIN_ATTR_UNUSED) + { + if (TdsDiscardbytes(length)) + { + pfree(temp_utf8.data); + pfree(buf.data); + return STATUS_ERROR; + } + + read += length; + + if (read > request->length) + return STATUS_ERROR; + + continue; + } + + /* Since, it has UTF-16 format */ + length *= 2; + + resetStringInfo(&buf); + enlargeStringInfo(&buf, length); + + if (TdsGetbytes(buf.data, length)) + { + pfree(temp_utf8.data); + pfree(buf.data); + return STATUS_ERROR; + } + + read += length; + + if (read > request->length) + return STATUS_ERROR; + + buf.len += length; + + /* + * The password field is an obfusticated unicode string. So, we've + * to handle it differently. + */ + if (i == TDS_LOGIN_ATTR_PASSWORD) + { + int j; + for (j = 0; j < length; j++) + { + uint8_t p = buf.data[j]; + + p = (((p & 0xff) ^ 0xA5) << 4) | (((p & 0xff) ^ 0xA5) >> 4); + buf.data[j] = p & 0xff; + } + + } + + TdsUTF16toUTF8StringInfo(&temp_utf8, buf.data, length); + + switch(i) + { + case TDS_LOGIN_ATTR_HOSTNAME: + request->hostname = pstrdup(temp_utf8.data); + break; + case TDS_LOGIN_ATTR_USERNAME: + request->username = pstrdup(temp_utf8.data); + break; + case TDS_LOGIN_ATTR_PASSWORD: + request->password = pstrdup(temp_utf8.data); + break; + case TDS_LOGIN_ATTR_APPNAME: + request->appname = pstrdup(temp_utf8.data); + break; + case TDS_LOGIN_ATTR_SERVERNAME: + request->servername = pstrdup(temp_utf8.data); + break; + case TDS_LOGIN_ATTR_LIBRARY: + request->library = pstrdup(temp_utf8.data); + break; + case TDS_LOGIN_ATTR_LANGUAGE: + request->language = pstrdup(temp_utf8.data); + break; + case TDS_LOGIN_ATTR_DATABASE: + request->database = pstrdup(temp_utf8.data); + break; + default: + /* shouldn't reach here */ + Assert(0); + break; + } + resetStringInfo(&temp_utf8); + } + } + + pfree(temp_utf8.data); + pfree(buf.data); + + if (sspiOffsetLen > 0) + { + uint16_t offset = (uint16_t) sspiOffsetLen; + request->sspiLen = (uint16_t) (sspiOffsetLen >> 16); + + if (request->sspiLen > 0) + { + /* XXX: large SSPI data when length==USHORT_MAX - not supported yet */ + if (request->sspiLen == -1) + { + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_CB_SSPI_LONG); + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("large SSPI is not supported yet"))); + } + + + /* Skip bytes till the offset */ + if (TdsDiscardbytes(offset - read)) + { + pfree(temp_utf8.data); + pfree(buf.data); + return STATUS_ERROR; + } + + read = offset; + + request->sspi = palloc(request->sspiLen); + + if (TdsGetbytes(request->sspi, request->sspiLen)) + { + pfree(temp_utf8.data); + pfree(buf.data); + return STATUS_ERROR; + } + + read += request->sspiLen; + } + } + + /* Now, discard rest of the bytes, if any */ + if (TdsDiscardbytes((size_t) (request->length - read))) + return STATUS_ERROR; + + DebugPrintLoginMessage(request); + + TDS_DEBUG(TDS_DEBUG1, "message_type: TDS7 Login"); + + return STATUS_OK; +} + +/* + * ProcessLoginFlags -- Processes the information stored in the following Flags: + * + * 1. information stored in optionFlags1 (in least significant bit order): + * fByteOrder: 0 = ORDER_X86, 1 = ORDER_68000 + * The byte order used by client for numeric and datetime data types. + * fChar: 0 = CHARSET_ASCII, 1 = CHARSET_EBCDIC + * The character set used on the client. + * fFloat: 0 = FLOAT_IEEE_754, 1 = FLOAT_VAX, 2 = ND5000 + * The type of floating point representation used by the client. + * fDumpLoad: 0 = DUMPLOAD_ON, 1 = DUMPLOAD_OFF + * Set if dump/load or BCP capabilities are needed by the client. + * fUseDB: 0 = USE_DB_OFF, 1 = USE_DB_ON + * Set if the client requires warning messages on execution of the USE + * SQL statement. If this flag is not set, the server MUST NOT inform + * the client when the database changes, and therefore the client will + * be unaware of any accompanying collation changes. + * fDatabase: 0 = INIT_DB_WARN, 1 = INIT_DB_FATAL + * Set if the change to initial database needs to succeed if the + * connection is to succeed. + * fSetLang: 0 = SET_LANG_OFF, 1 = SET_LANG_ON + * Set if the client requires warning messages on execution of a language + * change statement. + * + * 2. information stored in optionFlags2 (in least significant bit order): + * fLanguage: 0 = INIT_LANG_WARN, 1 = INIT_LANG_FATAL + * Set if the change to initial language needs to succeed if the + * connect is to succeed. + * fODBC: 0 = ODBC_OFF, 1 = ODBC_ON + * Set if the client is the ODBC driver. This causes the server to + * set ANSI_DEFAULTS to ON, CURSOR_CLOSE_ON_COMMIT and + * IMPLICIT_TRANSACTIONS to OFF, TEXTSIZE to 0x7FFFFFFF (2GB) + * (TDS 7.2 and earlier), TEXTSIZE to infinite (introduced in + * TDS 7.3), and ROWCOUNT to infinite. + * fTransBoundary + * fCacheConnect + * fUserType: 0 = USER_NORMAL—regular logins, + * 1 = USER_SERVER—reserved, + * 2 = USER_REMUSER—Distributed Query login, + * 3 = USER_SQLREPL—replication login + * The type of user connecting to the server. + * fIntSecurity: 0 = INTEGRATED_SECURTY_OFF, 1 = INTEGRATED_SECURITY_ON + * The type of security required by the client. + * + * 3. information stored in typeFlags (in least significant bit order): + * fSQLType: 0 = SQL_DFLT, 1 = SQL_TSQL + * The type of SQL the client sends to the server. + * fOLEDB: 0 = OLEDB_OFF, 1 = OLEDB_ON + * Set if the client is the OLEDB driver. This causes the server + * to set ANSI_DEFAULTS to ON, CURSOR_CLOSE_ON_COMMIT and + * IMPLICIT_TRANSACTIONS to OFF, TEXTSIZE to 0x7FFFFFFF (2GB) + * (TDS 7.2 and earlier), TEXTSIZE to infinite (introduced in + * TDS 7.3), and ROWCOUNT to infinite.<21> + * fReadOnlyIntent: This bit was introduced in TDS 7.4; however, TDS 7.1, 7.2, + * and 7.3 clients can also use this bit in LOGIN7 to specify + * that the application intent of the connection is read-only. The + * server SHOULD ignore this bit if the highest TDS version + * supported by the server is lower than TDS 7.4. + * + * 4. information stored in optionFlags3 (in least significant bit order): + * fChangePassword: 0 = No change request. ibChangePassword MUST be 0. + * 1 = Request to change login's password. + * Specifies whether the login request SHOULD change password. + * fSendYukonBinaryXML: 1 if XML data type instances are returned as binary XML. + * fUserInstance: 1 if client is requesting separate process to be spawned + * as user instance. + * fUnknownCollationHandling: + * 0 = The server MUST restrict the collations sent + * to a specific set of collations. It MAY disconnect or + * send an error if some other value is outside the specific + * collation set. The client MUST properly support all + * collations within the collation set. + * 1 = The server MAY send any collation that fits in the + * storage space. The client MUST be able to both properly + * support collations and gracefully fail for those it does + * not support. This bit is used by the server to determine + * if a client is able to properly handle collations introduced + * after TDS 7.2. TDS 7.2 and earlier clients are encouraged + * to use this login packet bit. Servers MUST ignore this + * bit when it is sent by TDS 7.3 or 7.4 clients. See + * [MSDN-SQLCollation] and [MS-LCID] for the complete list + * of collations for a database server that supports SQL + * and LCIDs. + * fExtension: 0 = ibExtension/cbExtension fields are not used. The + * fields are treated the same as ibUnused/cchUnused. + * 1 = ibExtension/cbExtension fields are used. + * Specifies whether ibExtension/cbExtension fields are used. + */ +static void ProcessLoginFlags(LoginRequest loginInfo) +{ + GetLoginFlagsInstrumentation(loginInfo); + + /* fODBC and fOLEDB */ + if ((loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_ODBC) || + (loginInfo->typeFlags & LOGIN_TYPE_FLAGS_OLEDB)) + { + char *textSize = psprintf("%d" , (loginInfo->tdsVersion <= TDS_VERSION_7_2) ? + TEXT_SIZE_2GB : TEXT_SIZE_INFINITE); + char *rowCount = psprintf("%d" ,INT_MAX); + + set_config_option("babelfishpg_tsql.ansi_defaults", + "ON", + PGC_USERSET, + PGC_S_CLIENT, + GUC_ACTION_SET, + true, + 0, + false); + + set_config_option("babelfishpg_tsql.implicit_transactions", + "OFF", + PGC_USERSET, + PGC_S_CLIENT, + GUC_ACTION_SET, + true, + 0, + false); + set_config_option("babelfishpg_tsql.cursor_close_on_commit", + "OFF", + PGC_USERSET, + PGC_S_CLIENT, + GUC_ACTION_SET, + true, + 0, + false); + set_config_option("babelfishpg_tsql.textsize", + textSize, + PGC_USERSET, + PGC_S_CLIENT, + GUC_ACTION_SET, + true, + 0, + false); + set_config_option("babelfishpg_tsql.rowcount", + rowCount, + PGC_USERSET, + PGC_S_CLIENT, + GUC_ACTION_SET, + true, + 0, + false); + } + + if (loginInfo->optionFlags3 & LOGIN_OPTION_FLAGS3_CHANGE_PASSWORD) + { + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_CHANGE_PASSWORD); + ereport(FATAL, + errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("Password change request is not supported")); + } +} + +/* + * ProcessLoginInternal - internal workhorse for processing login + * request. + * + * Read a TDS client's login packet and do something according to it. + * + * Returns STATUS_OK or STATUS_ERROR, or might call ereport(FATAL) and + * not return at all. + */ +static int +ProcessLoginInternal(Port *port) +{ + MemoryContext oldContext; + LoginRequest request; + const char* gucDatabaseName = NULL; + + /* + * Only use "babelfishpg_tsql.database_name" GUC when we have + * enabled "babelfishpg_tds.set_db_session_property" GUC + */ + if (tds_enable_db_session_property) + { + gucDatabaseName = GetConfigOption("babelfishpg_tsql.database_name", true, false); + if (gucDatabaseName == NULL) + ereport(FATAL, (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Configuration parameter \"babelfishpg_tsql.database_name\" is not defined"), + errhint("Set GUC value by specifying it in postgresql.conf or by ALTER SYSTEM"))); + } + + /* + * We want to keep all login related information around even after + * postmaster context gets deleted and after a connection reset. + */ + oldContext = MemoryContextSwitchTo(TopMemoryContext); + + /* We're allocating the memory in postmaster context. */ + request = palloc0(sizeof(LoginRequestData)); + + TdsErrorContext->err_text = "Fetch Login Request"; + /* fetch and parse the login packet */ + if (FetchLoginRequest(request) != STATUS_OK) + return STATUS_ERROR; + + TdsErrorContext->err_text = "Validate Login Request"; + /* validate the login request */ + ValidateLoginRequest(request); + + /* + * Copy the username and database name in port structure so that no one + * messes up with the local copy. + */ + if (request->username != NULL) + port->user_name = pstrdup(request->username); + if (request->database != NULL) + port->database_name = pstrdup(request->database); + + /* + * We set application name in port structure in case we want to log + * connections in future. + */ + if (request->appname != NULL) + { + char *tmpAppName = pstrdup(request->appname); + + pg_clean_ascii(tmpAppName); + + port->application_name = tmpAppName; + } + + /* + * If GUC "babelfishpg_tsql.database_name" is "none", database + * name is set as whatever is specified in login request. Else, + * database name specified in login request is overridden by + * "babelfish_pgtsql.database_name" + */ + if (gucDatabaseName == NULL || strcmp(gucDatabaseName, "none") == 0) + { + if (request->database != NULL) + port->database_name = pstrdup(request->database); + } + else + port->database_name = pstrdup(gucDatabaseName); + + if (request->sspiLen > 0) + { + char tempusername[10] = ""; + port->user_name = pstrdup(tempusername); + } + + /* Check a user name was given. */ + if (port->user_name == NULL || port->user_name[0] == '\0') + ereport(FATAL, + errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("no PostgreSQL user name specified in startup packet")); + + /* The database defaults to the user name. */ + if (port->database_name == NULL || port->database_name[0] == '\0') + port->database_name = pstrdup(TSQL_DEFAULT_DB); + + /* save the login information for the entire session */ + loginInfo = request; + + /* + * Truncate given database and user names to length of a Postgres name. + * This avoids lookup failures when overlength names are given. + */ + if (strlen(port->database_name) >= NAMEDATALEN) + port->database_name[NAMEDATALEN - 1] = '\0'; + if (strlen(port->user_name) >= NAMEDATALEN) + port->user_name[NAMEDATALEN - 1] = '\0'; + + /* + * Done putting stuff in TopMemoryContext. + */ + MemoryContextSwitchTo(oldContext); + + /* + * If we're going to reject the connection due to database state, say so + * now instead of wasting cycles on an authentication exchange. (This also + * allows a pg_ping utility to be written.) + */ + switch (port->canAcceptConnections) + { + case CAC_STARTUP: + ereport(FATAL, + errcode(ERRCODE_CANNOT_CONNECT_NOW), + errmsg("the database system is starting up")); + break; + case CAC_SHUTDOWN: + ereport(FATAL, + errcode(ERRCODE_CANNOT_CONNECT_NOW), + errmsg("the database system is shutting down")); + break; + case CAC_RECOVERY: + ereport(FATAL, + errcode(ERRCODE_CANNOT_CONNECT_NOW), + errmsg("the database system is in recovery mode")); + break; + case CAC_TOOMANY: + ereport(FATAL, + errcode(ERRCODE_TOO_MANY_CONNECTIONS), + errmsg("sorry, too many clients already")); + break; + case CAC_SUPERUSER: + /* OK for now, will check in InitPostgres */ + break; + case CAC_OK: + break; + } + + TdsErrorContext->err_text = "Process Login Flags"; + ProcessLoginFlags(loginInfo); + return STATUS_OK; +} + +/* + * Plaintext password authentication. + */ +static int +CheckAuthPassword(Port *port, char **logdetail) +{ + char *passwd; + int result; + char *shadowPass; + + passwd = loginInfo->password; + + if (passwd == NULL) + return STATUS_EOF; /* client wouldn't send password */ + + shadowPass = get_role_password(port->user_name, logdetail); + if (shadowPass) + { + result = plain_crypt_verify(port->user_name, shadowPass, passwd, + logdetail); + } + else + result = STATUS_ERROR; + + if (shadowPass) + pfree(shadowPass); + pfree(passwd); + + /* since we've freed the password, set it to NULL */ + loginInfo->password = NULL; + + return result; +} + +/*---------------------------------------------------------------- + * GSSAPI authentication system + *---------------------------------------------------------------- + */ +#ifdef ENABLE_GSS + +#if defined(WIN32) && !defined(_MSC_VER) +/* + * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW + * that contain the OIDs required. Redefine here, values copied + * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c + */ +static const gss_OID_desc GSS_C_NT_USER_NAME_desc = +{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"}; +static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc; +#endif + + +/* + * Generate an error for GSSAPI authentication. The caller should apply + * _() to errmsg to make it translatable. + * + * This function is similar to pg_GSS_Error(). + */ +static void +SendGSSAuthError(int severity, const char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat) +{ + gss_buffer_desc gmsg; + OM_uint32 lmin_s, + msg_ctx; + char msg_major[128], + msg_minor[128]; + + /* Fetch major status message */ + msg_ctx = 0; + gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE, + GSS_C_NO_OID, &msg_ctx, &gmsg); + strlcpy(msg_major, gmsg.value, sizeof(msg_major)); + gss_release_buffer(&lmin_s, &gmsg); + + if (msg_ctx) + + /* + * More than one message available. XXX: Should we loop and read all + * messages? (same below) + */ + ereport(WARNING, + (errmsg_internal("incomplete GSS error report"))); + + /* Fetch mechanism minor status message */ + msg_ctx = 0; + gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE, + GSS_C_NO_OID, &msg_ctx, &gmsg); + strlcpy(msg_minor, gmsg.value, sizeof(msg_minor)); + gss_release_buffer(&lmin_s, &gmsg); + + if (msg_ctx) + ereport(WARNING, + (errmsg_internal("incomplete GSS minor error report"))); + + /* + * errmsg_internal, since translation of the first part must be done + * before calling this function anyway. + */ + ereport(severity, + (errmsg_internal("%s", errmsg), + errdetail_internal("%s: %s", msg_major, msg_minor))); +} + +static void +SendGSSAuthResponse(Port *port, char *extradata, uint16_t extralen) +{ + /* + * If not already in RESPONSE mode, switch the TDS protocol to RESPONSE + * mode. + */ + TdsSetMessageType(TDS_RESPONSE); + + TdsPutInt8(TDS_TOKEN_SSPI); + TdsPutInt16LE(extralen); + TdsPutbytes(extradata, extralen); + + TdsFlush(); + + TDSInstrumentation(INSTR_TDS_TOKEN_SSPI); +} + +/* + * This function is similar to pg_GSS_recvauth() but to authenticate a TDS + * client. + */ +static int +CheckGSSAuth(Port *port) +{ + LoginRequest request = loginInfo; + OM_uint32 maj_stat, + min_stat, + lmin_s, + gflags; + int ret; + gss_buffer_desc gbuf; + MemoryContext oldContext; + + if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0) + { + /* + * Set default Kerberos keytab file for the Krb5 mechanism. + * + * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv() + * not always available. + */ + if (getenv("KRB5_KTNAME") == NULL) + { + size_t kt_len = strlen(pg_krb_server_keyfile) + 14; + char *kt_path = malloc(kt_len); + + if (!kt_path || + snprintf(kt_path, kt_len, "KRB5_KTNAME=%s", + pg_krb_server_keyfile) != kt_len - 2 || + putenv(kt_path) != 0) + { + ereport(LOG, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"))); + return STATUS_ERROR; + } + } + } + + /* + * We accept any service principal that's present in our keytab. This + * increases interoperability between kerberos implementations that see + * for example case sensitivity differently, while not really opening up + * any vector of attack. + */ + port->gss->cred = GSS_C_NO_CREDENTIAL; + + /* + * Initialize sequence with an empty context + */ + port->gss->ctx = GSS_C_NO_CONTEXT; + + do + { + /* Map to GSSAPI style buffer */ + gbuf.length = request->sspiLen; + gbuf.value = request->sspi; + + elog(DEBUG4, "Processing received GSS token of length %u", + (unsigned int) gbuf.length); + + maj_stat = gss_accept_sec_context( + &min_stat, + &port->gss->ctx, + port->gss->cred, + &gbuf, + GSS_C_NO_CHANNEL_BINDINGS, + &port->gss->name, + NULL, + &port->gss->outbuf, + &gflags, + NULL, + NULL); + + /* gbuf no longer used */ + pfree(request->sspi); + request->sspiLen = 0; + + elog(DEBUG4, "gss_accept_sec_context major: %d, " + "minor: %d, outlen: %u, outflags: %x", + maj_stat, min_stat, + (unsigned int) port->gss->outbuf.length, gflags); + + if (port->gss->outbuf.length != 0) + { + /* + * Negotiation generated data to be sent to the client. + */ + elog(DEBUG4, "sending GSS response token of length %u", + (unsigned int) port->gss->outbuf.length); + + SendGSSAuthResponse(port, port->gss->outbuf.value, + port->gss->outbuf.length); + + gss_release_buffer(&lmin_s, &port->gss->outbuf); + } + + if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) + { + gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER); + SendGSSAuthError(ERROR, + _("accepting GSS security context failed"), + maj_stat, min_stat); + } + + /* + * XXX: First we need a reproducible case to implement the following + * feature. + */ + if (maj_stat == GSS_S_CONTINUE_NEEDED) + { + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_GSS_S_CONTINUE_NEEDED); + elog(FATAL, "GSS continue needed - not supported yet"); + } + + } while (maj_stat == GSS_S_CONTINUE_NEEDED); + + if (port->gss->cred != GSS_C_NO_CREDENTIAL) + { + /* + * Release service principal credentials + */ + gss_release_cred(&min_stat, &port->gss->cred); + } + + /* + * GSS_S_COMPLETE indicates that authentication is now complete. + * + * Get the name of the user that authenticated, and compare it to the pg + * username that was specified for the connection. + */ + maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL); + if (maj_stat != GSS_S_COMPLETE) + SendGSSAuthError(ERROR, + _("retrieving GSS user name failed"), + maj_stat, min_stat); + + /* + * XXX: In PG there are options to match realm names or perform ident mappings. + * We're not going to do those checks now. If required, we can implement the + * same in future. + * For now, we just get the realm(domain) name and store it in loginInfo. + * + * We also include the realm name along with username. And, we don't support + * stripping off the realm name from username. So, an username will always + * have the following format: username@realname. + */ + + oldContext = MemoryContextSwitchTo(TopMemoryContext); + pfree(port->user_name); + port->user_name = pstrdup(gbuf.value); + if (strchr(gbuf.value, '@')) + { + char *cp = strchr(gbuf.value, '@'); + cp++; + if (loginInfo) + loginInfo->domainname = pstrdup(cp); + } + MemoryContextSwitchTo(oldContext); + + ret = STATUS_OK; + gss_release_buffer(&lmin_s, &gbuf); + + return ret; +} +#endif /* ENABLE_GSS */ + +static void +SendLoginError(Port *port, char *logdetail) +{ + LoginRequest request = loginInfo; + + if (request->sspiLen > 0) + ereport(FATAL, + errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("GSSAPI authentication failed")); + else + ereport(FATAL, + errcode(ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION), + errmsg("Login failed for user \"%s\"", + request->username)); +} + +/* + * TdsClientAuthentication - Similar to ClientAuthentication, but specific + * to TDS client authentication + * + * TDS Client authentication starts here. If there is an error, this function + * does not return and the backend process is terminated. + * + * Note that this method should be called in postmaster context so that we can + * access the login request information. + */ +void +TdsClientAuthentication(Port *port) +{ + int status = STATUS_ERROR; + char *logdetail = NULL; +#ifdef ENABLE_GSS + StringInfoData ps_data; +#endif + + if (loginInfo->sspiLen > 0) + { +#ifdef ENABLE_GSS + + /* NTLMSSP Authentication Isn't Supported yet. */ + if (strcmp(loginInfo->sspi ,"NTLMSSP") == 0) + { + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_NTLMSSP); + + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("Authentication method \"NTLMSSP\" not supported"))); + } + + /* We might or might not have the gss workspace already */ + if (port->gss == NULL) + port->gss = (pg_gssinfo *) + MemoryContextAllocZero(TopMemoryContext, + sizeof(pg_gssinfo)); + port->gss->auth = true; + + status = CheckGSSAuth(port); + + if (status == STATUS_ERROR) + SendLoginError(port, logdetail); + + if (status != STATUS_ERROR) + TDSInstrumentation(INSTR_TDS_LOGIN_ACTIVE_DIRECTORY); + + initStringInfo(&ps_data); + appendStringInfo(&ps_data, "%s ", port->user_name); + appendStringInfo(&ps_data, "%s", port->remote_host); + if (port->remote_port[0] != '\0') + appendStringInfo(&ps_data, "(%s)", port->remote_port); + + init_ps_display(ps_data.data); +#else + ereport(FATAL, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("invalid authentication method \"GSSAPI\": not supported by this build"))); +#endif + } + + /* + * Get the authentication method to use for this frontend/database + * combination. Note: we do not parse the file at this point; this has + * already been done elsewhere. hba.c dropped an error message into the + * server logfile if parsing the hba config file failed. + */ + hba_getauthmethod(port); + + CHECK_FOR_INTERRUPTS(); + + /* + * Now proceed to do the actual authentication check + * + * We only support password-based authentication. So, if we cannot trust + * the user, fall back to password based authentication. + */ + switch (port->hba->auth_method) + { + case uaReject: + + /* + * An explicit "reject" entry in pg_hba.conf. This report exposes + * the fact that there's an explicit reject entry, which is + * perhaps not so desirable from a security standpoint; but the + * message for an implicit reject could confuse the DBA a lot when + * the true situation is a match to an explicit reject. And we + * don't want to change the message for an implicit reject. As + * noted below, the additional information shown here doesn't + * expose anything not known to an attacker. + */ + { + char hostinfo[NI_MAXHOST]; + + pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, + hostinfo, sizeof(hostinfo), + NULL, 0, + NI_NUMERICHOST); + +#ifdef USE_SSL + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s", + hostinfo, port->user_name, + port->database_name, + port->ssl_in_use ? _("SSL on") : _("SSL off")))); +#else + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\"", + hostinfo, port->user_name, + port->database_name))); +#endif + break; + } + case uaImplicitReject: + + /* + * No matching entry, so tell the user we fell through. + * + * NOTE: the extra info reported here is not a security breach, + * because all that info is known at the frontend and must be + * assumed known to bad guys. We're merely helping out the less + * clueful good guys. + */ + { + char hostinfo[NI_MAXHOST]; + + pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, + hostinfo, sizeof(hostinfo), + NULL, 0, + NI_NUMERICHOST); + +#define HOSTNAME_LOOKUP_DETAIL(port) \ + (port->remote_hostname ? \ + (port->remote_hostname_resolv == +1 ? \ + errdetail_log("Client IP address resolved to \"%s\", forward lookup matches.", \ + port->remote_hostname) : \ + port->remote_hostname_resolv == 0 ? \ + errdetail_log("Client IP address resolved to \"%s\", forward lookup not checked.", \ + port->remote_hostname) : \ + port->remote_hostname_resolv == -1 ? \ + errdetail_log("Client IP address resolved to \"%s\", forward lookup does not match.", \ + port->remote_hostname) : \ + port->remote_hostname_resolv == -2 ? \ + errdetail_log("Could not translate client host name \"%s\" to IP address: %s.", \ + port->remote_hostname, \ + gai_strerror(port->remote_hostname_errcode)) : \ + 0) \ + : (port->remote_hostname_resolv == -2 ? \ + errdetail_log("Could not resolve client IP address to a host name: %s.", \ + gai_strerror(port->remote_hostname_errcode)) : \ + 0)) + +#ifdef USE_SSL + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s", + hostinfo, port->user_name, + port->database_name, + port->ssl_in_use ? _("SSL on") : _("SSL off")), + HOSTNAME_LOOKUP_DETAIL(port))); +#else + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\"", + hostinfo, port->user_name, + port->database_name), + HOSTNAME_LOOKUP_DETAIL(port))); +#endif + + pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, + hostinfo, sizeof(hostinfo), + NULL, 0, + NI_NUMERICHOST); + break; + } + case uaSSPI: + case uaPeer: + case uaIdent: + case uaSCRAM: + case uaPAM: + case uaBSD: + case uaLDAP: + case uaCert: + case uaRADIUS: + /* the above authentication methods are not supported for TDS */ + { + char hostinfo[NI_MAXHOST]; + + pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, + hostinfo, sizeof(hostinfo), + NULL, 0, + NI_NUMERICHOST); + + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("pg_hba.conf entry specifies unsupported TDS authentication for host \"%s\", user \"%s\", database \"%s\"", + hostinfo, port->user_name, port->database_name), + errhint("Supported methods are trust, password, md5 and gssapi"))); + } + break; + case uaGSS: + /* + * If pg_hba.conf specifies that the entry should be authenticated using + * GSSAPI. If we reach here, we should've already authenticated using + * GSSAPI. So, we can just check the status.. + */ + if (status != STATUS_OK) + { + char hostinfo[NI_MAXHOST]; + + pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, + hostinfo, sizeof(hostinfo), + NULL, 0, + NI_NUMERICHOST); + + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("invalid TDS authentication request for host \"%s\", user \"%s\", database \"%s\"", + hostinfo, port->user_name, port->database_name), + errhint("Expected authentication request: gssapi"))); + } + break; + case uaMD5: + case uaPassword: + /* + * If pg_hba.conf specifies that the entry should be authenticated using + * password and the request doesn't contain a password, we should + * throw an error. + */ + if (!loginInfo->password) + { + char hostinfo[NI_MAXHOST]; + + pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, + hostinfo, sizeof(hostinfo), + NULL, 0, + NI_NUMERICHOST); + + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("invalid TDS authentication request for host \"%s\", user \"%s\", database \"%s\"", + hostinfo, port->user_name, port->database_name), + errhint("Expected authentication request: md5 or password"))); + } + + /* we've a password, let's verify it */ + status = CheckAuthPassword(port, &logdetail); + break; + case uaTrust: + status = STATUS_OK; + break; + } + + /* If authentication failed, tell the user. */ + if (status != STATUS_OK) + SendLoginError(port, logdetail); + + /* + * Authentication succeeded. But, we cannot send the login acknowledgement + * response until we successfully initialize POSTGRES. If we encounter an + * error during initialization we've to send the error along with a login + * failed response to the TDS client. Check InitPostgres for different + * initialization failure scenarios. + */ +} + +void +TdsClientInit(void) +{ + /* set up process-exit hook to close the socket */ + /* on_proc_exit(socket_close, 0); TODO Enable it later */ + + /* + * In backends (as soon as forked) we operate the underlying socket in + * nonblocking mode and use latches to implement blocking semantics if + * needed. That allows us to provide safely interruptible reads and + * writes. + * + * Use COMMERROR on failure, because ERROR would try to send the error to + * the client, which might require changing the mode again, leading to + * infinite recursion. + */ +#ifndef WIN32 + if (!pg_set_noblock(MyProcPort->sock)) + ereport(COMMERROR, + (errmsg("could not set socket to nonblocking mode: %m"))); +#endif + + FeBeWaitSet = CreateWaitEventSet(TopMemoryContext, 3); + AddWaitEventToSet(FeBeWaitSet, WL_SOCKET_WRITEABLE, MyProcPort->sock, + NULL, NULL); + AddWaitEventToSet(FeBeWaitSet, WL_LATCH_SET, -1, MyLatch, NULL); + AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, -1, NULL, NULL); + TdsCommInit(TDS_DEFAULT_INIT_PACKET_SIZE, + tds_secure_read, tds_secure_write); +} + +/* + * Attempt to negotiate secure session. + */ +static int +SecureOpenServer(Port *port) +{ + int r = 0; + +#ifdef USE_SSL + TDSInstrumentation(INSTR_TDS_LOGIN_SSL); + + r = Tds_be_tls_open_server(port); + + ereport(DEBUG2, + (errmsg("SSL connection from \"%s\"", + port->peer_cn ? port->peer_cn : "(anonymous)"))); +#endif + + return r; +} + +/* + * : Process a TDS login handshake + */ +int +TdsProcessLogin(Port *port, bool loadedSsl) +{ + int rc = 0; + int loadEncryption = 0; + + /* Set the LOGIN7 request type for error context */ + TdsErrorContext->phase = 0; + TdsErrorContext->reqType = TDS_LOGIN7; + + PG_TRY(); + { + TdsErrorContext->err_text = "Parsing PreLogin Request"; + /* Pre-Login */ + rc = ParsePreLoginRequest(); + if (rc < 0) + return rc; + + TdsErrorContext->err_text = "Make PreLogin Response"; + + loadEncryption = MakePreLoginResponse(port, loadedSsl); + TdsFlush(); + + TdsErrorContext->err_text = "Setup SSL Handshake"; + /* Setup the SSL handshake */ + if (loadEncryption == TDS_ENCRYPT_ON || + loadEncryption == TDS_ENCRYPT_OFF || + loadEncryption == TDS_ENCRYPT_REQ) + SecureOpenServer(port); + + if (loadEncryption == TDS_ENCRYPT_ON) + TDSInstrumentation(INSTR_TDS_LOGIN_END_TO_END_ENCRYPT); + + /* Login */ + rc = ProcessLoginInternal(port); + + } + PG_CATCH(); + { + PG_RE_THROW(); + } + PG_END_TRY(); + + TdsErrorContext->err_text = ""; + + if (rc < 0) + return rc; + + /* Free up the SSL strcture if TDS_ENCRYPT_OFF is set */ + if (loadEncryption == TDS_ENCRYPT_OFF) + TdsFreeSslStruct(port); + + return rc; +} + +/* + * TdsSendLoginAck - Send a login acknowledgement to the client + * + * This function should be called in postmaster context. + */ +void +TdsSendLoginAck(Port *port) +{ + uint16_t temp16; + char mbuf[1024]; + char *dbname; /* TODO: where to get this? */ + int prognameLen = pg_mbstrlen(default_server_name); + LoginRequest request; + StringInfoData buf; + uint8 temp8; + uint32_t collationInfo; + char collationBytesNew[5]; + uint32_t tdsVersion = pg_hton32(loginInfo->tdsVersion); + + /* TODO: should these version numbers be hardcoded? */ + char srvVersionBytes[] = { + 0x0C, 0x00, 0x07, 0xd0 + }; + + PG_TRY(); + { + + /* Initialize the normal TDS protocol */ + TdsProtocolInit(); + + TdsErrorContext->err_text = "Initialising Collation Info"; + + /* Checking if babelfishpg_tsql extension is loaded before reading babelfishpg_tsql.server_collation_oid GUC*/ + StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); + if (get_extension_oid("babelfishpg_tsql", true) == InvalidOid) + elog(FATAL, "babelfishpg_tsql extension is not installed"); + PopActiveSnapshot(); + CommitTransactionCommand(); + + TdsDefineDefaultCollationInfo(); + /* + * Collation(total 5bytes) is made of below fields. And we have to send 5 bytes as part of + * enviornment change token. + * LCID(20 bits) + collationFlags(8 bits) + version(4 bits) + sortId (8 bits) + * Here, we are storing 5 bytes individually and then send it as part of enviornment change token. + */ + collationInfo = TdsDefaultLcid | (TdsDefaultCollationFlags << 20); + collationBytesNew[0] = (char) collationInfo & 0x000000ff; + collationBytesNew[1] = (char) ((collationInfo & 0x0000ff00) >> 8); + collationBytesNew[2] = (char) ((collationInfo & 0x00ff0000) >> 16); + collationBytesNew[3] = (char) ((collationInfo & 0xff000000) >> 24); + collationBytesNew[4] = (char) TdsDefaultSortid; + + initStringInfo(&buf); + /* get the login request */ + request = loginInfo; + + TdsErrorContext->err_text = "Verifying and Sending Login Acknowledgement"; + + /* Start a server->client message */ + /* TODO: Why do we do this? All messages the backend sends have this type */ + TdsSetMessageType(TDS_RESPONSE); + + /* Append the ENVCHANGE and INFO messages */ + /* TODO: find all the real values for EnvChange and Info messages */ + + /* + * In TDS the packet Size is rounded down to the nearest + * multiple of 4. + */ + if (request->packetSize == TDS_USE_SERVER_DEFAULT_PACKET_SIZE) + { + char old[10]; + char new[10]; + + /* set the packet size as server default */ + request->packetSize = tds_default_packet_size; + + snprintf(old, sizeof(old), "%u", tds_default_packet_size); + snprintf(new, sizeof(new), "%u", request->packetSize); + TdsSendEnvChange(TDS_ENVID_BLOCKSIZE, new, old); + } + else if (request->packetSize != tds_default_packet_size) + { + char old[10]; /* the values are between 512 and 32767 */ + char new[10]; + + /* + * SQL Server rounds down the packet Size to the nearest + * multiple of 4. + */ + request->packetSize = (((int) request->packetSize / 4) * 4); + + snprintf(old, sizeof(old), "%u", tds_default_packet_size); + snprintf(new, sizeof(new), "%u", request->packetSize); + TdsSendEnvChange(TDS_ENVID_BLOCKSIZE, new, old); + } + + if (tds_enable_db_session_property) + { + char *useCommand = "USE "; + StringInfoData useDbCommand; + MemoryContext oldContext = CurrentMemoryContext; + + initStringInfo(&useDbCommand); + appendStringInfoString(&useDbCommand, useCommand); + + if (request->database != NULL && request->database[0] != '\0') + { + Oid db_id; + + /* + * Before preparing the query, first check whether we got a + * valid database name and it exists. Otherwise, there'll be + * risk of SQL injection. + */ + StartTransactionCommand(); + db_id = pltsql_plugin_handler_ptr->pltsql_get_database_oid(request->database); + CommitTransactionCommand(); + MemoryContextSwitchTo(oldContext); + + if (!OidIsValid(db_id)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", request->database))); + + appendStringInfoString(&useDbCommand, request->database); + } + else + { + char *defaultDb = NULL; + char *temp = NULL; + + StartTransactionCommand(); + temp = pltsql_plugin_handler_ptr->pltsql_get_login_default_db(request->username ? + request->username : port->user_name); + MemoryContextSwitchTo(oldContext); + + if (temp == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("could not find default database for user \"%s\"", request->username))); + + defaultDb = pstrdup(temp); + CommitTransactionCommand(); + MemoryContextSwitchTo(oldContext); + + appendStringInfoString(&useDbCommand, defaultDb); + } + + /* + * Request has a database name provided, so we execute + * a "USE " through pgtsql inline handler + */ + StartTransactionCommand(); + ExecuteSQLBatch(useDbCommand.data); + CommitTransactionCommand(); + } + else + { + dbname = port->database_name; + snprintf(mbuf, sizeof(mbuf), "Changed database context to '%s'", + dbname); + if (strcmp(dbname, TSQL_DEFAULT_DB) != 0) + TdsSendEnvChange(TDS_ENVID_DATABASE, dbname, TSQL_DEFAULT_DB); + TdsSendInfo(5701, 1, 10, mbuf, 1); + } + + /* + * Set the GUC for language, it will take care of + * changing the GUC, doing language validity checks + * and sending INFO and ENV change tokens + */ + if (request->language != NULL) + { + int ret; + /* + * For varchar GUCs we call pltsql_truncate_identifier which calls get_namespace_oid + * which does catalog access, hence we require to be inside a transaction command. + */ + StartTransactionCommand(); + ret = set_config_option("babelfishpg_tsql.language", + request->language, + PGC_USERSET, + PGC_S_CLIENT, + GUC_ACTION_SET, + true /* changeVal */, + 0 /* elevel */, + false /* is_reload */); + CommitTransactionCommand(); + if (ret != 1) + { + /* TODO Error handling */ + Assert(false); + } + } + + /* Set the GUC for application_name. */ + if (request->appname != NULL) + { + int ret; + char *tmpAppName = pstrdup(request->appname); + + pg_clean_ascii(tmpAppName); + + /* + * For varchar GUCs we call pltsql_truncate_identifier which calls get_namespace_oid + * which does catalog access, hence we require to be inside a transaction command. + */ + StartTransactionCommand(); + ret = set_config_option("application_name", + tmpAppName, + PGC_USERSET, + PGC_S_CLIENT, + GUC_ACTION_SET, + true /* changeVal */, + 0 /* elevel */, + false /* is_reload */); + CommitTransactionCommand(); + + if (ret != 1) + { + /* TODO Error handling */ + Assert(false); + } + } + + TdsSendEnvChangeBinary(TDS_ENVID_COLLATION, + collationBytesNew, sizeof(collationBytesNew), + NULL, 0); + + /* Append the LOGINACK message */ + TDS_DEBUG(TDS_DEBUG2, "TdsSendLoginAck: token=0x%02x", TDS_TOKEN_LOGINACK); + temp8 = TDS_TOKEN_LOGINACK; + TdsPutbytes(&temp8, sizeof(temp8)); + + temp16 = 1 /* interface */ + + sizeof(tdsVersion) + + 1 /* prognameLen */ + + prognameLen * 2 + + sizeof(srvVersionBytes); + TdsPutbytes(&temp16, sizeof(temp16)); + + temp8 = 0x01; + TdsPutbytes(&temp8, sizeof(temp8)); /* interface ??? */ + + TdsPutbytes(&tdsVersion, sizeof(tdsVersion)); + TdsPutbytes(&prognameLen, sizeof(temp8)); + + TdsUTF8toUTF16StringInfo(&buf, default_server_name, prognameLen); + TdsPutbytes(buf.data, buf.len); + + TdsPutbytes(&srvVersionBytes, sizeof(srvVersionBytes)); + + pfree(buf.data); + + /* Append the DONE message */ + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_FINAL, 2, 0); + + TdsFlush(); + + /* Now, set the network packet size that'll be used further TDS + * communication. + * + * CAUTION: If required, this internally repallocs memory for TDS send and + * receive buffers. So, we should do this after sending the login response. + */ + TdsErrorContext->err_text = "Resetting the TDS Buffer size"; + TdsSetBufferSize(request->packetSize); + + } + PG_CATCH(); + { + /* Before terminating the connection, send the response to the client */ + EmitErrorReport(); + FlushErrorState(); + + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_ERROR, 0, 0); + TdsFlush(); + + TdsErrorContext->err_text = "Verifying and Sending Login Acknowledgement"; + + ereport(FATAL, + (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), + errmsg("Login failed for user \"%s\"", port->user_name))); + + } + PG_END_TRY(); +} + +/* + * GetClientTDSVersion - exposes TDS version of client being connected. + */ +uint32_t +GetClientTDSVersion(void) +{ + /* This should not happen. */ + if (loginInfo == NULL) + ereport(FATAL, + (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Login Info should not be NULL"))); + return loginInfo->tdsVersion; +} + +/* + * This function will return the AD domain name. + */ +char* +get_tds_login_domainname(void) +{ + if (loginInfo) + return loginInfo->domainname; + else + return NULL; +} + +/* + * To initialise information of default collation based on "babelfishpg_tsql.server_collation_oid" GUC. + */ +static void +TdsDefineDefaultCollationInfo(void) +{ + coll_info_t cinfo = (pltsql_plugin_handler_ptr)->lookup_collation_table_callback(InvalidOid); + if (unlikely(cinfo.oid == InvalidOid)) + elog(FATAL, "Oid of default collation is not valid, This might mean that value of server_collation_name GUC is invalid"); + + TdsDefaultLcid = cinfo.lcid; + TdsDefaultCollationFlags = cinfo.collateflags; + TdsDefaultSortid = (uint8_t) cinfo.sortid; +} + +/* + * Increment appropriate instrumentation metric for TDS version + */ +static void +GetTDSVersionInstrumentation(uint32_t version) +{ + switch (version) + { + case TDS_VERSION_7_0: + TDSInstrumentation(INSTR_TDS_VERSION_7_0); + break; + case TDS_VERSION_7_1: + TDSInstrumentation(INSTR_TDS_VERSION_7_1); + break; + case TDS_VERSION_7_1_1: + TDSInstrumentation(INSTR_TDS_VERSION_7_1_1); + break; + case TDS_VERSION_7_2: + TDSInstrumentation(INSTR_TDS_VERSION_7_2); + break; + case TDS_VERSION_7_3_A: + TDSInstrumentation(INSTR_TDS_VERSION_7_3_A); + break; + case TDS_VERSION_7_3_B: + TDSInstrumentation(INSTR_TDS_VERSION_7_3_B); + break; + case TDS_VERSION_7_4: + TDSInstrumentation(INSTR_TDS_VERSION_7_4); + break; + default: + break; + } +} + +/* + * Increment appropriate instrumentation metric for unsupported login flags + */ +static void +GetLoginFlagsInstrumentation(LoginRequest loginInfo) +{ + /* OptionFlags1 */ + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_BYTE_ORDER_68000) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_BYTE_ORDER_68000); + + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_CHAR_EBCDIC) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_CHAR_EBCDIC); + + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_FLOAT_VAX) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_FLOAT_VAX); + + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_FLOAT_ND5000) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_FLOAT_ND5000); + + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_DUMP_LOAD_OFF) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_DUMP_LOAD_OFF); + + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_USE_DB_ON) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_USE_DB_ON); + + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_DATABASE_FATAL) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_DATABASE_FATAL); + + if (loginInfo->optionFlags1 & LOGIN_OPTION_FLAGS1_SET_LANG_ON) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_SET_LANG_ON); + + /* OptionFlags2 */ + if (loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_LANGUAGE_FATAL) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_LANGUAGE_FATAL); + + if ((GetClientTDSVersion() < TDS_VERSION_7_2) && (loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_TRAN_BOUNDARY)) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_TRAN_BOUNDARY); + + if ((GetClientTDSVersion() < TDS_VERSION_7_2) && (loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_CACHE_CONNECT)) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_CACHE_CONNECT); + + if (loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_USER_TYPE_SERVER) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_USER_TYPE_SERVER); + + if (loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_USER_TYPE_REMUSER) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_USER_TYPE_REMUSER); + + if (loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_USER_TYPE_SQLREPL) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_USER_TYPE_SQLREPL); + + if (loginInfo->optionFlags2 & LOGIN_OPTION_FLAGS2_INT_SECURITY_ON) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_INT_SECURITY_ON); + + /* TypeFlags */ + if (loginInfo->typeFlags & LOGIN_TYPE_FLAGS_SQL_TSQL) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_TYPE_FLAGS_SQL_TSQL); + + if ((GetClientTDSVersion() == TDS_VERSION_7_4) && (loginInfo->typeFlags & LOGIN_TYPE_FLAGS_READ_ONLY_INTENT)) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_TYPE_FLAGS_READ_ONLY_INTENT); + + /* OptionFlags3 */ + if ((GetClientTDSVersion() >= TDS_VERSION_7_2) && (loginInfo->optionFlags3 & LOGIN_OPTION_FLAGS3_USER_INSTANCE)) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_USER_INSTANCE); + + if ((GetClientTDSVersion() >= TDS_VERSION_7_2) && (loginInfo->optionFlags3 & LOGIN_OPTION_FLAGS3_SEND_YUKON_BINARY_XML)) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_SEND_YUKON_BINARY_XML); + + if ((GetClientTDSVersion() >= TDS_VERSION_7_3_A) && (loginInfo->optionFlags3 & LOGIN_OPTION_FLAGS3_UNKNOWN_COLLATION_HANDLING)) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_UNKNOWN_COLLATION_HANDLING); + + if ((GetClientTDSVersion() == TDS_VERSION_7_4) && (loginInfo->optionFlags3 & LOGIN_OPTION_FLAGS3_EXTENSION)) + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_EXTENSION); +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdsprinttup.c b/contrib/babelfishpg_tds/src/backend/tds/tdsprinttup.c new file mode 100644 index 00000000000..4b32f8a2232 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsprinttup.c @@ -0,0 +1,116 @@ +/*------------------------------------------------------------------------- + * + * tdsprinttup.c + * Routines to print out tuples to the destination (both frontend + * clients and standalone backends are supported here). + * + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdsprinttup.c + * + *------------------------------------------------------------------------- + */ + +static void TdsPrinttupStartup(DestReceiver *self, int operation, + TupleDesc typeinfo); +static void TdsShutdown(DestReceiver *self); +static void TdsDestroy(DestReceiver *self); + +/* ---------------------------------------------------------------- + * tdsprinttup support + * ---------------------------------------------------------------- + */ + +/* ---------------- + * Maintain and use carefully + * + * Private state for a printtup destination object + * + * NOTE: finfo is the lookup info for either typoutput or typsend, whichever + * we are using for this column. + * ---------------- + */ +typedef struct +{ /* Per-attribute information */ + Oid typoutput; /* Oid for the type's text output fn */ + Oid typsend; /* Oid for the type's binary output fn */ + bool typisvarlena; /* is it varlena (ie possibly toastable)? */ + int16 format; /* format code for this column */ + FmgrInfo finfo; /* Precomputed call info for output fn */ +} PrinttupAttrInfo; + +typedef struct +{ + DestReceiver pub; /* publicly-known function pointers */ + Portal portal; /* the Portal we are printing from */ + bool sendDescrip; /* send RowDescription at startup? */ + TupleDesc attrinfo; /* The attr info we are set up for */ + int nattrs; + PrinttupAttrInfo *myinfo; /* Cached info about each attr */ + StringInfoData buf; /* output buffer (*not* in tmpcontext) */ + MemoryContext tmpcontext; /* Memory context for per-row workspace */ +} DR_printtup; + + +static void +TdsPrinttupStartup(DestReceiver *self, int operation, TupleDesc typeinfo) +{ + DR_printtup *myState = (DR_printtup *) self; + Portal portal = myState->portal; + + /* + * Create I/O buffer to be used for all messages. This cannot be inside + * tmpcontext, since we want to re-use it across rows. + */ + initStringInfo(&myState->buf); + + /* + * Create a temporary memory context that we can reset once per row to + * recover palloc'd memory. This avoids any problems with leaks inside + * datatype output routines, and should be faster than retail pfree's + * anyway. + */ + myState->tmpcontext = AllocSetContextCreate(CurrentMemoryContext, + "printtup", + ALLOCSET_DEFAULT_SIZES); + + TdsSendRowDescription(typeinfo, + FetchPortalTargetList(portal), + portal->formats); + return; +} + +/* ---------------- + * printtup_shutdown + * ---------------- + */ +static void +TdsShutdown(DestReceiver *self) +{ + DR_printtup *myState = (DR_printtup *) self; + + TdsPrintTupShutdown(); + + if (myState->myinfo) + pfree(myState->myinfo); + myState->myinfo = NULL; + + myState->attrinfo = NULL; + + if (myState->buf.data) + pfree(myState->buf.data); + myState->buf.data = NULL; + + if (myState->tmpcontext) + MemoryContextDelete(myState->tmpcontext); + myState->tmpcontext = NULL; +} + +static void +TdsDestroy(DestReceiver *self) +{ + pfree(self); +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdsprotocol.c b/contrib/babelfishpg_tds/src/backend/tds/tdsprotocol.c new file mode 100644 index 00000000000..c1a9c4bbbb3 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsprotocol.c @@ -0,0 +1,697 @@ +/*------------------------------------------------------------------------- + * + * tdsprotocol.c + * TDS Listener tokenized protocol handling + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdsprotocol.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/htup_details.h" /* for GETSTRUCT() to extract tuple data */ +#include "access/printtup.h" /* for SetRemoteDestReceiverParams() */ +#include "access/table.h" +#include "access/relation.h" +#include "access/relscan.h" +#include "access/genam.h" +#include "access/xact.h" /* for IsTransactionOrTransactionBlock() */ +#include "catalog/indexing.h" +#include "catalog/pg_type.h" +#include "commands/async.h" +#include "commands/defrem.h" +#include "commands/prepare.h" +#include "executor/spi.h" +#include "fmgr.h" +#include "libpq/pqformat.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "nodes/parsenodes.h" +#include "parser/parser.h" +#include "parser/parse_coerce.h" +#include "port/pg_bswap.h" +#include "tcop/pquery.h" +#include "utils/fmgroids.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/memdebug.h" +#include "utils/numeric.h" +#include "utils/portal.h" +#include "utils/rel.h" +#include "utils/snapmgr.h" + +#include "src/include/tds_debug.h" +#include "src/include/tds_int.h" +#include "src/include/tds_protocol.h" +#include "src/include/tds_response.h" +#include "src/include/faultinjection.h" + +/* + * When we reset the connection, we save the required information in the following + * structure that should be restored after the reset. + */ +typedef struct ResetConnectionData +{ + StringInfo message; + uint8_t messageType; + uint8_t status; +} ResetConnectionData; +typedef ResetConnectionData* ResetConnection; + +/* + * Local structures + */ +TdsRequestCtrlData *TdsRequestCtrl = NULL; + +ResetConnection resetCon = NULL; + +/* Local functions */ +static void ResetTDSConnection(void); +static TDSRequest GetTDSRequest(bool *resetProtocol); +static void ProcessTDSRequest(TDSRequest request); + +/* + * TDSDiscardAll - copy of DiscardAll + */ +static +void TdsDiscardAll() +{ + /* + * Disallow DISCARD ALL in a transaction block. This is arguably + * inconsistent (we don't make a similar check in the command sequence + * that DISCARD ALL is equivalent to), but the idea is to catch mistakes: + * DISCARD ALL inside a transaction block would leave the transaction + * still uncommitted. + */ + PreventInTransactionBlock(true, "DISCARD ALL"); + + /* Closing portals might run user-defined code, so do that first. */ + PortalHashTableDeleteAll(); + SetPGVariable("session_authorization", NIL, false); + ResetAllOptions(); + DropAllPreparedStatements(); + Async_UnlistenAll(); + LockReleaseAll(USER_LOCKMETHOD, true); + ResetPlanCache(); + ResetTempTableNamespace(); + ResetSequenceCaches(); +} + +/* + * ResetTDSConnection - resets the TDS connection + * + * It resets the TDS connection by calling DISCARD ALL api in . Also, it + * releases the memory allocated in TDS layer and re-initializes different + * buffers and structures. Additionally, it sends an environment change token + * for RESETCON. + */ +static void +ResetTDSConnection(void) +{ + const char *isolationOld; + + Assert(TdsRequestCtrl->request == NULL); + Assert(TdsRequestCtrl->requestContext != NULL); + TdsErrorContext->err_text = "Resetting the TDS connection"; + + /* Make sure we've killed any active transaction */ + AbortOutOfAnyTransaction(); + + /* + * Save the transaction isolation level that should be restored after connection + * reset. + */ + isolationOld = GetConfigOption("default_transaction_isolation", false, false); + + /* + * Start an implicit transaction block because the internal code may need + * to access the catalog. + */ + StartTransactionCommand(); + TdsDiscardAll(); + pltsql_plugin_handler_ptr->reset_session_properties(); + CommitTransactionCommand(); + + /* + * Now reset the TDS top memory context and re-initialize everything. Also, + * restore the transaction isolation level. + */ + MemoryContextReset(TdsMemoryContext); + TdsCommReset(); + TdsProtocolInit(); + TdsResetCache(); + TdsResponseReset(); + SetConfigOption("default_transaction_isolation", isolationOld, + PGC_BACKEND, PGC_S_CLIENT); + + tvp_lookup_list = NIL; + + /* send an environement change token */ + TdsSendEnvChange(TDS_ENVID_RESETCON, NULL, NULL); +} + +/* + * GetTDSRequest - Fetch and parse a TDS packet and generate a TDS request that + * can be processed later. + * + * Note that, this method is called in TDS Request Context so that any allocation + * done here will get reset only after sending the response. + * + * resetProtocol - set to true if we've reset the connection. + */ +static TDSRequest +GetTDSRequest(bool *resetProtocol) +{ + uint8_t messageType; + uint8_t status; + TDSRequest request; + StringInfoData message; + + initStringInfo(&message); + + /* + * Setup error traceback support for ereport() + */ + TdsErrorContext->err_text = "Fetching TDS Request"; + TdsErrorContext->spType = "Unknown (Pre-Parsing Request)"; + TdsErrorContext->txnType = "Unknown (Pre-Parsing Request)"; + PG_TRY(); + { + /* + * If we've saved the TDS request earlier, process the same instead of trying + * to fetch a new request. + */ + if (resetCon != NULL) + { + messageType = resetCon->messageType; + status = resetCon->status; + appendBinaryStringInfo(&message, resetCon->message->data, resetCon->message->len); + + /* cleanup and reset */ + pfree(resetCon->message->data); + pfree(resetCon->message); + pfree(resetCon); + resetCon = NULL; + } + else + { + /* + * If TdsRequestCtrl->request is not NULL then + * there are mutliple RPCs in a Batch and we would restore the message + * Otherwise we would fetch the next packet.] + */ + if(TdsRequestCtrl->request == NULL) + { + int ret; + + /* + * We should hold the interrupts untill we read the entire + * request. + */ + HOLD_CANCEL_INTERRUPTS(); + ret = TdsReadNextRequest(&message, &status, &messageType); + RESUME_CANCEL_INTERRUPTS(); + TdsErrorContext->err_text = "Fetching TDS Request"; + + if (ret != 0) + { + TDS_DEBUG(TDS_DEBUG1, "EOF on TDS socket"); + pfree(message.data); + return NULL; + } + TdsRequestCtrl->status = status; + } + else + RestoreRPCBatch(&message, &status, &messageType); + } + + DebugPrintMessageData("Fetched message:", message); + + TdsErrorContext->reqType = messageType; + + #ifdef FAULT_INJECTOR + { + TdsMessageWrapper wrapper; + wrapper.message = &message; + wrapper.messageType = messageType; + + FAULT_INJECT(PreParsingType, &wrapper); + } + #endif + + Assert(messageType != 0); + + /* + * If we have to reset the connection, we save the TDS request in top memory + * context before exit so that we can process the request later. + */ + if (status & TDS_PACKET_HEADER_STATUS_RESETCON) + { + MemoryContextSwitchTo(TopMemoryContext); + + if (resetCon == NULL) + resetCon = palloc(sizeof(ResetConnectionData)); + + resetCon->message = makeStringInfo(); + appendBinaryStringInfo(resetCon->message, message.data, message.len); + resetCon->messageType = messageType; + resetCon->status = (status & ~TDS_PACKET_HEADER_STATUS_RESETCON); + + ResetTDSConnection(); + TdsErrorContext->err_text = "Fetching TDS Request"; + *resetProtocol = true; + return NULL; + } + + /* + * XXX: We don't support the following feature. But, throw an error to + * detect the case in case we get such a request. + */ + if (status & TDS_PACKET_HEADER_STATUS_RESETCONSKIPTRAN) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("RESETCONSKIPTRAN is not supported"))); + + /* An attention request can also have message.len = 0. */ + if (message.len == 0 && messageType != TDS_ATTENTION) + { + TDS_DEBUG(TDS_DEBUG1, "zero byte client message on TDS socket"); + pfree(message.data); + return NULL; + } + + /* Parse the packet */ + switch (messageType) + { + case TDS_QUERY: /* Simple SQL BATCH */ + { + request = GetSQLBatchRequest(&message); + + pfree(message.data); + } + break; + case TDS_RPC: /* Remote procedure call */ + { + request = GetRPCRequest(&message); + } + break; + case TDS_TXN: /* Transaction management request */ + { + request = GetTxnMgmtRequest(&message); + } + break; + case TDS_ATTENTION: /* Attention request */ + { + /* Return an empty request with the attention type. */ + request = palloc0(sizeof(TDSRequest)); + request->reqType = TDS_REQUEST_ATTN; + } + break; + default: + DebugPrintMessageData("Ignored message", message); + elog(ERROR, "TDSRequest: ignoring request type 0x%02x", + message.data[0]); + } + } + PG_CATCH(); + { + PG_RE_THROW(); + } + PG_END_TRY(); + + FAULT_INJECT(PostParsingType, request); + + return request; +} + +/* + * ProcessTDSRequest - TDS specific processing of the request + * + * Note that, this method is called in MessageContext so that any allocation + * done here can be reset in next TCOP iteration. + */ +static void +ProcessTDSRequest(TDSRequest request) +{ + /* + * Setup error traceback support for ereport() + */ + TdsErrorContext->err_text = "Processing TDS Request"; + + /* + * We shouldn't be in this state as we handle the aborted case on + * babelfishpg_tsql extension itself. But, if we somehow end up + * in this state, throw error and disconnect immediately. + */ + if (IsAbortedTransactionBlockState()) + elog(FATAL, "terminating connection due to unexpected TSQL transaction state"); + + PG_TRY(); + { + StartTransactionCommand(); + MemoryContextSwitchTo(MessageContext); + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + + switch (request->reqType) + { + case TDS_REQUEST_SP_NUMBER: + { + ProcessRPCRequest(request); + } + break; + case TDS_REQUEST_SQL_BATCH: + { + ProcessSQLBatchRequest(request); + } + break; + case TDS_REQUEST_TXN_MGMT: + { + ProcessTxnMgmtRequest(request); + } + break; + case TDS_REQUEST_ATTN: + { + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_ATTN, 0xfd, 0); + } + break; + default: + /* GetTDSRequest() should've returned the correct request type */ + Assert(0); + break; + } + CommitTransactionCommand(); + MemoryContextSwitchTo(MessageContext); + + } + PG_CATCH(); + { + int token_type; + int command_type = TDS_CMD_UNKNOWN; + + CommitTransactionCommand(); + MemoryContextSwitchTo(MessageContext); + + EmitErrorReport(); + FlushErrorState(); + + if (request->reqType == TDS_REQUEST_SP_NUMBER) + token_type = TDS_TOKEN_DONEPROC; + else + token_type = TDS_TOKEN_DONE; + + TdsSendDone(token_type, TDS_DONE_ERROR, command_type, 0); + } + PG_END_TRY(); +} + +void +TdsProtocolInit(void) +{ + MemoryContext oldContext; + + SetConfigOption("babelfishpg_tsql.sql_dialect", "tsql", PGC_SU_BACKEND, PGC_S_OVERRIDE); + oldContext = MemoryContextSwitchTo(TdsMemoryContext); + TdsRequestCtrl = palloc(sizeof(TdsRequestCtrlData)); + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_INIT; + + TdsRequestCtrl->requestContext = AllocSetContextCreate(TdsMemoryContext, + "TDS Request", + ALLOCSET_DEFAULT_SIZES); + TdsRequestCtrl->request = NULL; + TdsRequestCtrl->status = 0; + + MemoryContextSwitchTo(oldContext); +} + +void +TdsProtocolFinish(void) +{ + SetConfigOption("babelfishpg_tsql.sql_dialect", "postgres", PGC_SU_BACKEND, PGC_S_OVERRIDE); + + if (TdsRequestCtrl->requestContext) + { + MemoryContextDelete(TdsRequestCtrl->requestContext); + TdsRequestCtrl->requestContext = NULL; + } + + pfree(TdsRequestCtrl); + TdsRequestCtrl = NULL; +} + +/* + * TdsSocketBackend() Is called for frontend-backend TDS connections + * + * This function reads requests from a TDS client and executes the same. We + * leave the function only in case of an error or if connection is lost. EOF + * is returned if the connection is lost. + */ +int +TdsSocketBackend(void) +{ + bool resetProtocol; + bool loop = true; + while (loop) + { + PG_TRY(); + { + switch (TdsRequestCtrl->phase) + { + case TDS_REQUEST_PHASE_INIT: + { + MemoryContext oldContext; + resetProtocol = false; + + TdsErrorContext->phase = "TDS_REQUEST_PHASE_INIT"; + /* + * Switch to the request context. We reset this context once + * once TDSfunctionCache is loaded + */ + oldContext = MemoryContextSwitchTo(TdsMemoryContext); + + InitTDSResponse(); + StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); + + /* + * Loading the cache tables in TdsMemoryContext Memory + * context and is loaded only once during the INIT step. + * TODO: Cache invalidate & reload if some enteries have + * changed + */ + TdsLoadTypeFunctionCache(); + TdsLoadEncodingLCIDCache(); + PopActiveSnapshot(); + CommitTransactionCommand(); + + MemoryContextSwitchTo(oldContext); + + /* we should have exec callbacks initialized by this time */ + if (!(pltsql_plugin_handler_ptr->sql_batch_callback)) + elog(FATAL, "sql_batch_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_executesql_callback)) + elog(FATAL, "sp_executesql_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_prepare_callback)) + elog(FATAL, "sp_prepare_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_execute_callback)) + elog(FATAL, "sp_execute_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_prepexec_callback)) + elog(FATAL, "sp_prepexec_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_unprepare_callback)) + elog(FATAL, "sp_unprepare_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->pltsql_declare_var_callback)) + elog(FATAL, "pltsql_declare_var_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->pltsql_read_out_param_callback)) + elog(FATAL, "pltsql_read_out_param_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_cursoropen_callback)) + elog(FATAL, "sp_cursoropen_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_cursorclose_callback)) + elog(FATAL, "sp_cursorclose_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_cursorfetch_callback)) + elog(FATAL, "sp_cursorfetch_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_cursorexecute_callback)) + elog(FATAL, "sp_cursorexecute_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_cursorprepexec_callback)) + elog(FATAL, "sp_cursorprepexec_callback is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_cursorprepare_callback)) + elog(FATAL, "sp_cursorprepare is not initialized"); + if (!(pltsql_plugin_handler_ptr->sp_cursorunprepare_callback)) + elog(FATAL, "sp_cursorunprepare is not initialized"); + if (!(pltsql_plugin_handler_ptr->send_column_metadata)) + elog(FATAL, "send_column_metadata is not initialized"); + + /* Ready to fetch the next request */ + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_FETCH; + break; + } + case TDS_REQUEST_PHASE_FETCH: + { + MemoryContext oldContext; + TdsErrorContext->phase = "TDS_REQUEST_PHASE_FETCH"; + + /* + * Switch to the request context. We reset this context once + * we send the response. + */ + oldContext = MemoryContextSwitchTo(TdsRequestCtrl->requestContext); + + /* + * Also consider releasing our catalog snapshot if any, so that it's + * not preventing advance of global xmin while we wait for the client. + */ + InvalidateCatalogSnapshotConditionally(); + + /* + * We should hold the interrupts untill we read the entire + * request. + */ + resetProtocol = false; + TdsRequestCtrl->request = GetTDSRequest(&resetProtocol); + + MemoryContextSwitchTo(oldContext); + + /* if we've reset the connection, break here. */ + if (resetProtocol) + { + /* the next phase should be set to init phase */ + Assert(TdsRequestCtrl->phase == TDS_REQUEST_PHASE_INIT); + break; + } + + if (TdsRequestCtrl->request == NULL) + return EOF; + + /* Switch the TDS protocol to RESPONSE mode */ + TdsSetMessageType(TDS_RESPONSE); + + /* we should be in tsql dialect */ + if (sql_dialect != SQL_DIALECT_TSQL) + elog(ERROR, "babelfishpg_tsql.sql_dialect is not set to tsql"); + + /* Now, process the request */ + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_PROCESS; + + /* + * Break here. We will process the request later in + * PostgresMain function. + */ + loop = false; + break; + } + + case TDS_REQUEST_PHASE_PROCESS: + { + TdsErrorContext->phase = "TDS_REQUEST_PHASE_PROCESS"; + TdsRequestCtrl->isEmptyResponse = true; + + ProcessTDSRequest(TdsRequestCtrl->request); + + /* we should be still in MessageContext */ + Assert(CurrentMemoryContext == MessageContext); + + /* + * If there are RPC packets left to + * fetch in the packet then we go back + * to the fetch phase + */ + if(TdsRequestCtrl->request->reqType == TDS_REQUEST_SP_NUMBER && RPCBatchExists(TdsRequestCtrl->request->sp)) + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_FETCH; + else + /* + * No more message to send to the TCOP loop. Send the + * response. + */ + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_FLUSH; + + /* + * Break here. We will Flush or Fetch the next request in the + * next iteration of PostgresMain function. + */ + loop = false; + break; + } + case TDS_REQUEST_PHASE_FLUSH: + { + TdsErrorContext->phase = "TDS_REQUEST_PHASE_FLUSH"; + /* Send the response now */ + TdsFlush(); + + /* Cleanups */ + MemoryContextReset(TdsRequestCtrl->requestContext); + + /* Reset the request */ + TdsRequestCtrl->request = NULL; + + /* Ready to fetch the next request */ + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_FETCH; + + break; + } + case TDS_REQUEST_PHASE_ERROR: + TdsErrorContext->phase = "TDS_REQUEST_PHASE_ERROR"; + + /* + * We've already sent an error token. If required, we can send + * more error tokens before flushing the response. + * N.B. We can reach this state only for some unexpected + * error condition. For normal execution error, babelfishpg_tsql + * extension already handles the error and doesn't + * rethrow to TDS. So, if we're getting some error at this + * level, we should investigate the error. + */ + + /* + * Send the done token that follows error + * XXX: Does it matter whether it's DONE or DONEPROC? This + * is anyway not an expected place to throw an error. Find + * a valid usecase before making this logic more complicated. + */ + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_ERROR, + TDS_CMD_UNKNOWN, 0); + + /* We're done. Send the response. */ + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_FLUSH; + break; + } + } + PG_CATCH(); + { + TdsRequestCtrl->phase = TDS_REQUEST_PHASE_ERROR; + + /* + * We need to rethrow the error as the error handling code in + * the main postgres tcop loop does a lot of necessary cleanups. + * But, if we want to do any further cleanup or take any further + * action, we can do that here as a pre-processing or in + * TDS_REQUEST_PHASE_ERROR state as post-processing. + */ + PG_RE_THROW(); + } + PG_END_TRY(); + } + + return 0; +} + +int +TestGetTdsRequest(uint8_t reqType, const char *expectedStr) +{ + int res = 0; + bool resetProtocol; + TDSRequest request = GetTDSRequest(&resetProtocol); + switch(reqType) + { + case TDS_TXN: + res = TestTxnMgmtRequest(request, expectedStr); + break; + default: + return -1; + } + return res; +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c b/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c new file mode 100644 index 00000000000..1e2909d5da3 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c @@ -0,0 +1,2805 @@ +/*------------------------------------------------------------------------- + * + * tdsresponse.c + * TDS Listener functions for sending a TDS response + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/htup_details.h" /* for GETSTRUCT() to extract tuple data */ +#include "access/printtup.h" /* for SetRemoteDestReceiverParams() */ +#include "access/xact.h" /* for IsTransactionOrTransactionBlock() */ +#include "access/genam.h" +#include "access/heapam.h" +#include "catalog/indexing.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "nodes/pathnodes.h" +#include "parser/parse_coerce.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/memdebug.h" +#include "utils/numeric.h" +#include "utils/portal.h" +#include "utils/rel.h" + +#include "src/include/tds_instr.h" +#include "src/include/tds_int.h" +#include "src/include/tds_protocol.h" +#include "src/include/tds_response.h" +#include "src/include/guc.h" + +#define SP_FLAGS_WITHRECOMP 0x01 +#define SP_FLAGS_NOMETADATA 0x02 +#define SP_FLAGS_REUSEMETADATA 0x04 + +/* ColInfo token flags */ +#define COLUMN_STATUS_EXPRESSION 0x04 +#define COLUMN_STATUS_KEY 0x08 +#define COLUMN_STATUS_HIDDEN 0x10 +#define COLUMN_STATUS_DIFFERENT_NAME 0x20 + +/* two possible values of rowstat column */ +#define SP_CURSOR_FETCH_SUCCEEDED 0x0001 +#define SP_CURSOR_FETCH_MISSING 0x0002 + +/* Numeirc operator OID from pg_proc.dat */ +#define NUMERIC_ADD_OID 1724 +#define NUMERIC_SUB_OID 1725 +#define NUMERIC_MUL_OID 1726 +#define NUMERIC_DIV_OID 1727 +#define NUMERIC_MOD_OID 1728 +#define NUMERIC_MOD_OID2 1729 +#define NUMERIC_UPLUS_OID 1915 +#define NUMERIC_UMINUS_OID 1771 + +#define Max(x, y) ((x) > (y) ? (x) : (y)) +#define Min(x, y) ((x) < (y) ? (x) : (y)) + +/* + * Local structures and functions copied from printtup.c + */ +typedef struct +{ /* Per-attribute information */ + Oid typoutput; /* Oid for the type's text output fn */ + Oid typsend; /* Oid for the type's binary output fn */ + bool typisvarlena; /* is it varlena (ie possibly toastable)? */ + int16 format; /* format code for this column */ + FmgrInfo finfo; /* Precomputed call info for output fn */ +} PrinttupAttrInfo; + +typedef struct +{ + DestReceiver pub; /* publicly-known function pointers */ + Portal portal; /* the Portal we are printing from */ + bool sendDescrip; /* send RowDescription at startup? */ + TupleDesc attrinfo; /* The attr info we are set up for */ + int nattrs; + PrinttupAttrInfo *myinfo; /* Cached info about each attr */ + StringInfoData buf; /* output buffer (*not* in tmpcontext) */ + MemoryContext tmpcontext; /* Memory context for per-row workspace */ +} DR_printtup; + +typedef struct TdsExecutionStateData +{ + int current_stack; + int error_stack_offset; + int cur_error_number; + int cur_error_severity; + int cur_error_state; +} TdsExecutionStateData; + +typedef TdsExecutionStateData *TdsExecutionState; + +/* Local variables */ +static bool TdsHavePendingDone = false; +static bool TdsPendingDoneNocount; +static uint8_t TdsPendingDoneToken; +static uint16_t TdsPendingDoneStatus; +static uint16_t TdsPendingDoneCurCmd; +static uint64_t TdsPendingDoneRowCnt; +static TdsExecutionState tds_estate = NULL; + +/* + * This denotes whether we've sent an error token and the next done token + * should have the error flag marked. + */ +static bool markErrorFlag = false; + +static TdsColumnMetaData *colMetaData = NULL; +static List *relMetaDataInfoList = NULL; + +static void FillTabNameWithNumParts(StringInfo buf, uint8 numParts, TdsRelationMetaDataInfo relMetaDataInfo); +static void FillTabNameWithoutNumParts(StringInfo buf, uint8 numParts, TdsRelationMetaDataInfo relMetaDataInfo); +static void SetTdsEstateErrorData(void); +static void ResetTdsEstateErrorData(void); + +static inline void +SendPendingDone(bool more) +{ + /* + * If this is the last token, there better be at least one pending done + * token to send. We also call this function after sending the prelogin + * response although we don't have any done token to send. So just do + * this check once protocol code is initialized. + */ + Assert(!TdsRequestCtrl || more || TdsHavePendingDone); + + /* + * If this is the last token, then the done token should be either DONE + * or DONEPROC. + */ + Assert(!TdsRequestCtrl || more || + (TdsPendingDoneToken == TDS_TOKEN_DONEPROC || + TdsPendingDoneToken == TDS_TOKEN_DONE)); + + if (TdsHavePendingDone) + { + uint32_t tdsVersion = GetClientTDSVersion(); + TdsHavePendingDone = false; + + /* In NOCOUNT=ON mode we need to suppress the DONE_COUNT */ + if (TdsPendingDoneNocount) + TdsPendingDoneStatus &= ~TDS_DONE_COUNT; + + /* If done token follows error token then suppress DONE_COUNT */ + if (TdsPendingDoneStatus & TDS_DONE_ERROR) + TdsPendingDoneStatus &= ~TDS_DONE_COUNT; + + if (more) + { + /* Suppress non-SELECT DONEINPROC while NOCOUNT=ON */ + if (TdsPendingDoneNocount && + TdsPendingDoneToken == TDS_TOKEN_DONEINPROC && + TdsPendingDoneCurCmd != TDS_CMD_SELECT) + { + return; + } + + TdsPendingDoneStatus |= TDS_DONE_MORE; + } + + /* extra handling if this follows an error token */ + if (tds_estate && (TdsPendingDoneStatus & TDS_DONE_ERROR)) + { + /* TODO: If we've saved the error command type, send the same. */ + + /* + * If we're sending a done token that follows an error token, then + * we must clear the error stack offset. Because, after that we'll + * be back to normal execution. + */ + tds_estate->error_stack_offset = 0; + + /* + * If a statement throws an error, the row count should be + * always 0. + */ + Assert(TdsPendingDoneRowCnt == 0); + } + + TDS_DEBUG(TDS_DEBUG3, "SendPendingDone: putbytes"); + TdsPutbytes(&TdsPendingDoneToken, sizeof(TdsPendingDoneToken)); + TdsPutbytes(&TdsPendingDoneStatus, sizeof(TdsPendingDoneStatus)); + TdsPutbytes(&TdsPendingDoneCurCmd, sizeof(TdsPendingDoneCurCmd)); + + /* + * For Client TDS Version less than or equal to 7.1 Done Row Count is of 4 bytes + * and for TDS versions higher than 7.1 it is of 8 bytes. + */ + if (tdsVersion <= TDS_VERSION_7_1_1) + { + uint32_t TdsPendingDoneRowCnt_32; + if (TdsPendingDoneRowCnt > PG_UINT32_MAX) + ereport(FATAL, (errmsg("Row Count execeeds UINT32_MAX"))); + else + TdsPendingDoneRowCnt_32 = (int32_t) TdsPendingDoneRowCnt; + TdsPutbytes(&TdsPendingDoneRowCnt_32, sizeof(TdsPendingDoneRowCnt_32)); + } + else + TdsPutbytes(&TdsPendingDoneRowCnt, sizeof(TdsPendingDoneRowCnt)); + } +} + +/* + * Given a relation, fetch the attributes number which are part of the primary + * key on this table. + */ +static AttrNumber * +getPkeyAttnames(Relation rel, int16 *indnkeyatts) +{ + Relation indexRelation; + ScanKeyData skey; + SysScanDesc scan; + HeapTuple indexTuple; + int i; + AttrNumber *result = NULL; + + /* initialize indnkeyatts to 0 in case no primary key exists */ + *indnkeyatts = 0; + + /* Prepare to scan pg_index for entries having indrelid = this rel. */ + indexRelation = table_open(IndexRelationId, AccessShareLock); + ScanKeyInit(&skey, + Anum_pg_index_indrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); + + scan = systable_beginscan(indexRelation, IndexIndrelidIndexId, true, + NULL, 1, &skey); + + while (HeapTupleIsValid(indexTuple = systable_getnext(scan))) + { + Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple); + + /* we're only interested if it is the primary key */ + if (index->indisprimary) + { + *indnkeyatts = index->indnkeyatts; + if (*indnkeyatts > 0) + { + result = (AttrNumber *) palloc(*indnkeyatts * sizeof(AttrNumber)); + + for (i = 0; i < *indnkeyatts; i++) + result[i] = (AttrNumber) DatumGetInt16(index->indkey.values[i]); + } + break; + } + } + + systable_endscan(scan); + table_close(indexRelation, AccessShareLock); + + return result; +} + +/* + * Fill Table Name With NumParts, a multi-part table name, which was introduced in + * TDS 7.2 for Column Metadata Token and introduced in TDS 7.1 revision 1 for TableName Token. + */ +static void +FillTabNameWithNumParts(StringInfo buf, uint8 numParts, TdsRelationMetaDataInfo relMetaDataInfo) +{ + StringInfoData tempBuf; + + initStringInfo(&tempBuf); + + /* + * XXX: In case a multi-part table name is used in the query, we should send + * the same fully qualified name here in multiple parts. For example, if the + * following format is used in query: + * select * from t1; + * we should send only part with partname 't1'. However, if the following + * format is used: + * select * from [dbo].[t1]; + * we should send two parts with partname 'dbo' and 't1'; + * + * In order to get this information, we definitely need some parser support. + * Probably, we can save this information in portal while parsing the table + * names. + * + * For now, always send it in two parts namespace.table name and hope that + * client won't complain about the same. + */ + + appendBinaryStringInfo(buf, (char *) &numParts, sizeof(numParts)); + while (numParts-- > 0) + { + uint16_t partNameLen; + char *partName = relMetaDataInfo->partName[numParts]; + + resetStringInfo(&tempBuf); + TdsUTF8toUTF16StringInfo(&tempBuf, partName, strlen(partName)); + + partNameLen = htoLE16((uint16_t) pg_mbstrlen(partName)); + appendBinaryStringInfo(buf, (char *) &partNameLen, sizeof(partNameLen)); + appendBinaryStringInfo(buf, tempBuf.data, tempBuf.len); + } + + pfree(tempBuf.data); +} + +/* + * Fill Table Name Without NumParts, a single-part table name, for Protocol versions below + * TDS 7.2 for Column Metadata Token and below TDS 7.1 revision 1 for TableName Token. + */ +static void +FillTabNameWithoutNumParts(StringInfo buf, uint8 numParts, TdsRelationMetaDataInfo relMetaDataInfo) +{ + uint16_t TableNameLen = 0; + StringInfoData tempBuf; + char *tableName = ""; + initStringInfo(&tempBuf); + + /* + * NumParts and PartName are not included in the response for TDS protocol versions + * lower than 7.1 revision (including TDS 7.1 revision 1 in case of ColumnMetadata Token). + * If the Table Name is in parts then we create a single string and convert it to + * UTF16 before putting it on the wire. For example for a table dbo.t1 + * we should send one single tableName as dbo.t1 + */ + + while (numParts-- > 0) + tableName = psprintf("%s.%s", tableName, relMetaDataInfo->partName[numParts]); + + if (strlen(tableName)) + tableName++; /* skip the first '.' */ + TableNameLen += htoLE16((uint16_t) pg_mbstrlen(tableName)); + + TdsUTF8toUTF16StringInfo(&tempBuf, tableName, strlen(tableName)); + appendBinaryStringInfo(buf, (char *) &TableNameLen, sizeof(TableNameLen)); + appendBinaryStringInfo(buf, tempBuf.data, tempBuf.len); + + pfree(tempBuf.data); +} + +/* + * Get the lookup info that TdsPrintTup() needs. + * Code is copied from backend/access/common/printtup.c + */ +static void +PrintTupPrepareInfo(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) +{ + int16 *formats = myState->portal->formats; + int i; + + /* get rid of any old data */ + if (myState->myinfo) + pfree(myState->myinfo); + myState->myinfo = NULL; + + myState->attrinfo = typeinfo; + myState->nattrs = numAttrs; + if (numAttrs <= 0) + return; + + myState->myinfo = (PrinttupAttrInfo *) + palloc0(numAttrs * sizeof(PrinttupAttrInfo)); + + for (i = 0; i < numAttrs; i++) + { + PrinttupAttrInfo *thisState = myState->myinfo + i; + int16 format = (formats ? formats[i] : 0); + Form_pg_attribute attr = TupleDescAttr(typeinfo, i); + + thisState->format = format; + if (format == 0) + { + getTypeOutputInfo(attr->atttypid, + &thisState->typoutput, + &thisState->typisvarlena); + fmgr_info(thisState->typoutput, &thisState->finfo); + } + else if (format == 1) + { + getTypeBinaryOutputInfo(attr->atttypid, + &thisState->typsend, + &thisState->typisvarlena); + fmgr_info(thisState->typsend, &thisState->finfo); + } + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unsupported format code: %d", format))); + } +} + +/* look for a typmod to return during a numeric expression */ +static int32 +resolve_numeric_typmod_from_exp(Node *expr) +{ + if (expr == NULL) + return -1; + switch (nodeTag(expr)) + { + case T_Const: + { + Const *con = (Const *) expr; + Numeric num; + + /* TODO: + * We used a workaround here, that we will assume typmod is 0 + * if the value we have is not numeric. See walkaround in T_FuncExpr part + * of this function. JIRA: BABEL-1007 + */ + if (con->consttype != NUMERICOID || con->constisnull) + return 0; // Typmod doesn't really matter since it's a const NULL. + else + { + num = (Numeric) con->constvalue; + return numeric_get_typmod(num); + } + } + case T_Var: + { + Var *var = (Var*) expr; + return var->vartypmod; + } + case T_OpExpr: + { + OpExpr *op = (OpExpr *) expr; + Node *arg1, *arg2; + int32 typmod1 = -1, typmod2 = -1; + uint8_t scale1, scale2, precision1, precision2; + uint8_t scale, precision; + + Assert(list_length(op->args) == 2 || list_length(op->args) == 1); + if (list_length(op->args) == 2) + { + arg1 = linitial(op->args); + arg2 = lsecond(op->args); + typmod1 = resolve_numeric_typmod_from_exp(arg1); + typmod2 = resolve_numeric_typmod_from_exp(arg2); + scale1 = (typmod1 - VARHDRSZ) & 0xffff; + precision1 = ((typmod1 - VARHDRSZ) >> 16) & 0xffff; + scale2 = (typmod2 - VARHDRSZ) & 0xffff; + precision2 = ((typmod2 - VARHDRSZ) >> 16) & 0xffff; + } + else if (list_length(op->args) == 1) + { + arg1 = linitial(op->args); + typmod1 = resolve_numeric_typmod_from_exp(arg1); + scale1 = (typmod1 - VARHDRSZ) & 0xffff; + precision1 = ((typmod1 - VARHDRSZ) >> 16) & 0xffff; + scale2 = 0; + precision2 = 0; + } + else + { + /* Shoudn't get here, just need this code to suppress the compiler warnings */ + precision1 = tds_default_numeric_precision; + precision2 = tds_default_numeric_precision; + scale1 = tds_default_numeric_scale; + scale2 = tds_default_numeric_scale; + } + + /* + * BABEL-2048 Handling arithmetic overflow exception + * when one of the operands is of NON-numeric datatype. + * Use tds_default_numeric_precision/scale if both operands + * are without typmod which probabaly won't happen. + * If one of the operand doesn't have typmod, apply + * the same typmod as the other operand. This makes sense + * because it's equivalent to casting the operand without + * typmod to the other operand's type and typmod then + * do the operation. + */ + if (typmod1 == -1 && typmod2 == -1) + { + precision = tds_default_numeric_precision; + scale = tds_default_numeric_scale; + return ((precision << 16) | scale) + VARHDRSZ; + } + else if (typmod1 == -1) + { + precision1 = precision2; + scale1 = scale2; + } + else if (typmod2 == -1) + { + precision2 = precision1; + scale2 = scale1; + } + + /* + * Refer to details of precision and scale calculation in the following link: + * https://github.com/MicrosoftDocs/sql-docs/blob/live/docs/t-sql/data-types/precision-scale-and-length-transact-sql.md + */ + switch (op->opfuncid) + { + case NUMERIC_ADD_OID: + case NUMERIC_SUB_OID: + scale = Max(scale1, scale2); + precision = Max(precision1 - scale1, precision2 - scale2) + 1 + scale; + break; + case NUMERIC_MUL_OID: + scale = scale1 + scale2; + precision = precision1 + precision2 + 1; + break; + case NUMERIC_DIV_OID: + scale = Max(6, scale1 + precision2 + 1); + precision = precision1 - scale1 + scale2 + scale; + break; + case NUMERIC_MOD_OID: + case NUMERIC_MOD_OID2: + scale = Max(scale1, scale2); + precision = Min(precision1-scale1, precision2 -scale2) + scale; + break; + case NUMERIC_UPLUS_OID: + case NUMERIC_UMINUS_OID: + scale = scale1; + precision = precision1; + break; + default: + return -1; + } + + /* handle precision overflow, use max allowed precision:38 */ + if (precision > TDS_MAX_NUM_PRECISION) + { + int delta = precision - TDS_MAX_NUM_PRECISION; + precision = TDS_MAX_NUM_PRECISION; + scale = Max(scale - delta, 0); + } + return ((precision << 16) | scale) + VARHDRSZ; + } + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) expr; + Oid func_oid = InvalidOid; + int rettypmod = -1; + + /* Function argument list can be empty */ + if (func->args == NIL) + return -1; + + /* + * Look up the return type typmod from a persistent + * store using the function oid. + */ + func_oid = func->funcid; + Assert(func_oid != InvalidOid); + + if(func->funcresulttype != VOIDOID) + rettypmod = pltsql_plugin_handler_ptr->pltsql_read_numeric_typmod(func_oid, + func->args->length, func->funcresulttype); + return rettypmod; + } + case T_NullIfExpr: + { + /* + * Nullif returns a null value if the two specified expressions are equal, + * Otherwise it returns the first argument. + */ + NullIfExpr *nullif = (NullIfExpr *) expr; + Node *arg1; + + Assert(nullif->args != NIL); + + arg1 = linitial(nullif->args); + return resolve_numeric_typmod_from_exp(arg1); + } + case T_CoalesceExpr: + { + /* Find max possible precision and scale in a CoalesceExpr */ + CoalesceExpr *coale = (CoalesceExpr *) expr; + ListCell *lc; + Node *arg; + int32 arg_typmod; + uint8_t precision, max_precision = 0, scale, max_scale = 0; + + Assert(coale->args != NIL); + + /* Loop through the list of Coalesce arguments */ + foreach(lc, coale->args) + { + arg = lfirst(lc); + arg_typmod = resolve_numeric_typmod_from_exp(arg); + /* return -1 if we fail to resolve one of the arg's typmod */ + if (arg_typmod == -1) + return -1; + /* skip the const NULL, which should have 0 returned as typmod */ + if (arg_typmod == 0) + continue; + scale = (arg_typmod - VARHDRSZ) & 0xffff; + precision = ((arg_typmod - VARHDRSZ) >> 16) & 0xffff; + max_scale = Max(scale, max_scale); + max_precision = Max(precision, max_precision); + } + return ((max_precision << 16) | max_scale) + VARHDRSZ; + } + case T_CaseExpr: + { + /* Find max possible precision and scale in a CaseExpr */ + CaseExpr *case_expr = (CaseExpr *) expr; + ListCell *lc; + CaseWhen *casewhen; + Node *casewhen_result; + int32 typmod; + uint8_t precision, max_precision = 0, scale, max_scale = 0; + + Assert(case_expr->args != NIL); + + /* Loop through the list of WHEN clauses */ + foreach(lc, case_expr->args) + { + casewhen = lfirst(lc); + casewhen_result = (Node *) casewhen->result; + typmod = resolve_numeric_typmod_from_exp(casewhen_result); + /* return -1 if we fail to resolve one of the result's typmod */ + if (typmod == -1) + return -1; + /* skip the const NULL, which should have 0 returned as typmod */ + if (typmod == 0) + continue; + scale = (typmod - VARHDRSZ) & 0xffff; + precision = ((typmod - VARHDRSZ) >> 16) & 0xffff; + max_scale = Max(scale, max_scale); + max_precision = Max(precision, max_precision); + } + return ((max_precision << 16) | max_scale) + VARHDRSZ; + } + case T_Aggref: + { + /* select max(a) from t; max(a) is an Aggref */ + Aggref *aggref = (Aggref *) expr; + TargetEntry *te; + + Assert(aggref->args != NIL); + + te = (TargetEntry *) linitial(aggref->args); + return resolve_numeric_typmod_from_exp((Node *) te->expr); + } + case T_PlaceHolderVar: + { + PlaceHolderVar *phv = (PlaceHolderVar *) expr; + + return resolve_numeric_typmod_from_exp((Node *) phv->phexpr); + } + /* TODO handle more Expr types if needed */ + default: + return -1; + } +} + +void +InitTDSResponse(void) +{ + tds_estate = palloc(sizeof(TdsExecutionStateData)); + tds_estate->current_stack = 0; + tds_estate->error_stack_offset = 0; + tds_estate->cur_error_number = -1; + tds_estate->cur_error_severity = -1; + tds_estate->cur_error_state = -1; +} + +void +TdsResponseReset(void) +{ + tds_estate = NULL; +} + +/* + * MakeEmptyParameterToken - prepare an empty parameter token + * + * In this function, we prepare a parameter token corresponding to the + * caller provided pg_type.oid and corresponding atttypmod and attcollation. + * Additionally, we assign NULL as value to the parameter. + */ +ParameterToken +MakeEmptyParameterToken(char *name, int atttypid, int32 atttypmod, int attcollation) +{ + ParameterToken temp = palloc0(sizeof(ParameterTokenData)); + TdsIoFunctionInfo finfo; + TdsColumnMetaData *col; + Oid serverCollationOid; + uint32_t tdsVersion = GetClientTDSVersion(); + + coll_info_t cinfo = (pltsql_plugin_handler_ptr)->lookup_collation_table_callback(InvalidOid); + serverCollationOid = cinfo.oid; + if (unlikely(serverCollationOid == InvalidOid)) + elog(FATAL, "Oid of default collation is not valid, This might mean that value of server_collation_name GUC is invalid"); + + initStringInfo(&(temp->paramMeta.colName)); + appendStringInfo(&(temp->paramMeta.colName), "%s", name); + + col = &(temp->paramMeta); + finfo = TdsLookupTypeFunctionsByOid(atttypid, &atttypmod); + SetParamMetadataCommonInfo(col, finfo); + + temp->paramOrdinal = -1; + temp->len = -1; + temp->maxLen = -1; + temp->isNull = true; + + switch (finfo->sendFuncId) + { + /* TODO boolean is equivalent to TSQL BIT type */ + case TDS_SEND_BIT: + SetColMetadataForFixedType(col, TDS_TYPE_BIT, 1); + temp->maxLen = 1; + break; + case TDS_SEND_TINYINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 1); + temp->maxLen = 1; + break; + case TDS_SEND_SMALLINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 2); + temp->maxLen = 2; + break; + case TDS_SEND_INTEGER: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 4); + temp->maxLen = 4; + break; + case TDS_SEND_BIGINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 8); + temp->maxLen = 8; + break; + case TDS_SEND_FLOAT4: + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, 4); + temp->maxLen = 4; + break; + case TDS_SEND_FLOAT8: + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, 8); + temp->maxLen = 8; + break; + case TDS_SEND_CHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_CHAR, + attcollation, (atttypmod - 4)); + /* if attypmod is -1, consider the datatype as CHAR(MAX) */ + if (atttypmod == -1) + temp->maxLen = 0xFFFF; + break; + case TDS_SEND_NCHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NCHAR, + attcollation, (atttypmod-4) * 2); + /* if attypmod is -1, consider the datatype as NCHAR(MAX) */ + if (atttypmod == -1) + temp->maxLen = 0xFFFF; + break; + case TDS_SEND_VARCHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_VARCHAR, + attcollation, (atttypmod == -1) ? + atttypmod : (atttypmod - 4)); + /* if attypmod is -1, consider the datatype as VARCHAR(MAX) */ + if (atttypmod == -1) + temp->maxLen = 0xFFFF; + break; + case TDS_SEND_NVARCHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, + attcollation, (atttypmod == -1) ? + atttypmod : (atttypmod - 4) * 2); + /* if attypmod is -1, consider the datatype as NVARCHAR(MAX) */ + if (atttypmod == -1) + temp->maxLen = 0xFFFF; + break; + case TDS_SEND_MONEY: + SetColMetadataForFixedType(col, TDS_TYPE_MONEYN, 8); + temp->maxLen = 8; + break; + case TDS_SEND_SMALLMONEY: + SetColMetadataForFixedType(col, TDS_TYPE_MONEYN, 4); + temp->maxLen = 4; + break; + case TDS_SEND_TEXT: + SetColMetadataForTextTypeHelper(col, TDS_TYPE_TEXT, + attcollation, (atttypmod - 4)); + break; + case TDS_SEND_NTEXT: + SetColMetadataForTextTypeHelper(col, TDS_TYPE_NTEXT, + attcollation, (atttypmod - 4) * 2); + break; + case TDS_SEND_DATE: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATE as NVARCHAR. + * Max len here would be 20 ('YYYY-MM-DD'). + * and Making use of default collation Oid. + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 20); + else + { + SetColMetadataForDateType(col, TDS_TYPE_DATE); + temp->maxLen = 3; + } + break; + case TDS_SEND_DATETIME: + SetColMetadataForFixedType(col, TDS_TYPE_DATETIMEN, 8); + temp->maxLen = 8; + break; + case TDS_SEND_NUMERIC: + { + uint8_t precision = 18, scale = 0; + + /* + * Get the precision and scale out of the typmod value if typmod is valid + * Otherwise tds_default_numeric_precision/scale will be used. + */ + if (atttypmod >= VARHDRSZ) + { + scale = (atttypmod - VARHDRSZ) & 0xffff; + precision = ((atttypmod - VARHDRSZ) >> 16) & 0xffff; + } + else + { + precision = tds_default_numeric_precision; + scale = tds_default_numeric_scale; + } + SetColMetadataForNumericType(col, TDS_TYPE_NUMERICN, 17, precision, scale); + temp->maxLen = 17; + } + break; + case TDS_SEND_SMALLDATETIME: + SetColMetadataForFixedType(col, TDS_TYPE_DATETIMEN, 4); + temp->maxLen = 4; + break; + case TDS_SEND_IMAGE: + SetColMetadataForImageType(col, TDS_TYPE_IMAGE); + break; + case TDS_SEND_BINARY: + SetColMetadataForBinaryType(col, TDS_TYPE_BINARY, atttypmod - 4); + /* if attypmod is -1, consider the datatype as BINARY(MAX) */ + if (atttypmod == -1) + temp->maxLen = 0xFFFF; + break; + case TDS_SEND_VARBINARY: + /* Generate the typmod from hex const input because typmod won't be specified */ + SetColMetadataForBinaryType(col, TDS_TYPE_VARBINARY, (atttypmod == -1) ? + atttypmod : atttypmod - VARHDRSZ); + /* if attypmod is -1, consider the datatype as VARBINARY(MAX) */ + if (atttypmod == -1) + temp->maxLen = 0xFFFF; + break; + case TDS_SEND_UNIQUEIDENTIFIER: + SetColMetadataForFixedType(col, TDS_TYPE_UNIQUEIDENTIFIER, 16); + temp->maxLen = 16; + break; + case TDS_SEND_TIME: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats TIME as NVARCHAR. + * Max len here would be 32 ('hh:mm:ss[.nnnnnnn]'). + * and Making use of default collation Oid. + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 32); + else + { + /* + * if time data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (atttypmod == -1) + atttypmod = 6; + SetColMetadataForTimeType(col, TDS_TYPE_TIME, atttypmod); + temp->maxLen = 5; + } + break; + case TDS_SEND_DATETIME2: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATETIME2 as NVARCHAR. + * Max len here would be 54('YYYY-MM-DD hh:mm:ss[.nnnnnnn]'). + * and Making use of default collation Oid. + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 54); + else + { + /* + * if Datetime2 data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (atttypmod == -1) + atttypmod = 6; + SetColMetadataForTimeType(col, TDS_TYPE_DATETIME2, atttypmod); + temp->maxLen = 8; + } + break; + case TDS_SEND_XML: + if (tdsVersion > TDS_VERSION_7_1_1) + SetColMetadataForFixedType(col, TDS_TYPE_XML, 0); + else + /* + * If client being connected is using TDS version lower than or equal to 7.1 + * then TSQL treats XML as NText. + */ + SetColMetadataForTextTypeHelper(col, TDS_TYPE_NTEXT, + attcollation, (atttypmod - 4) * 2); + break; + case TDS_SEND_SQLVARIANT: + SetColMetadataForImageType(col, TDS_TYPE_SQLVARIANT); + break; + case TDS_SEND_DATETIMEOFFSET: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATETIMEOFFSET as NVARCHAR. + * Max len here would be 64('YYYY-MM-DD hh:mm:ss[.nnnnnnn] [+|-]hh:mm'). + * and Making use of default collation Oid. + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 64); + else + { + if (atttypmod == -1) + atttypmod = 6; + SetColMetadataForTimeType(col, TDS_TYPE_DATETIMEOFFSET, atttypmod); + temp->maxLen = 10; + } + break; + default: + /* + * TODO: Need to create a mapping table for user defined + * data types and handle it here. + */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("data type %d not supported as out parameter", atttypid))); + } + + temp->type = col->metaEntry.type1.tdsTypeId; + + return temp; +} + +/* + * SendColumnMetadataToken - send the COLMETADATA token + * + * natts - number of attributes + * sendRowStat - true if we need to send an additional ROWSTAT column, false + * otherwise. + * NB: The ROWSTAT columns (row status indicator) are sent as hidden columns, + * at the end of each row with the column name ROWSTAT and data type INT4. This + * ROWSTAT column has one of the values - FETCH_SUCCEEDED or FETCH_MISSING. + * Because the TDS protocol provides no way to send the trailing status column + * without sending the previous columns, dummy data is sent for missing rows + * (nullable fields set to null, fixed length fields set to 0, blank, or the + * default for that column, as appropriate). + */ +void +SendColumnMetadataToken(int natts, bool sendRowStat) +{ + StringInfoData tempBuf; + int attno; + uint32_t tdsVersion = GetClientTDSVersion(); + + /* Now send out the COLMETADATA token */ + TDS_DEBUG(TDS_DEBUG2, "SendColumnMetadataToken: token=0x%02x", TDS_TOKEN_COLMETADATA); + TdsPutInt8(TDS_TOKEN_COLMETADATA); + TdsPutInt16LE(sendRowStat ? natts + 1 : natts); + + initStringInfo(&tempBuf); + + for (attno = 0; attno < natts; attno++) + { + uint8 temp8; + TdsColumnMetaData *col = &colMetaData[attno]; + + /* + * Instead of hardcoding the userType to 0 at various strucutures inside col->metaEntry + * we can write 0x0000 (for versions lower than TDS 7.2) or 0x00000000 (for TDS 7.2 and higher) + * directly on the wire depending version; + * + * TODO: TDS doc mentions some non-zero values for timestamp and aliases + * NOTE: We have always sent UserType as 0 and clients have never complained about it. + */ + if (tdsVersion <= TDS_VERSION_7_1_1) + TdsPutInt16LE(0); + else + TdsPutInt32LE(0); + /* column meta */ + TdsPutbytes(&(col->metaEntry), col->metaLen); + + if (col->sendTableName) + { + uint8 numParts; + + if (col->relinfo != NULL) + { + numParts = 2; + resetStringInfo(&tempBuf); + /* + * In column Metatdata Token -- NumParts, a multi-part table name, + * was intoduced in TDS 7.2 + */ + if (tdsVersion > TDS_VERSION_7_1_1) + FillTabNameWithNumParts(&tempBuf, numParts, col->relinfo); + else + FillTabNameWithoutNumParts(&tempBuf, numParts, col->relinfo); + TdsPutbytes(tempBuf.data, tempBuf.len); + } + else + { + /* Expression columns doesn't have table name */ + numParts = 1; + /* + * In column Metatdata Token -- NumParts, a multi-part table name, + * was introduced in TDS 7.2 + */ + if (tdsVersion > TDS_VERSION_7_1_1) + TdsPutbytes(&numParts, sizeof(numParts)); + TdsPutInt16LE(0); + } + } + + /* + * If it is an expression column, send "0" as the column len + * + * NOTE: Relaxing this condition to send "0" as the column len + * when column name is "?column?" (default column alias for + * columns with no name in engine) + * + * This is needed to send a column name for a column which is + * not part of a table but has an alias [BABEL-544] + * + */ + if (strcmp(col->colName.data, "?column?") == 0) + { + temp8 = 0; + TdsPutbytes(&temp8, sizeof(temp8)); + } + else + { + + /* column length and name */ + if (col->colName.len > 0) + temp8 = (uint8_t) pg_mbstrlen(col->colName.data); + else + temp8 = 0; + + resetStringInfo(&tempBuf); + TdsUTF8toUTF16StringInfo(&tempBuf, col->colName.data, + col->colName.len); + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(tempBuf.data, tempBuf.len); + } + } + + if (sendRowStat) + { + /* + * XXX: Since the column information for a ROWSTAT column is fixed, the + * value (except the userType) is hard-coded for now. Should this come from the engine? + * This is also sent for FOR BROWSE queries. + */ + char arr[] = { + 0x00, 0x00, 0x38, 0x07, 0x52, 0x00, 0x4f, 0x00, 0x57, + 0x00, 0x53, 0x00, 0x54, 0x00, 0x41, 0x00, 0x54, 0x00 + }; + + /* + * Instead of hardcoding the userType to 0 in the above array we can write + * 0x0000 (for versions lower than TDS 7.2) or 0x00000000 (for TDS 7.2 and higher) + * directly on the wire depending version; + */ + if (tdsVersion <= TDS_VERSION_7_1_1) + TdsPutInt16LE(0); + else + TdsPutInt32LE(0); + + TdsPutbytes(arr, sizeof(arr)); + } + + pfree(tempBuf.data); +} + +/* + * SendTabNameToken - send the TABNAME token + * + * It sends all the table names corresponding the columns included in the target + * list. For expression columns, we don't send any table name. + */ +void +SendTabNameToken(void) +{ + StringInfoData buf; + ListCell *lc; + uint32_t tdsVersion = GetClientTDSVersion(); + + if (relMetaDataInfoList == NIL) + return; + + initStringInfo(&buf); + + foreach(lc, relMetaDataInfoList) + { + TdsRelationMetaDataInfo relMetaDataInfo = (TdsRelationMetaDataInfo) lfirst(lc); + uint8 numParts = 2; + + /* + * In Table Name token -- NumParts, a multi-part table name, + * was intoduced in tds 7.1 revision 1. + */ + if (tdsVersion > TDS_VERSION_7_1) + FillTabNameWithNumParts(&buf, numParts, relMetaDataInfo); + else + FillTabNameWithoutNumParts(&buf, numParts, relMetaDataInfo); + } + + TDS_DEBUG(TDS_DEBUG2, "SendTabNameToken: token=0x%02x", TDS_TOKEN_TABNAME); + TdsPutInt8(TDS_TOKEN_TABNAME); + TdsPutInt16LE((uint16_t) buf.len); + TdsPutbytes(buf.data, buf.len); + pfree(buf.data); + + TDSInstrumentation(INSTR_TDS_TOKEN_TABNAME); +} + +/* + * SendColInfoToken - send the COLINFO token + * + * It sends some additional info for a column: + * colNum - column number in the result set + * tableNum - number of the base table that the column was derived from. The value + * is 0 for expression column. + * status - EXPRESSION (the column was the result of an expression) + * KEY (the column is part of a key for the associated table and result set) + * HIDDEN (the column was not requested, but was added because it was part + * of a key for the associated table) + * DIFFERENT_NAME (the column name is different than the requested column + * name in the case of a column alias) + * colName - The base column name. This only occurs if DIFFERENT_NAME is set in Status. + */ +void +SendColInfoToken(int natts, bool sendRowStat) +{ + StringInfoData buf; + StringInfoData tempBuf; + int attno; + + TDS_DEBUG(TDS_DEBUG2, "SendColInfoToken: token=0x%02x", TDS_TOKEN_COLINFO); + TdsPutInt8(TDS_TOKEN_COLINFO); + initStringInfo(&buf); + initStringInfo(&tempBuf); + + for (attno = 0; attno < natts; attno++) + { + TdsColumnMetaData *col = &colMetaData[attno]; + uint8 colNum, + tableNum, + status = 0; + uint8 temp8; + + colNum = attno + 1; + + if (col->relOid == 0) + { + status |= COLUMN_STATUS_EXPRESSION; + tableNum = 0; + } + else + { + status = 0; + tableNum = col->relinfo->tableNum; + + resetStringInfo(&tempBuf); + + if (strcmp(col->baseColName, col->colName.data) != 0) + status |= COLUMN_STATUS_DIFFERENT_NAME; + + { + int tempatt; + + for (tempatt = 0; tempatt < col->relinfo->numkeyattrs; tempatt++) + if (col->attrNum == col->relinfo->keyattrs[tempatt]) + status |= COLUMN_STATUS_KEY; + } + } + + /* column num, table num, status */ + appendBinaryStringInfo(&buf, (const char *)&colNum, sizeof(colNum)); + appendBinaryStringInfo(&buf, (const char *)&tableNum, sizeof(tableNum)); + appendBinaryStringInfo(&buf, (const char *)&status, sizeof(status)); + + if (status & COLUMN_STATUS_DIFFERENT_NAME) + { + Assert(col->baseColName != NULL); + temp8 = (uint8_t) pg_mbstrlen(col->baseColName); + appendBinaryStringInfo(&buf, (const char *)&temp8, sizeof(uint8)); + TdsUTF8toUTF16StringInfo(&buf, col->baseColName, strlen(col->baseColName)); + } + } + + if (sendRowStat) + { + uint8 colNum, + tableNum, + status = 0; + + colNum = natts + 1; + tableNum = 0; + status |= COLUMN_STATUS_EXPRESSION | COLUMN_STATUS_HIDDEN; + + /* column num, table num, status */ + appendBinaryStringInfo(&buf, (const char *)&colNum, sizeof(colNum)); + appendBinaryStringInfo(&buf, (const char *)&tableNum, sizeof(tableNum)); + appendBinaryStringInfo(&buf, (const char *)&status, sizeof(status)); + } + + TdsPutInt16LE((uint16_t) buf.len); + TdsPutbytes(buf.data, buf.len); + + pfree(buf.data); +} + +/* + * PrepareRowDescription - prepare the information needed to construct COLMETADATA + * token, TABNAME token and COLINFO token. + * + * extendedInfo - If false, it doesn't collect the additional information needed + * to construct the TABNAME token and COLINFO token. + * fetchPkeys - If true and extendedInfo is true, it fetches the primary keys + * for a relation. (used for keyset and dynamic cursors) + */ +void +PrepareRowDescription(TupleDesc typeinfo, List *targetlist, int16 *formats, + bool extendedInfo, bool fetchPkeys) +{ + int natts = typeinfo->natts; + int attno; + MemoryContext oldContext; + ListCell *tlist_item = list_head(targetlist); + bool sendTableName = false; + uint8_t precision = 18, scale = 0; + + relMetaDataInfoList = NIL; + + TdsErrorContext->err_text = "Preparing to Send Back the Tds response"; + + SendPendingDone(true); + + /* + * The colMetaData is also used in the TdsPrintTup() callback + * below so we place it into the memory context that will be + * reset once per TCOP main loop iteration. + */ + oldContext = MemoryContextSwitchTo(MessageContext); + colMetaData = palloc0(sizeof(TdsColumnMetaData) * natts); + + /* + * We collect all the information first so that we don't have + * to abort half way through the COLMETADATA tag in case of + * an error (like unsupported data type). + */ + for (attno = 0; attno < natts; attno++) + { + Oid serverCollationOid; + TdsIoFunctionInfo finfo; + Form_pg_attribute att = TupleDescAttr(typeinfo, attno); + Oid atttypid = att->atttypid; + int32 atttypmod = att->atttypmod; + TdsColumnMetaData *col = &colMetaData[attno]; + uint32_t tdsVersion = GetClientTDSVersion(); + TargetEntry *tle = NULL; + coll_info_t cinfo = (pltsql_plugin_handler_ptr)->lookup_collation_table_callback(InvalidOid); + + serverCollationOid = cinfo.oid; + if (unlikely(serverCollationOid == InvalidOid)) + elog(FATAL, "Oid of default collation is not valid, This might mean that value of server_collation_name GUC is invalid"); + + /* + * Get the IO function info from our type cache + */ + finfo = TdsLookupTypeFunctionsByOid(atttypid, &atttypmod); + // atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod); +#if 0 + { + /* Test a reverse lookup */ + TdsIoFunctionInfo finfo2; + int32_t typeid = finfo->ttmtdstypeid; + int32_t typelen = finfo->ttmtdstypelen; + + elog(LOG, "found finfo for Oid %d: tdstype=%d tdstyplen=%d", + atttypid, typeid, typelen); + if (!att->attbyval) + typelen = 80; + finfo2 = TdsLookupTypeFunctionsByTdsId(typeid, typelen); + elog(LOG, "found reverse finfo for type %d,%d: Oid=%d", + typeid, typelen, finfo2->ttmtypeid); + } +#endif + + /* + * Fill in column info that is common to all data types + */ + SetParamMetadataCommonInfo(col, finfo); + initStringInfo(&(col->colName)); + appendStringInfoString(&col->colName, NameStr(att->attname)); + + /* Do we have a non-resjunk tlist item? */ + while (tlist_item && + ((TargetEntry *) lfirst(tlist_item))->resjunk) + tlist_item = lnext(targetlist, tlist_item); + if (tlist_item) + { + tle = (TargetEntry *) lfirst(tlist_item); + + col->relOid = tle->resorigtbl; + col->attrNum = tle->resorigcol; + + tlist_item = lnext(targetlist, tlist_item); + } + else + { + /* No info available, so send zeroes */ + col->relOid = 0; + col->attrNum = 0; + } + + switch (finfo->sendFuncId) + { + /* TODO PG boolean is equivalent to TSQL BIT type */ + case TDS_SEND_BIT: + SetColMetadataForFixedType(col, TDS_TYPE_BIT, 1); + break; + case TDS_SEND_TINYINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 1); + break; + case TDS_SEND_SMALLINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 2); + break; + case TDS_SEND_INTEGER: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 4); + break; + case TDS_SEND_BIGINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, 8); + break; + case TDS_SEND_FLOAT4: + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, 4); + break; + case TDS_SEND_FLOAT8: + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, 8); + break; + case TDS_SEND_CHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_CHAR, + att->attcollation, (atttypmod - 4)); + break; + case TDS_SEND_NCHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NCHAR, + att->attcollation, (atttypmod-4) * 2); + break; + case TDS_SEND_VARCHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_VARCHAR, + att->attcollation, (atttypmod == -1) ? + atttypmod : (atttypmod - 4)); + break; + case TDS_SEND_NVARCHAR: + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, + att->attcollation, (atttypmod == -1) ? + atttypmod : (atttypmod - 4) * 2); + break; + case TDS_SEND_MONEY: + SetColMetadataForFixedType(col, TDS_TYPE_MONEYN, 8); + break; + case TDS_SEND_SMALLMONEY: + SetColMetadataForFixedType(col, TDS_TYPE_MONEYN, 4); + break; + case TDS_SEND_TEXT: + SetColMetadataForTextTypeHelper(col, TDS_TYPE_TEXT, + att->attcollation, (atttypmod - 4)); + sendTableName |= col->sendTableName; + break; + case TDS_SEND_NTEXT: + SetColMetadataForTextTypeHelper(col, TDS_TYPE_NTEXT, + att->attcollation, (atttypmod - 4) * 2); + sendTableName |= col->sendTableName; + break; + case TDS_SEND_DATE: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATE as NVARCHAR. + * Max len here would be 20 ('YYYY-MM-DD'). + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 20); + else + SetColMetadataForDateType(col, TDS_TYPE_DATE); + break; + case TDS_SEND_DATETIME: + SetColMetadataForFixedType(col, TDS_TYPE_DATETIMEN, 8); + break; + case TDS_SEND_NUMERIC: + { + /* + * Try to resolve the typmod from tle->expr when typmod is not specified + * TDS client requires a valid typmod other than -1. + */ + if (atttypmod == -1 && tle != NULL) + atttypmod = resolve_numeric_typmod_from_exp((Node *) tle->expr); + + /* + * Get the precision and scale out of the typmod value if typmod is valid + * Otherwise tds_default_numeric_precision/scale will be used. + */ + if (atttypmod >= VARHDRSZ) + { + scale = (atttypmod - VARHDRSZ) & 0xffff; + precision = ((atttypmod - VARHDRSZ) >> 16) & 0xffff; + } + else + { + precision = tds_default_numeric_precision; + scale = tds_default_numeric_scale; + } + SetColMetadataForNumericType(col, TDS_TYPE_NUMERICN, 17, precision, scale); + } + break; + case TDS_SEND_SMALLDATETIME: + SetColMetadataForFixedType(col, TDS_TYPE_DATETIMEN, 4); + break; + case TDS_SEND_IMAGE: + SetColMetadataForImageType(col, TDS_TYPE_IMAGE); + sendTableName |= col->sendTableName; + break; + case TDS_SEND_BINARY: + SetColMetadataForBinaryType(col, TDS_TYPE_BINARY, atttypmod - 4); + break; + case TDS_SEND_VARBINARY: + /* Generate the typmod from hex const input because typmod won't be specified */ + if (atttypmod == -1 && tle != NULL && IsA(tle->expr, Const)) + { + Const *con= (Const *) tle->expr; + if (!con->constisnull) + { + bytea *source = (bytea *) con->constvalue; + atttypmod = VARSIZE_ANY(source); + } + } + SetColMetadataForBinaryType(col, TDS_TYPE_VARBINARY, (atttypmod == -1) ? + atttypmod : atttypmod - VARHDRSZ); + break; + case TDS_SEND_UNIQUEIDENTIFIER: + SetColMetadataForFixedType(col, TDS_TYPE_UNIQUEIDENTIFIER, 16); + break; + case TDS_SEND_TIME: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats TIME as NVARCHAR. + * Max len here would be 32 ('hh:mm:ss[.nnnnnnn]'). + * and Making use of default collation Oid. + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 32); + else + { + /* + * if time data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (atttypmod == -1) + atttypmod = 6; + SetColMetadataForTimeType(col, TDS_TYPE_TIME, atttypmod); + } + break; + case TDS_SEND_DATETIME2: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATETIME2 as NVARCHAR. + * Max len here would be 54('YYYY-MM-DD hh:mm:ss[.nnnnnnn]'). + * and Making use of default collation Oid. + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 54); + else + { + /* + * if Datetime2 data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (atttypmod == -1) + atttypmod = 6; + SetColMetadataForTimeType(col, TDS_TYPE_DATETIME2, atttypmod); + } + break; + case TDS_SEND_XML: + if (tdsVersion > TDS_VERSION_7_1_1) + SetColMetadataForFixedType(col, TDS_TYPE_XML, 0); + else + { + /* + * If client being connected is using TDS version lower than or equal to 7.1 + * then TSQL treats XML as NText. + */ + SetColMetadataForTextTypeHelper(col, TDS_TYPE_NTEXT, + att->attcollation, (atttypmod - 4) * 2); + sendTableName |= col->sendTableName; + } + break; + case TDS_SEND_SQLVARIANT: + SetColMetadataForImageType(col, TDS_TYPE_SQLVARIANT); + break; + case TDS_SEND_DATETIMEOFFSET: + if (tdsVersion < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATETIMEOFFSET as NVARCHAR. + * Max len here would be 64('YYYY-MM-DD hh:mm:ss[.nnnnnnn] [+|-]hh:mm'). + * and Making use of default collation Oid. + */ + SetColMetadataForCharTypeHelper(col, TDS_TYPE_NVARCHAR, serverCollationOid, 64); + else + { + if (atttypmod == -1) + atttypmod = 6; + SetColMetadataForTimeType(col, TDS_TYPE_DATETIMEOFFSET, atttypmod); + } + break; + default: + /* + * TODO: Need to create a mapping table for user defined + * data types and handle it here. + */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("data type %d not supported yet", atttypid))); + } + } + + MemoryContextSwitchTo(oldContext); + + if (extendedInfo || sendTableName) + { + uint8 tableNum = 0; + + oldContext = MemoryContextSwitchTo(MessageContext); + relMetaDataInfoList = NULL; + + for (attno = 0; attno < natts; attno++) + { + TdsColumnMetaData *col = &colMetaData[attno]; + ListCell *lc; + bool found = false; + + if (col->relOid == 0) + { + col->baseColName = NULL; + col->relinfo = NULL; + continue; + } + + /* fetch the actual column name */ + col->baseColName = get_attname(col->relOid, col->attrNum, false); + + /* look for a relation entry match */ + foreach(lc, relMetaDataInfoList) + { + TdsRelationMetaDataInfo relMetaDataInfo = (TdsRelationMetaDataInfo) lfirst(lc); + Oid relOid = relMetaDataInfo->relOid; + + if (relOid == col->relOid) + { + found = true; + col->relinfo = relMetaDataInfo; + break; + } + } + + /* if not found, add one */ + if (!found) + { + Relation rel; + TdsRelationMetaDataInfo relMetaDataInfo; + + relMetaDataInfo = (TdsRelationMetaDataInfo) palloc(sizeof(TdsRelationMetaDataInfoData)); + tableNum++; + + relMetaDataInfo->relOid = col->relOid; + relMetaDataInfo->tableNum = tableNum; + + rel = relation_open(col->relOid, AccessShareLock); + + /* fetch the primary key attributes if needed */ + if (fetchPkeys) + relMetaDataInfo->keyattrs = getPkeyAttnames(rel, &relMetaDataInfo->numkeyattrs); + else + { + relMetaDataInfo->keyattrs = NULL; + relMetaDataInfo->numkeyattrs = 0; + } + + /* fetch the relation name, schema name */ + relMetaDataInfo->partName[0] = RelationGetRelationName(rel); + relMetaDataInfo->partName[1] = get_namespace_name(RelationGetNamespace(rel)); + + relation_close(rel, AccessShareLock); + + relMetaDataInfoList = lappend(relMetaDataInfoList, relMetaDataInfo); + col->relinfo = relMetaDataInfo; + } + } + + MemoryContextSwitchTo(oldContext); + } +} + +/* + * SendReturnValueTokenInternal + * + * status - stored procedure (0x01) or UDF (0x02) + * forceCoercion - true if it needs datatype coercion before sending the data + */ +void +SendReturnValueTokenInternal(ParameterToken token, uint8 status, + FmgrInfo *finfo, Datum datum, bool isNull, + bool forceCoercion) +{ + uint8 temp8; + uint16 temp16; + StringInfo name; + FmgrInfo temp; + uint32_t tdsVersion = GetClientTDSVersion(); + + SendPendingDone(true); + + /* token type */ + TDS_DEBUG(TDS_DEBUG2, "SendReturnValueTokenInternal: token=0x%02x", TDS_TOKEN_RETURNVALUE); + temp8 = TDS_TOKEN_RETURNVALUE; + TdsPutbytes(&temp8, sizeof(temp8)); + + /* param ordinal */ + if (tdsVersion <= TDS_VERSION_7_1_1) + /* + * "BY OBSERVATION" The param ordinal is set to 13 instead of starting from 0 + * for clients with TDS verstion lower than or equal to TDS 7.1 revision 1; + * + * This isn't mentioned in any of the documentations and making this change is necessary + * Since without this change we get TDS Protocol error from the Driver for RPCs. + */ + temp16 = 13; /* TODO: why 13? */ + else + temp16 = token->paramOrdinal; + TdsPutbytes(&temp16, sizeof(temp16)); + + /* param name len and param name ((here column name is in UTF-8 format) */ + name = &(token->paramMeta.colName); + if (name->len > 0) + { + StringInfoData tempName; + + initStringInfo(&tempName); + TdsUTF8toUTF16StringInfo(&tempName, name->data, name->len); + + temp8 = (uint8_t) pg_mbstrlen(name->data); + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(tempName.data, tempName.len); + + pfree(tempName.data); + } + else + { + temp8 = name->len; + TdsPutbytes(&temp8, sizeof(temp8)); + } + + /* status */ + TdsPutbytes(&status, sizeof(status)); + + /* + * Instead of hardcoding the userType to 0 at various strucutures inside col->metaEntry + * we can write 0x0000 (for versions lower than TDS 7.2) or 0x00000000 (for TDS 7.2 and higher) + * directly on the wire depending version; + * + * TODO: TDS doc mentions some non-zero values for timestamp and aliases + * NOTE: We have always sent UserType as 0 and clients have never complained about it. + */ + if (tdsVersion <= TDS_VERSION_7_1_1) + TdsPutInt16LE(0); + else + TdsPutInt32LE(0); + + /* meta entries */ + TdsPutbytes(&(token->paramMeta.metaEntry), token->paramMeta.metaLen); + + if (isNull) + { + switch (token->paramMeta.metaEntry.type1.tdsTypeId) + { + case TDS_TYPE_TEXT: + case TDS_TYPE_NTEXT: + case TDS_TYPE_IMAGE: + /* + * MS-TDS doc, section 2.2.4.2.1.3 - Null is represented by a + * length of -1 (0xFFFFFFFF). + */ + TdsPutUInt32LE(0xFFFFFFFF); + break; + case TDS_TYPE_CHAR: + case TDS_TYPE_NCHAR: + case TDS_TYPE_VARCHAR: + case TDS_TYPE_NVARCHAR: + case TDS_TYPE_BINARY: + case TDS_TYPE_VARBINARY: + case TDS_TYPE_XML: + /* + * MS-TDS doc, section 2.2.4.2.1.3 - Null is represented by a + * length of -1 (0xFFFF). + */ + if (token->maxLen == 0xFFFF) + TdsPutUInt64LE(PLP_NULL); + else + TdsPutUInt16LE(0xFFFF); + break; + default: + /* + * MS-TDS doc, section 2.2.4.2.1.2 - Null is represented by a + * length of 0. (Fixed length datatypes) + */ + temp16 = 0; + TdsPutbytes(&temp16, token->paramMeta.sizeLen); + break; + } + + /* we're done */ + return; + } + else if (forceCoercion) + { + int32 result = -1; + Oid castFuncOid = InvalidOid; + CoercionPathType pathtype; + + /* + * In TDS, we should send the OUT parameters with the length/scale/precision + * specified by the caller. In that case, we may need to do a self-casting. + * Here are the steps: + * 1. Find the self-cast function if it's available. + * 2. Call the typmodin function that returns the attypmod corresponding + * to the caller provided length/scale/precision. + * 3. Call the self-cast function to cast the datum with the above attypmod. + */ + + /* Check if the type has a function for length/scale/precision coercion */ + pathtype = find_typmod_coercion_function(token->paramMeta.pgTypeOid, &castFuncOid); + + /* + * If we found a function to perform the coercion, do it. We don't support + * other types of coearcion, so just ignore it. + */ + if (pathtype == COERCION_PATH_FUNC) + result = GetTypModForToken(token); + + /* If we found a valid attypmod, perform the casting. */ + if (result != -1) + { + datum = OidFunctionCall3(castFuncOid, datum, + Int32GetDatum(result), + BoolGetDatum(true)); + } + } + + /* should in a transaction, because we'll do a catalog lookup */ + if (!finfo && IsTransactionState()) + { + Oid typoutputfunc; + bool typIsVarlena; + Assert(token->paramMeta.pgTypeOid != InvalidOid); + getTypeOutputInfo(token->paramMeta.pgTypeOid, &typoutputfunc, &typIsVarlena); + fmgr_info(typoutputfunc, &temp); + finfo = &temp; + } + + /* send the data */ + (token->paramMeta.sendFunc)(finfo, datum, (void *) &token->paramMeta); +} + +int +GetTypModForToken(ParameterToken token) +{ + int32 typmod = -1; + Datum *datums = NULL; + ArrayType *arrtypmod = NULL; + char *cstr = NULL; + int n; + Oid pgtypemodin; + + /* + * Forcing coercion needs catalog access. Hence, we should be in a + * transaction. + */ + Assert(IsTransactionState()); + + /* + * Prepare the argument for calling the typmodin function. We need + * to pass the argument as an array. Each type will have different + * number of elements in the array. + */ + n = 0; + switch (token->paramMeta.metaEntry.type1.tdsTypeId) + { + case TDS_TYPE_CHAR: + case TDS_TYPE_VARCHAR: + /* We don't have to perform any length coercion for PLP data */ + if (token->maxLen == 0xFFFF) + break; + + /* it only consists of the maxlen */ + datums = (Datum *) palloc(1 * sizeof(Datum)); + + cstr = psprintf("%ld", (long) token->paramMeta.metaEntry.type2.maxSize); + datums[n++] = CStringGetDatum(cstr); + break; + case TDS_TYPE_NCHAR: + case TDS_TYPE_NVARCHAR: + /* We don't have to perform any length coercion for PLP data */ + if (token->maxLen == 0xFFFF) + break; + + /* it only consists of the maxlen */ + datums = (Datum *) palloc(1 * sizeof(Datum)); + + cstr = psprintf("%ld", (long) token->paramMeta.metaEntry.type2.maxSize / 2); + datums[n++] = CStringGetDatum(cstr); + break; + case TDS_TYPE_DECIMALN: + case TDS_TYPE_NUMERICN: + /* it consists of scale and precision */ + datums = (Datum *) palloc(2 * sizeof(Datum)); + + cstr = psprintf("%ld", (long) token->paramMeta.metaEntry.type5.precision); + datums[n++] = CStringGetDatum(cstr); + cstr = psprintf("%ld", (long) token->paramMeta.metaEntry.type5.scale); + datums[n++] = CStringGetDatum(cstr); + break; + case TDS_TYPE_TIME: + case TDS_TYPE_DATETIME2: + /* it only consists of scale */ + datums = (Datum *) palloc(1 * sizeof(Datum)); + + cstr = psprintf("%ld", (long) token->paramMeta.metaEntry.type6.scale); + datums[n++] = CStringGetDatum(cstr); + break; + case TDS_TYPE_BINARY: + case TDS_TYPE_VARBINARY: + /* We don't have to perform any length coercion for PLP data */ + if (token->maxLen == 0xFFFF) + break; + + /* it only consists of the maxlen */ + datums = (Datum *) palloc(1 * sizeof(Datum)); + cstr = psprintf("%ld", (long) token->paramMeta.metaEntry.type7.maxSize); + datums[n++] = CStringGetDatum(cstr); + break; + case TDS_TYPE_IMAGE: + ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("Data type 0x22(Image) is a deprecated LOB.\ + Deprecated types are not supported as output parameters."))); + break; + default: + break; + } + + /* If we've prepared the argument, proceed. */ + if (datums) + { + /* hardwired knowledge about cstring's representation details here */ + arrtypmod = construct_array(datums, n, CSTRINGOID, + -2, false, 'c'); + + pgtypemodin = get_typmodin(token->paramMeta.pgTypeOid); + typmod = DatumGetInt32(OidFunctionCall1(pgtypemodin, + PointerGetDatum(arrtypmod))); + + /* be tidy */ + pfree(datums); + pfree(arrtypmod); + } + + return typmod; +} + +void +TdsSendEnvChange(int envid, const char *new_val, const char *old_val) +{ + StringInfoData newUtf16; + StringInfoData oldUtf16; + int16_t totalLen; + uint8 temp8; + + initStringInfo(&newUtf16); + initStringInfo(&oldUtf16); + + SendPendingDone(true); + + if (new_val) + TdsUTF8toUTF16StringInfo(&newUtf16, new_val, strlen(new_val)); + if (old_val) + TdsUTF8toUTF16StringInfo(&oldUtf16, old_val, strlen(old_val)); + totalLen = 1 /* envid */ + + 1 /* new len */ + + newUtf16.len + + 1 /* old len */ + + oldUtf16.len; + + TDS_DEBUG(TDS_DEBUG2, "TdsSendEnvChange: token=0x%02x", TDS_TOKEN_ENVCHANGE); + temp8 = TDS_TOKEN_ENVCHANGE; + TdsPutbytes(&temp8, sizeof(temp8)); + + TdsPutbytes(&totalLen, sizeof(totalLen)); + TdsPutbytes(&envid, sizeof(temp8)); + + if (new_val) + { + temp8 = strlen(new_val); + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(newUtf16.data, newUtf16.len); + } + else + { + temp8 = 0; + TdsPutbytes(&temp8, sizeof(temp8)); + } + + if (old_val) + { + temp8 = strlen(old_val); + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(oldUtf16.data, oldUtf16.len); + } + else + { + temp8 = 0; + TdsPutbytes(&temp8, sizeof(temp8)); + } + + pfree(newUtf16.data); + pfree(oldUtf16.data); +} + +void +TdsSendEnvChangeBinary(int envid, void *new, int newNbytes, + void *old, int oldNbytes) +{ + int16_t totalLen; + uint8 temp8; + + SendPendingDone(true); + + totalLen = 1 /* envid */ + + 1 /* new len */ + + newNbytes + + 1 /* old len */ + + oldNbytes; + + TDS_DEBUG(TDS_DEBUG2, "TdsSendEnvChangeBinary: token=0x%02x", TDS_TOKEN_ENVCHANGE); + temp8 = TDS_TOKEN_ENVCHANGE; + TdsPutbytes(&temp8, sizeof(temp8)); + + TdsPutbytes(&totalLen, sizeof(totalLen)); + temp8 = envid; + TdsPutbytes(&envid, sizeof(temp8)); + + temp8 = newNbytes; + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(new, newNbytes); + + temp8 = oldNbytes; + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(old, oldNbytes); +} + +void +TdsSendInfo(int number, int state, int class, + char *message, int lineNo) +{ + TdsSendInfoOrError(TDS_TOKEN_INFO, number, state, class, + message, + "BABELFISH", /* TODO: where to get this? */ + "", /* TODO: where to get this? */ + lineNo); +} + +void +TdsSendError(int number, int state, int class, + char *message, int lineNo) +{ + /* + * If not already in RESPONSE mode, switch the TDS protocol to RESPONSE + * mode. + */ + TdsSetMessageType(TDS_RESPONSE); + + TdsSendInfoOrError(TDS_TOKEN_ERROR, number, state, class, + message, + "BABELFISH", /* TODO: where to get this? */ + "", /* TODO: where to get this? */ + lineNo); + + markErrorFlag = true; +} + +void +TdsSendInfoOrError(int token, int number, int state, int class, + char *message, char *serverName, char *procName, + int lineNo) +{ + StringInfoData messageUtf16; + StringInfoData serverNameUtf16; + StringInfoData procNameUtf16; + int lineNoLen; + int messageLen = strlen(message); + int serverNameLen = strlen(serverName); + int procNameLen = strlen(procName); + int16_t messageLen_16 = pg_mbstrlen(message); + int32_t number_32 = (int32_t)number; + int32_t lineNo_32 = (int32_t)lineNo; + int16_t totalLen; + uint8 temp8; + uint32_t tdsVersion = GetClientTDSVersion(); + + /* + * For Client TDS Version less than or equal to 7.1 Line Number is of 2 bytes + * and for TDS versions higher than 7.1 it is of 4 bytes. + */ + if (tdsVersion <= TDS_VERSION_7_1_1) + lineNoLen = sizeof(int16_t); + else + lineNoLen = sizeof(int32_t); + + initStringInfo(&messageUtf16); + initStringInfo(&serverNameUtf16); + initStringInfo(&procNameUtf16); + + TdsUTF8toUTF16StringInfo(&messageUtf16, message, messageLen); + TdsUTF8toUTF16StringInfo(&serverNameUtf16, serverName, serverNameLen); + TdsUTF8toUTF16StringInfo(&procNameUtf16, procName, procNameLen); + + SendPendingDone(true); + + totalLen = sizeof(number_32) /* error number */ + + 1 /* state */ + + 1 /* class */ + + sizeof(messageLen_16) /* message len */ + + messageUtf16.len /* message */ + + 1 /* server_name_len */ + + serverNameUtf16.len /* server_name */ + + 1 /* proc_name_len */ + + procNameUtf16.len /* proc_name */ + + lineNoLen; /* line_no */ + + /* Send Info or Error Token. */ + TDS_DEBUG(TDS_DEBUG2, "TdsSendInfoOrError: token=0x%02x", token); + temp8 = token; + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(&totalLen, sizeof(totalLen)); + TdsPutbytes(&number_32, sizeof(number_32)); + + temp8 = state; + TdsPutbytes(&temp8, sizeof(temp8)); + + temp8 = class; + TdsPutbytes(&temp8, sizeof(temp8)); + + TdsPutbytes(&messageLen_16, sizeof(messageLen_16)); + TdsPutbytes(messageUtf16.data, messageUtf16.len); + + temp8 = serverNameLen; + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(serverNameUtf16.data, serverNameUtf16.len); + + temp8 = procNameLen; + TdsPutbytes(&temp8, sizeof(temp8)); + TdsPutbytes(procNameUtf16.data, procNameUtf16.len); + + if (tdsVersion <= TDS_VERSION_7_1_1) + { + int16_t lineNo_16; + if (lineNo > PG_INT16_MAX) + ereport(FATAL, (errmsg("Line Number execeeds INT16_MAX"))); + else + lineNo_16 = (int16_t) lineNo; + TdsPutbytes(&lineNo_16, lineNoLen); + } + else + TdsPutbytes(&lineNo_32, lineNoLen); + + pfree(messageUtf16.data); + pfree(serverNameUtf16.data); + pfree(procNameUtf16.data); +} + +void +TdsSendRowDescription(TupleDesc typeinfo, + List *targetlist, int16 *formats) +{ + TDSRequest request = TdsRequestCtrl->request; + + /* If we reach here, typeinfo should not be null. */ + Assert(typeinfo != NULL); + + /* Prepare the column metadata first */ + PrepareRowDescription(typeinfo, targetlist, formats, false, false); + + /* + * If fNoMetadata flags is set in RPC header flag, the server doesn't need + * to send the metadata again for COLMETADATA token. In that case the, the + * server sends only NoMetaData (0xFFFF) field in COLMETADATA token. + */ + if (request->reqType == TDS_REQUEST_SP_NUMBER) + { + TDSRequestSP req = (TDSRequestSP) request; + + /* Send Column Metadata for SP_PREPARE, SP_PREPEXEC, SP_EXECUTE and SP_EXECUTESQL even if + * the FLAG is set to true, since TSQL does the same. */ + if ((req->spFlags & SP_FLAGS_NOMETADATA) && (req->spType != SP_PREPARE) + && (req->spType != SP_PREPEXEC) && (req->spType != SP_EXECUTE) && (req->spType != SP_EXECUTESQL)) + { + TDS_DEBUG(TDS_DEBUG2, "SendColumnMetadataToken: token=0x%02x", TDS_TOKEN_COLMETADATA); + TdsPutInt8(TDS_TOKEN_COLMETADATA); + TdsPutInt8(0xFF); + TdsPutInt8(0xFF); + return; + } + } + + SendColumnMetadataToken(typeinfo->natts, false); +} + +bool +TdsPrintTup(TupleTableSlot *slot, DestReceiver *self) +{ + TupleDesc typeinfo = slot->tts_tupleDescriptor; + DR_printtup *myState = (DR_printtup *) self; + MemoryContext oldContext; + int natts = typeinfo->natts; + int attno; + uint8_t rowToken; + TDSRequest request = TdsRequestCtrl->request; + bool sendRowStat = false; + int nullMapSize = 0; + int simpleRowSize = 0; + uint32_t tdsVersion = GetClientTDSVersion(); + uint8_t *nullMap = NULL; + + TdsErrorContext->err_text = "Writing the Tds response to the socket"; + if (request->reqType == TDS_REQUEST_SP_NUMBER) + { + TDSRequestSP req = (TDSRequestSP) request; + + /* ROWSTAT token is sent for sp_cursorfetch */ + if (req->spType == SP_CURSORFETCH) + sendRowStat = true; + } + + /* Set or update my derived attribute info, if needed */ + if (myState->attrinfo != typeinfo || myState->nattrs != natts) + PrintTupPrepareInfo(myState, typeinfo, natts); + + /* Make sure the tuple is fully deconstructed */ + slot_getallattrs(slot); + + /* Switch into per-row context so we can recover memory below */ + oldContext = MemoryContextSwitchTo(myState->tmpcontext); + + if (tdsVersion >= TDS_VERSION_7_3_B) + { + /* + * NBCROW token was introduced in TDS version 7.3B. + * Determine the row type we send. For rows that don't contain any + * NULL values in variable size columns (like NVARCHAR) we can send + * the simple ROW (0xD1) format. Rows that do (specifically + * NVARCHAR/VARCHAR/CHAR/NCHAR/BINARY datatypes) need to be sent as + * NBCROW (0xD2). Count the number of nullable columns and build the + * null bitmap just in case while we are at it. + */ + nullMapSize = (natts + 7) / 8; + nullMap = palloc0(nullMapSize); + MemSet(nullMap, 0, nullMapSize * sizeof(int8_t)); + for (attno = 0; attno < natts; attno++) + { + TdsColumnMetaData *col = &colMetaData[attno]; + + if (col->metaEntry.type1.flags & TDS_COLMETA_NULLABLE) + { + if (slot->tts_isnull[attno]) + { + nullMap[attno / 8] |= (0x01 << (attno & 0x07)); + switch (col->metaEntry.type1.tdsTypeId) + { + case TDS_TYPE_VARCHAR: + case TDS_TYPE_NVARCHAR: + if (col->metaEntry.type2.maxSize == 0xffff) + /* + * To send NULL for VARCHAR(max) or NVARCHAR(max), + * we have to indicate it using 0xffffffffffffffff (PLP_NULL) + */ + simpleRowSize += 8; + else + /* + * For regular case of VARCHAR/NVARCHAR, + * we have to send 0xffff (CHARBIN_NULL) to indicate NULL + */ + simpleRowSize += 2; + break; + case TDS_TYPE_VARBINARY: + if (col->metaEntry.type7.maxSize == 0xffff) + /* To send NULL for VARBINARY(max),we have to indicate it using 0xffffffffffffffff (PLP_NULL) */ + simpleRowSize += 8; + else + /* For regular case of VARBINARY,we have to send 0xffff (CHARBIN_NULL) to indicate NULL */ + simpleRowSize += 2; + break; + case TDS_TYPE_CHAR: + case TDS_TYPE_NCHAR: + case TDS_TYPE_XML: + case TDS_TYPE_BINARY: + /* For these datatypes, we need to send 0xffff (CHARBIN_NULL) to indicate NULL */ + simpleRowSize += 2; + break; + case TDS_TYPE_SQLVARIANT: + /* For sql_variant, we need to send 0x00000000 to indicate NULL */ + simpleRowSize += 4; + break; + default: + /* for other datatypes, we need to send 0x00 (1 byte) only */ + simpleRowSize += 1; + break; + } + } + } + } + + if (nullMapSize < simpleRowSize) + { + rowToken = TDS_TOKEN_NBCROW; + } + else + { + rowToken = TDS_TOKEN_ROW; + } + } + else + /* ROW is only token to send data for TDS version lower or equal to 7.3A. */ + rowToken = TDS_TOKEN_ROW; + /* Send the row token and the NULL bitmap if it is NBCROW */ + TDS_DEBUG(TDS_DEBUG2, "rowToken = 0x%02x", rowToken); + TdsPutbytes(&rowToken, sizeof(rowToken)); + + if (rowToken == TDS_TOKEN_NBCROW) + { + TdsPutbytes(nullMap, nullMapSize); + TDSInstrumentation(INSTR_TDS_TOKEN_NBCROW); + } + + if (nullMap != NULL) + pfree(nullMap); + + /* And finally send the actual column values */ + for (attno = 0; attno < natts; attno++) + { + PrinttupAttrInfo *thisState; + Datum attr; + TdsColumnMetaData *col = &colMetaData[attno]; + + if (slot->tts_isnull[attno]) + { + /* Handle NULL values */ + /* when NBCROW token is used, all NULL values are + * sent using NULL bitmap only + */ + if (rowToken == TDS_TOKEN_ROW) + { + switch (col->metaEntry.type1.tdsTypeId) + { + case TDS_TYPE_VARCHAR: + case TDS_TYPE_NVARCHAR: + if (col->metaEntry.type2.maxSize == 0xffff) + /* + * To send NULL for VARCHAR(max) or NVARCHAR(max), + * we have to indicate it using 0xffffffffffffffff (PLP_NULL) + */ + TdsPutUInt64LE(0xffffffffffffffff); + else + /* + * For regular case of VARCHAR/NVARCHAR, + * we have to send 0xffff (CHARBIN_NULL) to indicate NULL + */ + TdsPutInt16LE(0xffff); + break; + case TDS_TYPE_VARBINARY: + if (col->metaEntry.type7.maxSize == 0xffff) + /* To send NULL for VARBINARY(max),we have to indicate it using 0xffffffffffffffff (PLP_NULL) */ + TdsPutUInt64LE(0xffffffffffffffff); + else + /* For regular case of VARBINARY,we have to send 0xffff (CHARBIN_NULL) to indicate NULL */ + TdsPutInt16LE(0xffff); + break; + case TDS_TYPE_CHAR: + case TDS_TYPE_NCHAR: + case TDS_TYPE_XML: + case TDS_TYPE_BINARY: + /* In case of TDS version lower than or equal to 7.3A, we need to send 0xffff (CHARBIN_NULL)*/ + TdsPutInt16LE(0xffff); + break; + case TDS_TYPE_SQLVARIANT: + /* For sql_variant, we need to send 0x00000000 to indicate NULL */ + TdsPutInt32LE(0); + break; + default: + /* for these datatypes, we need to send 0x00 (1 byte) only */ + TdsPutUInt8(0); + break; + } + } + continue; + } + + thisState = myState->myinfo + attno; + attr = slot->tts_values[attno]; + + /* + * Here we catch undefined bytes in datums that are returned to the + * client without hitting disk; see comments at the related check in + * PageAddItem(). This test is most useful for uncompressed, + * non-external datums, but we're quite likely to see such here when + * testing new C functions. + */ + if (thisState->typisvarlena) + VALGRIND_CHECK_MEM_IS_DEFINED(DatumGetPointer(attr), + VARSIZE_ANY(attr)); + + /* Call the type specific output function */ + (col->sendFunc)(&thisState->finfo, attr, (void *)col); + } + + /* + * For cursor fetch operation, we need to send the row status information. + * It can be either SP_CURSOR_FETCH_SUCCEEDED or SP_CURSOR_FETCH_MISSING. + * Since, we've reached here, we are definitely returning a tuple. So, we + * should set the flag as succeeded. + * + * XXX: We need to figure out a way to set the flag SP_CURSOR_FETCH_MISSING + * when we can't fetch the underlying tuple. It's only possible in case of + * sensitive cursors when the underlying tuple may have been deleted. In that + * case, the tds protocol prepares a dummy row with the missing data (nullable + * fields set to null, fixed length fields set to 0, blank, or the default for + * that column, as appropriate) followed by SP_CURSOR_FETCH_MISSING as the + * value of ROWSTAT column. + */ + if (sendRowStat) + (void) TdsPutInt32LE(SP_CURSOR_FETCH_SUCCEEDED); + + /* Return to caller's context, and flush row's temporary memory */ + MemoryContextSwitchTo(oldContext); + MemoryContextReset(myState->tmpcontext); + + return true; +} + +void +TdsPrintTupShutdown(void) +{ + pfree(colMetaData); + colMetaData = NULL; +} + +/* -------------------------------- + * TdsSendReturnStatus - send a return status token + * -------------------------------- + */ +void +TdsSendReturnStatus(int status) +{ + uint8 temp8; + int32_t tmp; + + TdsErrorContext->err_text = "Writing Return Status Token"; + SendPendingDone(true); + + TDS_DEBUG(TDS_DEBUG2, "TdsSendReturnStatus: token=0x%02x", TDS_TOKEN_RETURNSTATUS); + temp8 = TDS_TOKEN_RETURNSTATUS; + TdsPutbytes(&temp8, sizeof(temp8)); + + tmp = htoLE32(status); + TdsPutbytes(&status, sizeof(tmp)); +} + +/* -------------------------------- + * TdsSendDone - Queue a DONE message + * + * Since we don't know for sure if this is going to be the final DONE + * message we only queue it at this point. The next time TdsPutbytes() + * or TdsFlush is called we finalize the flags and send add it to + * the output stream. + * -------------------------------- + */ +void +TdsSendDone(int token, int status, int curcmd, uint64_t nprocessed) +{ + bool gucNocount = false; + + + TdsErrorContext->err_text = "Writing Done Token"; + + if (GetConfigOption("babelfishpg_tsql.nocount", true, false) && + strcmp(GetConfigOption("babelfishpg_tsql.nocount", true, false), "on") == 0) + gucNocount = true; + + if (TdsRequestCtrl) + TdsRequestCtrl->isEmptyResponse = false; + + TDS_DEBUG(TDS_DEBUG2, "TdsSendDone: token=0x%02x, status=%d, curcmd=%d, " + "nprocessed=%lu nocount=%d", + token, status, curcmd, nprocessed, gucNocount); + /* + * If we have a pending DONE token and encounter another one then + * the pending DONE is not the final one. Add the DONE_MORE flag + * and add it to the output buffer. + */ + SendPendingDone(true); + + /* Remember the DONE information as pending */ + TdsHavePendingDone = true; + TdsPendingDoneNocount = gucNocount; + TdsPendingDoneToken = token; + TdsPendingDoneStatus = status; + TdsPendingDoneCurCmd = curcmd; + TdsPendingDoneRowCnt = nprocessed; + + if (markErrorFlag) + TdsPendingDoneStatus |= TDS_DONE_ERROR; + + markErrorFlag = false; +} + +int +TdsFlush(void) +{ + SendPendingDone(false); + + /* reset flags */ + markErrorFlag = false; + + /* + * The current execution stack must be zero. Otherwise, + * some of our execution assumtion may have gone wrong. + */ + Assert(!tds_estate || tds_estate->current_stack == 0); + + /* reset error data */ + if (tds_estate) + ResetTdsEstateErrorData(); + + return TdsSocketFlush(); +} + +void +TDSStatementBeginCallback(PLtsql_execstate *estate, PLtsql_stmt *stmt) +{ + if(tds_estate == NULL) + return; + + TDS_DEBUG(TDS_DEBUG3, "begin %d", tds_estate->current_stack); + tds_estate->current_stack++; + + /* shouldn't have any un-handled error while begining the next statement */ + Assert(tds_estate->error_stack_offset == 0); + + if (stmt == NULL) + return; + + /* + * TODO: It's possible that for some statements, we've to send a done toke + * when we start the command and another done token when we end the command. + * TRY..CATCH is one such example. We can use this function to send + * the done token at the beginning of the command. + */ +} + +static void +StatementEnd_Internal(PLtsql_execstate *estate, PLtsql_stmt *stmt, bool error) +{ + int token_type = TDS_TOKEN_DONEPROC; + int command_type = TDS_CMD_UNKNOWN; + int flags = 0; + uint64_t nprocessed = 0; + bool toplevel = false; + bool is_proc = false; + bool skip_done = false; + bool row_count_valid = false; + + tds_estate->current_stack--; + TDS_DEBUG(TDS_DEBUG3, "end %d", tds_estate->current_stack); + toplevel = (tds_estate->current_stack == 0); + + + /* + * If we're ending a statement, that means we've already handled the error. + * In that case, just clear the error offset. + */ + tds_estate->error_stack_offset = 0; + + if (stmt == NULL) + return; + + /* TODO: handle all the cases */ + switch(stmt->cmd_type) + { + case PLTSQL_STMT_GOTO: + case PLTSQL_STMT_RETURN: + /* Used in inline table valued functions */ + case PLTSQL_STMT_RETURN_QUERY: + /* Used in multi-statement table valued functions */ + case PLTSQL_STMT_DECL_TABLE: + case PLTSQL_STMT_RETURN_TABLE: + { + /* Done token is not expected for these commands */ + skip_done = true; + } + break; + case PLTSQL_STMT_ASSIGN: + case PLTSQL_STMT_PUSH_RESULT: + { + command_type = TDS_CMD_SELECT; + row_count_valid = true; + } + break; + case PLTSQL_STMT_EXECSQL: + { + ListCell *l; + PLtsql_expr *expr = ((PLtsql_stmt_execsql *) stmt)->sqlstmt; + + /* + * XXX: Once an error occurs, the expr and expr->plan may be + * freed. In that case, we've to save the command type in + * PLtsql_stmt_execsql before the execution. + */ + if (expr && expr->plan) + { + foreach(l, SPI_plan_get_plan_sources(expr->plan)) + { + CachedPlanSource *plansource = (CachedPlanSource *) lfirst(l); + + if (plansource->commandTag) + { + if (plansource->commandTag == CMDTAG_INSERT) + { + command_type = TDS_CMD_INSERT; + /* + * row_count should be invalid if the INSERT is + * inside the procedure of an INSERT-EXEC, or if + * the INSERT itself is an INSERT-EXEC and it + * just returned error. + */ + row_count_valid = !estate->insert_exec && + !(markErrorFlag && + ((PLtsql_stmt_execsql *)stmt)->insert_exec); + } + else if (plansource->commandTag == CMDTAG_UPDATE) + { + command_type = TDS_CMD_UPDATE; + row_count_valid = !estate->insert_exec; + } + else if (plansource->commandTag == CMDTAG_DELETE) + { + command_type = TDS_CMD_DELETE; + row_count_valid = !estate->insert_exec; + } + /* + * [BABEL-2090] SELECT statement should show + * 'rows affected' count + */ + else if (plansource->commandTag == CMDTAG_SELECT) + { + command_type = TDS_CMD_SELECT; + row_count_valid = !estate->insert_exec; + } + } + } + } + + /* + * Done token is not expected for INSERT/UPDATE/DELETE + * statements on table variables in user-defined functions. + */ + if (((PLtsql_stmt_execsql *) stmt)->mod_stmt_tablevar && + estate->func->fn_prokind == PROKIND_FUNCTION && + estate->func->fn_is_trigger == PLTSQL_NOT_TRIGGER && + strcmp(estate->func->fn_signature, "inline_code_block") != 0) + skip_done = true; + } + break; + case PLTSQL_STMT_EXEC: + case PLTSQL_STMT_EXEC_BATCH: + case PLTSQL_STMT_EXEC_SP: + { + is_proc = true; + command_type = TDS_CMD_EXECUTE; + } + break; + default: + break; + } + + /* + * XXX: For SP_CUSTOMTYPE, if we're done executing the top level stored + * procedure, we need to send the return status and OUT parameters + * before the DONEPROC token. + */ + if (toplevel && is_proc) + { + TDSRequest request = TdsRequestCtrl->request; + if (request->reqType == TDS_REQUEST_SP_NUMBER) + { + TDSRequestSP req = (TDSRequestSP) request; + + if (req->spType == SP_CUSTOMTYPE) + return; + } + } + + /* + * Send return status token if executed a procedure at top-level + * N.B. It's possible that the EXEC statement itself throws an error. In + * that case, this token will follow an error token. We should not send + * a return status in that case. + */ + if (!markErrorFlag && toplevel && is_proc) + { + if (stmt->cmd_type == PLTSQL_STMT_EXEC) + { + /* + * If we're returning from a TOP-level procedure, send the return + * status token. It's possible that we've executed a scalar UDF + * with EXEC keyword. In that case, we don't have to send the + * return status token. + */ + if (!((PLtsql_stmt_exec *) stmt)->is_scalar_func) + { + Assert(pltsql_plugin_handler_ptr->pltsql_read_proc_return_status != NULL); + TdsSendReturnStatus(*(pltsql_plugin_handler_ptr->pltsql_read_proc_return_status)); + } + } + else + { + /* + * For EXEC batch, SP cursors and SP executeSQL, we just have to + * return 0 for a successful execution. Since, babelfishpg_tsql + * extension doesn't have return statement implementation for + * these cases, we've tosend it from here. + * + * TODO: Add this support in babelfishpg_tsql extension instead. + * In that case, we can remove this check. + */ + TdsSendReturnStatus(0); + } + } + + /* + * If we shouldn't send a done token for the current command, we can return + * from here. + */ + if (skip_done) + return; + + /* + * If count is valid for this command, set the count and the corresponding + * flag. + */ + if (row_count_valid) + { + nprocessed = (error ? 0 : estate->eval_processed); + flags |= TDS_DONE_COUNT; + } + + if (toplevel && is_proc) + token_type = TDS_TOKEN_DONEPROC; + else if (toplevel) + token_type = TDS_TOKEN_DONE; + else + token_type = TDS_TOKEN_DONEINPROC; + + if (toplevel) + flags |= TDS_DONE_FINAL; + else + flags |= TDS_DONE_MORE; + + TdsSendDone(token_type, flags, command_type, nprocessed); +} + +void +TDSStatementEndCallback(PLtsql_execstate *estate, PLtsql_stmt *stmt) +{ + if(tds_estate == NULL) + return; + + StatementEnd_Internal(estate, stmt, false); +} + +void +TDSStatementExceptionCallback(PLtsql_execstate *estate, PLtsql_stmt *stmt, bool terminate_batch) +{ + if(tds_estate == NULL) + return; + + TDS_DEBUG(TDS_DEBUG3, "exception %d", tds_estate->current_stack); + + SetTdsEstateErrorData(); + + /* + * If we're terminating the batch, then we should not send any done token + * from this level. The done token will be sent from a higher level + * where the error got handled. + */ + if (terminate_batch) + { + if (tds_estate->error_stack_offset == 0) + { + /* TODO: save the command type */ + } + + tds_estate->current_stack--; + tds_estate->error_stack_offset++; + + return; + } + + StatementEnd_Internal(estate, stmt, true); + + /* + * TODO: We should add the current command in a queue. In the current + * state, we don't know whether there is a TRY..CATCH in the upper level + * that catches this error. In that case, we don't have to mark the + * error flag in the done token. Once we have that information, we'll + * send done tokens for each entry in this queue and empty the queue. + */ +} + +/* + * SendColumnMetadata - Api to Send the Column Metatadata, + * used in sp_prepare and called from babelfishpg_tsql extension. + */ +void +SendColumnMetadata(TupleDesc typeinfo, List *targetlist, int16 *formats) +{ + TdsSendRowDescription(typeinfo, targetlist, formats); + TdsPrintTupShutdown(); +} + +/* + * Record error data in tds_estate + */ +static void +SetTdsEstateErrorData(void) +{ + int number, severity, state; + + if (GetTdsEstateErrorData(&number, &severity, &state)) + { + tds_estate->cur_error_number = number; + tds_estate->cur_error_severity = severity; + tds_estate->cur_error_state = state; + } +} + +/* + * Reset error data in tds_estate + */ +static void +ResetTdsEstateErrorData(void) +{ + tds_estate->cur_error_number = -1; + tds_estate->cur_error_severity = -1; + tds_estate->cur_error_state = -1; +} + +/* + * Read error data in tds_estate + */ +bool +GetTdsEstateErrorData(int *number, int *severity, int *state) +{ + if (tds_estate != NULL && + tds_estate->cur_error_number != -1 && + tds_estate->cur_error_severity != -1 && + tds_estate->cur_error_state != -1) + { + if (number) + *number = tds_estate->cur_error_number; + if (severity) + *severity = tds_estate->cur_error_severity; + if (state) + *state = tds_estate->cur_error_state; + return true; + } + /* + * If tds_estate doesn't have valid error data, try to find it in + * exec_state_call_stack + */ + else + return pltsql_plugin_handler_ptr->pltsql_get_errdata(number, severity, state); +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdsrpc.c b/contrib/babelfishpg_tds/src/backend/tds/tdsrpc.c new file mode 100644 index 00000000000..9017400bd82 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsrpc.c @@ -0,0 +1,3839 @@ +#include "postgres.h" + +#include "access/printtup.h" +#include "access/xact.h" /* for IsTransactionOrTransactionBlock() */ +#include "commands/prepare.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "executor/spi.h" +#include "libpq/pqformat.h" +#include "lib/stringinfo.h" +#include "miscadmin.h" +#include "parser/parser.h" +#include "parser/scansup.h" +#include "tcop/pquery.h" +#include "tcop/tcopprot.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/snapmgr.h" + +#include "src/include/tds_debug.h" +#include "src/include/tds_int.h" +#include "src/include/tds_protocol.h" +#include "src/include/tds_response.h" +#include "src/include/tds_instr.h" +#include "src/include/faultinjection.h" + +#define SP_FLAGS_BYREFVALUE 0x01 +#define SP_FLAGS_DEFAULTVALUE 0x02 +#define SP_FLAGS_ENCRYPTED 0x08 + +/* + * sign, 10 digits, '\0' + * + * This is important for converting integer to string. Else, we've to dynamically + * allocate memory just for the conversion. + */ +#define INT32_STRLEN 12 + +/* For checking the invalid length parameters */ +#define CheckForInvalidLength(temp) \ +do \ +{ \ + if (temp->len > temp->maxLen) \ + { \ + ereport(ERROR, \ + (errcode(ERRCODE_PROTOCOL_VIOLATION), \ + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " \ + "Parameter %d (\"%s\"): Data type 0x%02X has an invalid data length or metadata length.", \ + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->type))); \ + } \ +} while(0) + +/* Check if retStatus Not OK */ +#define CheckPLPStatusNotOK(temp, retStatus) \ +do \ +{ \ + if (retStatus != STATUS_OK) \ + { \ + ereport(ERROR, \ + (errcode(ERRCODE_PROTOCOL_VIOLATION), \ + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " \ + "Parameter %d (\"%s\"): The chunking format is incorrect for a large object parameter of type 0x%02X.", \ + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->type))); \ + } \ +} while(0) + +/* For identifying the Batch Separator. */ +#define GetRpcBatchSeparator(tdsVersion) ((tdsVersion > TDS_VERSION_7_1_1) ? 0xFF : 0x80) + +/* Different cursor options */ +#define SP_CURSOR_SCROLLOPT_KEYSET 0x0001 +#define SP_CURSOR_SCROLLOPT_DYNAMIC 0x0002 +#define SP_CURSOR_SCROLLOPT_FORWARD_ONLY 0x0004 +#define SP_CURSOR_SCROLLOPT_STATIC 0x0008 +#define SP_CURSOR_SCROLLOPT_FAST_FORWARD 0x10 +#define SP_CURSOR_SCROLLOPT_PARAMETERIZED_STMT 0x1000 +#define SP_CURSOR_SCROLLOPT_AUTO_FETCH 0x2000 +#define SP_CURSOR_SCROLLOPT_AUTO_CLOSE 0x4000 +#define SP_CURSOR_SCROLLOPT_CHECK_ACCEPTED_TYPES 0x8000 +#define SP_CURSOR_SCROLLOPT_KEYSET_ACCEPTABLE 0x10000 +#define SP_CURSOR_SCROLLOPT_DYNAMIC_ACCEPTABLE 0x20000 +#define SP_CURSOR_SCROLLOPT_FORWARD_ONLY_ACCEPTABLE 0x40000 +#define SP_CURSOR_SCROLLOPT_STATIC_ACCEPTABLE 0x80000 +#define SP_CURSOR_SCROLLOPT_FAST_FORWARD_ACCEPTABLE 0x100000 + +#define SP_CURSOR_CCOPT_READ_ONLY 0x0001 +#define SP_CURSOR_CCOPT_SCROLL_LOCKS 0x0002 /* previously known as LOCKCC */ +#define SP_CURSOR_CCOPT_OPTIMISTIC1 0x0004 /* previously known as OPTCC */ +#define SP_CURSOR_CCOPT_OPTIMISTIC2 0x0008 /* previously known as OPTCCVAL */ +#define SP_CURSOR_CCOPT_ALLOW_DIRECT 0x2000 +#define SP_CURSOR_CCOPT_UPDT_IN_PLACE 0x4000 +#define SP_CURSOR_CCOPT_CHECK_ACCEPTED_OPTS 0x8000 +#define SP_CURSOR_CCOPT_READ_ONLY_ACCEPTABLE 0x10000 +#define SP_CURSOR_CCOPT_SCROLL_LOCKS_ACCEPTABLE 0x20000 +#define SP_CURSOR_CCOPT_OPTIMISTIC_ACCEPTABLE 0x40000 +#define SP_CURSOR_CCOPT_OPTIMISITC_ACCEPTABLE 0x80000 + +/* different fetch options in sp_cursorfetch */ +#define SP_CURSOR_FETCH_FIRST 0x0001 +#define SP_CURSOR_FETCH_NEXT 0x0002 +#define SP_CURSOR_FETCH_PREV 0x0004 +#define SP_CURSOR_FETCH_LAST 0x0008 +#define SP_CURSOR_FETCH_ABSOLUTE 0x10 +#define SP_CURSOR_FETCH_RELATIVE 0x20 +#define SP_CURSOR_FETCH_REFRESH 0x80 +#define SP_CURSOR_FETCH_INFO 0x100 +#define SP_CURSOR_FETCH_PREV_NOADJUST 0x200 +#define SP_CURSOR_FETCH_SKIP_UPDT_CNCY 0x400 + +/* To get the datatype from the parameter */ +#define FetchDataTypeNameFromParameter(param) (param->paramMeta.metaEntry.type1.tdsTypeId) + +/* different print option in sp_cursor */ +#define PRINT_CURSOR_HANDLE 0x0001 +#define PRINT_PREPARED_CURSOR_HANDLE 0x0002 +#define PRINT_BOTH_CURSOR_HANDLE 0x0004 + +/* Local functions */ +static void GetSPHandleParameter(TDSRequestSP request); +static void GetSPCursorPreparedHandleParameter(TDSRequestSP request); +static void GetSPCursorHandleParameter(TDSRequestSP request); +static inline void FillStoredProcedureCallFromParameterToken(TDSRequestSP req, + StringInfo inBuf); +static inline void FillQueryFromParameterToken(TDSRequestSP req, + StringInfo inBuf); +static inline void InitializeDataParamTokenIndex(TDSRequestSP req); +static void InitialiseParameterToken(TDSRequestSP request); +static inline Portal GetPortalFromCursorHandle(const int portalHandle, bool missingOk); +static void SendCursorResponse(TDSRequestSP req); +static inline void FetchCursorOptions(TDSRequestSP req); +static int SetCursorOption(TDSRequestSP req); +static void HandleSPCursorOpenCommon(TDSRequestSP req); +static void HandleSPCursorCloseRequest(TDSRequestSP req); +static void HandleSPCursorUnprepareRequest(TDSRequestSP req); +static void GenerateBindParamsData(TDSRequestSP req); +static int ReadParameters(TDSRequestSP request, uint64_t offset, StringInfo message, int *parameterCount); +static void SPExecuteSQL(TDSRequestSP req); +static void SPPrepare(TDSRequestSP req); +static void SPExecute(TDSRequestSP req); +static void SPPrepExec(TDSRequestSP req); +static void SPCustomType(TDSRequestSP req); +static void SPUnprepare(TDSRequestSP req); +static void TDSLogStatementCursorHandler(TDSRequestSP req, char *stmt, int option); +static InlineCodeBlockArgs* DeclareVariables(TDSRequestSP req, FunctionCallInfo *fcinfo, unsigned long options); +List *tvp_lookup_list = NIL; +bool lockForFaultInjection = false; + +static InlineCodeBlockArgs* +CreateArgs(int nargs) +{ + InlineCodeBlockArgs *args; + + args = (InlineCodeBlockArgs *) palloc0(sizeof(InlineCodeBlockArgs)); + args->numargs = nargs; + + args->argtypes = (Oid *) palloc(sizeof(Oid) * args->numargs); + args->argtypmods = (int32 *) palloc(sizeof(int32) * args->numargs); + args->argnames = (char **) palloc(sizeof(char *) * args->numargs); + args->argmodes = (char *) palloc(sizeof(char) * args->numargs); + + return args; +} + +/* + * DeclareVariables - Declare TSQL variables by calling pltsql API directly + * + * We prepare the InlineCodeBlockArgs and the same as the second argument + * of fcinfo. + * If fcinfo is NULL, then don't call the pltsql API - just get the args and set + * up TVP lookup. + */ +static InlineCodeBlockArgs* +DeclareVariables(TDSRequestSP req, FunctionCallInfo *fcinfo, unsigned long options) +{ + InlineCodeBlockArgs *args = NULL; + ParameterToken token = NULL; + int i = 0, index = 0; + bool resolveParamNames = false; + char *tmp = NULL, + *fToken = NULL; + + args = (InlineCodeBlockArgs *) palloc0(sizeof(InlineCodeBlockArgs)); + args->numargs = req->nTotalParams; + args->options = options; + + if (fcinfo) + { + /* now add the same as second argument */ + (*fcinfo)->args[1].value = PointerGetDatum(args); + (*fcinfo)->args[1].isnull = false; + (*fcinfo)->nargs++; + } + + /* set variables if there is any */ + if (req->nTotalParams <= 0) + return args; + + args->argtypes = (Oid *) palloc(sizeof(Oid) * args->numargs); + args->argtypmods = (int32 *) palloc(sizeof(int32) * args->numargs); + args->argnames = (char **) palloc(sizeof(char *) * args->numargs); + args->argmodes = (char *) palloc(sizeof(char) * args->numargs); + /* + * We have the assumption that either all parameters will have names + * or none of them will have. + * So, check the parameter name for the first token and set the flag. + * If above assumption is invalid, then we will raise the error in + * below for loop. + */ + if (req->dataParameter->paramMeta.colName.len == 0) + { + resolveParamNames = true; + if (req->metaDataParameterValue->len) + { + tmp = pnstrdup(req->metaDataParameterValue->data, + req->metaDataParameterValue->len); + + /* + * XXX: Ugly hack - When the client driver doesn't specify the parameter names + * along with each parameter token, it can be of the either of the following + * two formats: + * + * @P0 , @P1 , ..... + * or + * @P1 , @P2 , ..... + * + * So, we just check the first parameter name whether it starts with "0" or + * "1" and auto-generate the parameter names. + */ + fToken = strtok (tmp, " "); + if (strcmp(fToken, "@P0") == 0) + i = 0; + else if (strcmp(fToken, "@P1") == 0) + i = 1; + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unexpected parameter definition %s", fToken))); + + pfree(tmp); + } + else + i = 0; + } + + /* + * For each token, we need to call pltsql_declare_var_block_handler API + * to declare the corresponding variable. + */ + for (token = req->dataParameter, index = 0; token != NULL; token = token->next, index++) + { + char *paramName; + StringInfo name; + Datum pval; + bool isNull; + TdsIoFunctionInfo tempFuncInfo; + + name = &(token->paramMeta.colName); + + /* + * TODO: Can we directly give the intermediate token (@P0 int, @P1 + * varchar))to the pltsql ? + * Also, maybe we can use the raw_parser() directly for getting the parameter + * names + */ + if (resolveParamNames && (name->len)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("not all Parameters have names"))); + else if(resolveParamNames) + { + char buf[10]; + + snprintf(buf, sizeof(buf), "@p%d", i); + paramName = pnstrdup(buf, strlen(buf));; + } + else + paramName = downcase_truncate_identifier(name->data, + strlen(name->data), true); + + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(token->type, token->maxLen); + isNull = token->isNull; + + if (!isNull && fcinfo) + pval = tempFuncInfo->recvFuncPtr(req->messageData, token); + else + pval = (Datum) 0; + + if (fcinfo) + pltsql_plugin_handler_ptr->pltsql_declare_var_callback ( + token->paramMeta.pgTypeOid, /* oid */ + GetTypModForToken(token), /* typmod */ + paramName, /* name */ + (token->flags == 0) ? + PROARGMODE_IN : PROARGMODE_INOUT, /* mode */ + pval, /* datum */ + isNull, /* null */ + index, + &args, + fcinfo); + else + { + MemoryContext xactContext; + MemoryContext oldContext = CurrentMemoryContext; + StartTransactionCommand(); + if (get_typtype(token->paramMeta.pgTypeOid) == TYPTYPE_COMPOSITE) + { + TvpLookupItem *item; + xactContext = MemoryContextSwitchTo(oldContext); + item = (TvpLookupItem *) palloc(sizeof(TvpLookupItem)); + item->name = paramName; + item->tableRelid = get_typ_typrelid(token->paramMeta.pgTypeOid); + item->tableName = NULL; + tvp_lookup_list = lappend(tvp_lookup_list, item); + MemoryContextSwitchTo(xactContext); + } + CommitTransactionCommand(); + MemoryContextSwitchTo(oldContext); + } + + i++; + } + + return args; +} + +/* + * SetVariables - Set TSQL variables by calling pltsql API directly + * + * For sp_execute, we only need to set the values to the associated args in + * fcinfo. In this case, param type and name are not important, hence set + * to NULL. + */ +static void +SetVariables(TDSRequestSP req, FunctionCallInfo *fcinfo) +{ + InlineCodeBlockArgs *codeblock_args; + ParameterToken token = NULL; + int i = 0, index = 0; + + /* should be only called for sp_execute */ + Assert(req->spType == SP_EXECUTE); + + codeblock_args = (InlineCodeBlockArgs *) palloc0(sizeof(InlineCodeBlockArgs)); + codeblock_args->handle = (int) req->handle; + codeblock_args->options = (BATCH_OPTION_EXEC_CACHED_PLAN | + BATCH_OPTION_NO_FREE); + + /* Set variable if any. */ + if (req->nTotalParams > 0) + { + /* + * For each token, we need to call pltsql_declare_var_block_handler API + * to declare the corresponding variable. + */ + for (token = req->dataParameter, index = 0; token != NULL; token = token->next, index++) + { + Datum pval; + bool isNull; + TdsIoFunctionInfo tempFuncInfo; + + + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(token->type, token->maxLen); + isNull = token->isNull; + + if (!isNull) + pval = tempFuncInfo->recvFuncPtr(req->messageData, token); + else + pval = (Datum) 0; + + pltsql_plugin_handler_ptr->pltsql_declare_var_callback(token->paramMeta.pgTypeOid, /* oid */ + GetTypModForToken(token), /* typmod */ + NULL, /* name */ + (token->flags == 0) ? + PROARGMODE_IN : PROARGMODE_INOUT, /* mode */ + pval, /* datum */ + isNull, /* null */ + index, + NULL, + fcinfo); + + i++; + } + } + + /* Set the second argument as null just to satisfy the arg requirements */ + (*fcinfo)->args[1].value = PointerGetDatum(codeblock_args); + (*fcinfo)->args[1].isnull = false; + (*fcinfo)->nargs++; +} + + +/* + * errdetail_params + * + * Add an errdetail() line showing bind-parameter data, if available. + */ +static int +errdetail_params(int nTotalParams) +{ + ParamListInfo params; + params = (ParamListInfo) palloc(offsetof(ParamListInfoData, params) + + nTotalParams * sizeof(ParamExternData)); + + /* We have static list of params, so no hooks needed. */ + params->paramFetch = NULL; + params->paramFetchArg = NULL; + params->paramCompile = NULL; + params->paramCompileArg = NULL; + params->parserSetup = NULL; + params->parserSetupArg = NULL; + params->numParams = nTotalParams; + + TdsFetchInParamValues(params); + + /* We mustn't call user-defined I/O functions when in an aborted xact */ + if (params && params->numParams > 0 && !IsAbortedTransactionBlockState()) + { + StringInfoData param_str; + int paramno; + MemoryContext oldcontext; + + /* This code doesn't support dynamic param lists */ + Assert(params->paramFetch == NULL); + + /* Make sure any trash is generated in MessageContext */ + oldcontext = MemoryContextSwitchTo(MessageContext); + + initStringInfo(¶m_str); + + for (paramno = 0; paramno < params->numParams; paramno++) + { + ParamExternData *prm = ¶ms->params[paramno]; + Oid typoutput; + bool typisvarlena; + char *pstring; + char *p; + + appendStringInfo(¶m_str, "%s$%d = ", + paramno > 0 ? ", " : "", + paramno + 1); + + if (prm->isnull || !OidIsValid(prm->ptype)) + { + appendStringInfoString(¶m_str, "NULL"); + continue; + } + + getTypeOutputInfo(prm->ptype, &typoutput, &typisvarlena); + + pstring = OidOutputFunctionCall(typoutput, prm->value); + + appendStringInfoCharMacro(¶m_str, '\''); + for (p = pstring; *p; p++) + { + if (*p == '\'') /* double single quotes */ + appendStringInfoCharMacro(¶m_str, *p); + appendStringInfoCharMacro(¶m_str, *p); + } + appendStringInfoCharMacro(¶m_str, '\''); + + pfree(pstring); + } + + errdetail("Parameters: %s", param_str.data); + pfree(param_str.data); + MemoryContextSwitchTo(oldcontext); + } + + return 0; +} + +static void +SPExecuteSQL(TDSRequestSP req) +{ + StringInfoData s; + InlineCodeBlock *codeblock; + LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS); + + int paramno; + Datum retval; + Datum *values; + bool *nulls; + + TdsErrorContext->err_text = "Processing SP_EXECUTESQL Request"; + if ((req->nTotalParams + 2) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Too many parameters were provided in this RPC request. The maximum is %d", + FUNC_MAX_ARGS))); + + TDSInstrumentation(INSTR_TDS_SP_EXECUTESQL); + + initStringInfo(&s); + FillQueryFromParameterToken(req, &s); + + codeblock = makeNode(InlineCodeBlock); + codeblock->source_text = s.data; + codeblock->langOid = 0; /* TODO does it matter */ + codeblock->langIsTrusted = true; + codeblock->atomic = false; + + /* Just to satisfy argument requirement */ + MemSet(fcinfo, 0, SizeForFunctionCallInfo(FUNC_MAX_ARGS)); + fcinfo->nargs = 1; + fcinfo->args[0].value = PointerGetDatum(codeblock); + fcinfo->args[0].isnull = false; + + /* declare variables if there is any */ + if (req->nTotalParams > 0) + DeclareVariables(req, &fcinfo, 0); + + TDSStatementBeginCallback(NULL, NULL); + + PG_TRY(); + { + /* Now, execute the same and retrieve the composite datum */ + retval = pltsql_plugin_handler_ptr->sp_executesql_callback (fcinfo); + MemoryContextSwitchTo(MessageContext); + + /* If pltsql_inline_handler does not end normally */ + if (fcinfo->isnull) + elog(ERROR, "pltsql_inline_handler failed"); + + /* Read out params and nulls after checking the retrived Datum for NULL */ + if (retval) + pltsql_plugin_handler_ptr->pltsql_read_out_param_callback(retval, &values, &nulls); + else if (req->nOutParams > 0) + elog(ERROR, "missing OUT parameter values from pltsql handler"); + } + PG_CATCH(); + { + if (TDS_DEBUG_ENABLED(TDS_DEBUG2)) + ereport(LOG, + (errmsg("sp_executesql statement: %s", s.data), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* Return Status: 0 (success) or non-zero (failure) */ + TdsSendReturnStatus(0); + + /* Send OUT parameters */ + for (paramno = 0; paramno < req->nOutParams; paramno++) + SendReturnValueTokenInternal(req->idxOutParams[paramno], 0x01, NULL, + values[paramno], nulls[paramno], true); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* + * Log immediately if dictated by log_statement + */ + if (pltsql_plugin_handler_ptr->stmt_needs_logging || TDS_DEBUG_ENABLED(TDS_DEBUG2)) + { + + ErrorContextCallback *plerrcontext = error_context_stack; + error_context_stack = plerrcontext->previous; + + ereport(LOG, + (errmsg("sp_executesql statement: %s", s.data), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + error_context_stack = plerrcontext; + } + + /* + * Print TDS log duration, if log_duration is set + */ + TDSLogDuration(s.data); + pfree(codeblock); +} + +static void +SPPrepare(TDSRequestSP req) +{ + StringInfoData s; + LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS); + + Datum retval; + Datum *values; + bool *nulls; + + TdsErrorContext->err_text = "Processing SP_PREPARE Request"; + TDSInstrumentation(INSTR_TDS_SP_PREPARE); + + tvp_lookup_list = NIL; + + if ((req->nTotalParams + 2) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Too many parameters were provided in this RPC request. The maximum is %d", + FUNC_MAX_ARGS))); + + initStringInfo(&s); + FillQueryFromParameterToken(req, &s); + + MemSet(fcinfo, 0, SizeForFunctionCallInfo(FUNC_MAX_ARGS)); + + fcinfo->nargs = 3; + fcinfo->args[1].value = PointerGetDatum(cstring_to_text(req->metaDataParameterValue->data)); + if (req->metaDataParameterValue->len == 0) + fcinfo->args[1].isnull = true; + else + fcinfo->args[1].isnull = false; + + fcinfo->args[2].value = PointerGetDatum(cstring_to_text(s.data)); + if (s.len == 0) + fcinfo->args[2].isnull = true; + else + fcinfo->args[2].isnull = false; + + TDSStatementBeginCallback(NULL, NULL); + + PG_TRY(); + { + /* Now, call the prepare handler and retrieve the handle */ + retval = pltsql_plugin_handler_ptr->sp_prepare_callback (fcinfo); + MemoryContextSwitchTo(MessageContext); + + /* If sp_prepare_handler does not end normally */ + if (fcinfo->isnull) + elog(ERROR, "sp_prepare_handler failed"); + + /* Read out params and nulls after checking the retrived Datum for NULL */ + if (retval) + pltsql_plugin_handler_ptr->pltsql_read_out_param_callback(retval, &values, &nulls); + else if (req->nOutParams > 0) + elog(ERROR, "missing OUT parameter values from pltsql handler"); + } + PG_CATCH(); + { + TDSStatementExceptionCallback(NULL, NULL, false); + tvp_lookup_list = NIL; + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* Return Status: 0 (success) or non-zero (failure) */ + TdsSendReturnStatus(0); + + /* Send the handle */ + SendReturnValueTokenInternal(req->handleParameter, 0x01, NULL, + values[0], false, false); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + tvp_lookup_list = NIL; +} + +static void +SPExecute(TDSRequestSP req) +{ + InlineCodeBlock *codeblock; + LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS); + + int paramno; + Datum retval; + Datum *values; + bool *nulls; + + TdsErrorContext->err_text = "Processing SP_EXECUTE Request"; + TDSInstrumentation(INSTR_TDS_SP_EXECUTE); + + tvp_lookup_list = NIL; + + if ((req->nTotalParams + 2) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Too many parameters were provided in this RPC request. The maximum is %d", + FUNC_MAX_ARGS))); + + codeblock = makeNode(InlineCodeBlock); + codeblock->source_text = NULL; + codeblock->langOid = 0; /* TODO does it matter */ + codeblock->langIsTrusted = true; + codeblock->atomic = false; + + /* Just to satisfy argument requirement. */ + MemSet(fcinfo, 0, SizeForFunctionCallInfo(FUNC_MAX_ARGS)); + fcinfo->nargs = 1; + fcinfo->args[0].value = PointerGetDatum(codeblock); + fcinfo->args[0].isnull = false; + + SetVariables(req, &fcinfo); + + TDSStatementBeginCallback(NULL, NULL); + + PG_TRY(); + { + /* Now, call the execute handler and retrieve the composite datum. */ + retval = pltsql_plugin_handler_ptr->sp_execute_callback (fcinfo); + MemoryContextSwitchTo(MessageContext); + + /* If sp_execute_handler does not end normally. */ + if (fcinfo->isnull) + elog(ERROR, "sp_execute_handler failed"); + + /* Read the handle retrived if the returned Datum is not NULL. */ + if (retval) + pltsql_plugin_handler_ptr->pltsql_read_out_param_callback(retval, &values, &nulls); + else if (req->nOutParams > 0) + elog(ERROR, "missing OUT parameter values from pltsql handler"); + } + PG_CATCH(); + { + if (TDS_DEBUG_ENABLED(TDS_DEBUG2)) + ereport(LOG, + (errmsg("sp_execute handle: %d", req->handle), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + + TDSStatementExceptionCallback(NULL, NULL, false); + tvp_lookup_list = NIL; + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* Return Status: 0 (success) or non-zero (failure). */ + TdsSendReturnStatus(0); + + /* Send OUT parameters. */ + for (paramno = 0; paramno < req->nOutParams; paramno++) + SendReturnValueTokenInternal(req->idxOutParams[paramno], 0x01, NULL, + values[paramno], nulls[paramno], true); + + /* Command type - execute (0xe0). */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* + * Log immediately if dictated by log_statement + */ + if (pltsql_plugin_handler_ptr->stmt_needs_logging || TDS_DEBUG_ENABLED(TDS_DEBUG2)) + { + ErrorContextCallback *plerrcontext = error_context_stack; + error_context_stack = plerrcontext->previous; + + ereport(LOG, + (errmsg("sp_execute handle: %d", req->handle), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + error_context_stack = plerrcontext; + } + + /* + * Print TDS log duration, if log_duration is set + */ + TDSLogDuration(req->messageData); + + pfree(codeblock); + tvp_lookup_list = NIL; +} + +static void +SPPrepExec(TDSRequestSP req) +{ + StringInfoData s; + InlineCodeBlock *codeblock; + InlineCodeBlockArgs* codeblock_args; + LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS); + + int paramno; + Datum retval; + Datum *values; + bool *nulls; + + tvp_lookup_list = NIL; + TdsErrorContext->err_text = "Processing SP_PREPEXEC Request"; + + if ((req->nTotalParams + 2) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Too many parameters were provided in this RPC request. The maximum is %d", + FUNC_MAX_ARGS))); + + TDSInstrumentation(INSTR_TDS_SP_PREPEXEC); + + initStringInfo(&s); + FillQueryFromParameterToken(req, &s); + + codeblock = makeNode(InlineCodeBlock); + codeblock->source_text = s.data; + codeblock->langOid = 0; /* TODO does it matter */ + codeblock->langIsTrusted = true; + codeblock->atomic = false; + + /* Just to satisfy argument requirement */ + MemSet(fcinfo, 0, SizeForFunctionCallInfo(FUNC_MAX_ARGS)); + fcinfo->nargs = 1; + fcinfo->args[0].value = PointerGetDatum(codeblock); + fcinfo->args[0].isnull = false; + + codeblock_args = DeclareVariables(req, &fcinfo, + (BATCH_OPTION_CACHE_PLAN | BATCH_OPTION_NO_FREE)); + + TDSStatementBeginCallback(NULL, NULL); + + PG_TRY(); + { + /* Now, call the execute handler and retrieve the composite datum. */ + retval = pltsql_plugin_handler_ptr->sp_prepexec_callback (fcinfo); + MemoryContextSwitchTo(MessageContext); + + /* If sp_prepexec_handler does not end normally. */ + if (fcinfo->isnull) + elog(ERROR, "sp_prepexec_handler failed"); + + /* Read out params and nulls after checking the retrived Datum for NULL */ + if (retval) + pltsql_plugin_handler_ptr->pltsql_read_out_param_callback(retval, &values, &nulls); + else if (req->nOutParams > 0) + elog(ERROR, "missing OUT parameter values from pltsql handler"); + } + PG_CATCH(); + { + if (TDS_DEBUG_ENABLED(TDS_DEBUG2)) + ereport(LOG, + (errmsg("sp_prepexec handle: %d, " + "statement: %s", req->handle, s.data), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + + TDSStatementExceptionCallback(NULL, NULL, false); + tvp_lookup_list = NIL; + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* TODO: other values than 0? */ + TdsSendReturnStatus(0); + + /* Send the handle */ + SendReturnValueTokenInternal(req->handleParameter, 0x01, NULL, + codeblock_args->handle, false, true); + + /* Send OUT parameters */ + for (paramno = 0; paramno < req->nOutParams; paramno++) + SendReturnValueTokenInternal(req->idxOutParams[paramno], 0x01, NULL, + values[paramno], nulls[paramno], true); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* + * Log immediately if dictated by log_statement + */ + if (pltsql_plugin_handler_ptr->stmt_needs_logging || TDS_DEBUG_ENABLED(TDS_DEBUG2)) + { + ErrorContextCallback *plerrcontext = error_context_stack; + + error_context_stack = plerrcontext->previous; + ereport(LOG, + (errmsg("sp_prepexec handle: %d, " + "statement: %s", req->handle, s.data), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + error_context_stack = plerrcontext; + } + + /* + * Print TDS log duration, if log_duration is set + */ + TDSLogDuration(s.data); + + pfree(codeblock); + tvp_lookup_list = NIL; +} + +/* + * DeclareSPVariables - declare arguments and return type of a stored procedure + * or a scalar UDF. + */ +static ParameterToken +DeclareSPVariables(TDSRequestSP req, FunctionCallInfo *fcinfo) +{ + InlineCodeBlockArgs *args = NULL; + ParameterToken token = NULL; + int index = 0; + ParameterToken returnToken; + Oid atttypid; + Oid atttypmod; + int attcollation; + + /* + * The return type is not sent by the client. So, we first look up the + * function/procedure name from the catalog using a builtin system + * function. Then, we check the type of the function. If it's a procedure + * the return type will be always an integer in case of babel, and if + * it's a UDF, we just fetch the return type from catalog. + */ + pltsql_plugin_handler_ptr->pltsql_read_procedure_info( + &req->name, + &req->isStoredProcedure, + &atttypid, + &atttypmod, + &attcollation); + + args = CreateArgs(req->nTotalParams + 1); + + /* now add the same as second argument */ + (*fcinfo)->args[1].value = PointerGetDatum(args); + (*fcinfo)->args[1].isnull = false; + (*fcinfo)->nargs++; + + /* + * Once we know the return type, we've to prepare a parameter token, so that + * we can send the return value of as OUT parameter if required. + */ + returnToken = MakeEmptyParameterToken("", atttypid, atttypmod, attcollation); + returnToken->paramOrdinal = 0; + + pltsql_plugin_handler_ptr->pltsql_declare_var_callback ( + returnToken->paramMeta.pgTypeOid, /* oid */ + GetTypModForToken(returnToken), /* typmod */ + "@p0", /* name */ + PROARGMODE_INOUT, /* mode */ + (Datum) 0, /* datum */ + true, /* null */ + index, + &args, + fcinfo); + index++; + + /* + * For each token, we need to call pltsql_declare_var_block_handler API + * to declare the corresponding variable. + */ + for (token = req->dataParameter; token != NULL; token = token->next, index++) + { + char *paramName; + StringInfo name; + Datum pval; + bool isNull; + TdsIoFunctionInfo tempFuncInfo; + + name = &(token->paramMeta.colName); + + if (name->len == 0) + { + char buf[10]; + + snprintf(buf, sizeof(buf), "@p%d", index); + paramName = pnstrdup(buf, strlen(buf));; + } + else + paramName = downcase_truncate_identifier(name->data, + strlen(name->data), true); + + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(token->type, token->maxLen); + isNull = token->isNull; + + if (!isNull) + pval = tempFuncInfo->recvFuncPtr(req->messageData, token); + else + pval = (Datum) 0; + + pltsql_plugin_handler_ptr->pltsql_declare_var_callback ( + token->paramMeta.pgTypeOid, /* oid */ + GetTypModForToken(token), /* typmod */ + paramName, /* name */ + (token->flags == 0) ? + PROARGMODE_IN : PROARGMODE_INOUT, /* mode */ + pval, /* datum */ + isNull, /* null */ + index, + &args, + fcinfo); + } + + return returnToken; +} + +static void +SPCustomType(TDSRequestSP req) +{ + StringInfoData s; + InlineCodeBlock *codeblock; + LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS); + ParameterToken returnParamToken = NULL; + + int paramno; + Datum retval; + Datum *values; + bool *nulls; + + TdsErrorContext->err_text = "Processing SP_CUSTOMTYPE Request"; + if ((req->nTotalParams + 2) > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Too many parameters were provided in this RPC request. The maximum is %d", + FUNC_MAX_ARGS))); + + TDSInstrumentation(INSTR_TDS_USER_CUSTOM_SP); + + initStringInfo(&s); + FillStoredProcedureCallFromParameterToken(req, &s); + + codeblock = makeNode(InlineCodeBlock); + codeblock->source_text = s.data; + codeblock->langOid = 0; /* TODO does it matter */ + codeblock->langIsTrusted = true; + codeblock->atomic = false; + + /* Just to satisfy argument requirement */ + MemSet(fcinfo, 0, SizeForFunctionCallInfo(FUNC_MAX_ARGS)); + fcinfo->nargs = 1; + fcinfo->args[0].value = PointerGetDatum(codeblock); + fcinfo->args[0].isnull = false; + + PG_TRY(); + { + /* declare variables if there is any */ + returnParamToken = DeclareSPVariables(req, &fcinfo); + + /* Now, execute the same and retrieve the composite datum */ + retval = pltsql_plugin_handler_ptr->sp_executesql_callback (fcinfo); + MemoryContextSwitchTo(MessageContext); + + /* If pltsql_inline_handler does not end normally */ + if (fcinfo->isnull) + elog(ERROR, "pltsql_inline_handler failed"); + + /* Read out params and nulls after checking the retrived Datum for NULL */ + if (retval) + pltsql_plugin_handler_ptr->pltsql_read_out_param_callback(retval, &values, &nulls); + else if (req->nOutParams > 0) + elog(ERROR, "missing OUT parameter values from pltsql handler"); + } + PG_CATCH(); + { + if (TDS_DEBUG_ENABLED(TDS_DEBUG2)) + ereport(LOG, + (errmsg("stored procedure: %s", req->name.data), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + + tvp_lookup_list = NIL; + + PG_RE_THROW(); + } + PG_END_TRY(); + + /* + * Return value is sent as ReturnStatus token for SP and ReturnValue token + * for scalar UDFs. + */ + if (req->isStoredProcedure) + { + TdsSendReturnStatus(DatumGetInt32(values[0])); + } + else + { + SendReturnValueTokenInternal(returnParamToken, 0x02, NULL, + values[0], nulls[0], true); + } + + /* + * Send OUT parameters. Please note that the first entry contains the + * return status that we've already sent. + */ + for (paramno = 0; paramno < req->nOutParams; paramno++) + SendReturnValueTokenInternal(req->idxOutParams[paramno], 0x01, NULL, + values[paramno + 1], nulls[paramno + 1], true); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* + * Log immediately if dictated by log_statement + */ + if (pltsql_plugin_handler_ptr->stmt_needs_logging || TDS_DEBUG_ENABLED(TDS_DEBUG2)) + { + ErrorContextCallback *plerrcontext = error_context_stack; + error_context_stack = plerrcontext->previous; + ereport(LOG, + (errmsg("stored procedure: %s", req->name.data), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + error_context_stack = plerrcontext; + } + + /* + * Print TDS log duration, if log_duration is set + */ + TDSLogDuration(req->name.data); + pfree(codeblock); +} + +static void +SPUnprepare(TDSRequestSP req) +{ + LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS); + TdsErrorContext->err_text = "Processing SP_UNPREPARE Request"; + + TDSInstrumentation(INSTR_TDS_SP_UNPREPARE); + + /* Just to satisfy argument requirement */ + MemSet(fcinfo, 0, SizeForFunctionCallInfo(FUNC_MAX_ARGS)); + fcinfo->nargs = 1; + fcinfo->args[0].value = PointerGetDatum(req->handle); + fcinfo->args[0].isnull = false; + + TDSStatementBeginCallback(NULL, NULL); + + PG_TRY(); + { + /* Now, execute the unprepare handler and retrieve the composite datum */ + pltsql_plugin_handler_ptr->sp_unprepare_callback (fcinfo); + MemoryContextSwitchTo(MessageContext); + + /* If sp_unprepare_handler does not end normally. */ + if (fcinfo->isnull) + elog(ERROR, "pltsql_sp_unprepare_handler failed"); + } + PG_CATCH(); + { + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + + /* Return Status: 0 (success) or non-zero (failure). */ + TdsSendReturnStatus(0); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); +} + +static int +GetSetColMetadataForCharType(ParameterToken temp, StringInfo message, uint8_t tdsType, + uint64_t *mainOffset) +{ + + uint32_t collation; + uint8_t sortId; + uint64_t offset = *mainOffset; + uint16_t tempLen; + pg_enc enc; + + if ((offset + sizeof(tempLen) + + sizeof(collation) + + sizeof(sortId)) > + message->len) + return STATUS_ERROR; + + memcpy(&tempLen, &message->data[offset], sizeof(tempLen)); + temp->maxLen = tempLen; + offset += sizeof(tempLen); + memcpy(&collation, &message->data[offset], sizeof(collation)); + offset += sizeof(collation); + sortId = message->data[offset]; + offset += sizeof(sortId); + + /* If we recieve 0 value for LCID then we should treat it as a default LCID.*/ + enc = TdsGetEncoding(collation); + + /* + * TODO: we should send collation name here instead of Locale ID. + */ + if (enc == -1) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Babelfish does not support %d Locale with %d collate flags and %d SortId", collation & 0xFFFFF, (collation & 0xFFF00000) >> 20, sortId))); + + SetColMetadataForCharType(&temp->paramMeta, tdsType, + collation & 0xFFFFF, enc, + (collation & 0xFFF00000) >> 20, + sortId, tempLen); + + *mainOffset = offset; + return STATUS_OK; +} + +static int +GetSetColMetadataForTextType(ParameterToken temp, StringInfo message, uint8_t tdsType, + uint64_t *mainOffset) +{ + + uint32_t collation; + uint8_t sortId; + uint64_t offset = *mainOffset; + pg_enc enc; + + if ((offset + sizeof(temp->maxLen) + + sizeof(collation) + + sizeof(sortId)) > message->len) + return STATUS_ERROR; + + memcpy(&temp->maxLen, &message->data[offset], sizeof(temp->maxLen)); + offset += sizeof(temp->maxLen); + memcpy(&collation, &message->data[offset], sizeof(collation)); + offset += sizeof(collation); + sortId = message->data[offset]; + offset += sizeof(sortId); + + /* If we recieve 0 value for LCID then we should treat it as a default LCID.*/ + enc = TdsGetEncoding(collation); + + /* + * TODO: we should send collation name here instead of Locale ID. + */ + if (enc == -1) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Babelfish does not support %d Locale with %d collate flags and %d SortId", collation & 0xFFFFF, (collation & 0xFFF00000) >> 20, sortId))); + + SetColMetadataForTextType(&temp->paramMeta, tdsType, + collation & 0xFFFFF, enc, + (collation & 0xFFF00000) >> 20, + sortId, temp->maxLen); + + *mainOffset = offset; + return STATUS_OK; +} + +int +ReadPlp(ParameterToken temp, StringInfo message, uint64_t *mainOffset) +{ + + uint64_t plpTok; + Plp plpTemp, plpPrev = NULL; + unsigned long lenCheck = 0; + uint64_t offset = *mainOffset; + + memcpy(&plpTok , &message->data[offset], sizeof(plpTok)); + offset += sizeof(plpTok); + temp->plp = NULL; + + /* NULL Check */ + if (plpTok == PLP_NULL) + { + temp->isNull = true; + *mainOffset = offset; + return STATUS_OK; + } + + while (true) + { + uint32_t tempLen; + + if (offset + sizeof(tempLen) > message->len) + return STATUS_ERROR; + memcpy(&tempLen , &message->data[offset], sizeof(tempLen)); + offset += sizeof(tempLen); + + /* PLP Terminator */ + if (tempLen == PLP_TERMINATOR) + break; + plpTemp = palloc0(sizeof(PlpData)); + plpTemp->next = NULL; + plpTemp->offset = offset; + plpTemp->len = tempLen; + if (plpPrev == NULL) + { + plpPrev = plpTemp; + temp->plp = plpTemp; + } + else + { + plpPrev->next = plpTemp; + plpPrev = plpPrev->next; + } + if (offset + plpTemp->len > message->len) + return STATUS_ERROR; + + offset += plpTemp->len; + lenCheck += plpTemp->len; + } + + if (plpTok != PLP_UNKNOWN_LEN) + { + /* Length check */ + if (lenCheck != plpTok) + return STATUS_ERROR; + } + + *mainOffset = offset; + return STATUS_OK; +} + +static void +InitialiseParameterToken(TDSRequestSP request) +{ + /* Initialize */ + request->handleParameter = NULL; + request->cursorHandleParameter = NULL; + request->cursorPreparedHandleParameter = NULL; + request->queryParameter = NULL; + request->cursorExtraArg1 = NULL; + request->cursorExtraArg2 = NULL; + request->cursorExtraArg3 = NULL; + request->dataParameter = NULL; +} + +static int +ReadParameters(TDSRequestSP request, uint64_t offset, StringInfo message, int *parameterCount) +{ + ParameterToken temp, prev = NULL; + int len = 0; + TdsIoFunctionInfo tempFuncInfo; + uint16 paramOrdinal = 0; + int retStatus; + + while(offset < message->len) + { + uint8_t tdsType; + + /* + * If next byte after a parameter is a BatchFlag + * we store the following parameters for the next RPC packet in the Batch. + * BatchFlag is '0xFF' For TDS versions more than or equal to 7.2 + * and '0x80' for Versions lower than or equal to TDS 7.1 + */ + if((uint8_t) message->data[offset] == GetRpcBatchSeparator(GetClientTDSVersion())) + { + /* Increment offset by 1 to ignore the batch-separator. */ + request->batchSeparatorOffset = offset + 1; + + /* Need to save the lenght of the message, since only messageData field is set for TdsRequestCtrl. */ + request->messageLen = message->len; + return STATUS_OK; + } + + temp = palloc0(sizeof(ParameterTokenData)); + len = message->data[offset++]; + + /* + * Call initStringInfo for every parameter name even if len is 0 + * so that the processing logic can check the length field from + * temp->name->len + */ + initStringInfo(&(temp->paramMeta.colName)); + + if (len > 0) + { + /* + * FIXME: parameter name is in UTF-16 format. Fix this separately. + */ + TdsUTF16toUTF8StringInfo(&(temp->paramMeta.colName), &(message->data[offset]), 2 * len); + offset += 2 * len; + len = 0; + } + + memcpy(&temp->flags, &message->data[offset], sizeof(temp->flags)); + offset += sizeof(temp->flags); + +#ifdef FAULT_INJECTOR + /* + * We need to have a lock since we are injecting pre-parsing + * fault while parsing ReadParameters. + */ + if (!lockForFaultInjection) + { + TdsMessageWrapper wrapper; + lockForFaultInjection = true; + wrapper.message = message; + wrapper.messageType = TDS_RPC; + wrapper.offset = offset; + FAULT_INJECT(ParseRpcType, &wrapper); + lockForFaultInjection = false; + } +#endif + tdsType = message->data[offset++]; + + temp->type = tdsType; + temp->paramOrdinal = paramOrdinal; + paramOrdinal++; + + switch (tdsType) + { + case TDS_TYPE_TEXT: + case TDS_TYPE_NTEXT: + { + /* Type TEXT and NTEXT are deprecated large objects */ + if(temp->flags & SP_FLAGS_BYREFVALUE) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("Invalid parameter %d (\"%s\"): Data type 0x%02X is a deprecated large object, or LOB, but is marked as output parameter. " + "Deprecated types are not supported as output parameters. Use current large object types instead.", + paramOrdinal, temp->paramMeta.colName.data, tdsType))); + retStatus = GetSetColMetadataForTextType(temp, message, tdsType, &offset); + if (retStatus != STATUS_OK) + return retStatus; + + memcpy(&temp->len, &message->data[offset], sizeof(temp->len)); + + /* for Null values, Len field is set to -1(0xFFFFFFFF) */ + if (temp->len == 0xFFFFFFFF) + { + temp->len = 0; + temp->isNull = true; + } + + CheckForInvalidLength(temp); + + offset += sizeof(temp->len); + temp->dataOffset = offset; + offset += temp->len; + } + break; + case TDS_TYPE_IMAGE: + case TDS_TYPE_SQLVARIANT: + { + /* Type IMAGE is a deprecated large object*/ + if((temp->flags & SP_FLAGS_BYREFVALUE) && tdsType == TDS_TYPE_IMAGE) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("Invalid parameter %d (\"%s\"): Data type 0x%02X is a deprecated large object, or LOB, but is marked as output parameter. " + "Deprecated types are not supported as output parameters. Use current large object types instead.", + paramOrdinal, temp->paramMeta.colName.data, tdsType))); + SetColMetadataForImageType(&temp->paramMeta, tdsType); + + memcpy(&temp->len, &message->data[offset], sizeof(temp->len)); + + /* for Null values, Len field is set to -1(0xFFFFFFFF) or 0 */ + if (temp->len == 0xFFFFFFFF || + (tdsType == TDS_TYPE_SQLVARIANT && temp->len == 0)) + { + temp->len = 0; + temp->isNull = true; + } + + if (tdsType == TDS_TYPE_SQLVARIANT && temp->len > temp->paramMeta.metaEntry.type8.maxSize) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Parameter %d (\"%s\"): Data type 0x%02X (sql_variant) has an invalid length for type-specific metadata.", + paramOrdinal, temp->paramMeta.colName.data, tdsType))); + + /* + * Skipping two sequence of 4 Bytes, each sequence containing + * actual image file length + */ + offset += 2 * sizeof(temp->len); + + temp->dataOffset = offset; + offset += temp->len; + } + break; + case TDS_TYPE_CHAR: + case TDS_TYPE_NCHAR: + case TDS_TYPE_VARCHAR: + case TDS_TYPE_NVARCHAR: + { + retStatus = GetSetColMetadataForCharType(temp, message, tdsType, &offset); + if (retStatus != STATUS_OK) + return retStatus; + + /* + * If varchar/Nvchar is created with max keyword, then + * data will come in PLP chuncks + */ + if (temp->maxLen == 0xFFFF) + { + retStatus = ReadPlp(temp, message, &offset); + CheckPLPStatusNotOK(temp, retStatus); + } + else + { + /* + * Nvarchar datatype have length field of 2 byte + */ + uint16_t tempLen; + + if (offset + sizeof(tempLen) > message->len) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Parameter %d (\"%s\"): The supplied length is not valid for data type CHAR/NCHAR/VARCHAR/NVARCHAR. " + "Check the source data for invalid lengths. An example of an invalid length is data of nchar type with an odd length in bytes.", + paramOrdinal, temp->paramMeta.colName.data))); + + memcpy(&tempLen , &message->data[offset], sizeof(tempLen)); + temp->len = tempLen; + offset += sizeof(tempLen); + temp->dataOffset = offset; + + /* + * For Null values, Len field is set to 65535(0xffff) + */ + if (temp->len == 0xffff) + { + temp->len = 0; + temp->isNull = true; + } + + if (offset + temp->len > message->len) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Parameter %d (\"%s\"): Data type 0x%02X has an invalid data length or metadata length.", + paramOrdinal, temp->paramMeta.colName.data, tdsType))); + + offset += temp->len; + } + } + break; + case TDS_TYPE_BIT: + case TDS_TYPE_INTEGER: + case TDS_TYPE_FLOAT: + case TDS_TYPE_MONEYN: + case TDS_TYPE_DATETIMEN: + case TDS_TYPE_UNIQUEIDENTIFIER: + { + if ((offset + 2) > message->len) + return STATUS_ERROR; + temp->maxLen = message->data[offset++]; + /* + * Fixed-length datatypes have length field of 1 byte + */ + temp->len = message->data[offset++]; + + if (temp->len == 0) + temp->isNull = true; + + CheckForInvalidLength(temp); + + temp->dataOffset = offset; + if (offset + temp->len > message->len) + return STATUS_ERROR; + offset += temp->len; + + SetColMetadataForFixedType(&temp->paramMeta, tdsType, temp->maxLen); + } + break; + case TDS_TYPE_TABLE: + { + temp->tvpInfo = palloc0(sizeof(TvpData)); + + /* Sets the col metadata and also the corresponding row data. */ + SetColMetadataForTvp(temp, message, &offset); + } + break; + case TDS_TYPE_BINARY: + case TDS_TYPE_VARBINARY: + { + uint16 len; + + memcpy(&len, &message->data[offset], sizeof(len)); + offset += sizeof(len); + temp->maxLen = len; + + + SetColMetadataForBinaryType(&temp->paramMeta, tdsType, temp->maxLen); + + /* + * If varbinary is created with max keyword, + * data will come in PLP chuncks + */ + if (temp->maxLen == 0xffff) + { + retStatus = ReadPlp(temp, message, &offset); + CheckPLPStatusNotOK(temp, retStatus); + } + else + { + memcpy(&len, &message->data[offset], sizeof(len)); + offset += sizeof(len); + temp->len = len; + /* + * Binary, varbinary datatypes have length field of 2 bytes + * For NULL value, Len field is set to 65535(0xffff) + */ + if (temp->len == 0xffff) + { + temp->len = 0; + temp->isNull = true; + } + + CheckForInvalidLength(temp); + + temp->dataOffset = offset; + if (offset + temp->len > message->len) + return STATUS_ERROR; + offset += temp->len; + } + } + break; + case TDS_TYPE_DATE: + { + if ((offset + 1) > message->len) + return STATUS_ERROR; + + temp->len = message->data[offset++]; + temp->maxLen = 3; + + if (temp->len == 0) + temp->isNull = true; + + CheckForInvalidLength(temp); + + temp->dataOffset = offset; + if (offset + temp->len > message->len) + return STATUS_ERROR; + offset += temp->len; + + SetColMetadataForDateType(&temp->paramMeta, tdsType); + } + break; + case TDS_TYPE_TIME: + case TDS_TYPE_DATETIME2: + case TDS_TYPE_DATETIMEOFFSET: + { + uint8_t scale = message->data[offset++]; + temp->len = message->data[offset++]; + + if (temp->len == 0) + temp->isNull = true; + + if (tdsType == TDS_TYPE_TIME) + temp->maxLen = 5; + else if (tdsType == TDS_TYPE_DATETIME2) + temp->maxLen = 8; + else if (tdsType == TDS_TYPE_DATETIMEOFFSET) + temp->maxLen = 10; + + CheckForInvalidLength(temp); + + temp->dataOffset = offset; + if ((offset + temp->len) > message->len) + return STATUS_ERROR; + offset += temp->len; + + SetColMetadataForTimeType(&temp->paramMeta, tdsType, scale); + } + break; + case TDS_TYPE_DECIMALN: + case TDS_TYPE_NUMERICN: + { + uint8_t scale; + uint8_t precision; + + temp->maxLen = message->data[offset++]; + + precision = message->data[offset++]; + scale = message->data[offset++]; + + if (scale > precision) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Parameter %d (\"%s\"): The supplied value is not a valid instance of data type Numeric/Decimal. Check the source data for invalid values. " + "An example of an invalid value is data of numeric type with scale greater than precision", + paramOrdinal, temp->paramMeta.colName.data))); + + temp->len = message->data[offset++]; + + if (temp->len == 0) + temp->isNull = true; + + CheckForInvalidLength(temp); + + temp->dataOffset = offset; + + if ((offset + temp->len) > message->len) + return STATUS_ERROR; + + /* + * XXX: We do not support DECIMAL so internally we store + * DECIMAL as NUMERIC. + */ + temp->type = TDS_TYPE_NUMERICN; + tdsType = TDS_TYPE_NUMERICN; + + SetColMetadataForNumericType(&temp->paramMeta, TDS_TYPE_NUMERICN, temp->maxLen, + precision, scale); + offset += temp->len; + } + break; + case TDS_TYPE_XML: + { + temp->maxLen = message->data[offset++]; + retStatus = ReadPlp(temp, message, &offset); + CheckPLPStatusNotOK(temp, retStatus); + } + break; + default: + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Parameter %d (\"%s\"): Data type 0x%02X is unknown.", + paramOrdinal, temp->paramMeta.colName.data, tdsType))); + } + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(tdsType, temp->maxLen); + + /* + * We save the recvFunc address here, so that during the bind we can directly + * use the recv function and save one extra lookup. We also store the sender + * sendFunc address here which can be used to send back OUT parameters. + */ + SetParamMetadataCommonInfo(&(temp->paramMeta), tempFuncInfo); + + /* Explicity retrieve the oid for TVP type and map it. */ + if (temp->paramMeta.pgTypeOid == InvalidOid && tdsType == TDS_TYPE_TABLE) + { + int rc; + HeapTuple row; + bool isnull; + TupleDesc tupdesc; + char * query; + + StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); + if ((rc = SPI_connect()) < 0) + elog(ERROR, "SPI_connect() failed in TDS Listener " + "with return code %d", rc); + + query = psprintf("SELECT '%s'::regtype::oid", temp->tvpInfo->tvpTypeName); + + rc = SPI_execute(query, false, 1); + if(rc != SPI_OK_SELECT) + elog(ERROR, "Failed to insert in the underlying table for table-valued parameter: %d", rc); + + tupdesc = SPI_tuptable->tupdesc; + row = SPI_tuptable->vals[0]; + + temp->paramMeta.pgTypeOid = DatumGetObjectId(SPI_getbinval(row, tupdesc, + 1, &isnull)); + + SPI_finish(); + PopActiveSnapshot(); + CommitTransactionCommand(); + } + + temp->next = NULL; + if (prev == NULL) + { + prev = temp; + request->parameter = temp; + } + else + { + prev->next = temp; + prev = temp; + } + *parameterCount += 1; + } + /* + * We set the flag for offset as an invalid value so as to + * to execute the Flush phase in TdsSocketBackend. + */ + request->batchSeparatorOffset = message->len; + return STATUS_OK; +} + +/* + * GetSPCursorHandleParameter - Generate or fetch the cursor handle parameter for a + * SP_CURSOR* request. + */ +static void +GetSPCursorHandleParameter(TDSRequestSP request) +{ + switch(request->spType) + { + case SP_CURSORPREPEXEC: + case SP_CURSOREXEC: + case SP_CURSOROPEN: + break; + case SP_CURSORFETCH: + case SP_CURSORCLOSE: + case SP_CURSOR: + case SP_CURSOROPTION: + { + /* Fetch the handle from the request */ + ParameterToken token = request->cursorHandleParameter; + + /* handle must exist */ + Assert(token); + + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&request->cursorHandle, &request->messageData[token->dataOffset], + sizeof(uint32)); + + /* the handle must be valid */ + if (request->cursorHandle == SP_CURSOR_HANDLE_INVALID) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cursor %d doesn't exist", request->cursorHandle))); + + } + break; + case SP_CURSORUNPREPARE: + case SP_CURSORPREPARE: + case SP_PREPARE: + case SP_PREPEXEC: + case SP_PREPEXECRPC: + case SP_EXECUTE: + case SP_UNPREPARE: + case SP_CUSTOMTYPE: + case SP_EXECUTESQL: + request->cursorHandle = SP_CURSOR_HANDLE_INVALID; + break; + default: + Assert(0); + } +} + +/* + * GetSPCursorPreparedHandleParameter - Generate or fetch the handle parameter + * for a SP_CURSOR_[PREPEXEC/EXEC] request. + */ +static void +GetSPCursorPreparedHandleParameter(TDSRequestSP request) +{ + switch(request->spType) + { + case SP_CURSORPREPEXEC: + case SP_CURSOROPEN: + case SP_CURSORFETCH: + case SP_CURSORCLOSE: + case SP_CURSOR: + case SP_CURSOROPTION: + case SP_CUSTOMTYPE: + case SP_EXECUTESQL: + case SP_PREPARE: + case SP_PREPEXEC: + case SP_EXECUTE: + case SP_PREPEXECRPC: + case SP_UNPREPARE: + /* handle will be retrieved from babelfishpg_tsql extension */ + request->cursorPreparedHandle = SP_CURSOR_PREPARED_HANDLE_INVALID; + break; + case SP_CURSOREXEC: + case SP_CURSORUNPREPARE: + case SP_CURSORPREPARE: + { + /* Fetch the handle from the request */ + ParameterToken token = request->cursorPreparedHandleParameter; + + /* handle must exist */ + Assert(token); + + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&request->cursorPreparedHandle, &request->messageData[token->dataOffset], + sizeof(uint32)); + + /* the handle must be valid */ + Assert(request->cursorPreparedHandle != SP_CURSOR_PREPARED_HANDLE_INVALID); + + } + break; + default: + Assert(0); + } +} + +/* + * GetSPHandleParameter - Generate or fetch the handle parameter for a SP_* + * request. + */ +static void +GetSPHandleParameter(TDSRequestSP request) +{ + switch(request->spType) + { + case SP_CURSORPREPARE: + case SP_CURSORPREPEXEC: + case SP_CURSOREXEC: + case SP_CURSOROPEN: + case SP_CURSORFETCH: + case SP_CURSORCLOSE: + case SP_CURSOR: + case SP_CURSOROPTION: + case SP_CURSORUNPREPARE: + case SP_CUSTOMTYPE: + case SP_EXECUTESQL: + case SP_PREPARE: + case SP_PREPEXEC: + case SP_PREPEXECRPC: + /* handle will be retrieved from babelfishpg_tsql extension */ + request->handle = SP_HANDLE_INVALID; + break; + case SP_EXECUTE: + case SP_UNPREPARE: + { + /* Fetch the handle from the request */ + ParameterToken token = request->handleParameter; + + /* handle must exist */ + Assert(token); + + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&request->handle, &request->messageData[token->dataOffset], + sizeof(uint32)); + + /* the handle must be valid */ + Assert(request->handle != SP_HANDLE_INVALID); + + } + break; + default: + Assert(0); + } +} + +/* + * FillStoredProcedureCallFromParameterToken - construct a query string in the + * following format: + * + * if tsql dialect, then + * EXECUTE @P0, @P1, .... + * or + * EXECUTE @param1 = @param1, @param2 = @param2, .... + * + * XXX: The format @param1 = @param1 is not currently supported for UDFs in + * Babel. Once we support that feature, we need to build the string format + * accordingly. Currently, it's commented out. + */ +static inline void +FillStoredProcedureCallFromParameterToken(TDSRequestSP req, StringInfo inBuf) +{ + int paramno; + int numParams; + ParameterToken token = NULL; + StringInfo name; + + Assert(req->queryParameter == NULL); + + numParams = req->nTotalParams; + + if (sql_dialect != SQL_DIALECT_TSQL) + elog(ERROR, "sql dialect is not set to TSQL"); + + paramno = 0; + appendStringInfo(inBuf, "EXECUTE @p%d = %s ", paramno, req->name.data); + paramno++; + + if (numParams > 0) + { + token = req->dataParameter; + name = &(token->paramMeta.colName); + + if (name->len == 0) + appendStringInfo(inBuf, "@p%d", paramno); + else + appendStringInfo(inBuf, "%s = %s", name->data, name->data); + token = token->next; + paramno++; + } + + for (; token != NULL; token = token->next) + { + name = &(token->paramMeta.colName); + + if (name->len == 0) + appendStringInfo(inBuf, ", @P%d", paramno); + else + appendStringInfo(inBuf, ", %s = %s", name->data, name->data); + paramno++; + } + + appendStringInfoCharMacro(inBuf, '\0'); +} + +/* + * GetQueryFromParameterToken - extract the query from parameter token + * + */ +static inline void +FillQueryFromParameterToken(TDSRequestSP req, StringInfo inBuf) +{ + ParameterToken token = req->queryParameter; + + TdsReadUnicodeDataFromTokenCommon(req->messageData, token, inBuf); + appendStringInfoCharMacro(inBuf, '\0'); +} + +/* + * InitializeDataParamTokenIndex - initialize the IN/OUT parameter index array + */ +static inline void +InitializeDataParamTokenIndex(TDSRequestSP request) +{ + ParameterToken token; + uint16 idOutParam = 0; + int32 paramCount = 0; + + request->nOutParams = 0; + request->idxOutParams = NULL; + + for (token = request->dataParameter; token != NULL; token = token->next) + { + request->nTotalParams++; + + if ((token->flags & 0x01) == 1) + request->nOutParams++; + } + + /* Allocate the memory for OUT parameter array. */ + if (request->nOutParams > 0) + request->idxOutParams = palloc(request->nOutParams + * sizeof(ParameterToken)); + + /* + * Store all OUT parameters together. + */ + for (token = request->dataParameter; token != NULL; token = token->next) + { + + if ((token->flags & 0x01) == 1) + request->idxOutParams[idOutParam++] = token; + paramCount++; + } + + Assert(request->nOutParams == idOutParam); +} + +static inline void +FillOidsFromParameterToken(TDSRequestSP req, StringInfo inBuf) +{ + ParameterToken token; + + /* store num of data params amd their oids */ + enlargeStringInfo(inBuf, sizeof(uint16) + + req->nTotalParams * sizeof(Oid)); + + pq_writeint16(inBuf, req->nTotalParams); + + for (token = req->dataParameter; token != NULL; token = token->next) + { + uint32 paramType = (uint32) token->paramMeta.pgTypeOid; + pq_writeint32(inBuf, paramType); + } +} + +static inline void +FillColumnInfoFromParameterToken(TDSRequestSP req, StringInfo inBuf) +{ + /* + * 1. store num of data params amd their formats + * + * TODO: For now, we set this to zero to indicate that there are no + * parameters or that the parameters all use the default format (text). + */ + enlargeStringInfo(inBuf, sizeof(uint16)); + + pq_writeint16(inBuf, 0); + + /* 2. store num of data params */ + enlargeStringInfo(inBuf, sizeof(uint16)); + pq_writeint16(inBuf, req->nTotalParams); +} + +static inline Portal +GetPortalFromCursorHandle(const int portalHandle, bool missingOk) +{ + Portal portal; + char cursorHandle[INT32_STRLEN]; + + sprintf(cursorHandle, "%d", portalHandle); + + portal = GetPortalByName(cursorHandle); + + if (!missingOk && !PortalIsValid(portal)) + elog(ERROR, "portal \"%s\" does not exist", cursorHandle); + + return portal; +} + +static inline void +FetchCursorOptions(TDSRequestSP req) +{ + ParameterToken token; + + token = req->cursorExtraArg1; + Assert(token); + + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&req->scrollopt, &req->messageData[token->dataOffset], + sizeof(uint32)); + + token = req->cursorExtraArg2; + Assert(token); + + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&req->ccopt, &req->messageData[token->dataOffset], + sizeof(uint32)); + +} + +static int +SetCursorOption(TDSRequestSP req) +{ + + int curoptions = 0; + + /* we're always going to fetch in binary format */ + curoptions = CURSOR_OPT_BINARY; + + /* + * XXX: For now, map STATIC cursor to WITH HOLD cursor option. It materializes + * the result in a temp file when the transaction is closed. But, a STATIC + * cursor should also return the number of tuples in the result set. We've + * not implemented that yet. + */ + if (req->scrollopt & SP_CURSOR_SCROLLOPT_STATIC) + curoptions |= CURSOR_OPT_HOLD; + else if ((req->ccopt & SP_CURSOR_SCROLLOPT_CHECK_ACCEPTED_TYPES) && + (req->ccopt & SP_CURSOR_SCROLLOPT_STATIC_ACCEPTABLE)) + curoptions |= CURSOR_OPT_HOLD; + + if (req->scrollopt & SP_CURSOR_SCROLLOPT_FORWARD_ONLY) + curoptions |= CURSOR_OPT_NO_SCROLL; + else if ((req->ccopt & SP_CURSOR_SCROLLOPT_CHECK_ACCEPTED_TYPES) && + (req->ccopt & SP_CURSOR_SCROLLOPT_FORWARD_ONLY_ACCEPTABLE)) + curoptions |= CURSOR_OPT_NO_SCROLL; + else + curoptions |= CURSOR_OPT_SCROLL; + + return curoptions; +} + +/* + * Send the Cursor Response: + * Only for sp_cursorprepexec, we send the handle for the prepared statement + * otherwise this is common to sp_cursoropen, sp_cursorprepexec, sp_cursorexec + */ +static void +SendCursorResponse(TDSRequestSP req) +{ + int cmd_type = TDS_CMD_UNKNOWN; + Portal portal; + + /* fetch the portal */ + portal = GetPortalFromCursorHandle(req->cursorHandle, false); + + /* + * If we are in aborted transaction state, we can't run + * PrepareRowDescription(), because that needs catalog accesses. + * Hence, refuse to Describe portals that return data. + */ + if (IsAbortedTransactionBlockState() && + portal->tupDesc) + elog(ERROR, "current transaction is aborted, " + "commands ignored until end of transaction block"); + + if (portal->commandTag && portal->commandTag == CMDTAG_SELECT) + { + cmd_type = TDS_CMD_SELECT; + } + else + { + elog(ERROR, "TDS: unhandled cursor completionTag '%s'", + portal->commandTag ? GetCommandTagName(portal->commandTag) : ""); + cmd_type = TDS_CMD_UNKNOWN; + } + + /* + * First get all the information needed to construct the tokens. We don't + * want to throw error in the middle of sending the response. That'll + * break the protocol. We also need to fetch the primary keys for dynamic + * and keyset cursors (XXX: these cursors are not yet implemented). + */ + PrepareRowDescription(portal->tupDesc, FetchPortalTargetList(portal), + portal->formats, true, + (req->scrollopt & (SP_CURSOR_SCROLLOPT_DYNAMIC | SP_CURSOR_SCROLLOPT_KEYSET))); + + /* Send COLMETADATA token, TABNAME token and COLINFO token */ + SendColumnMetadataToken(portal->tupDesc->natts, true /* send ROWSTAT column */); + SendTabNameToken(); + SendColInfoToken(portal->tupDesc->natts, true /* send ROWSTAT column */); + + TdsSendDone(TDS_TOKEN_DONEINPROC, TDS_DONE_MORE, cmd_type, 0); + + /* + * return codes - procedure executed successfully (0) + * + * XXX: How to implement other return codes related to different error scenarios? + */ + TdsSendReturnStatus(0); + + /* + * Send the handle for the PrePared Plan only for the + * sp_cursorprepexec request + * + */ + if (req->spType == SP_CURSORPREPEXEC) + SendReturnValueTokenInternal(req->cursorPreparedHandleParameter, 0x01, NULL, + UInt32GetDatum(req->cursorPreparedHandle), false, false); + + /* send the cursor handle */ + SendReturnValueTokenInternal(req->cursorHandleParameter, 0x01, NULL, + UInt32GetDatum(req->cursorHandle), false, false); + + /* + * If the scrollopt value is not appropriate for the cursor w.r.t the sql statement, + * the engine can override the scrollopt value. + * TODO: Implement this feature in the engine. Currently, PG engine doesn't have + * a way to modify the input value. For now, just return the input value. + */ + if (req->cursorExtraArg1 && (req->cursorExtraArg1->flags & 0x01) == 1) + SendReturnValueTokenInternal(req->cursorExtraArg1, 0x01, NULL, + UInt32GetDatum((int) req->scrollopt), false, false); + + /* + * If the ccopt value is not appropriate for the cursor w.r.t the sql statement, + * the engine can override the ccopt value. + * TODO: Implement this feature in the engine. Currently, PG engine doesn't have + * a way to modify the input value. For now, just return the input value. + */ + if (req->cursorExtraArg2 && (req->cursorExtraArg2->flags & 0x01) == 1) + SendReturnValueTokenInternal(req->cursorExtraArg2, 0x01, NULL, + UInt32GetDatum((int) req->ccopt), false, false); + + /* + * If the cursor is populated as part of sp_cursoropen request packet (STATIC, + * INSENSITIVE cursors), then we should return the actual number of rows in + * the result set. For dynamic cursors, we should return -1. Ideally, we should + * fetch the correct value from @@CURSOR_ROWS global variable. + * + * TODO: Implement @@CURSOR_ROWS global variable. As part of that implementation, + * we should check how to get the correct number of rows without fetching anything + * from the cursor. For now, always send -1 and hope the client driver doesn't + * complain. + */ + if (req->cursorExtraArg3 && (req->cursorExtraArg3->flags & 0x01) == 1) + SendReturnValueTokenInternal(req->cursorExtraArg3, 0x01, NULL, + UInt32GetDatum((int) -1), false, false); + + /* + * command type - execute (0xe0) + */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); +} + +static void +HandleSPCursorOpenCommon(TDSRequestSP req) +{ + int curoptions = 0; + int ret; + StringInfoData buf; + + TdsErrorContext->err_text = "Processing SP_CURSOROPEN Common Request"; + /* fetch cursor options */ + FetchCursorOptions(req); + + curoptions = SetCursorOption(req); + + TDSStatementBeginCallback(NULL, NULL); + + PG_TRY(); + { + if (req->spType == SP_CURSOREXEC) + { + ret = pltsql_plugin_handler_ptr->sp_cursorexecute_callback((int)req->cursorPreparedHandle, (int *)&req->cursorHandle, &req->scrollopt, &req->ccopt, + NULL /* TODO row_count */, req->nTotalParams, req->boundParamsData, req->boundParamsNullList); + } + + else + { + initStringInfo(&buf); + /* fetch the query */ + FillQueryFromParameterToken(req, &buf); + + switch (req->spType) + { + case SP_CURSOROPEN: + ret = pltsql_plugin_handler_ptr->sp_cursoropen_callback((int *)&req->cursorHandle, buf.data, &req->scrollopt, &req->ccopt, + NULL /* TODO row_count */, req->nTotalParams, req->boundParamsData, req->boundParamsNullList); + break; + case SP_CURSORPREPARE: + ret = pltsql_plugin_handler_ptr->sp_cursorprepare_callback((int *)&req->cursorPreparedHandle, buf.data, curoptions, &req->scrollopt, &req->ccopt, + (int)req->nTotalBindParams, req->boundParamsOidList); + break; + case SP_CURSORPREPEXEC: + ret = pltsql_plugin_handler_ptr->sp_cursorprepexec_callback((int *)&req->cursorPreparedHandle, (int *)&req->cursorHandle, buf.data, curoptions, &req->scrollopt, &req->ccopt, + NULL /* TODO row_count */, req->nTotalParams, (int)req->nTotalBindParams, req->boundParamsOidList, req->boundParamsData, req->boundParamsNullList); + break; + default: + Assert(0); + } + + if (ret > 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("sp_cursoropen failed: %d", ret))); + + } + + MemoryContextSwitchTo(MessageContext); + } + + PG_CATCH(); + { + TDSLogStatementCursorHandler(req, buf.data, PRINT_BOTH_CURSOR_HANDLE); + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* Send the response now */ + SendCursorResponse(req); + + /* Log immediately if dictated by log_statement and log_duration */ + TDSLogStatementCursorHandler(req, buf.data, PRINT_BOTH_CURSOR_HANDLE); +} + +/* + * TODO: can be reused this for prepare-exec + */ +static void +GenerateBindParamsData(TDSRequestSP req) +{ + uint16_t count = 0; + ParameterToken tempBindParam; + bool isNull; + TdsIoFunctionInfo tempFuncInfo; + Oid ptype; + Datum pval; + + TdsErrorContext->err_text = "Generating Bind Parameters' Data"; + tempBindParam = req->dataParameter; + + while (tempBindParam != NULL) + { + count++; + tempBindParam = tempBindParam->next; + } + + req->nTotalBindParams = count; + + /* If count == 0, then there is no bind Parameter */ + if (count == 0) + { + req->boundParamsData = NULL; + req->boundParamsNullList = NULL; + req->boundParamsOidList = NULL; + return; + } + req->boundParamsData = palloc0(sizeof(Datum) * count); + req->boundParamsNullList = palloc0(sizeof(char) * count); + req->boundParamsOidList = palloc0(sizeof(Oid) * count); + + tempBindParam = req->dataParameter; + + count = 0; + while (tempBindParam != NULL) + { + + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(tempBindParam->type, + tempBindParam->maxLen); + isNull = tempBindParam->isNull; + + ptype = tempBindParam->paramMeta.pgTypeOid; + + if (!isNull) + pval = tempFuncInfo->recvFuncPtr(req->messageData, tempBindParam); + else + pval = (Datum) 0; + + req->boundParamsData[count] = pval; + req->boundParamsNullList[count] = (isNull) ? 'n' : ' '; + req->boundParamsOidList[count] = ptype; + + count++; + tempBindParam = tempBindParam->next; + } +} + +static void +FetchAndValidateCursorFetchOptions(TDSRequestSP req, int *fetchType, + int *rownum, int *howMany) +{ + ParameterToken token; + + token = req->cursorExtraArg1; + Assert(token); + + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(fetchType, &req->messageData[token->dataOffset], sizeof(uint32)); + + switch(*fetchType) + { + case SP_CURSOR_FETCH_FIRST: + case SP_CURSOR_FETCH_NEXT: + case SP_CURSOR_FETCH_PREV: + case SP_CURSOR_FETCH_LAST: + case SP_CURSOR_FETCH_ABSOLUTE: + break; + /* + * The following cursor options are not supported in postgres. Although + * postgres supports the relative cursor fetch option, but the behaviour + * in TDS protocol is very different from postgres. + */ + case SP_CURSOR_FETCH_RELATIVE: + case SP_CURSOR_FETCH_REFRESH: + case SP_CURSOR_FETCH_INFO: + case SP_CURSOR_FETCH_PREV_NOADJUST: + case SP_CURSOR_FETCH_SKIP_UPDT_CNCY: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cursor fetch type %X not supported", *fetchType))); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid cursor fetch type %X", *fetchType))); + } + + token = req->cursorExtraArg2; + + if (token) + { + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(rownum, &req->messageData[token->dataOffset], sizeof(uint32)); + + /* + * Rownum is used to specify the row position for the ABSOLUTE and INFO + * fetchtype. And, it serves as the row offset for the fetchtype bit + * value RELATIVE. It is ignored for all other values. + */ + if (*fetchType != SP_CURSOR_FETCH_ABSOLUTE && + *fetchType != SP_CURSOR_FETCH_RELATIVE && + *fetchType != SP_CURSOR_FETCH_INFO) + *rownum = -1; + + } + else + *rownum = -1; + + token = req->cursorExtraArg3; + + if (token) + { + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(howMany, &req->messageData[token->dataOffset], sizeof(uint32)); + + /* + * For the fetchtype values of NEXT, PREV, ABSOLUTE, RELATIVE, and + * PREV_NOADJUST, an nrow value of 0 is not valid. + */ + if (*howMany == 0) + { + if (*fetchType == SP_CURSOR_FETCH_NEXT || + *fetchType == SP_CURSOR_FETCH_PREV || + *fetchType == SP_CURSOR_FETCH_ABSOLUTE || + *fetchType == SP_CURSOR_FETCH_RELATIVE || + *fetchType == SP_CURSOR_FETCH_PREV_NOADJUST) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid nrow value 0 for cursor type %X", *fetchType))); + } + } + else + { + /* If nrows is not specified, the default value is 20 rows. */ + *howMany = 20; + } +} + +static void +HandleSPCursorFetchRequest(TDSRequestSP req) +{ + int ret; + int fetchType; + int rownum; + int nrows; + + TdsErrorContext->err_text = "Processing SP_CURSORFETCH Request"; + FetchAndValidateCursorFetchOptions(req, &fetchType, &rownum, &nrows); + + TDSStatementBeginCallback(NULL, NULL); + + PG_TRY(); + { + ret = pltsql_plugin_handler_ptr->sp_cursorfetch_callback((int)req->cursorHandle, &fetchType, &rownum, &nrows); + MemoryContextSwitchTo(MessageContext); + + if (ret > 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("sp_cursorfetch failed: %d", ret))); + } + + PG_CATCH(); + { + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + TdsSendDone(TDS_TOKEN_DONEINPROC, TDS_DONE_MORE, TDS_CMD_SELECT, SPI_processed); + + /* for success, the return status is 0 */ + TdsSendReturnStatus(0); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* Log immediately if dictated by log_statement and log_duration */ + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); +} + +static void +HandleSPCursorCloseRequest(TDSRequestSP req) +{ + int ret; + + TdsErrorContext->err_text = "Processing SP_CURSORCLOSE Request"; + TDSStatementBeginCallback(NULL, NULL); + + /* close the cursor */ + PG_TRY(); + { + ret = pltsql_plugin_handler_ptr->sp_cursorclose_callback((int)req->cursorHandle); + MemoryContextSwitchTo(MessageContext); + + if (ret > 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("sp_cursorclose failed: %d", ret))); + } + + PG_CATCH(); + { + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* for success, the return status is 0 */ + TdsSendReturnStatus(0); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* Log immediately if dictated by log_statement and log_duration */ + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); +} + +static void +HandleSPCursorUnprepareRequest(TDSRequestSP req) +{ + int ret; + + TdsErrorContext->err_text = "Processing SP_CURSORUNPREPARE Request"; + TDSStatementBeginCallback(NULL, NULL); + + /* close the cursor */ + PG_TRY(); + { + ret = pltsql_plugin_handler_ptr->sp_cursorunprepare_callback((int)req->cursorPreparedHandle); + MemoryContextSwitchTo(MessageContext); + + if (ret > 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("sp_cursorunprepare failed: %d", ret))); + } + + PG_CATCH(); + { + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* for success, the return status is 0 */ + TdsSendReturnStatus(0); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* Log immediately if dictated by log_statement and log_duration */ + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); +} + +static void +HandleSPCursorOptionRequest(TDSRequestSP req) +{ + int ret; + ParameterToken token; + int code; + int value; + + TDSStatementBeginCallback(NULL, NULL); + + token = req->cursorExtraArg1; + + if (token) + { + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&code, &req->messageData[token->dataOffset], sizeof(uint32)); + } + + token = req->cursorExtraArg2; + + if (token) + { + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&value, &req->messageData[token->dataOffset], sizeof(uint32)); + } + + PG_TRY(); + { + ret = pltsql_plugin_handler_ptr->sp_cursoroption_callback((int)req->cursorHandle, code, value); + MemoryContextSwitchTo(MessageContext); + + if (ret > 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("sp_cursoroption failed: %d", ret))); + } + + PG_CATCH(); + { + TDSLogStatementCursorHandler(req, req->messageData, PRINT_PREPARED_CURSOR_HANDLE); + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* for success, the return status is 0 */ + TdsSendReturnStatus(0); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* Log immediately if dictated by log_statement and log_duration */ + TDSLogStatementCursorHandler(req, req->messageData, PRINT_PREPARED_CURSOR_HANDLE); +} + +static void +HandleSPCursorRequest(TDSRequestSP req) +{ + int ret; + List *values = NIL; + int i; + ParameterToken token; + int optype; + int rownum; + + for (i = 0; i < req->nTotalBindParams; i++) + values = lappend(values, &req->boundParamsData[i]); + + TDSStatementBeginCallback(NULL, NULL); + + token = req->cursorExtraArg1; + + if (token) + { + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&optype, &req->messageData[token->dataOffset], sizeof(uint32)); + } + + token = req->cursorExtraArg2; + + if (token) + { + /* the token must be integer */ + Assert(token->paramMeta.metaLen == sizeof(token->paramMeta.metaEntry.type1) && + token->paramMeta.metaEntry.type1.tdsTypeId == TDS_TYPE_INTEGER); + + memcpy(&rownum, &req->messageData[token->dataOffset], sizeof(uint32)); + } + + PG_TRY(); + { + StringInfo buf = makeStringInfo(); + ParameterToken token = req->cursorExtraArg3; + + TdsReadUnicodeDataFromTokenCommon(req->messageData, token, buf); + appendStringInfoCharMacro(buf, '\0'); + + ret = pltsql_plugin_handler_ptr->sp_cursor_callback((int)req->cursorHandle, optype, rownum, buf->data, values); + MemoryContextSwitchTo(MessageContext); + + if (ret > 0) + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("sp_cursoroption failed: %d", ret))); + } + + PG_CATCH(); + { + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); + TDSStatementExceptionCallback(NULL, NULL, false); + PG_RE_THROW(); + } + PG_END_TRY(); + + TDSStatementEndCallback(NULL, NULL); + + /* for success, the return status is 0 */ + TdsSendReturnStatus(0); + + /* command type - execute (0xe0) */ + TdsSendDone(TDS_TOKEN_DONEPROC, TDS_DONE_FINAL, 0xe0, 0); + + /* Log immediately if dictated by log_statement and log_duration */ + TDSLogStatementCursorHandler(req, req->messageData, PRINT_CURSOR_HANDLE); +} + +TDSRequest +GetRPCRequest(StringInfo message) +{ + TDSRequestSP request; + uint64_t offset = 0; + uint16_t len = 0; + int messageLen = 0; + int parameterCount = 0; + uint32_t tdsVersion = GetClientTDSVersion(); + + TdsErrorContext->err_text = "Fetching RPC Request"; + /* + * In the ALL_HEADERS rule, the Query Notifications header and the Transaction + * Descriptor header were introduced in TDS 7.2. We need to to Process them only + * for TDS versions more than or equal to 7.2, otherwise we do not increment the offset. + */ + if (tdsVersion > TDS_VERSION_7_1_1) + offset = ProcessStreamHeaders(message); + + /* Build return structure */ + if(TdsRequestCtrl->request != NULL && RPCBatchExists(TdsRequestCtrl->request->sp)) + { + /* + * If previously an RPC batch separator was found and if another RPC is left to process + * then we set the offset to the first byte of the next RPC packet + * and use the existing request after initialising. + */ + offset = TdsRequestCtrl->request->sp.batchSeparatorOffset; + messageLen = TdsRequestCtrl->request->sp.messageLen; + request = &TdsRequestCtrl->request->sp; + memset(request, 0, sizeof(TDSRequestSPData)); + request->messageLen = messageLen; + } + else + request = palloc0(sizeof(TDSRequestSPData)); + + request->reqType = TDS_REQUEST_SP_NUMBER; + memcpy(&len, &(message->data[offset]), sizeof(len)); + /* + * initStringInfo even if len is 0, so that + * the processing logic can check the length field from + * request.name->len + */ + initStringInfo(&request->name); + offset += sizeof(len); /* Procedure name len */ + + /* + * The RPC packet will contain the SP name + * (dotnet SP) or will contain the TDS spec + * defined SPType (prep-exec, Java SP) + */ + if (len != 0xffff) + { + TdsUTF16toUTF8StringInfo(&request->name, &(message->data[offset]), 2 * len); + offset += 2 * len; + request->spType = SP_CUSTOMTYPE; + } + else + { + memcpy(&request->spType, &(message->data[offset]), sizeof(request->spType)); + offset += sizeof(request->spType); + } + + request->isStoredProcedure = false; + request->metaDataParameterValue = makeStringInfo(); + + memcpy(&request->spFlags, &(message->data[offset]), sizeof(request->spFlags)); + offset += sizeof(request->spFlags); + + /* + * Store the address of message data, so that + * the Process step can fetch it + */ + request->messageData = message->data; + + if (ReadParameters(request, offset, message, ¶meterCount) != STATUS_OK) + elog(FATAL, "corrupted TDS_RPC message - " + "offset beyond the message length"); + /*Initialise*/ + InitialiseParameterToken(request); + + /* TODO: SP might need special handling, this is only for prep-exec */ + switch (request->spType) + { + case SP_CURSOROPEN: + { + TdsErrorContext->spType = "SP_CURSOROPEN"; + TDSInstrumentation(INSTR_TDS_SP_CURSOR_OPEN); + /* + * + * The order of the parameter is cursor handle, cursor statement, + * scrollopt, ccopt, rowcount and boundparams. Cursor handle + * and statement are mandatory, the rest are optional parameters. + * If one optional parameter exists, all previous optional parameter + * must be there in the packet. + */ + if (unlikely(parameterCount < 2)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 2))); + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor handle", "integer"))); + request->cursorHandleParameter = request->parameter; + if (unlikely(!request->parameter->next)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("%s parameter should not be null", "Query"))); + if (unlikely(FetchDataTypeNameFromParameter(request->parameter->next) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->parameter->next) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + request->queryParameter = request->parameter->next; + + /* + * ExtraArg1 = scrollopt + * ExtraArg2 = ccopt + * ExtraArg3 = Rowcount + * dataParameter = boundparams + */ + request->cursorExtraArg1 = request->queryParameter->next; + + if (request->cursorExtraArg1) + { + request->cursorExtraArg2 = request->cursorExtraArg1->next; + + if (request->cursorExtraArg2) + { + request->cursorExtraArg3 = request->cursorExtraArg2->next; + + if (request->cursorExtraArg3->next != NULL) + { + TdsReadUnicodeDataFromTokenCommon(message->data, + request->cursorExtraArg3->next, + request->metaDataParameterValue); + request->dataParameter = request->cursorExtraArg3->next->next; + } + } + } + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursoropen"); + } + break; + case SP_CURSOREXEC: + { + TdsErrorContext->spType = "SP_CURSOREXEC"; + TDSInstrumentation(INSTR_TDS_SP_CURSOR_EXEC); + if (unlikely(parameterCount < 2)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 2))); + /* + * 1. Cursor prepared Handle parameter (mandatory) + * 2. Cursor parameter (mandatory) + * 3. ExtraArg1 = scrollopt + * 4. ExtraArg2 = ccopt + * 5. ExtraArg3 = Rowcount + * 6. dataParameter = boundparams + */ + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor prepared handle", "integer"))); + request->cursorPreparedHandleParameter = request->parameter; + if (unlikely(!request->cursorPreparedHandleParameter->next)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("%s parameter should not be null", "Cursor handle"))); + if (unlikely(FetchDataTypeNameFromParameter(request->cursorPreparedHandleParameter->next) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor handle", "integer"))); + request->cursorHandleParameter = request->cursorPreparedHandleParameter->next; + + if (request->cursorHandleParameter) + { + request->cursorExtraArg1 = + request->cursorHandleParameter->next; + if (request->cursorExtraArg1) + { + request->cursorExtraArg2 = + request->cursorExtraArg1->next; + if (request->cursorExtraArg2) + { + request->cursorExtraArg3 = + request->cursorExtraArg2->next; + if (request->cursorExtraArg3) + request->dataParameter = + request->cursorExtraArg3->next; + } + } + } + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursorexec"); + } + break; + case SP_CURSORPREPEXEC: + { + TdsErrorContext->spType = "SP_CURSORPREPEXEC"; + TDSInstrumentation(INSTR_TDS_SP_CURSOR_PREPEXEC); + + if (unlikely(parameterCount < 3)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 3))); + /* + * 1. Cursor prepared Handle parameter (mandatory) + * 2. Cursor parameter (mandatory) + * 3. query parameter (mandatory) + * 4. ExtraArg1 = scrollopt + * 5. ExtraArg2 = ccopt + * 6. ExtraArg3 = Rowcount + * 7. dataParameter = boundparams + */ + + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor prepared handle", "integer"))); + request->cursorPreparedHandleParameter = request->parameter; + if (unlikely(!request->cursorPreparedHandleParameter->next)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("%s parameter should not be null", "Cursor handle"))); + if (unlikely(FetchDataTypeNameFromParameter(request->cursorPreparedHandleParameter->next) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor handle", "integer"))); + request->cursorHandleParameter = request->cursorPreparedHandleParameter->next; + + /* + * We haven't seen the case where dataParameter is absent in case of cursorprepexec + * So, indirectly, metaData (For ex. @P1 int, @P2 int etc) will always be there. + */ + + TdsReadUnicodeDataFromTokenCommon(message->data, + request->cursorHandleParameter->next, + request->metaDataParameterValue); + + if (unlikely(!request->cursorHandleParameter->next->next)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("%s parameter should not be null", "Query"))); + if (unlikely(FetchDataTypeNameFromParameter(request->cursorHandleParameter->next->next) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->cursorHandleParameter->next->next) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + request->queryParameter = request->cursorHandleParameter->next->next; + + if (request->queryParameter) + { + request->cursorExtraArg1 = request->queryParameter->next; + if (request->cursorExtraArg1) + { + request->cursorExtraArg2 = + request->cursorExtraArg1->next; + if (request->cursorExtraArg2) + { + request->cursorExtraArg3 = + request->cursorExtraArg2->next; + if (request->cursorExtraArg3) + request->dataParameter = + request->cursorExtraArg3->next; + } + } + } + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursorprepexec"); + } + break; + case SP_CURSORFETCH: + { + TdsErrorContext->spType = "SP_CURSORFETCH"; + TDSInstrumentation(INSTR_TDS_SP_CURSOR_FETCH); + if (unlikely(parameterCount < 1)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 1))); + + /* + * + * The order of the parameter is cursor handle, fetch + * type, rownum and nrows. Only Cursor handle is mandatory, + * the rest are optional parameters. If one optional + * parameter exists, all previous optional parameter + * must be there in the packet. + */ + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor handle", "integer"))); + request->cursorHandleParameter = request->parameter; + + /* + * ExtraArg1 = fetch type + * ExtraArg2 = rownum + * ExtraArg3 = nrows + */ + request->cursorExtraArg1 = request->cursorHandleParameter->next; + + if (request->cursorExtraArg1) + { + request->cursorExtraArg2 = request->cursorExtraArg1->next; + + if (request->cursorExtraArg2) + request->cursorExtraArg3 = request->cursorExtraArg2->next; + } + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursorfetch"); + } + break; + case SP_CURSORCLOSE: + { + TdsErrorContext->spType = "SP_CURSORCLOSE"; + TDSInstrumentation(INSTR_TDS_SP_CURSOR_CLOSE); + if (unlikely(parameterCount < 1)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 1))); + + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor handle", "integer"))); + request->cursorHandleParameter = request->parameter; + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursorclose"); + } + break; + case SP_CURSORUNPREPARE: + { + TdsErrorContext->spType = "SP_CURSORUNPREPARE"; + TDSInstrumentation(INSTR_TDS_SP_CURSOR_UNPREPARE); + + if (unlikely(parameterCount < 1)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 1))); + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor prepared handle", "integer"))); + request->cursorPreparedHandleParameter = request->parameter; + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursorunprepare"); + } + break; + case SP_CURSOR: + { + /* + * 1. Cursor parameter (mandatory) + * 2. ExtraArg1 = optype (mandatory) + * 3. ExtraArg2 = rownum (mandatory) + * 4. ExtraArg3 = table (mandatory) + * 5. dataParameter = value + */ + TdsErrorContext->spType = "SP_CURSOR"; + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_SP_CURSOR); + if (unlikely(parameterCount < 4)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 4))); + + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor handle", "integer"))); + request->cursorHandleParameter = request->parameter; + + request->cursorExtraArg1 = request->cursorHandleParameter->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg1) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "optype", "integer"))); + + request->cursorExtraArg2 = request->cursorExtraArg1->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg2) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "rownum", "integer"))); + + /* table should be of string datatype */ + request->cursorExtraArg3 = request->cursorExtraArg2->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_VARCHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_NCHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_CHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_NTEXT && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_TEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "table", "string"))); + + if (request->cursorExtraArg3->next) + { + /* value should be of string datatype */ + request->dataParameter = request->cursorExtraArg3->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_VARCHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_NCHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_CHAR && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_NTEXT && + FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_TEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "value", "string"))); + } + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursor"); + } + break; + case SP_CURSOROPTION: + { + /* + * 1. Cursor parameter (mandatory) + * 2. ExtraArg1 = code (mandatory) + * 3. ExtraArg2 = value (mandatory) + */ + TdsErrorContext->spType = "SP_CURSOROPTION"; + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_SP_CURSOROPTION); + if (unlikely(parameterCount < 3)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 3))); + + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor handle", "integer"))); + request->cursorHandleParameter = request->parameter; + + request->cursorExtraArg1 = request->cursorHandleParameter->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg1) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "code", "integer"))); + + request->cursorExtraArg2 = request->cursorExtraArg1->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg2) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "value", "integer"))); + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_cursoroption"); + } + break; + case SP_CURSORPREPARE: + { + /* + * 1. Cursor prepared Handle parameter (mandatory) + * 2. query parameter (mandatory) + * 3. ExtraArg1 = scrollopt + * 4. ExtraArg2 = ccopt + */ + TDSInstrumentation(INSTR_UNSUPPORTED_TDS_SP_CURSORPREPARE); + if (unlikely(parameterCount < 4)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 4))); + + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Cursor prepared handle", "integer"))); + request->cursorPreparedHandleParameter = request->parameter; + + TdsReadUnicodeDataFromTokenCommon(message->data, + request->cursorPreparedHandleParameter->next, + request->metaDataParameterValue); + + if (unlikely(!request->cursorPreparedHandleParameter->next->next)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("%s parameter should not be null", "Query"))); + if (unlikely(FetchDataTypeNameFromParameter(request->cursorPreparedHandleParameter->next->next) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->cursorPreparedHandleParameter->next->next) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + request->queryParameter = request->cursorPreparedHandleParameter->next->next; + + if (unlikely(FetchDataTypeNameFromParameter(request->queryParameter) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_NCHAR && + FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + + request->cursorExtraArg1 = request->queryParameter->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg1) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "options", "integer"))); + + request->cursorExtraArg2 = request->queryParameter->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg2) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "scrollopt", "integer"))); + + request->cursorExtraArg3 = request->queryParameter->next; + if (unlikely(FetchDataTypeNameFromParameter(request->cursorExtraArg3) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "ccopt", "integer"))); + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("\n Tds %s not supported yet", "SP_CURSORPREPARE"))); + } + break; + case SP_EXECUTESQL: + { + TdsErrorContext->spType = "SP_EXECUTESQL"; + if (unlikely(parameterCount < 1)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 1))); + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + request->queryParameter = request->parameter; + + if (request->queryParameter->next && + request->queryParameter->next->next) + { + TdsReadUnicodeDataFromTokenCommon(message->data, + request->queryParameter->next, + request->metaDataParameterValue); + request->dataParameter = + request->queryParameter->next->next; + } + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_executesql"); + } + break; + case SP_PREPARE: + { + TdsErrorContext->spType = "SP_PREPARE"; + if (unlikely(parameterCount < 2)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 2))); + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Handle", "integer"))); + request->handleParameter = request->parameter; + + /* + * In case, IN/OUT parameters are absent + * then the intermediate parameter will + * (@P0 int, ...) will be absent, and + * next parameter after the handle parameter + * should be the actual query + */ + if (request->parameter->next && request->parameter->next->next) + { + if (request->handleParameter) + { + TdsReadUnicodeDataFromTokenCommon(message->data, + request->handleParameter->next, + request->metaDataParameterValue); + } + if (unlikely(FetchDataTypeNameFromParameter(request->parameter->next->next) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->parameter->next->next) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + + request->queryParameter = request->parameter->next->next; + } + else + { + if (unlikely(FetchDataTypeNameFromParameter(request->parameter->next) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->parameter->next) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + request->queryParameter = request->parameter->next; + } + if (request->queryParameter->next) + request->dataParameter = request->queryParameter->next; + + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_prepare"); + } + break; + case SP_EXECUTE: + { + TdsErrorContext->spType = "SP_EXECUTE"; + if (unlikely(parameterCount < 1)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 1))); + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Handle", "INT"))); + + request->handleParameter = request->parameter; + + if (request->parameter->next) + request->dataParameter = request->parameter->next; + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_execute"); + } + break; + case SP_PREPEXEC: + { + TdsErrorContext->spType = "SP_PREPEXEC"; + if (unlikely(parameterCount < 2)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 2))); + if (unlikely(FetchDataTypeNameFromParameter(request->parameter) != TDS_TYPE_INTEGER)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Handle", "INT"))); + request->handleParameter = request->parameter; + + /* + * In case, IN/OUT parameters are absent + * then the intermediate parameter will + * (@P0 int, ...) will be absent, and + * next parameter after the handle parameter + * should be the actual query + */ + if (request->parameter->next && + request->parameter->next->next) + { + if (request->handleParameter) + { + TdsReadUnicodeDataFromTokenCommon(message->data, + request->handleParameter->next, + request->metaDataParameterValue); + } + if (unlikely(FetchDataTypeNameFromParameter(request->parameter->next->next) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->parameter->next->next) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + request->queryParameter = + request->parameter->next->next; + } + else + { + if (unlikely(FetchDataTypeNameFromParameter(request->parameter->next) != TDS_TYPE_NVARCHAR && + FetchDataTypeNameFromParameter(request->parameter->next) != TDS_TYPE_NTEXT)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s parameter should be of %s type", "Query", "NVARCHAR or NTEXT"))); + request->queryParameter = request->parameter->next; + } + if (request->queryParameter->next) + request->dataParameter = request->queryParameter->next; + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_prepexec"); + } + break; + case SP_PREPEXECRPC: + { + TdsErrorContext->spType = "SP_PREPEXECRPC"; + /* Not supported yet */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SP_PREPEXECRPC not supported yet"))); + } + break; + case SP_UNPREPARE: + { + TdsErrorContext->spType = "SP_UNPREPARE"; + if (unlikely(parameterCount < 1)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The minimum number of parameters should be %d", 1))); + request->handleParameter = request->parameter; + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: sp_unprepare"); + } + break; + case SP_CUSTOMTYPE: + { + TdsErrorContext->spType = "SP_CUSTOMTYPE"; + request->dataParameter = request->parameter; + TDS_DEBUG(TDS_DEBUG1, "message_type: Remote Procedure Call (3) rpc_packet_type: user defined procedure"); + } + break; + default: + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. The RPC name is invalid."))); + } + + /* initialize the IN/OUT parameter index array */ + InitializeDataParamTokenIndex(request); + + /* get the SP handle */ + GetSPHandleParameter(request); + + /* get the SP cursor handle */ + GetSPCursorHandleParameter(request); + + /* get the SP cursor Prepared handle */ + GetSPCursorPreparedHandleParameter(request); + + return (TDSRequest)request; +} + +void +RestoreRPCBatch(StringInfo message, uint8_t *status, uint8_t *messageType) +{ + /* Restore the common Packet for the Batch. */ + Assert(TdsRequestCtrl->request->reqType == TDS_REQUEST_SP_NUMBER); + Assert(RPCBatchExists(TdsRequestCtrl->request->sp)); + message->data = TdsRequestCtrl->request->sp.messageData; + message->len = TdsRequestCtrl->request->sp.messageLen; + *messageType = TDS_RPC; /* Hardcoded the type since we do an assert at the start. */ + *status = TdsRequestCtrl->status; +} + +void +ProcessRPCRequest(TDSRequest request) +{ + TDSRequestSP req; + + req = (TDSRequestSP) request; + + switch(req->spType) + { + case SP_CURSOR: + GenerateBindParamsData(req); + HandleSPCursorRequest(req); + break; + case SP_CURSOROPEN: + case SP_CURSORPREPARE: + case SP_CURSORPREPEXEC: + case SP_CURSOREXEC: + GenerateBindParamsData(req); + HandleSPCursorOpenCommon(req); + break; + case SP_CURSORFETCH: + HandleSPCursorFetchRequest(req); + break; + case SP_CURSORCLOSE: + HandleSPCursorCloseRequest(req); + break; + case SP_CURSORUNPREPARE: + HandleSPCursorUnprepareRequest(req); + break; + case SP_CURSOROPTION: + HandleSPCursorOptionRequest(req); + break; + case SP_PREPARE: + SPPrepare(req); + break; + case SP_PREPEXECRPC: + Assert(0); + break; + case SP_PREPEXEC: + SPPrepExec(req); + break; + case SP_EXECUTE: + SPExecute(req); + break; + case SP_EXECUTESQL: + SPExecuteSQL(req); + break; + case SP_CUSTOMTYPE: + SPCustomType(req); + break; + case SP_UNPREPARE: + SPUnprepare(req); + break; + } +} + +/* + * TdsIsSPPrepare - Returns true if sp_prepare packet is being processed + */ +bool +TdsIsSPPrepare() +{ + TDSRequestSP req; + req = (TDSRequestSP) TdsRequestCtrl->request; + if (req->spType == SP_PREPARE) + return true; + return false; +} + +/* + * TdsFetchInParamValues - fetch the IN parameters from TDS message buffer + * + * params - (OUT argument) store the actual Datums + * + * It's the responsibility of the caller allocate memory for the same. + * + * Also note that we send the OUT parameters as INOUT parameters. The TDS + * protocol sends the value of these parameters as NULL. So, we're going to + * bind NULL as values for these paramters. + */ +void +TdsFetchInParamValues(ParamListInfo params) +{ + ParameterToken token; + TDSRequest request = TdsRequestCtrl->request; + TDSRequestSP req; + int paramno = 0; + + Assert(params != NULL); + + Assert(request->reqType == TDS_REQUEST_SP_NUMBER); + req = (TDSRequestSP) request; + + for (token = req->dataParameter; token != NULL; token = token->next, paramno++) + { + Oid ptype; + Datum pval; + bool isNull; + TdsIoFunctionInfo tempFuncInfo; + + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(token->type, token->maxLen); + isNull = token->isNull; + + ptype = token->paramMeta.pgTypeOid; + + if (!isNull && token->type != TDS_TYPE_TABLE) + pval = tempFuncInfo->recvFuncPtr(req->messageData, token); + else if (token->type == TDS_TYPE_TABLE) + pval = (Datum) token->tvpInfo->tvpTypeName; + else + pval = (Datum) 0; + + params->params[paramno].value = pval; + params->params[paramno].isnull = isNull; + + /* + * We mark the params as CONST. This ensures that any custom plan + * makes full use of the parameter values. + */ + params->params[paramno].pflags = PARAM_FLAG_CONST; + params->params[paramno].ptype = ptype; + } +} + +/* + * In case of SP or prep-exec, parameter names are optional. + * If client applications doesn't specify the parameter name then internally + * driver sends the default parameter name with query text. + * For ex: insert into tablenm values (@P0). + * + * Engine needs the parameter index to store the values appropriately. + * For example: index 1 for @P0, index 2 for @P1 + * + * TdsGetAndSetParamIndex function acts as a reference counter to send the + * paramter Index starting from 1 for valid param. + * For anything else, return 0. + * + * Assumption: + * 1. Parameter values will always be in the serial order in case of SP_[CURSOR]EXEC + * 2. Valid Parameter names will always start from @P + */ +int +TdsGetAndSetParamIndex(const char *name) +{ + TDSRequestSP req; + + if ((TdsRequestCtrl == NULL) || (TdsRequestCtrl->request == NULL) + || (TdsRequestCtrl->request->reqType != TDS_REQUEST_SP_NUMBER)) + { + return 0; + } + + /* + * Default parameters should always start from @P + */ + if (strlen(name) < 3 || name[0] != '@') + { + return 0; + } + + req = (TDSRequestSP) TdsRequestCtrl->request; + /* + * We need to use the intermediate Parameter + * For ex: (@P0 int, @P1 int etc) when available. + */ + if (req->metaDataParameterValue->len > 0) + { + int i = 0, temp = 0; + const char *source = req->metaDataParameterValue->data; + char *pos; + int ptr; + int qlen = strlen(source); + int nlen = strlen(name); + while (temp < qlen) + { + int j; + + /* + * If param names are not given by the application, then driver + * default params names always start with "@P" + */ + pos = strstr(source, "@P"); + + /* + * If parameter names don't match, return 0 + */ + if (pos == NULL) + { + return 0; + } + ptr = pos - source; + for (j = ptr; j < ptr + nlen && j < qlen; j++) + { + /* + * Parameter names comparison seems to be dependent on collation + * (case sensitive vs insensitive). So here, we will have to do the + * comparision depending on collation. For now, convert everything + * into lower case and compare since by default collation in TSQL + * is case insensitive (SQL_Latin1_General_CP1_CI_AS) + */ + if (tolower(source[j]) != tolower(name[j - ptr])) + { + break; + } + } + /* + * If no characters match, then return 0 + */ + if (j == ptr) + { + return 0; + } + + if (j == ptr + nlen) + return i + 1; + + temp = j; + source = &source[temp]; + i++; + } + return 0; + } + + /* + * We shouldn't reach here other than SP_[CURSOR]EXEC SP request. + * + * Assumption: In case of SP_[CURSOR]EXEC SP request, + * this function will be called only once during exec_bind_message. + * + * If in future we encounter a case, where above assumption doesn't work, + * then only option is to parse the complete query text to get the param index. + * This is kind of an optimization to save us from the complete query + * parsing based on above assumptions. + */ + Assert(req->spType == SP_EXECUTE || + req->spType == SP_CURSOREXEC); + + return ++(req->paramIndex); +} + +/* + * TdsGetParamNames - fetch TDS IN/OUT parameter names + * + * We'll follow the same IN/OUT parameter order in which we've received. Also, + * we're allocating the memory in callers memory context. So, it's + * responsibility of the caller to perform cleanups. + */ +bool +TdsGetParamNames(List **pnames) +{ + TDSRequest request; + TDSRequestSP req; + ParameterToken token; + + if ((TdsRequestCtrl == NULL) || (TdsRequestCtrl->request == NULL) + || (TdsRequestCtrl->request->reqType != TDS_REQUEST_SP_NUMBER)) + return false; + + request = TdsRequestCtrl->request; + req = (TDSRequestSP) request; + + if (req->spType == SP_EXECUTESQL + || req->spType == SP_PREPARE + || req->spType == SP_PREPEXEC + || req->spType == SP_EXECUTE) + return false; + + if (req->spType == SP_CUSTOMTYPE) + return false; + + if (req->nTotalParams == 0) + return false; + + for (token = req->dataParameter; token != NULL; token = token->next) + { + StringInfo name; + TdsParamName item = palloc(sizeof(TdsParamNameData)); + + name = &(token->paramMeta.colName); + + /* + * When parameter names aren't given by the client driver, then + * simply return true. + * We make an assumption that all parameters will either have a name + * or not. There won't be a case where some parameters in a packet have name, + * while others don't. + */ + if (name->len == 0) + return true; + + item->name = pnstrdup(name->data, strlen(name->data)); + item->type = (token->flags == 0) ? 0 : 1; + + *pnames = lappend(*pnames, item); + } + + return true; +} + +/* + * TDS function to log statement duration related info + */ +void +TDSLogDuration(char *query) +{ + char msec_str[32]; + + switch (check_log_duration(msec_str, false)) + { + case 1: + ereport(LOG, (errmsg("Query duration: %s ms", msec_str), + errhidestmt(true))); + break; + case 2: + ereport(LOG, (errmsg("Query: %s duration: %s ms", + query, msec_str), errhidestmt(true))); + break; + default: + break; + } + return; +} + +/* + * TDS function to log statement handler and duration detail for cursor + */ +static void +TDSLogStatementCursorHandler(TDSRequestSP req, char *stmt, int option) +{ + if (pltsql_plugin_handler_ptr->stmt_needs_logging || TDS_DEBUG_ENABLED(TDS_DEBUG2)) + { + ErrorContextCallback *plerrcontext = error_context_stack; + error_context_stack = plerrcontext->previous; + + switch (option) + { + case PRINT_CURSOR_HANDLE: + ereport(LOG, + (errmsg("sp_cursor handle: %d; statement: %s", + req->cursorHandle, stmt), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + break; + case PRINT_PREPARED_CURSOR_HANDLE: + ereport(LOG, + (errmsg("sp_cursor prepared handle: %d; statement: %s", + req->cursorPreparedHandle, stmt), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + break; + case PRINT_BOTH_CURSOR_HANDLE: + ereport(LOG, + (errmsg("sp_cursor handle: %d; sp_cursor prepared handle: %d; statement: %s", + req->cursorHandle, req->cursorPreparedHandle, stmt), + errhidestmt(true), + errdetail_params(req->nTotalParams))); + break; + default: + break; + } + + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + error_context_stack = plerrcontext; + } + + /* Print TDS log duration, if log_duration is set */ + TDSLogDuration(stmt); +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdssecure.c b/contrib/babelfishpg_tds/src/backend/tds/tdssecure.c new file mode 100644 index 00000000000..a8cdb028754 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdssecure.c @@ -0,0 +1,411 @@ +/*------------------------------------------------------------------------- + * + * tdssecure.c + * TDS Listener TLS connection code + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdssecure.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#include +#endif + +#include "libpq/libpq.h" +#include "miscadmin.h" +#include "pgstat.h" +#include "tcop/tcopprot.h" +#include "utils/memutils.h" +#include "storage/ipc.h" +#include "storage/proc.h" + +#include "src/include/tds_secure.h" +#include "src/include/tds_int.h" + +int tds_ssl_min_protocol_version; +int tds_ssl_max_protocol_version; +#ifdef USE_SSL +/* + * SslRead - TDS secure read function, similar to my_sock_read + */ +static int +SslRead(BIO *h, char *buf, int size) +{ + int res = 0; + + if (buf != NULL) + { + res = secure_raw_read(((Port *) BIO_get_data(h)), buf, size); + BIO_clear_retry_flags(h); + if (res <= 0) + { + /* If we were interrupted, tell caller to retry */ + if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) + { + BIO_set_retry_read(h); + } + } + } + + return res; +} + +/* + * my_tds_sock_read - TDS secure read function, similar to my_sock_read + * During the initial handshake, strip off the inital 8 bytes header, when + * filling in the data in buf called from openssl library + */ +static int +SslHandShakeRead(BIO *h, char *buf, int size) +{ + int res = 0; + + res = SslRead(h, buf, size); + + /* very first packet of prelogin SSL handshake */ + if (size > 0 && res > 0 && buf[0] == TDS_PRELOGIN) + { + + if (res < TDS_PACKET_HEADER_SIZE) + { + int remainingRead = TDS_PACKET_HEADER_SIZE - res; + char tempBuf[TDS_PACKET_HEADER_SIZE]; + res = 0; + + /* Read the complete remaining of the header and throw away the bytes */ + while(res < remainingRead) + { + res += secure_raw_read(((Port *) BIO_get_data(h)), tempBuf, + remainingRead - res); + } + + /* + * Read the actual data and return the res of the actual data read + * Don't worry if complete read, Openssl library will take care + */ + res = secure_raw_read(((Port *) BIO_get_data(h)), buf, size); + } + else + { + int i = TDS_PACKET_HEADER_SIZE; + for (i = TDS_PACKET_HEADER_SIZE; i < res; i++) + { + buf[i - TDS_PACKET_HEADER_SIZE] = buf[i]; + } + res -= TDS_PACKET_HEADER_SIZE; + + /* + * Read remaining of the data. Even if the read is less than + * requested size due to whatever reasons, we are good, since + * we are returning the correct res value, so caller will take + * care of reading the remaining data + */ + + res += SslRead(h, &buf[res], TDS_PACKET_HEADER_SIZE); + } + } + + return res; +} + +/* + * SslWrite - Tds secure write function, similar to my_sock_write. + */ +static int +SslWrite(BIO *h, const char *buf, int size) +{ + int res = 0; + + res = secure_raw_write(((Port *) BIO_get_data(h)), buf, size); + BIO_clear_retry_flags(h); + if (res <= 0) + { + /* If we were interrupted, tell caller to retry */ + if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) + { + BIO_set_retry_write(h); + } + } + + return res; +} + +/* + * TdsSshHandShakeWrite - Tds secure write function, similar to my_sock_write. + * During the initial handshake add the 8 bytes header to the final data which + * is sent to client + */ +static int +SslHandShakeWrite(BIO *h, const char *buf, int size) +{ + StringInfoData str; + char tmp[2]; + uint16_t tsize; + int res = 0; + + /* Nothing to write */ + if (size < 0) + return size; + + initStringInfo(&str); + appendStringInfoChar(&str, TDS_PRELOGIN); + appendStringInfoChar(&str, TDS_PACKET_HEADER_STATUS_EOM); + tsize = pg_hton16(size + TDS_PACKET_HEADER_SIZE); + memcpy(&tmp,(char *) &tsize, 2); + + appendStringInfoChar(&str, tmp[0]); + appendStringInfoChar(&str, tmp[1]); + appendStringInfoChar(&str, 0x00); + appendStringInfoChar(&str, 0x00); + appendStringInfoChar(&str, 0x00); + appendStringInfoChar(&str, 0x00); + + appendBinaryStringInfo(&str, buf, size); + buf = str.data; + size += TDS_PACKET_HEADER_SIZE; + + /* Write the complete data */ + while (res < size) + { + res += SslWrite(h, &buf[res], size - res); + } + + return res; +} + +/* + * TdsBioSecureSocket - Similar to my_BIO_s_socket + * Used to setup, TDS listener read and write API + * for the initial SSL handshake + */ +BIO_METHOD * +TdsBioSecureSocket(BIO_METHOD *my_bio_methods) +{ + if (my_bio_methods == NULL) + { + BIO_METHOD *biom = (BIO_METHOD *) BIO_s_socket(); +#ifdef HAVE_BIO_METH_NEW + int my_bio_index; + + my_bio_index = BIO_get_new_index(); + if (my_bio_index == -1) + return NULL; + my_bio_methods = BIO_meth_new(my_bio_index, "PostgreSQL backend socket"); + if (!my_bio_methods) + return NULL; + if (!BIO_meth_set_write(my_bio_methods, SslHandShakeWrite) || + !BIO_meth_set_read(my_bio_methods, SslHandShakeRead) || + !BIO_meth_set_gets(my_bio_methods, BIO_meth_get_gets(biom)) || + !BIO_meth_set_puts(my_bio_methods, BIO_meth_get_puts(biom)) || + !BIO_meth_set_ctrl(my_bio_methods, BIO_meth_get_ctrl(biom)) || + !BIO_meth_set_create(my_bio_methods, BIO_meth_get_create(biom)) || + !BIO_meth_set_destroy(my_bio_methods, BIO_meth_get_destroy(biom)) || + !BIO_meth_set_callback_ctrl(my_bio_methods, BIO_meth_get_callback_ctrl(biom))) + { + BIO_meth_free(my_bio_methods); + my_bio_methods = NULL; + return NULL; + } +#else +#ifdef USE_SSL + my_bio_methods = malloc(sizeof(BIO_METHOD)); + if (!my_bio_methods) + return NULL; + memcpy(my_bio_methods, biom, sizeof(BIO_METHOD)); + my_bio_methods->bread = SslHandShakeRead; + my_bio_methods->bwrite = SslHandShakeWrite; +#endif +#endif + } + return my_bio_methods; +} +#endif + +/* + * Frees the strcture for the SSL + */ +void +TdsFreeSslStruct(Port *port) +{ +#ifdef USE_SSL + if (port->ssl) + { + /* + * Don't call the SSL_shutdown - + * since it shutdowns the connection + */ + SSL_free(port->ssl); + port->ssl = NULL; + port->ssl_in_use = false; + } +#endif +} + +/* + * Read data from a secure connection. + */ +ssize_t +tds_secure_read(Port *port, void *ptr, size_t len) +{ + ssize_t n; + int waitfor; + + /* Deal with any already-pending interrupt condition. */ + ProcessClientReadInterrupt(false); + +retry: +#ifdef USE_SSL + waitfor = 0; + if (port->ssl_in_use) + { + /* TDS specific TLS read */ + n = Tds_be_tls_read(port, ptr, len, &waitfor); + } + else +#endif + { + n = secure_raw_read(port, ptr, len); + waitfor = WL_SOCKET_READABLE; + } + + /* In blocking mode, wait until the socket is ready */ + if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN)) + { + WaitEvent event; + + Assert(waitfor); + + ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL); + + WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1, + WAIT_EVENT_CLIENT_READ); + + /* + * If the postmaster has died, it's not safe to continue running, + * because it is the postmaster's job to kill us if some other backend + * exists uncleanly. Moreover, we won't run very well in this state; + * helper processes like walwriter and the bgwriter will exit, so + * performance may be poor. Finally, if we don't exit, pg_ctl will be + * unable to restart the postmaster without manual intervention, so no + * new connections can be accepted. Exiting clears the deck for a + * postmaster restart. + * + * (Note that we only make this check when we would otherwise sleep on + * our latch. We might still continue running for a while if the + * postmaster is killed in mid-query, or even through multiple queries + * if we never have to wait for read. We don't want to burn too many + * cycles checking for this very rare condition, and this should cause + * us to exit quickly in most cases.) + */ + if (event.events & WL_POSTMASTER_DEATH) + ereport(FATAL, + (errcode(ERRCODE_ADMIN_SHUTDOWN), + errmsg("terminating connection due to unexpected postmaster exit"))); + + /* Handle interrupt. */ + if (event.events & WL_LATCH_SET) + { + ResetLatch(MyLatch); + ProcessClientReadInterrupt(true); + + /* + * We'll retry the read. Most likely it will return immediately + * because there's still no data available, and we'll wait for the + * socket to become ready again. + */ + } + goto retry; + } + + /* + * Process interrupts that happened during a successful (or non-blocking, + * or hard-failed) read. + */ + ProcessClientReadInterrupt(false); + + return n; +} + +/* + * Write data to a secure connection. + */ +ssize_t +tds_secure_write(Port *port, void *ptr, size_t len) +{ + ssize_t n; + int waitfor; + + /* Deal with any already-pending interrupt condition. */ + ProcessClientWriteInterrupt(false); + +retry: + waitfor = 0; +#ifdef USE_SSL + if (port->ssl_in_use) + { + /* TDS specific SSL write */ + n = Tds_be_tls_write(port, ptr, len, &waitfor); + } + else +#endif + { + n = secure_raw_write(port, ptr, len); + waitfor = WL_SOCKET_WRITEABLE; + } + + if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN)) + { + WaitEvent event; + + Assert(waitfor); + + ModifyWaitEvent(FeBeWaitSet, 0, waitfor, NULL); + + WaitEventSetWait(FeBeWaitSet, -1 /* no timeout */ , &event, 1, + WAIT_EVENT_CLIENT_WRITE); + + /* See comments in secure_read. */ + if (event.events & WL_POSTMASTER_DEATH) + ereport(FATAL, + (errcode(ERRCODE_ADMIN_SHUTDOWN), + errmsg("terminating connection due to unexpected postmaster exit"))); + + /* Handle interrupt. */ + if (event.events & WL_LATCH_SET) + { + ResetLatch(MyLatch); + ProcessClientWriteInterrupt(true); + + /* + * We'll retry the write. Most likely it will return immediately + * because there's still no buffer space available, and we'll wait + * for the socket to become ready again. + */ + } + goto retry; + } + + /* + * Process interrupts that happened during a successful (or non-blocking, + * or hard-failed) write. + */ + ProcessClientWriteInterrupt(false); + + return n; +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdssqlbatch.c b/contrib/babelfishpg_tds/src/backend/tds/tdssqlbatch.c new file mode 100644 index 00000000000..43924ea56e2 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdssqlbatch.c @@ -0,0 +1,135 @@ +/*------------------------------------------------------------------------- + * + * tdssqlbatch.c + * TDS Listener functions for handling SQL Batch requests + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdssqlbatch.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/printtup.h" +#include "access/xact.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "nodes/parsenodes.h" +#include "tcop/tcopprot.h" + +#include "src/include/tds_int.h" +#include "src/include/tds_protocol.h" +#include "src/include/tds_request.h" +#include "src/include/tds_response.h" + +TDSRequest +GetSQLBatchRequest(StringInfo message) +{ + TDSRequestSQLBatch request; + int query_offset = 0; + int query_len; + uint32_t tdsVersion = GetClientTDSVersion(); + + TdsErrorContext->err_text = "Fetching SQL Batch Request"; + /* + * In the ALL_HEADERS rule, the Query Notifications header and the Transaction + * Descriptor header were introduced in TDS 7.2. We need to to Process them only + * for TDS versions more than or equal to 7.2, otherwise we do not increment the offset. + */ + if (tdsVersion > TDS_VERSION_7_1_1) + query_offset = ProcessStreamHeaders(message); + query_len = message->len - query_offset; + + /* Build return structure */ + request = palloc0(sizeof(TDSRequestSQLBatchData)); + request->reqType = TDS_REQUEST_SQL_BATCH; + + initStringInfo(&(request->query)); + + TdsUTF16toUTF8StringInfo(&(request->query), + &(message->data[query_offset]), + query_len); + + return (TDSRequest)request; +} + +/* + * Helper function to execute a SQL Batch + * query using pltsql inline handler + */ +void +ExecuteSQLBatch(char *query) +{ + LOCAL_FCINFO(fcinfo,1); + InlineCodeBlock *codeblock = makeNode(InlineCodeBlock); + + TdsErrorContext->err_text = "Processing SQL Batch Request"; + + /* Only source text matters to handler */ + codeblock->source_text = query; + codeblock->langOid = 0; /* TODO does it matter */ + codeblock->langIsTrusted = true; + codeblock->atomic = false; + + /* Just to satisfy argument requirement */ + MemSet(fcinfo, 0, SizeForFunctionCallInfo(1)); + fcinfo->args[0].value = PointerGetDatum(codeblock); + fcinfo->args[0].isnull = false; + PG_TRY(); + { + pltsql_plugin_handler_ptr->sql_batch_callback (fcinfo); + } + PG_CATCH(); + { + if (TDS_DEBUG_ENABLED(TDS_DEBUG2)) + ereport(LOG, + (errmsg("sql_batch statement: %s", query), + errhidestmt(true))); + + PG_RE_THROW(); + } + PG_END_TRY(); + + /* + * Log immediately if dictated by log_statement + */ + if (pltsql_plugin_handler_ptr->stmt_needs_logging || TDS_DEBUG_ENABLED(TDS_DEBUG2)) + { + ErrorContextCallback *plerrcontext = error_context_stack; + error_context_stack = plerrcontext->previous; + ereport(LOG, + (errmsg("sql_batch statement: %s", query), + errhidestmt(true))); + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + error_context_stack = plerrcontext; + } + + /* + * Print TDS log duration, if log_duration is set + */ + TDSLogDuration(query); + +} + +/* + * SQL batch requests directly go to pltsql + * inline block handler + */ +void +ProcessSQLBatchRequest(TDSRequest request) +{ + TDSRequestSQLBatch req = (TDSRequestSQLBatch)request; + + ExecuteSQLBatch(req->query.data); + MemoryContextSwitchTo(MessageContext); + + /* If there was an empty query, send a done token */ + if (TdsRequestCtrl->isEmptyResponse) + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_FINAL, 0xfd, 0); +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdstimestamp.c b/contrib/babelfishpg_tds/src/backend/tds/tdstimestamp.c new file mode 100644 index 00000000000..63bce51be50 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdstimestamp.c @@ -0,0 +1,473 @@ +/*------------------------------------------------------------------------- + * + * tdstimestamp.c + * Handler functions for TDS timestamp datatype + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdstimestamp.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "utils/datetime.h" + +#include "src/include/tds_timestamp.h" + +int DaycountInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static inline +int IsLeap(int y) +{ + if ((y%100 != 0 && y%4 == 0) || y %400 == 0) + return 1; + + return 0; +} + +void +TdsCheckDateValidity(DateADT result) +{ + /* Limit to the same range that date_in() accepts. */ + if (DATE_NOT_FINITE(result) || (!IS_VALID_DATE(result))) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range"))); +} + +static inline int +CountLeapYears(struct pg_tm *t) +{ + int years = t->tm_year; + if (t->tm_mon <= 2) + years--; + + return years / 4 - years / 100 + years / 400; +} + +static inline int +GetDayDifference(struct pg_tm *t1, struct pg_tm *t2) +{ + long int n1, n2; + int i; + + n1 = t1->tm_year * 365 + t1->tm_mday; + for (i = 0; i < t1->tm_mon - 1; i++) + n1 += DaycountInMonth[i]; + n1 += CountLeapYears(t1); + + n2 = t2->tm_year * 365 + t2->tm_mday; + for (i = 0; i < t2->tm_mon - 1; i++) + n2 += DaycountInMonth[i]; + n2 += CountLeapYears(t2); + + return (n2 - n1); +} + +uint32 +TdsGetDayDifferenceHelper(int day, int mon, int year, bool isDateType) +{ + uint32 numDays = 0; + struct pg_tm tj, ti, *tm = &ti, *tt = &tj; + tm->tm_mday = day, tm->tm_mon = mon, tm->tm_year = year; + tt->tm_mday = 1, tt->tm_mon = 1; + if (isDateType) + tt->tm_year = 1; + else + tt->tm_year = 1900; + numDays = GetDayDifference(tt, tm); + return numDays; +} + +static inline void +GetDateFromDatum(Datum date, struct pg_tm *tm) +{ + if (!DATE_NOT_FINITE(date)) + { + j2date(date + POSTGRES_EPOCH_JDATE, + &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); + } + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("date out of range"))); +} + +static inline void +GetDatetimeFromDatum(Datum value, fsec_t *fsec, struct pg_tm *tm) +{ + Timestamp timestamp = (Timestamp)value; + + if (TIMESTAMP_NOT_FINITE(timestamp) || + timestamp2tm(timestamp, NULL, tm, fsec, NULL, NULL) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("Datetime out of range"))); +} + +/* + * Get numDays elapsed between client date and 1-1-0001 + */ +uint32 +TdsDayDifference(Datum value) +{ + uint32 numDays = 0; + struct pg_tm tj, ti, *tm = &ti, *tt = &tj; + + GetDateFromDatum(value, tm); + + tt->tm_mday = 1, tt->tm_mon = 1, tt->tm_year = 1; + numDays = GetDayDifference(tt, tm); + return numDays; +} + +/* + * Decides whether the effective date to consider is the next day + * based on hour, minute, second value 23:59:59 + */ +static inline void +GetNumDaysHelper(struct pg_tm *tm) +{ + tm->tm_hour = tm->tm_min = tm->tm_sec = 0; + if (tm->tm_mday == DaycountInMonth[tm->tm_mon - 1] && + tm->tm_mon == 12) + { + tm->tm_year++; + tm->tm_mon = tm->tm_mday = 1; + } + else if ((tm->tm_mday == DaycountInMonth[tm->tm_mon - 1] && tm->tm_mon != 2) || + (tm->tm_mon == 2 && tm->tm_mday == 29 && IsLeap(tm->tm_year)) || + (tm->tm_mon == 2 && tm->tm_mday == 28 && !IsLeap(tm->tm_year))) + { + tm->tm_mon++; + tm->tm_mday = 1; + } + else + tm->tm_mday++; +} + +/* + * Returns numDays and numTicks elapsed between given date + * and 1-1-1900 + */ +void +TdsTimeDifferenceSmalldatetime(Datum value, uint16 *numDays, + uint16 *numMins) +{ + struct pg_tm tj, ti, *tm = &ti, *tt = &tj; + fsec_t fsec = 0; + + GetDatetimeFromDatum(value, &fsec, tm); + tt->tm_mday = 1, tt->tm_mon = 1, tt->tm_year = 1900; + + *numDays = (uint16)GetDayDifference(tt, tm); + + if (tm->tm_hour == 23 && tm->tm_min == 59 && tm->tm_sec == 59) + { + fsec = 0; + GetNumDaysHelper(tm); + (*numDays)++; + } + else if ((tm->tm_sec == 29 && (fsec/1000) > 998) || tm->tm_sec > 29) + tm->tm_min++; + + tm->tm_sec = 0; + *numMins = (tm->tm_hour * 60) + tm->tm_min; +} + +/* + * Returns numDays and numTicks elapsed between given date + * and 1-1-1900 + */ +void +TdsTimeDifferenceDatetime(Datum value, uint32 *numDays, + uint32 *numTicks) +{ + uint32 milliCount = 0; + struct pg_tm tj, ti, *tm = &ti, *tt = &tj; + fsec_t fsec; + int unit = 0; + + GetDatetimeFromDatum(value, &fsec, tm); + tt->tm_mday = 1, tt->tm_mon = 1, tt->tm_year = 1900; + + *numDays = GetDayDifference(tt, tm); + + if (tm->tm_hour == 23 && tm->tm_min == 59 && tm->tm_sec == 59 && + fsec == 999000) + { + fsec = 0; + GetNumDaysHelper(tm); + (*numDays)++; + } + else + { + unit = (fsec/1000) % 10; + if (unit == 1 || unit == 4 || unit == 8) + fsec = ((fsec/1000)-1) * 1000; + else if (unit == 2 || unit == 6 || unit == 9) + fsec = ((fsec/1000)+1) * 1000; + else if (unit == 5) + fsec = ((fsec/1000)+2) * 1000; + } + milliCount = (((tm->tm_hour * 60 + tm->tm_min) * 60 + + tm->tm_sec) * 1000 + (int)fsec/1000); + + *numTicks = (int)(milliCount/3.3333333); +} + +/* + * Given a year and days elapsed in it, outputs month and + * day of the date found by adding offset #days to day1 + */ +static inline +void RevoffsetDays(int offset, int *y, int *d, int *m) +{ + int month[13] = { 0, 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 }; + int i; + + if (IsLeap(*y)) + month[2] = 29; + + for (i = 1; i <= 12; i++) + { + if (offset <= month[i]) + break; + offset = offset - month[i]; + } + *d = offset; + *m = i; +} + +/* + * Adds x days to specific start date(1.1.0001 or 1.1.1900) in order to + * retrieve target client date + */ +static inline +void CalculateTargetDate(int y1, int *d2, int *m2, int *y2, int x) +{ + int y2days = 0; + int offset1 = 1; + int remDays = IsLeap(y1)?(366-offset1):(365-offset1); + + int offset2; + if (x <= remDays) + { + *y2 = y1; + offset2 = offset1 + x; + } + else + { + x -= remDays; + *y2 = y1 + 1; + y2days = IsLeap(*y2)?366:365; + while (x >= y2days) + { + x -= y2days; + (*y2)++; + y2days = IsLeap(*y2)?366:365; + } + offset2 = x; + } + + RevoffsetDays(offset2, y2, d2, m2); +} + +/* + * Get date info(day, month, year) from numDays elapsed from + * 1-1-0001. + */ +void +TdsTimeGetDatumFromDays(uint32 numDays, uint64 *val) +{ + int y1 = 1; + int d2 = 0, m2 = 0, y2 = 0; + int res; + struct pg_tm ti, *tm = &ti; + + CalculateTargetDate(y1, &d2, &m2, &y2, numDays); + tm->tm_mday = d2; + tm->tm_mon = m2; + tm->tm_year = y2; + + res = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); + res -= POSTGRES_EPOCH_JDATE; + *val = (uint64)res; +} + +/* + * Get date info(day, month, year) from numDays elapsed from + * 1-1-1900. + * Get time info from number of ticks (milliseconds/3.33333333) + * elapsed. + */ +static inline +void GetDatetimeFromDaysTicks(uint32 numDays, uint32 numTicks, + struct pg_tm *tm, fsec_t *fsec) +{ + int y1 = 1900; + int d2 = 0, m2 = 0, y2 = 0; + int min, hour, sec, numMilli = 0; + + CalculateTargetDate(y1, &d2, &m2, &y2, numDays); + + numMilli = 3.33333333 * numTicks; + *fsec = (numMilli % 1000) * 1000; + numMilli /= 1000; + + /* need explicit assignment for JDBC prep-exec query + * where time datatype is sent as datetime in case + * sendTimeAsDateTime parameter is not explicitly set + * to false + */ + if (*fsec == 999000) + { + numMilli++; + *fsec = 0; + } + + sec = numMilli % 60; + numMilli /= 60; + min = numMilli % 60; + numMilli /= 60; + hour = numMilli; + + tm->tm_mday = d2; + tm->tm_mon = m2; + tm->tm_year = y2; + tm->tm_hour = hour; + tm->tm_min = min; + tm->tm_sec = sec; +} + +/* + * Get hour, min, sec, millisecond, date info from numDays elapsed from 1-1-1900 and + * numTicks (= numMilliSecond / 3.3333333) elapsed from 12AM of that day. + * Also, do necessary millisecond adjustment before storing client datetime in system. + * Ex.- 1955-12-13 23:59:59.999 is stored as 1955-12-14 0:0:0.0 + */ +void +TdsTimeGetDatumFromDatetime(uint32 numDays, uint32 numTicks, + Timestamp *timestamp) +{ + struct pg_tm ti, *tm = &ti; + fsec_t fsec; + + GetDatetimeFromDaysTicks(numDays, numTicks, tm, &fsec); + if (tm2timestamp(tm, fsec, NULL, timestamp) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); +} + +static inline +void GetDatetimeFromDaysMins(uint16 numDays, uint16 numMins, + struct pg_tm *tm, fsec_t *fsec) +{ + int y1 = 1900; + int d2 = 0, m2 = 0, y2 = 0; + int min, hour; + + CalculateTargetDate(y1, &d2, &m2, &y2, numDays); + + min = numMins % 60; + numMins /= 60; + hour = numMins; + + tm->tm_mday = d2; + tm->tm_mon = m2; + tm->tm_year = y2; + tm->tm_hour = hour; + tm->tm_min = min; +} + +/* + * Get hour, min, date info from numDays elapsed from 1-1-1900 + */ +void +TdsTimeGetDatumFromSmalldatetime(uint16 numDays, uint16 numMins, + Timestamp *timestamp) +{ + struct pg_tm ti, *tm = &ti; + fsec_t fsec = 0; + + GetDatetimeFromDaysMins(numDays, numMins, tm, &fsec); + tm->tm_sec = 0; + if (tm2timestamp(tm, fsec, NULL, timestamp) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); +} + +void +TdsGetDayTimeFromTimestamp(Timestamp value, uint32 *numDays, uint64 *numSec, + int scale) +{ + struct pg_tm ti, tj, *tm = &ti, *tt = &tj; + fsec_t fsec = 0; + double res = 0; + + if (timestamp2tm((Timestamp)value, NULL, tm, &fsec, NULL, NULL) != 0) + ereport(ERROR,(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + tt->tm_mday = 1, tt->tm_mon = 1, tt->tm_year = 1; + *numDays = (uint32)GetDayDifference(tt, tm); + + res = (double)(((tm->tm_hour * 60 + tm->tm_min) * 60 + tm->tm_sec) + + ((double)fsec/ 1000000)); + while (scale--) + res *= 10; + + *numSec = (uint64_t)res; +} + +void TdsGetTimestampFromDayTime(uint32 numDays, uint64 numMicro, int tz, + Timestamp *timestamp, int scale) +{ + struct pg_tm ti, *tm = &ti; + fsec_t fsec; + int y1 = 1; + int d2 = 0, m2 = 0, y2 = 0, min, hour, sec; + double result; + + CalculateTargetDate(y1, &d2, &m2, &y2, numDays); + + result = (double)numMicro; + while (scale--) + result /= 10; + result *= 1000000; + + /* + * Casting result to unint64_t will always round it down to the nearest integer (similar + * to what floor() does). Instead, we should round it to the nearest integer. + */ + numMicro = (result - (uint64_t)result <= 0.5) ? (uint64_t)result : (uint64_t)result + 1; + + fsec = numMicro % 1000000; + numMicro /= 1000000; + sec = numMicro % 60; + numMicro /= 60; + min = numMicro % 60; + numMicro /= 60; + hour = numMicro; + + tm->tm_mday = d2; + tm->tm_mon = m2; + tm->tm_year = y2; + tm->tm_hour = hour; + tm->tm_min = min; + tm->tm_sec = sec; + + if (tm2timestamp(tm, fsec, &tz, timestamp) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); +} + + diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c b/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c new file mode 100644 index 00000000000..cd683da053a --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c @@ -0,0 +1,3396 @@ +/*------------------------------------------------------------------------- + * + * tdstypeio.c + * TDS Listener functions for PG-Datum <-> TDS-protocol conversion + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/xact.h" +#include "catalog/pg_authid.h" +#include "catalog/pg_type.h" +#include "catalog/pg_namespace.h" +#include "executor/spi.h" +#include "fmgr.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "parser/scansup.h" +#include "utils/cash.h" +#include "utils/hsearch.h" +#include "utils/builtins.h" /* for format_type_be() */ +#include "utils/guc.h" +#include "utils/lsyscache.h" /* for getTypeInputInfo() and OidInputFunctionCall()*/ +#include "utils/numeric.h" +#include "utils/snapmgr.h" +#include "utils/syscache.h" +#include "utils/uuid.h" +#include "utils/varlena.h" +#include "utils/xml.h" + +#include "src/include/tds_int.h" +#include "src/include/tds_timestamp.h" +#include "src/include/tds_typeio.h" +#include "src/include/err_handler.h" +#include "src/include/tds_instr.h" + +#include "tds_data_map.c" /* include tables that used to initialize hashmaps */ + +#define TDS_RETURN_DATUM(x) return ((Datum) (x)) + +#define GetPgOid(pgTypeOid, finfo) \ +do { \ + pgTypeOid = (finfo->ttmbasetypeid != InvalidOid) ? \ + finfo->ttmbasetypeid : finfo->ttmtypeid; \ +} while(0); + +static HTAB *functionInfoCacheByOid = NULL; +static HTAB *functionInfoCacheByTdsId = NULL; + +static HTAB *TdsEncodingInfoCacheByLCID = NULL; + +void CopyMsgBytes(StringInfo msg, char *buf, int datalen); +int GetMsgByte(StringInfo msg); +const char * GetMsgBytes(StringInfo msg, int datalen); +unsigned int GetMsgInt(StringInfo msg, int b); +int64 GetMsgInt64(StringInfo msg); +uint128 GetMsgUInt128(StringInfo msg); +float4 GetMsgFloat4(StringInfo msg); +float8 GetMsgFloat8(StringInfo msg); +static void SwapData(StringInfo buf, int st, int end); +static Datum TdsAnyToServerEncodingConversion(Oid oid, pg_enc enc, char *str, int len); +int TdsUTF16toUTF8XmlResult(StringInfo buf, void **resultPtr); + +Datum TdsTypeBitToDatum(StringInfo buf); +Datum TdsTypeIntegerToDatum(StringInfo buf, int maxLen); +Datum TdsTypeFloatToDatum(StringInfo buf, int maxLen); +Datum TdsTypeVarcharToDatum(StringInfo buf, Oid pgTypeOid, uint32_t collation); +Datum TdsTypeNCharToDatum(StringInfo buf); +Datum TdsTypeNumericToDatum(StringInfo buf, int scale); +Datum TdsTypeVarbinaryToDatum(StringInfo buf); +Datum TdsTypeDatetime2ToDatum(StringInfo buf, int scale, int len); +Datum TdsTypeDatetimeToDatum(StringInfo buf); +Datum TdsTypeDateToDatum(StringInfo buf); +Datum TdsTypeTimeToDatum(StringInfo buf, int scale, int len); +Datum TdsTypeMoneyToDatum(StringInfo buf); +Datum TdsTypeXMLToDatum(StringInfo buf); +Datum TdsTypeUIDToDatum(StringInfo buf); +Datum TdsTypeSqlVariantToDatum(StringInfo buf); + +/* Local structures for the Function Cache by TDS Type ID */ +typedef struct FunctionCacheByTdsIdKey +{ + int32_t tdstypeid; + int32_t tdstypelen; +} FunctionCacheByTdsIdKey; + +typedef struct FunctionCacheByTdsIdEntry +{ + FunctionCacheByTdsIdKey key; + TdsIoFunctionData data; +} FunctionCacheByTdsIdEntry; + +/* + * getSendFunc - get the function pointer for type output + * + * Given the ttmsendfunc id returns the function pointer for the + * corresponding output function to call. + */ +static inline TdsSendTypeFunction +getSendFunc(int funcId) +{ + switch (funcId) + { + case TDS_SEND_BIT: return TdsSendTypeBit; + case TDS_SEND_TINYINT: return TdsSendTypeTinyint; + case TDS_SEND_SMALLINT: return TdsSendTypeSmallint; + case TDS_SEND_INTEGER: return TdsSendTypeInteger; + case TDS_SEND_BIGINT: return TdsSendTypeBigint; + case TDS_SEND_FLOAT4: return TdsSendTypeFloat4; + case TDS_SEND_FLOAT8: return TdsSendTypeFloat8; + case TDS_SEND_VARCHAR: return TdsSendTypeVarchar; + case TDS_SEND_NVARCHAR: return TdsSendTypeNVarchar; + case TDS_SEND_MONEY: return TdsSendTypeMoney; + case TDS_SEND_SMALLMONEY: return TdsSendTypeSmallmoney; + case TDS_SEND_CHAR: return TdsSendTypeChar; + case TDS_SEND_NCHAR: return TdsSendTypeNChar; + case TDS_SEND_SMALLDATETIME: return TdsSendTypeSmalldatetime; + case TDS_SEND_TEXT: return TdsSendTypeText; + case TDS_SEND_NTEXT: return TdsSendTypeNText; + case TDS_SEND_DATE: return TdsSendTypeDate; + case TDS_SEND_DATETIME: return TdsSendTypeDatetime; + case TDS_SEND_NUMERIC: return TdsSendTypeNumeric; + case TDS_SEND_IMAGE: return TdsSendTypeImage; + case TDS_SEND_BINARY: return TdsSendTypeBinary; + case TDS_SEND_VARBINARY: return TdsSendTypeVarbinary; + case TDS_SEND_UNIQUEIDENTIFIER: return TdsSendTypeUniqueIdentifier; + case TDS_SEND_TIME: return TdsSendTypeTime; + case TDS_SEND_DATETIME2: return TdsSendTypeDatetime2; + case TDS_SEND_XML: return TdsSendTypeXml; + case TDS_SEND_SQLVARIANT: return TdsSendTypeSqlvariant; + case TDS_SEND_DATETIMEOFFSET: return TdsSendTypeDatetimeoffset; + /* TODO: should Assert here once all types are implemented */ + default: return NULL; + } +} + +/* + * TdsRecvTypeFunction - get the function pointer for type input + * + * Given the ttmsendfunc id returns the function pointer for the + * corresponding input function to call. + */ +static inline TdsRecvTypeFunction +getRecvFunc(int funcId) +{ + switch (funcId) + { + case TDS_RECV_BIT: return TdsRecvTypeBit; + case TDS_RECV_TINYINT: return TdsRecvTypeTinyInt; + case TDS_RECV_SMALLINT: return TdsRecvTypeSmallInt; + case TDS_RECV_INTEGER: return TdsRecvTypeInteger; + case TDS_RECV_BIGINT: return TdsRecvTypeBigInt; + case TDS_RECV_FLOAT4: return TdsRecvTypeFloat4; + case TDS_RECV_FLOAT8: return TdsRecvTypeFloat8; + case TDS_RECV_VARCHAR: return TdsRecvTypeVarchar; + case TDS_RECV_NVARCHAR: return TdsRecvTypeNVarchar; + case TDS_RECV_MONEY: return TdsRecvTypeMoney; + case TDS_RECV_SMALLMONEY: return TdsRecvTypeSmallmoney; + case TDS_RECV_CHAR: return TdsRecvTypeChar; + case TDS_RECV_NCHAR: return TdsRecvTypeNChar; + case TDS_RECV_SMALLDATETIME: return TdsRecvTypeSmalldatetime; + case TDS_RECV_TEXT: return TdsRecvTypeText; + case TDS_RECV_NTEXT: return TdsRecvTypeNText; + case TDS_RECV_DATE: return TdsRecvTypeDate; + case TDS_RECV_DATETIME: return TdsRecvTypeDatetime; + case TDS_RECV_NUMERIC: return TdsRecvTypeNumeric; + case TDS_RECV_IMAGE: return TdsRecvTypeBinary; + case TDS_RECV_BINARY: return TdsRecvTypeBinary; + case TDS_RECV_VARBINARY: return TdsRecvTypeVarbinary; + case TDS_RECV_UNIQUEIDENTIFIER: return TdsRecvTypeUniqueIdentifier; + case TDS_RECV_TIME: return TdsRecvTypeTime; + case TDS_RECV_DATETIME2: return TdsRecvTypeDatetime2; + case TDS_RECV_XML: return TdsRecvTypeXml; + case TDS_RECV_TABLE: return TdsRecvTypeTable; + case TDS_RECV_SQLVARIANT: return TdsRecvTypeSqlvariant; + case TDS_RECV_DATETIMEOFFSET: return TdsRecvTypeDatetimeoffset; + /* TODO: should Assert here once all types are implemented */ + default: return NULL; + } +} + +#ifdef USE_LIBXML + +static int +xmlChar_to_encoding(const xmlChar *encoding_name) +{ + int encoding = pg_char_to_encoding((const char *)encoding_name); + + if (encoding < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid encoding name \"%s\"", + (const char *)encoding_name))); + return encoding; +} +#endif + +int TdsUTF16toUTF8XmlResult(StringInfo buf, void **resultPtr) +{ + char *str; + int nbytes; + StringInfoData tempBuf; + void *result; + + initStringInfo(&tempBuf); + enlargeStringInfo(&tempBuf, buf->len); + TdsUTF16toUTF8StringInfo(&tempBuf, buf->data, buf->len); + buf = &tempBuf; + + nbytes = buf->len - buf->cursor; + + str = (char *)GetMsgBytes(buf, nbytes); + + result = palloc0(nbytes + 1 + VARHDRSZ); + SET_VARSIZE(result, nbytes + VARHDRSZ); + memcpy(VARDATA(result), str, nbytes); + str = VARDATA(result); + str[nbytes] = '\0'; + + *resultPtr = result; + + return PG_UTF8; +} + +/* + * TdsAnyToServerEncodingConversion - lookup the PG Encoding based on lcid + * and convert the encoding of input str + */ +static Datum +TdsAnyToServerEncodingConversion(Oid oid, pg_enc enc, char *str, int len) +{ + Oid typinput; + Oid typioparam; + char *pstring; + Datum pval; + + getTypeInputInfo(oid, &typinput, &typioparam); + pstring = pg_any_to_server(str, len, enc); + pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); + + /* Free result of encoding conversion, if any */ + if (pstring && pstring != str) + pfree(pstring); + + return pval; +} + +/* + * TdsResetTypeFunctionCache - reset the type function caches. + * + * During connection reset, this is used. + */ +void +TdsResetCache(void) +{ + functionInfoCacheByOid = NULL; + functionInfoCacheByTdsId = NULL; + TdsEncodingInfoCacheByLCID = NULL; + reset_error_mapping_cache(); +} + +void +TdsLoadEncodingLCIDCache(void) +{ + HASHCTL hashCtl; + + if (TdsEncodingInfoCacheByLCID == NULL) + { + /* Create the LCID - Encoding (code page in tsql's term) hash table in our TDS memory context */ + MemSet(&hashCtl, 0, sizeof(hashCtl)); + hashCtl.keysize = sizeof(int); + hashCtl.entrysize = 2 * sizeof(int); + hashCtl.hcxt = TdsMemoryContext; + TdsEncodingInfoCacheByLCID = hash_create("LCID - Encoding map cache", + SPI_processed, + &hashCtl, + HASH_ELEM | HASH_CONTEXT | HASH_BLOBS); + /* + * Load LCID - Encoding pair into our hash table. + */ + for (int i = 0; i < TdsLCIDToEncodingMap_datasize; i++) + { + int lcid; + TdsLCIDToEncodingMapInfo mInfo; + + /* Create the hash entry for lookup by LCID*/ + lcid = TdsLCIDToEncodingMap_data[i].lcid; + mInfo = (TdsLCIDToEncodingMapInfo)hash_search(TdsEncodingInfoCacheByLCID, + &lcid, + HASH_ENTER, + NULL); + mInfo->enc = TdsLCIDToEncodingMap_data[i].enc; + } + } +} + +/* + * TdsLookupEncodingByLCID - LCID - Encoding lookup + */ +int +TdsLookupEncodingByLCID(int lcid) +{ + bool found; + TdsLCIDToEncodingMapInfo mInfo; + + mInfo = (TdsLCIDToEncodingMapInfo)hash_search(TdsEncodingInfoCacheByLCID, + &lcid, + HASH_FIND, + &found); + + /* + * TODO: which encoding by default we should consider + * if appropriate Encoding is not found. + */ + if (!found) + { + return -1; + } + return mInfo->enc; +} + +void +TdsLoadTypeFunctionCache(void) +{ + HASHCTL hashCtl; + Oid sys_nspoid = get_namespace_oid("sys", false); + + /* Create the function info hash table in our TDS memory context */ + if (functionInfoCacheByOid == NULL) /* create hash table */ + { + MemSet(&hashCtl, 0, sizeof(hashCtl)); + hashCtl.keysize = sizeof(Oid); + hashCtl.entrysize = sizeof(TdsIoFunctionData); + hashCtl.hcxt = TdsMemoryContext; + functionInfoCacheByOid = hash_create("IO function info cache", + SPI_processed, + &hashCtl, + HASH_ELEM | HASH_CONTEXT | HASH_BLOBS); + } + + if (functionInfoCacheByTdsId == NULL) /* create hash table */ + { + MemSet(&hashCtl, 0, sizeof(hashCtl)); + hashCtl.keysize = sizeof(FunctionCacheByTdsIdKey); + hashCtl.entrysize = sizeof(FunctionCacheByTdsIdEntry); + hashCtl.hcxt = TdsMemoryContext; + functionInfoCacheByTdsId = hash_create("IO function info cache by TDS id", + SPI_processed, + &hashCtl, + HASH_ELEM | HASH_CONTEXT | HASH_BLOBS); + } + /* + * Load the contents of the table into our hash table. + */ + + for (int i = 0; i < TdsIoFunctionRawData_datasize; i++) + { + Oid typeoid; + Oid basetypeoid; + Oid nspoid; + TdsIoFunctionInfo finfo; + FunctionCacheByTdsIdKey fc2key; + FunctionCacheByTdsIdEntry *fc2ent; + + nspoid = strcmp(TdsIoFunctionRawData_data[i].typnsp, "sys") == 0 ? sys_nspoid : PG_CATALOG_NAMESPACE; + typeoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid, + CStringGetDatum(TdsIoFunctionRawData_data[i].typname), ObjectIdGetDatum(nspoid)); + + if (OidIsValid(typeoid)) + { + basetypeoid = getBaseType(typeoid); + finfo = (TdsIoFunctionInfo)hash_search(functionInfoCacheByOid, + &typeoid, + HASH_ENTER, + NULL); + finfo->ttmbasetypeid = typeoid == basetypeoid ? 0 : basetypeoid; + finfo->ttmtdstypeid = TdsIoFunctionRawData_data[i].ttmtdstypeid; + finfo->ttmtdstypelen = TdsIoFunctionRawData_data[i].ttmtdstypelen; + finfo->ttmtdslenbytes = TdsIoFunctionRawData_data[i].ttmtdslenbytes; + finfo->sendFuncId = TdsIoFunctionRawData_data[i].ttmsendfunc; + finfo->sendFuncPtr = getSendFunc(finfo->sendFuncId); + finfo->recvFuncId = TdsIoFunctionRawData_data[i].ttmrecvfunc; + finfo->recvFuncPtr = getRecvFunc(finfo->recvFuncId); + + /* Create the hash entry for lookup by TDS' type ID */ + fc2key.tdstypeid = TdsIoFunctionRawData_data[i].ttmtdstypeid; + fc2key.tdstypelen = TdsIoFunctionRawData_data[i].ttmtdstypelen; + + if(TdsIoFunctionRawData_data[i].ttmrecvfunc != TDS_RECV_INVALID) /* Do not load the Receiver function if its Invalid. */ + { + fc2ent = (FunctionCacheByTdsIdEntry *)hash_search(functionInfoCacheByTdsId, + &fc2key, + HASH_ENTER, + NULL); + finfo = &(fc2ent->data); + finfo->ttmtypeid = typeoid; + finfo->ttmbasetypeid = basetypeoid; + finfo->ttmtdstypeid = TdsIoFunctionRawData_data[i].ttmtdstypeid; + finfo->ttmtdstypelen = TdsIoFunctionRawData_data[i].ttmtdstypelen; + finfo->ttmtdslenbytes = TdsIoFunctionRawData_data[i].ttmtdslenbytes; + finfo->sendFuncId = TdsIoFunctionRawData_data[i].ttmsendfunc; + finfo->sendFuncPtr = getSendFunc(finfo->sendFuncId); + finfo->recvFuncId = TdsIoFunctionRawData_data[i].ttmrecvfunc; + finfo->recvFuncPtr = getRecvFunc(finfo->recvFuncId); + } + } + } + + { + /* Load Table Valued Paramerter since we can't have a static oid mapping for it.*/ + TdsIoFunctionInfo finfo_table; + FunctionCacheByTdsIdKey fc2key_table; + FunctionCacheByTdsIdEntry *fc2ent_table; + + fc2key_table.tdstypeid = TDS_TYPE_TABLE; + fc2key_table.tdstypelen = -1; + fc2ent_table = (FunctionCacheByTdsIdEntry *)hash_search(functionInfoCacheByTdsId, + &fc2key_table, + HASH_ENTER, + NULL); + finfo_table = &(fc2ent_table->data); + finfo_table->ttmtypeid = InvalidOid; + finfo_table->ttmbasetypeid = InvalidOid; + finfo_table->ttmtdstypeid = TDS_TYPE_TABLE; + finfo_table->ttmtdstypelen = -1; + finfo_table->ttmtdslenbytes = 1; + finfo_table->sendFuncId = -1; + finfo_table->sendFuncPtr = getSendFunc(-1); + finfo_table->recvFuncId = TDS_RECV_TABLE; + finfo_table->recvFuncPtr = getRecvFunc(TDS_RECV_TABLE); + } +} + +/* + * TdsLookupTypeFunctionsByOid - IO function cache lookup + */ +TdsIoFunctionInfo +TdsLookupTypeFunctionsByOid(Oid typeId, int32* typmod) +{ + TdsIoFunctionInfo finfo; + bool found; + Oid tmpTypeId; + + Assert(functionInfoCacheByOid != NULL); + + finfo = (TdsIoFunctionInfo)hash_search(functionInfoCacheByOid, + &typeId, + HASH_FIND, + &found); + + /* + * If an entry is not found on tds mapping table, we try to find whether + * we've an entry for its base type. If not found, we continue till the + * bottom base type. + */ + tmpTypeId = typeId; + while (!found) + { + HeapTuple tup; + Form_pg_type typTup; + + tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(tmpTypeId)); + if (!HeapTupleIsValid(tup)) + break; + + typTup = (Form_pg_type) GETSTRUCT(tup); + if (typTup->typtype != TYPTYPE_DOMAIN) + { + /* Not a domain, so stop descending */ + ReleaseSysCache(tup); + break; + } + + tmpTypeId = typTup->typbasetype; + + /* + * Typmod is allowed for domain only when enable_domain_typmod + * is enabled when executing the CREATE DOMAIN Statement, + * see DefineDomain for details. + */ + if (*typmod == -1) + *typmod = typTup->typtypmod; + + finfo = (TdsIoFunctionInfo)hash_search(functionInfoCacheByOid, + &tmpTypeId, + HASH_FIND, + &found); + ReleaseSysCache(tup); + } + + if (!found) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("data type %s is not supported yet", format_type_be(typeId)))); + + return finfo; +} + +/* + * TdsLookupTypeFunctionsByTdsId - IO function cache lookup + */ +TdsIoFunctionInfo +TdsLookupTypeFunctionsByTdsId(int32_t typeId, int32_t typeLen) +{ + FunctionCacheByTdsIdKey fc2key; + FunctionCacheByTdsIdEntry *fc2ent; + bool found; + + Assert(functionInfoCacheByTdsId != NULL); + + /* Try a lookup with the indicated length */ + fc2key.tdstypeid = typeId; + fc2key.tdstypelen = typeLen; + fc2ent = (FunctionCacheByTdsIdEntry *)hash_search(functionInfoCacheByTdsId, + &fc2key, + HASH_FIND, + &found); + if (found) + return &(fc2ent->data); + + /* Variable length types are configured with len=-1, so try that */ + fc2key.tdstypeid = typeId; + fc2key.tdstypelen = -1; + fc2ent = (FunctionCacheByTdsIdEntry *)hash_search(functionInfoCacheByTdsId, + &fc2key, + HASH_FIND, + &found); + if (found) + return &(fc2ent->data); + + /* Not found either way */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("data type %d not supported yet", typeId))); + + return NULL; +} + +/* -------------------------------- + * CopyMsgBytes - copy raw data from a message buffer + * + * Same as above, except data is copied to caller's buffer. + * Function definition closely matches to pq_copymsgbytes + * -------------------------------- + */ +void +CopyMsgBytes(StringInfo msg, char *buf, int datalen) +{ + if (datalen < 0 || datalen > (msg->len - msg->cursor)) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("insufficient data left in message"))); + memcpy(buf, &msg->data[msg->cursor], datalen); + msg->cursor += datalen; +} + +/* -------------------------------- + * GetMsgByte - get a raw byte from a message buffer + * Function definition closely matches pq_getmsgbyte + * -------------------------------- + */ +int +GetMsgByte(StringInfo msg) +{ + if (msg->cursor >= msg->len) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("no data left in message"))); + return (unsigned char) msg->data[msg->cursor++]; +} + +/* -------------------------------- + * GetMsgBytes - get raw data from a message buffer + * + * Returns a pointer directly into the message buffer; note this + * may not have any particular alignment. + * -------------------------------- + */ +const char * +GetMsgBytes(StringInfo msg, int datalen) +{ + const char *result; + + if (datalen < 0 || datalen > (msg->len - msg->cursor)) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("insufficient data left in message"))); + result = &msg->data[msg->cursor]; + msg->cursor += datalen; + return result; +} + +/* -------------------------------- + * GetMsgInt - get a binary integer from a message buffer + * + * Values are treated as unsigned. + * Function definition closely matches to pq_getmsgint + * -------------------------------- + */ +unsigned int +GetMsgInt(StringInfo msg, int b) +{ + unsigned int result; + unsigned char n8; + uint16 n16; + uint32 n32; + + switch (b) + { + case 1: + CopyMsgBytes(msg, (char *) &n8, 1); + result = n8; + break; + case 2: + CopyMsgBytes(msg, (char *) &n16, 2); + result = LEtoh16(n16); + break; + case 3: + memset(&n32, 0, sizeof(n32)); + CopyMsgBytes(msg, (char *) &n32, 3); + result = LEtoh32(n32); + break; + case 4: + CopyMsgBytes(msg, (char *) &n32, 4); + result = LEtoh32(n32); + break; + default: + elog(ERROR, "unsupported integer size %d", b); + result = 0; /* keep compiler quiet */ + break; + } + return result; +} + +/* -------------------------------- + * GetMsgInt64 - get a binary 8-byte int from a message buffer + * + * It is tempting to merge this with GetMesInt, but we'd have to make the + * result int64 for all data widths --- that could be a big performance + * hit on machines where int64 isn't efficient. + * Function definition closely mateches to pg_getmsgint64 + * -------------------------------- + */ +int64 +GetMsgInt64(StringInfo msg) +{ + uint64 n64; + + CopyMsgBytes(msg, (char *) &n64, sizeof(n64)); + + return LEtoh64(n64); +} + +/* -------------------------------- + * GetMsgUInt128 - get a binary 16-byte unsigned int from a message buffer + * -------------------------------- + */ +uint128 +GetMsgUInt128(StringInfo msg) +{ + uint128 n128; + + memcpy(&n128, &msg->data[msg->cursor], sizeof(n128)); + msg->cursor += sizeof(n128); + + return LEtoh128(n128); +} + +/* -------------------------------- + * GetMsgFloat4 - get a float4 from a message buffer + * + * Function definition closely matches to pq_getmsgfloat4 + * -------------------------------- + */ +float4 +GetMsgFloat4(StringInfo msg) +{ + union + { + float4 f; + uint32 i; + } swap; + + swap.i = GetMsgInt(msg, 4); + return swap.f; + +} + +/* -------------------------------- + * GetMsgFloat8 - get a float8 from a message buffer + * + * Function definition closely matches to pq_getmsgfloat8 + * -------------------------------- + */ +float8 +GetMsgFloat8(StringInfo msg) +{ + union + { + float8 f; + int64 i; + } swap; + + swap.i = GetMsgInt64(msg); + return swap.f; +} + +/* Helper Function to convert Bit value into Datum. */ +Datum +TdsTypeBitToDatum(StringInfo buf) +{ + int ext = GetMsgByte(buf); + PG_RETURN_BOOL((ext != 0) ? true : false); +} + +/* Helper Function to convert Integer value into Datum. */ +Datum +TdsTypeIntegerToDatum(StringInfo buf, int maxLen) +{ + switch(maxLen) + { + case TDS_MAXLEN_TINYINT: /* TINY INT. */ + { + uint8 res = GetMsgInt(buf, sizeof(int8)); + PG_RETURN_INT16((int16) res); + } + break; + case TDS_MAXLEN_SMALLINT: /* SMALL INT. */ + { + uint16 res = GetMsgInt(buf, sizeof(uint16)); + PG_RETURN_INT16((uint16) res); + } + break; + case TDS_MAXLEN_INT: /* INT. */ + { + unsigned int res = GetMsgInt(buf, sizeof(int32)); + PG_RETURN_INT32((int32) res); + } + break; + case TDS_MAXLEN_BIGINT: /* BIG INT. */ + { + uint64 res = GetMsgInt64(buf); + PG_RETURN_INT64((int64) res); + } + break; + default: + elog(ERROR, "unsupported integer size %d", maxLen); + PG_RETURN_INT32(0); /* keep compiler quiet */ + break; + } +} + +/* Helper Function to convert Float value into Datum. */ +Datum +TdsTypeFloatToDatum(StringInfo buf, int maxLen) +{ + switch(maxLen) + { + case TDS_MAXLEN_FLOAT4: + { + float4 res; + res = GetMsgFloat4(buf); + PG_RETURN_FLOAT4(res); + } + break; + case TDS_MAXLEN_FLOAT8: + { + float8 res; + res = GetMsgFloat8(buf); + PG_RETURN_FLOAT8(res); + } + default: + elog(ERROR, "unsupported float size %d", maxLen); + PG_RETURN_FLOAT4(0); /* keep compiler quiet */ + break; + } +} + +/* Helper Function to convert Varchar,Char and Text values into Datum. */ +Datum +TdsTypeVarcharToDatum(StringInfo buf, Oid pgTypeOid, uint32_t collation) +{ + char csave; + Datum pval; + pg_enc encoding; + + csave = buf->data[buf->len]; + buf->data[buf->len] = '\0'; + + /* If we recieve 0 value for LCID then we should treat it as a default LCID.*/ + encoding = TdsGetEncoding(collation); + + pval = TdsAnyToServerEncodingConversion(pgTypeOid, + encoding, + buf->data, buf->len); + buf->data[buf->len] = csave; + return pval; +} + +/* Helper Function to convert NVarchar, NChar and NText values into Datum. */ +Datum +TdsTypeNCharToDatum(StringInfo buf) +{ + void *result; + StringInfoData temp; + + initStringInfo(&temp); + TdsUTF16toUTF8StringInfo(&temp, buf->data, buf->len); + + result = tds_varchar_input(temp.data, temp.len, -1); + pfree(temp.data); + + PG_RETURN_VARCHAR_P(result); +} + +static inline char * +ReverseString(char *res) +{ + int lo, hi; + if (!res) + return NULL; + + lo = 0; + hi = strlen(res)-1; + + while (lo < hi) + { + res[lo] ^= res[hi]; + res[hi] ^= res[lo]; + res[lo] ^= res[hi]; + lo++; hi--; + } + return res; +} + +static inline void +Integer2String(uint128 num, char* str) +{ + int i = 0, rem = 0; + while (num) + { + rem = num % 10; + str[i++] = rem + '0'; + num = num/10; + } + str[i++] = '-'; + ReverseString(str); +} + +/* Helper Function to convert Numeric value into Datum. */ +Datum +TdsTypeNumericToDatum(StringInfo buf, int scale) +{ + Numeric res; + int len, sign; + char *decString; + int temp1, temp2; + uint128 num = 0; + + /* fetch the sign from the actual data which is the first byte */ + sign = (uint8_t)GetMsgInt(buf, 1); + + /* fetch the data but ignore the sign byte now */ + { + uint128 n128 = 0; + + memcpy(&n128, &buf->data[buf->cursor], TDS_MAXLEN_NUMERIC - 1); + buf->cursor += TDS_MAXLEN_NUMERIC - 1; + + num = LEtoh128(n128); + } + + decString = (char *)palloc0(sizeof(char) * 40); + + if (num != 0) + Integer2String(num, decString); + else + decString[0] = '0'; + + len = strlen(decString); + temp1 = '.'; + + if (num != 0) + { + while (scale) + { + temp2 = decString[len - scale]; + decString[len - scale] = temp1; + temp1 = temp2; + scale--; + } + decString[len++] = temp1; + } + else + { + decString[len++] = temp1; + while (scale) + { + decString[len++] = '0'; + scale--; + } + } + + if (sign == 1 && num != 0) + decString++; + + res = TdsSetVarFromStrWrapper(decString); + PG_RETURN_NUMERIC(res); +} + +/* Helper Function to convert Varbinary and Binary values into Datum. */ +Datum +TdsTypeVarbinaryToDatum(StringInfo buf) +{ + bytea *result; + int nbytes; + + nbytes = buf->len - buf->cursor; + result = (bytea *) palloc0(nbytes + VARHDRSZ); + SET_VARSIZE(result, nbytes + VARHDRSZ); + CopyMsgBytes(buf, VARDATA(result), nbytes); + + PG_RETURN_BYTEA_P(result); +} + +/* Helper Function to convert Datetime2 value into Datum. */ +Datum +TdsTypeDatetime2ToDatum(StringInfo buf, int scale, int len) +{ + uint64_t numMicro = 0; + uint32_t numDays = 0; + Timestamp timestamp; + + if (scale == 255) + scale = 6; + + memcpy(&numMicro, &buf->data[buf->cursor], len - 3); + buf->cursor += len - 3; + + memcpy(&numDays, &buf->data[buf->cursor], 3); + buf->cursor += 3; + + TdsGetTimestampFromDayTime(numDays, numMicro, 0, ×tamp, scale); + + PG_RETURN_TIMESTAMP((Timestamp)timestamp); +} + +/* Helper Function to convert Datetime value into Datum. */ +Datum +TdsTypeDatetimeToDatum(StringInfo buf) +{ + uint32 numDays, numTicks; + uint64 val; + Timestamp timestamp; + + val = (uint64)GetMsgInt64(buf); + numTicks = val >> 32; + numDays = val & 0x00000000ffffffff; + + TdsTimeGetDatumFromDatetime(numDays, numTicks, ×tamp); + + PG_RETURN_TIMESTAMP((uint64)timestamp); +} + +/* Helper Function to convert Date value into Datum. */ +Datum +TdsTypeDateToDatum(StringInfo buf) +{ + DateADT result; + uint64 val; + result = (DateADT)GetMsgInt(buf, 3); + TdsCheckDateValidity(result); + + TdsTimeGetDatumFromDays(result, &val); + + PG_RETURN_DATEADT(val); +} + +/* Helper Function to convert Time value into Datum. */ +Datum +TdsTypeTimeToDatum(StringInfo buf, int scale, int len) +{ + double result = 0; + uint64_t numMicro = 0; + + /* + * if time data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (scale == 255) + scale = 6; + + memcpy(&numMicro, &buf->data[buf->cursor], len); + buf->cursor += len; + + result = (double)numMicro; + while (scale--) + result /= 10; + + result *= 1000000; + if (result < INT64CONST(0) || result > USECS_PER_DAY) + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("time out of range"))); + + + PG_RETURN_TIMEADT((TimeADT)result); +} + +/* Helper Function to convert Money value into Datum. */ +Datum +TdsTypeMoneyToDatum(StringInfo buf) +{ + uint64 high, low; + uint64 val = GetMsgInt64(buf); + + high = val & 0xffffffff00000000; + low = val & 0x00000000ffffffff; + val = high >> 32 | low << 32; + + + PG_RETURN_CASH((Cash)val); +} + +/* Helper Function to convert XML value into Datum. */ +Datum +TdsTypeXMLToDatum(StringInfo buf) +{ + void *result; + char *str; + int nbytes; + void *doc; + int encoding = PG_UTF8; + xmlChar *encodingStr = NULL; + + /* + * Read the data in raw format. We don't know yet what the encoding is, as + * that information is embedded in the xml declaration; so we have to + * parse that before converting to server encoding. + */ + nbytes = buf->len - buf->cursor; + + str = (char *)GetMsgBytes(buf, nbytes); + + /* + * We need a null-terminated string to pass to parse_xml_decl(). Rather + * than make a separate copy, make the temporary result one byte bigger + * than it needs to be. + */ + result = palloc0(nbytes + 1 + VARHDRSZ); + SET_VARSIZE(result, nbytes + VARHDRSZ); + memcpy(VARDATA(result), str, nbytes); + str = VARDATA(result); + str[nbytes] = '\0'; + + /* + * TODO: handle the encoding list + * tds_parse_xml_decl((const char *) str, NULL, NULL, NULL, NULL); + * encoding = encodingStr ? xmlChar_to_encoding(encodingStr) : PG_UTF8; + */ + tds_parse_xml_decl((const xmlChar *)str, NULL, NULL, &encodingStr, NULL); + encoding = encodingStr ? xmlChar_to_encoding(encodingStr) : TdsUTF16toUTF8XmlResult(buf, &result); + + /* + * Parse the data to check if it is well-formed XML data. Assume that + * xml_parse will throw ERROR if not. + */ + doc = tds_xml_parse(result, XMLOPTION_CONTENT, true, encoding); + tds_xmlFreeDoc(doc); + + PG_RETURN_XML_P(result); +} + +/* Helper Function to convert UID value into Datum. */ +Datum +TdsTypeUIDToDatum(StringInfo buf) +{ + pg_uuid_t *uuid; + + /* + * Valid values for UUID are NULL or 16 byte value. + * NULL values are handled in the caller, so in the recv + * function we will only get 16 byte value + */ + Assert(buf->len == TDS_MAXLEN_UNIQUEIDENTIFIER); + + /* SWAP to match TSQL behaviour */ + SwapData(buf, buf->cursor + 0, buf->cursor + 3); + SwapData(buf, buf->cursor + 1, buf->cursor + 2); + SwapData(buf, buf->cursor + 4, buf->cursor + 5); + SwapData(buf, buf->cursor + 6, buf->cursor + 7); + + uuid = (pg_uuid_t *) palloc(UUID_LEN); + memcpy(uuid->data, GetMsgBytes(buf, UUID_LEN), UUID_LEN); + + PG_RETURN_POINTER(uuid); +} + +StringInfo +TdsGetPlpStringInfoBufferFromToken(const char *message, const ParameterToken token) +{ + StringInfo pbuf; + Plp plpHead = token->plp, temp; + uint64_t len = 0; + + temp = plpHead; + pbuf = makeStringInfo(); + + /* data of zero length */ + if (temp == NULL) + return pbuf; + + while(temp != NULL) + { + len += temp->len; + temp = temp->next; + } + + + /* + * Explicitly calling enlargeStringInfo. This will save + * some overhead incase the data is very large and needs + * repalloc again and again + */ + enlargeStringInfo(pbuf, len); + + temp = plpHead; + + while (temp != NULL) + { + appendBinaryStringInfo(pbuf, &message[temp->offset], temp->len); + temp = temp->next; + } + + return pbuf; +} + +StringInfo +TdsGetStringInfoBufferFromToken(const char *message, const ParameterToken token) +{ + StringInfo pbuf; + + const char *pvalue = &message[token->dataOffset]; + + pbuf = palloc(sizeof(StringInfoData)); + /* + * Rather than copying data around, we just set up a phony + * StringInfo pointing to the correct portion of the TDS message + * buffer. + */ + pbuf->data = (char *) pvalue; + pbuf->maxlen = token->len; + pbuf->len = token->len; + pbuf->cursor = 0; + + return pbuf; +} + +/* -------------------------------- + * TdsRevTypeBit - converts external binary format to bool + * + * The external representation is one byte. Any nonzero value is taken + * as "true". + * Function definition closely matches to get boolrecv + * -------------------------------- + */ +Datum +TdsRecvTypeBit(const char *message, const ParameterToken token) +{ + Datum res; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + res = TdsTypeBitToDatum(buf); + + pfree(buf); + return res; +} + +/* -------------------------------- + * TdsRecvTypeSmallInt - converts external binary format to int2 + * -------------------------------- + */ +Datum +TdsRecvTypeTinyInt(const char *message, const ParameterToken token) +{ + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + Datum res; + + res = TdsTypeIntegerToDatum(buf, sizeof(int8)); + + pfree(buf); + return res; +} + +/* -------------------------------- + * TdsRecvTypeSmallInt - converts external binary format to int2 + * Function definition closely matches to int2recv + * -------------------------------- + */ +Datum +TdsRecvTypeSmallInt(const char *message, const ParameterToken token) +{ + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + Datum res; + + res = TdsTypeIntegerToDatum(buf, sizeof(int16)); + + pfree(buf); + return res; +} + +/* -------------------------------- + * TdsRecvTypeInteger - converts external binary format to int4 + * Function definition closely matches to int4recv + * -------------------------------- + */ +Datum +TdsRecvTypeInteger(const char *message, const ParameterToken token) +{ + Datum res; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + res = TdsTypeIntegerToDatum(buf, sizeof(int32)); + + pfree(buf); + return res; +} + +/* -------------------------------- + * TdsRecvTypeBigInt - converts external binary format to int8 + * Function definition closely matches to int8recv + * -------------------------------- + */ +Datum +TdsRecvTypeBigInt(const char *message, const ParameterToken token) +{ + Datum res; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + res = TdsTypeIntegerToDatum(buf, sizeof(int64)); + + pfree(buf); + return res; +} + +/* -------------------------------- + * TdsRecvTypeFloat4 - converts external binary format to float4 + * Function definition closely matches to float4recv + * -------------------------------- + */ +Datum +TdsRecvTypeFloat4(const char *message, const ParameterToken token) +{ + Datum res; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + res = TdsTypeFloatToDatum(buf, sizeof(float4)); + + pfree(buf); + return res; +} + +/* -------------------------------- + * TdsRecvTypeFloat4 - converts external binary format to float8 + * Function definition closely matches to float8recv + * -------------------------------- + */ +Datum +TdsRecvTypeFloat8(const char *message, const ParameterToken token) +{ + Datum res; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + res = TdsTypeFloatToDatum(buf, sizeof(float8)); + + pfree(buf); + return res; +} + +/* -------------------------------- + * TdsRecvTypeBinary - converts external binary format to byte data + * -------------------------------- + */ +Datum +TdsRecvTypeBinary(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + result = TdsTypeVarbinaryToDatum(buf); + + pfree(buf); + return result; +} + +/* -------------------------------- + * TdsRecvTypeVarbinary - converts external varbinary format to byte data + * -------------------------------- + */ +Datum +TdsRecvTypeVarbinary(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf; + + if (token->maxLen == 0xffff) + buf = TdsGetPlpStringInfoBufferFromToken(message, token); + else + { + TDSInstrumentation(INSTR_TDS_DATATYPE_VARBINARY_MAX); + + buf = TdsGetStringInfoBufferFromToken(message, token); + } + + result = TdsTypeVarbinaryToDatum(buf); + + if (token->maxLen == 0xffff) + pfree(buf->data); + + pfree(buf); + return result; +} + +/* -------------------------------- + * TdsRecvTypeVarchar - converts external binary format to varchar + * + * Function defination closely matches to varcharrecv + * -------------------------------- + */ +Datum +TdsRecvTypeVarchar(const char *message, const ParameterToken token) +{ + StringInfo buf; + char csave; + Datum pval; + + if (token->maxLen == 0xFFFF) + { + TDSInstrumentation(INSTR_TDS_DATATYPE_VARCHAR_MAX); + + buf = TdsGetPlpStringInfoBufferFromToken(message, token); + } + else + buf = TdsGetStringInfoBufferFromToken(message, token); + + csave = buf->data[buf->len]; + buf->data[buf->len] = '\0'; + pval = TdsAnyToServerEncodingConversion(token->paramMeta.pgTypeOid, + token->paramMeta.encoding, + buf->data, buf->len); + buf->data[buf->len] = csave; + + if (token->maxLen == 0xFFFF) + pfree(buf->data); + + pfree(buf); + return pval; +} + +void +TdsReadUnicodeDataFromTokenCommon(const char *message, const ParameterToken token, StringInfo temp) +{ + StringInfo buf; + + /* + * XXX: We reuse this code for extracting the query from the TDS request. In + * some cases, the query is sent as non-unicode datatypes. In those cases, the + * data can come as PLP. + */ + if ((token->type == TDS_TYPE_NVARCHAR || token->type == TDS_TYPE_VARCHAR) && + (token->maxLen == 0xFFFF)) + buf = TdsGetPlpStringInfoBufferFromToken(message, token); + else + buf = TdsGetStringInfoBufferFromToken(message, token); + + enlargeStringInfo(temp, buf->len); + + TdsUTF16toUTF8StringInfo(temp, buf->data, buf->len); + + if ((token->type == TDS_TYPE_NVARCHAR || token->type == TDS_TYPE_VARCHAR) && + (token->maxLen == 0xFFFF)) + pfree(buf->data); + + pfree(buf); +} + +/* -------------------------------- + * TdsRecvTypeNVarchar - converts external binary format to varchar + * + * Function defination closely matches to varcharrecv + * -------------------------------- + */ +Datum +TdsRecvTypeNVarchar(const char *message, const ParameterToken token) +{ + void *result; + StringInfoData temp; + + if (token->maxLen == 0xFFFF) + TDSInstrumentation(INSTR_TDS_DATATYPE_NVARCHAR_MAX); + + initStringInfo(&temp); + + TdsReadUnicodeDataFromTokenCommon(message, token, &temp); + result = tds_varchar_input(temp.data, temp.len, -1); + + pfree(temp.data); + + PG_RETURN_VARCHAR_P(result); +} + +Datum +TdsRecvTypeText(const char *message, const ParameterToken token) +{ + char csave; + Datum pval; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + csave = buf->data[buf->len]; + buf->data[buf->len] = '\0'; + pval = TdsAnyToServerEncodingConversion(token->paramMeta.pgTypeOid, + token->paramMeta.encoding, buf->data, buf->len); + buf->data[buf->len] = csave; + + pfree(buf); + return pval; +} + +Datum +TdsRecvTypeNText(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + result = TdsTypeNCharToDatum(buf); + + pfree(buf); + return result; +} + +/* -------------------------------- + * TdsRecvTypeChar - converts external binary format to char + * + * Function defination closely matches to bpcharrecv + * -------------------------------- + */ +Datum +TdsRecvTypeChar(const char *message, const ParameterToken token) +{ + char csave; + Datum pval; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + csave = buf->data[buf->len]; + buf->data[buf->len] = '\0'; + pval = TdsAnyToServerEncodingConversion(token->paramMeta.pgTypeOid, + token->paramMeta.encoding, buf->data, buf->len); + buf->data[buf->len] = csave; + + pfree(buf); + return pval; +} + +/* -------------------------------- + * TdsRecvTypeNChar - converts external binary format to nchar + * + * Function defination closely matches to bpcharrecv + * -------------------------------- + */ +Datum +TdsRecvTypeNChar(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + result = TdsTypeNCharToDatum(buf); + + pfree(buf); + return result; +} + +/* + * TdsRecvTypeXml - convert extenal binary format to XML Datum + * Function defination closely matches to xml_recv + * XMLChar * is equivant to char * + */ +Datum +TdsRecvTypeXml(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf = TdsGetPlpStringInfoBufferFromToken(message, token); + + TDSInstrumentation(INSTR_TDS_DATATYPE_XML); + + result = TdsTypeXMLToDatum(buf); + + pfree(buf->data); + pfree(buf); + return result; +} + +/* -------------------------------- + * TdsRecvTypeMoney - converts external binary format to UniqueIdentifier + * + * Function defination closely matches to uuid_recv + * -------------------------------- + */ +Datum +TdsRecvTypeUniqueIdentifier(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + result = TdsTypeUIDToDatum(buf); + + pfree(buf); + return result; +} + +/* -------------------------------- + * TdsRecvTypeMoney - converts external binary format to uint64 for + * money data type + * Function defination closely matches to cash_recv + * -------------------------------- + */ +Datum +TdsRecvTypeMoney(const char *message, const ParameterToken token) +{ + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + uint64 high, low; + uint64 val = GetMsgInt64(buf); + + TDSInstrumentation(INSTR_TDS_DATATYPE_MONEY); + + high = val & 0xffffffff00000000; + low = val & 0x00000000ffffffff; + val = high >> 32 | low << 32; + + pfree(buf); + PG_RETURN_CASH((Cash)val); +} + +/* -------------------------------- + * TdsRecvTypeSmallmoney - converts external binary format to uint64 for + * Smallmoney data type + * Function defination closely matches to cash_recv + * -------------------------------- + */ +Datum +TdsRecvTypeSmallmoney(const char *message, const ParameterToken token) +{ + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + uint64 val = 0; + uint32 low = GetMsgInt(buf, 4); + + TDSInstrumentation(INSTR_TDS_DATATYPE_SMALLMONEY); + + val = (uint64)low; + pfree(buf); + PG_RETURN_CASH((Cash)val); +} + +/* -------------------------------- + * TdsRecvTypeSmalldatetime - converts external binary format to + * Small Datetime data type + * -------------------------------- + */ +Datum +TdsRecvTypeSmalldatetime(const char *message, const ParameterToken token) +{ + uint16 numDays, numMins; + uint32 val; + Timestamp timestamp; + + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + val = (uint32)GetMsgInt(buf, 4); + numMins = val >> 16; + numDays = val & 0x0000ffff; + + TdsTimeGetDatumFromSmalldatetime(numDays, numMins, ×tamp); + + pfree(buf); + PG_RETURN_TIMESTAMP((uint64)timestamp); +} + +/* ------------------------------- + * TdsRecvTypeDate - converts external binary format to + * Date data type + * -------------------------------- + */ +Datum +TdsRecvTypeDate(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + result = TdsTypeDateToDatum(buf); + + pfree(buf); + return result; +} + +/* -------------------------------- + * TdsRecvTypeDatetime - converts external binary format to + * Datetime data type + * -------------------------------- + */ +Datum +TdsRecvTypeDatetime(const char *message, const ParameterToken token) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + result = TdsTypeDatetimeToDatum(buf); + + pfree(buf); + return result; +} + +/* ------------------------------- + * TdsRecvTypeTime - converts external binary format to + * Time data type + * -------------------------------- + */ +Datum +TdsRecvTypeTime(const char *message, const ParameterToken token) +{ + Datum result; + int scale = 0; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + TdsColumnMetaData col = token->paramMeta; + scale = col.metaEntry.type6.scale; + + result = TdsTypeTimeToDatum(buf, scale, token->len); + + pfree(buf); + return result; +} + +Datum +TdsRecvTypeDatetime2(const char *message, const ParameterToken token) +{ + Datum result; + int scale = 0; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + TdsColumnMetaData col = token->paramMeta; + + scale = col.metaEntry.type6.scale; + + + result = TdsTypeDatetime2ToDatum(buf, scale, token->len); + + pfree(buf); + return result; +} + +static inline uint128 +StringToInteger(char *str) +{ + int i = 0, len = 0; + uint128 num = 0; + + if (!str) + return 0; + + len = strlen(str); + + for ( ; i < len; i++) + num = num * 10 + (str[i] - '0'); + + return num; +} + + +/* -------------------------------- + * TdsRecvTypeNumeric - converts external binary format to numeric/decimal + * Function definition closely matches to numeric_recv + * -------------------------------- + */ +Datum +TdsRecvTypeNumeric(const char *message, const ParameterToken token) +{ + Numeric res; + int scale, len, sign; + char *decString, *wholeString; + int temp1, temp2; + uint128 num = 0; + TdsColumnMetaData col = token->paramMeta; + + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + /* scale and precision are part of the type info */ + scale = col.metaEntry.type5.scale; + + /* fetch the sign from the actual data which is the first byte */ + sign = (uint8_t)GetMsgInt(buf, 1); + + /* fetch the data but ignore the sign byte now */ + { + uint128 n128 = 0; + + memcpy(&n128, &buf->data[buf->cursor], token->len - 1); + buf->cursor += token->len - 1; + + num = LEtoh128(n128); + } + + decString = (char *)palloc0(sizeof(char) * 40); + + if (num != 0) + Integer2String(num, decString); + else + decString[0] = '0'; + + wholeString = decString; + + len = strlen(decString); + temp1 = '.'; + + if (num != 0) + { + while (scale) + { + temp2 = decString[len - scale]; + decString[len - scale] = temp1; + temp1 = temp2; + scale--; + } + decString[len++] = temp1; + } + else + { + decString[len++] = temp1; + while (scale) + { + decString[len++] = '0'; + scale--; + } + } + + if (sign == 1 && num != 0) + decString++; + + res = TdsSetVarFromStrWrapper(decString); + pfree(wholeString); + pfree(buf); + PG_RETURN_NUMERIC(res); +} + +/* -------------------------------- + * TdsRecvTypeTable - creates a temp-table from the data being recevied on the wire + * and sends this temp-table's name to the engine. + * -------------------------------- + */ +Datum +TdsRecvTypeTable(const char *message, const ParameterToken token) +{ + char * tableName; + char * query; + StringInfo temp; + int rc; + TvpRowData *row = token->tvpInfo->rowData; + TvpColMetaData *colMetaData = token->tvpInfo->colMetaData; + bool xactStarted = IsTransactionOrTransactionBlock(); + char *finalTableName; + TvpLookupItem *item; + temp = palloc(sizeof(StringInfoData)); + initStringInfo(temp); + + TDSInstrumentation(INSTR_TDS_DATATYPE_TABLE_VALUED_PARAMETER); + + /* Setting a unique name for TVP temp table. */ + tableName = psprintf("%s_TDS_TVP_TEMP_TABLE_%d", token->tvpInfo->tableName, rand()); + + /* + * We change the dialect to postgres to create temp tables + * and execute a prep/exec insert query via SPI. + */ + set_config_option("babelfishpg_tsql.sql_dialect", "postgres", + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + + /* Connect to the SPI manager. */ + if ((rc = SPI_connect()) < 0) + elog(ERROR, "SPI_connect() failed in TDS Listener " + "with return code %d", rc); + + if (!xactStarted) + StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); + + + + query = psprintf("CREATE TEMPORARY TABLE IF NOT EXISTS %s (like %s including all)", + tableName, token->tvpInfo->tvpTypeName); + + + /* + * If table with the same name already exists, we should just use that table + * and ignore the NOTICE of "relation already exists, skipping". + */ + rc = SPI_execute(query, false, 1); + + if (rc != SPI_OK_UTILITY) + elog(ERROR, "Failed to create the underlying table for table-valued parameter: %d", rc); + + SPI_finish(); + PopActiveSnapshot(); + if (!xactStarted) + CommitTransactionCommand(); + + { + char *src; + int nargs = token->tvpInfo->colCount * token->tvpInfo->rowCount; + Datum *values = palloc(nargs * sizeof(Datum)); + char *nulls = palloc(nargs * sizeof(char)); + Oid *argtypes= palloc(nargs * sizeof(Datum)); + int i = 0; + query = " "; + + if (!xactStarted) + StartTransactionCommand(); + PushActiveSnapshot(GetTransactionSnapshot()); + + while (row) /* Create the prep/exec query to insert the rows. */ + { + TdsIoFunctionInfo tempFuncInfo; + int currentColumn = 0; + char *currentQuery = " "; + + while(currentColumn != token->tvpInfo->colCount) + { + temp = &(row->columnValues[currentColumn]); + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(colMetaData[currentColumn].columnTdsType, colMetaData[currentColumn].maxLen); + GetPgOid(argtypes[i], tempFuncInfo); + if (row->isNull[currentColumn] == 'n') + nulls[i] = row->isNull[currentColumn]; + else + switch(colMetaData[currentColumn].columnTdsType) + { + case TDS_TYPE_CHAR: + case TDS_TYPE_VARCHAR: + values[i] = TdsTypeVarcharToDatum(temp, argtypes[i], colMetaData[currentColumn].collation); + break; + case TDS_TYPE_NCHAR: + values[i] = TdsTypeNCharToDatum(temp); + break; + case TDS_TYPE_NVARCHAR: + if (!row->isNull[currentColumn]) /* NULL. */ + currentQuery = psprintf("%s,\'NULL\'", currentQuery); + else + currentQuery = psprintf("%s,\'%s\'", currentQuery, temp->data); + nargs--; + break; + case TDS_TYPE_INTEGER: + case TDS_TYPE_BIT: + values[i] = TdsTypeIntegerToDatum(temp, colMetaData[currentColumn].maxLen); + break; + case TDS_TYPE_FLOAT: + values[i] = TdsTypeFloatToDatum(temp, colMetaData[currentColumn].maxLen); + break; + case TDS_TYPE_NUMERICN: + case TDS_TYPE_DECIMALN: + values[i] = TdsTypeNumericToDatum(temp, colMetaData[currentColumn].scale); + break; + case TDS_TYPE_VARBINARY: + case TDS_TYPE_BINARY: + values[i] = TdsTypeVarbinaryToDatum(temp); + argtypes[i] = tempFuncInfo->ttmtypeid; + break; + case TDS_RECV_DATE: + values[i] = TdsTypeDateToDatum(temp); + break; + case TDS_TYPE_TIME: + values[i] = TdsTypeTimeToDatum(temp, colMetaData[currentColumn].scale, temp->len); + break; + case TDS_TYPE_DATETIME2: + values[i] = TdsTypeDatetime2ToDatum(temp, colMetaData[currentColumn].scale, temp->len); + break; + case TDS_TYPE_DATETIMEN: + values[i] = TdsTypeDatetimeToDatum(temp); + break; + case TDS_TYPE_MONEYN: + values[i] = TdsTypeMoneyToDatum(temp); + break; + case TDS_TYPE_XML: + values[i] = TdsTypeXMLToDatum(temp); + break; + case TDS_TYPE_UNIQUEIDENTIFIER: + values[i] = TdsTypeUIDToDatum(temp); + break; + case TDS_TYPE_SQLVARIANT: + values[i] = TdsTypeSqlVariantToDatum(temp); + break; + } + /* Build a string for bind parameters. */ + if (colMetaData[currentColumn].columnTdsType != TDS_TYPE_NVARCHAR || row->isNull[currentColumn] == 'n') + { + currentQuery = psprintf("%s,$%d", currentQuery, i + 1); + i++; + } + currentColumn++; + } + row = row->nextRow; + currentQuery[1] = ' '; /* Convert the first ',' into a blank space. */ + + /* Add each row values in a single insert query so that we call SPI only once. */ + query = psprintf("%s,(%s)", query, currentQuery); + } + + if (token->tvpInfo->rowData) /* If any row in TVP */ + { + query[1] = ' '; /* Convert the first ',' into a blank space. */ + + src = psprintf("Insert into %s values %s", tableName, query); + if ((rc = SPI_connect()) < 0) + elog(ERROR, "SPI_connect() failed in TDS Listener " + "with return code %d", rc); + + rc = SPI_execute_with_args(src, + nargs, argtypes, + values, nulls, + false, 1); + + if (rc != SPI_OK_INSERT) + elog(ERROR, "Failed to insert in the underlying table for table-valued parameter: %d", rc); + + SPI_finish(); + PopActiveSnapshot(); + if (!xactStarted) + CommitTransactionCommand(); + } + + set_config_option("babelfishpg_tsql.sql_dialect", "tsql", + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, GUC_ACTION_SAVE, true, 0, false); + } + + /* Free all the pointers. */ + while (token->tvpInfo->rowData) + { + TvpRowData *tempRow = token->tvpInfo->rowData; + token->tvpInfo->rowData = token->tvpInfo->rowData->nextRow; + pfree(tempRow); + } + pfree(token->tvpInfo->colMetaData); + + finalTableName = downcase_truncate_identifier(tableName, strlen(tableName), true); + + item = (TvpLookupItem *) palloc(sizeof(TvpLookupItem)); + item->name = downcase_truncate_identifier(token->paramMeta.colName.data, + strlen(token->paramMeta.colName.data), + true); + item->tableRelid = InvalidOid; + item->tableName = finalTableName; + tvp_lookup_list = lappend(tvp_lookup_list, item); + + PG_RETURN_CSTRING(finalTableName); +} + +/* TdsRecvTypeSqlvariant - converts external binary format to byte data + * based on sqlvariant base type + * -------------------------------- + */ +Datum +TdsRecvTypeSqlvariant(const char *message, const ParameterToken token) +{ + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + TDSInstrumentation(INSTR_TDS_DATATYPE_SQLVARIANT); + + return TdsTypeSqlVariantToDatum(buf); +} + +int +TdsSendTypeBit(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + int8_t out = DatumGetBool(value); + + if ((rc = TdsPutInt8(sizeof(out))) == 0) + rc = TdsPutInt8(out); + return rc; +} + +int +TdsSendTypeTinyint(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + int8_t out = DatumGetUInt8(value); + + if ((rc = TdsPutInt8(sizeof(out))) == 0) + rc = TdsPutInt8(out); + return rc; +} +int +TdsSendTypeSmallint(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + int16_t out = DatumGetInt16(value); + + if ((rc = TdsPutInt8(sizeof(out))) == 0) + rc = TdsPutInt16LE(out); + return rc; +} + +int +TdsSendTypeInteger(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + int32_t out = DatumGetInt32(value); + + if ((rc = TdsPutInt8(sizeof(out))) == 0) + rc = TdsPutInt32LE(out); + return rc; +} + +int +TdsSendTypeBigint(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + int64_t out = DatumGetInt64(value); + + if ((rc = TdsPutInt8(sizeof(out))) == 0) + rc = TdsPutInt64LE(out); + return rc; +} + +int +TdsSendTypeFloat4(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + float4 out = DatumGetFloat4(value); + + if ((rc = TdsPutInt8(sizeof(out))) == 0) + rc = TdsPutFloat4LE(out); + return rc; +} + +int +TdsSendTypeFloat8(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + float8 out = DatumGetFloat8(value); + + if ((rc = TdsPutInt8(sizeof(out))) == 0) + rc = TdsPutFloat8LE(out); + return rc; +} + +static int +TdsSendPlpDataHelper(char *data, int len) +{ + int rc; + uint32_t plpTerminator = PLP_TERMINATOR; + uint64_t tempOffset = 0; + uint32_t plpChunckLen = PLP_CHUNCK_LEN; + + if ((rc = TdsPutInt64LE(len)) == 0) + { + while (true) + { + if (plpChunckLen > (len - tempOffset)) + plpChunckLen = (len - tempOffset); + + // Either data is "0" or no more data to send + if (plpChunckLen == 0) + break; + + // need testing for "0" len + if ((rc = TdsPutUInt32LE(plpChunckLen)) == 0) + { + TdsPutbytes(&(data[tempOffset]), plpChunckLen); + } + if (rc != 0) + return rc; + + tempOffset += plpChunckLen; + Assert(tempOffset <= len); + } + rc |= TdsPutInt32LE(plpTerminator); + } + + return rc; +} + +int +TdsSendTypeXml(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + char *out = OutputFunctionCall(finfo, value); + StringInfoData buf; + + /* + * If client being connected is using TDS version lower than or equal to 7.1 + * then TSQL treats XML as NText. + */ + if (GetClientTDSVersion() <= TDS_VERSION_7_1_1) + return TdsSendTypeNText(finfo, value, vMetaData); + + TDSInstrumentation(INSTR_TDS_DATATYPE_XML); + + initStringInfo(&buf); + TdsUTF8toUTF16StringInfo(&buf, out, strlen(out)); + + rc = TdsSendPlpDataHelper(buf.data, buf.len); + + pfree(buf.data); + + return rc; +} + +int +TdsSendTypeBinary(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF,maxLen = 0; + bytea *vlena = DatumGetByteaPCopy(value); + bytea *buf; + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + maxLen = col->metaEntry.type7.maxSize; + buf = (bytea *)palloc0(sizeof(bytea) * maxLen); + memcpy(buf, VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena)); + + if ((rc = TdsPutUInt16LE(maxLen)) == 0) + TdsPutbytes(buf, maxLen); + + pfree(buf); + return rc; +} + +int +TdsSendTypeVarchar(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, len = 0; + char *destBuf, *buf = OutputFunctionCall(finfo, value); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + len = strlen(buf); + destBuf = server_to_any(buf, len, col->encoding); + if (destBuf != buf) + { + len = strlen(destBuf); + } + + if (col->metaEntry.type2.maxSize != 0xffff) + { + if ((rc = TdsPutInt16LE(len)) == 0) + rc = TdsPutbytes(destBuf, len); + } + else + { + TDSInstrumentation(INSTR_TDS_DATATYPE_VARCHAR_MAX); + + rc = TdsSendPlpDataHelper(destBuf, len); + } + + pfree(buf); + return rc; +} + +int +TdsSendTypeChar(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, len; + char *destBuf, *buf = OutputFunctionCall(finfo, value); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + len = col->metaEntry.type2.maxSize; + destBuf = server_to_any(buf, len, col->encoding); + + if ((rc = TdsPutUInt16LE(len)) == 0) + rc = TdsPutbytes(destBuf, len); + + pfree(buf); + return rc; +} + +int +TdsSendTypeVarbinary(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, len = 0, maxlen = 0; + bytea *vlena = DatumGetByteaPCopy(value); + char *buf = VARDATA_ANY(vlena); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + maxlen = col->metaEntry.type7.maxSize; + len = VARSIZE_ANY_EXHDR(vlena); + + if (maxlen != 0xffff) + { + if ((rc = TdsPutInt16LE(len)) == 0) + TdsPutbytes(buf, len); + } + else + { + TDSInstrumentation(INSTR_TDS_DATATYPE_VARBINARY_MAX); + + rc = TdsSendPlpDataHelper(buf, len); + } + return rc; +} + +static inline void +SendTextPtrInfo(void) +{ + /* + * For now, we are sending dummy data for textptr and texttimestamp + * TODO: Once the engine supports TEXTPTR, TIMESTAMP - BABEL-260, + * query & send the actual values + */ + uint8_t temp = 16; + char textptr[] = {0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x74, 0x65, 0x78, 0x74, + 0x70, 0x74, 0x72, 0x00, 0x00, 0x00}; + char texttimestamp[] = { 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x54, 0x53, 0x00}; + + TdsPutUInt8(temp); + + TdsPutbytes(textptr, sizeof(textptr)); + TdsPutbytes(texttimestamp, sizeof(texttimestamp)); +} + +int +TdsSendTypeText(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + uint32_t len; + char *destBuf, *buf = OutputFunctionCall(finfo, value); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + SendTextPtrInfo(); + + len = strlen(buf); + destBuf = server_to_any(buf, len, col->encoding); + if (destBuf != buf) + { + len = strlen(destBuf); + } + if ((rc = TdsPutUInt32LE(len)) == 0) + rc = TdsPutbytes(destBuf, len); + + pfree(buf); + return rc; +} + +int +TdsSendTypeImage(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, len; + bytea *vlena = DatumGetByteaPCopy(value); + char *buf = VARDATA(vlena); + + TDSInstrumentation(INSTR_TDS_DATATYPE_IMAGE); + + SendTextPtrInfo(); + + len = VARSIZE_ANY_EXHDR(vlena); + + if ((rc = TdsPutUInt32LE(len)) == 0) + TdsPutbytes(buf, len); + return rc; +} + +int +TdsSendTypeNText(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc; + char *out = OutputFunctionCall(finfo, value); + StringInfoData buf; + + SendTextPtrInfo(); + + initStringInfo(&buf); + TdsUTF8toUTF16StringInfo(&buf, out, strlen(out)); + + /* + * TODO: Enable below check: BABEL-298 + * This is a special case we are making for TDS clients. TSQL treats + * on-the-wire data really as UCS2, not UTF16. While we try our best + * to detect possible problems on input, the special rules about + * truncating trailing spaces allow to enter data that exceeds the + * number of 16-bit units to be sent here. In a best effort approach + * we strip extra spaces here. The FATAL error will never happen + * if the input rules are correct. + */ + /*while (buf.len > 0 && buf.len > col->metaEntry.type2.maxSize) + { + if (buf.data[buf.len - 2] != ' ' || buf.data[buf.len - 1] != '\0') + elog(FATAL, "UTF16 output of varchar/bpchar exceeds max length"); + buf.len -= 2; + }*/ + if ((rc = TdsPutUInt32LE(buf.len)) == 0) + TdsPutbytes(buf.data, buf.len); + + pfree(buf.data); + return rc; +} + +int +TdsSendTypeNVarchar(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + + int rc, maxlen; + char *out = OutputFunctionCall(finfo, value); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + StringInfoData buf; + + initStringInfo(&buf); + TdsUTF8toUTF16StringInfo(&buf, out, strlen(out)); + maxlen = col->metaEntry.type2.maxSize; + + if (maxlen != 0xffff) + { + + /* + * This is a special case we are making for TDS clients. TSQL treats + * on-the-wire data really as UCS2, not UTF16. While we try our best + * to detect possible problems on input, the special rules about + * truncating trailing spaces allow to enter data that exceeds the + * number of 16-bit units to be sent here. In a best effort approach + * we strip extra spaces here. The FATAL error will never happen + * if the input rules are correct. + */ + while (buf.len > 0 && buf.len > col->metaEntry.type2.maxSize) + { + if (buf.data[buf.len - 2] != ' ' || buf.data[buf.len - 1] != '\0') + elog(FATAL, "UTF16 output of varchar/bpchar exceeds max length"); + buf.len -= 2; + } + if ((rc = TdsPutInt16LE(buf.len)) == 0) + TdsPutbytes(buf.data, buf.len); + } + else + { + TDSInstrumentation(INSTR_TDS_DATATYPE_NVARCHAR_MAX); + + rc = TdsSendPlpDataHelper(buf.data, buf.len); + } + + pfree(buf.data); + return rc; +} + +int +TdsSendTypeNChar(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + + int rc, len; + char *out = OutputFunctionCall(finfo, value); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + StringInfoData buf; + + initStringInfo(&buf); + TdsUTF8toUTF16StringInfo(&buf, out, strlen(out)); + + /* + * This is a special case we are making for TDS clients. TSQL treats + * on-the-wire data really as UCS2, not UTF16. While we try our best + * to detect possible problems on input, the special rules about + * truncating trailing spaces allow to enter data that exceeds the + * number of 16-bit units to be sent here. In a best effort approach + * we strip extra spaces here. The FATAL error will never happen + * if the input rules are correct. + */ + while (buf.len > 0 && buf.len > col->metaEntry.type2.maxSize) + { + if (buf.data[buf.len - 2] != ' ' || buf.data[buf.len - 1] != '\0') + elog(FATAL, "UTF16 output of varchar/bpchar exceeds max length"); + buf.len -= 2; + } + + /* + * Add explicit padding, Otherwise can give garbage in some cases. + * This code needs to be removed and padding should be handled + * internally - BABEL-273 + */ + len = buf.len; + while (len < col->metaEntry.type2.maxSize) + { + appendStringInfoChar(&buf, 0x20); + appendStringInfoChar(&buf, 0x00); + len += 2; + } + + len = col->metaEntry.type2.maxSize; + + if ((rc = TdsPutInt16LE(len)) == 0) + TdsPutbytes(buf.data, len); + pfree(buf.data); + + return rc; +} + +int +TdsSendTypeMoney(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0, length = 8; + uint32 low = 0, high = 0; + uint64 out = DatumGetUInt64(value); + + TDSInstrumentation(INSTR_TDS_DATATYPE_MONEY); + + low = out & 0xffffffff; + high = (out >> 32) & 0xffffffff; + + if ((rc = TdsPutInt8(length)) == 0) + { + rc = TdsPutUInt32LE(high); + rc |= TdsPutUInt32LE(low); + } + return rc; +} + +int +TdsSendTypeSmallmoney(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0, length = 4; + uint32 low = 0, high = 0; + uint64 out = DatumGetUInt64(value); + + TDSInstrumentation(INSTR_TDS_DATATYPE_SMALLMONEY); + + low = out & 0xffffffff; + high = (out >> 32) & 0xffffffff; + if (high != 0xffffffff && high != 0) + { + ereport(ERROR,(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("SMALLMONEY exceeds permissible range of 4 bytes!"))); + return EOF; + } + + if ((rc = TdsPutInt8(length)) == 0) + rc = TdsPutUInt32LE(low); + return rc; +} + +int +TdsSendTypeSmalldatetime(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, length = 4; + uint16 numDays = 0, numMins = 0; + + TdsTimeDifferenceSmalldatetime(value, &numDays, &numMins); + + if ((rc = TdsPutInt8(length)) == 0) + { + rc = TdsPutUInt16LE(numDays); + rc |= TdsPutUInt16LE(numMins); + } + return rc; +} + +int +TdsSendTypeDate(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0, length = 3; + uint32 numDays = 0; + + if (GetClientTDSVersion() < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATE as NVARCHAR. + */ + return TdsSendTypeNVarchar(finfo, value, vMetaData); + + numDays = TdsDayDifference(value); + + if ((rc = TdsPutInt8(length)) == 0) + rc = TdsPutDate(numDays); + return rc; +} + +int +TdsSendTypeDatetime(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0, length = 8; + uint32 numDays = 0, numTicks = 0; + + TdsTimeDifferenceDatetime(value, &numDays, &numTicks); + + if ((rc = TdsPutInt8(length)) == 0) + { + rc = TdsPutUInt32LE(numDays); + rc |= TdsPutUInt32LE(numTicks); + } + return rc; +} + +/* + * TdsSendTypeNumeric() formats response for numeric + * data in TDS listener side before writing it to wire. + * Based on numeric prescision, TdsSendTypeNumeric() generates + * 4-16 byte data followed by data length and sign bytes and writes to wire. + */ +int +TdsSendTypeNumeric(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, precision = 0, scale = -1; + uint8 sign = 1, length = 0; + char *out, *decString; + uint128 num = 0; + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + uint8_t max_scale = col->metaEntry.type5.scale; + uint8_t max_precision = col->metaEntry.type5.precision; + + out = OutputFunctionCall(finfo, value); + if (out[0] == '-') + { + sign = 0; + out++; + } + if (out[0] == '0') + out++; + /* + * response string is formatted to obtain string representation + * of TDS unsigned integer along with its precision and scale + */ + decString = (char *)palloc(sizeof(char) * (strlen(out) + 1)); + /* While there is still digit in out and we haven't reached max_scale */ + while (*out && scale < max_scale) + { + if (*out == '.') + { + out++; + /* Start counting scale */ + scale = 0; + continue; + } + decString[precision++] = *out; + out++; + if (scale >= 0) + scale++; + } + + /* done scanning and haven't seen the decimal point, set scale to 0 */ + if (scale == -1) + scale = 0; + + /* + * Fill in the remaining 0's if the processed scale from out is less than max_scale + * This is needed because the output generated by engine may not always + * produce the same precision/scale as calculated by resolve_numeric_typmod_from_exp, + * which is the precision/scale we have sent to the client with column metadata. + */ + while (scale++ < max_scale) + { + decString[precision++] = '0'; + } + decString[precision] = '\0'; + + if (precision > TDS_MAX_NUM_PRECISION || + precision > max_precision) + ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("Arithmetic overflow error for data type numeric."))); + + if (precision >= 1 && precision < 10) + length = 4; + else if (precision < 20) + length = 8; + else if (precision < 29) + length = 12; + else if (precision < 39) + length = 16; + + num = StringToInteger(decString); + if (TdsPutInt8(length + 1) == 0 && TdsPutInt8(sign) == 0) + rc = TdsPutbytes(&num, length); + + pfree(decString); + return rc; +} + +static void +SwapData(StringInfo buf, int st, int end) +{ + char tempswap; + + if (buf->len < end || st > end) + return; + + tempswap = buf->data[st]; + buf->data[st] = buf->data[end]; + buf->data[end] = tempswap; +} + +int +TdsSendTypeUniqueIdentifier(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + pg_uuid_t *uuid = DatumGetUUIDP(value); + int rc; + StringInfoData buf; + + initStringInfo(&buf); + resetStringInfo(&buf); + appendBinaryStringInfo(&buf, (char *) uuid->data, UUID_LEN); + + /* SWAP to match TSQL behaviour */ + SwapData(&buf, 0, 3); + SwapData(&buf, 1, 2); + SwapData(&buf, 4, 5); + SwapData(&buf, 6, 7); + + if ((rc = TdsPutInt8(UUID_LEN)) == 0) + TdsPutbytes(buf.data, UUID_LEN); + + pfree(buf.data); + return rc; +} + +int +TdsSendTypeTime(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, length = 0, scale = 0; + uint64_t res = 0; + double numSec = 0; + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + if (GetClientTDSVersion() < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats TIME as NVARCHAR. + */ + return TdsSendTypeNVarchar(finfo, value, vMetaData); + + scale = col->metaEntry.type6.scale; + + /* + * if time data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (scale == 255) + scale = 6; + + if (scale >= 0 && scale < 3) + length = 3; + else if (scale >= 3 && scale < 5) + length = 4; + else if (scale >= 5 && scale <= 7) + length = 5; + + numSec = (double)value / 1000000; + while (scale--) + numSec *= 10; + + res = (uint64_t)numSec; + if ((rc = TdsPutInt8(length)) == 0) + rc = TdsPutbytes(&res, length); + return rc; +} + +int +TdsSendTypeDatetime2(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, length = 0, scale = 0; + uint64 numSec = 0; + uint32 numDays = 0; + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + if (GetClientTDSVersion() < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATETIME2 as NVARCHAR. + */ + return TdsSendTypeNVarchar(finfo, value, vMetaData); + + scale = col->metaEntry.type6.scale; + /* + * if Datetime2 data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (scale == 255) + scale = 6; + + if (scale >= 0 && scale < 3) + length = 6; + else if (scale >= 3 && scale < 5) + length = 7; + else if (scale >= 5 && scale <= 7) + length = 8; + + TdsGetDayTimeFromTimestamp((Timestamp)value, &numDays, + &numSec, scale); + + if (TdsPutInt8(length) == 0 && + TdsPutbytes(&numSec, length - 3) == 0) + rc = TdsPutDate(numDays); + + return rc; +} + +static void +SwapByte(char *buf, int st, int end) +{ + char temp = buf[st]; + buf[st] = buf[end]; + buf[end] = temp; +} + +/* Helper Function to convert SQL_VARIANT value into Datum. */ +Datum +TdsTypeSqlVariantToDatum(StringInfo buf) +{ + bytea *result = 0; + uint8 variantBaseType = 0; + int pgBaseType = 0; + int dataLen = 0, i = 0, len = 0; + int tempScale = 0, tempLen = 0; + int variantHeaderLen = 0, maxLen = 0; + uint8_t scale = 0, precision = 0, sign = 1, temp = 0; + DateADT date = 0; + uint64 numMicro = 0, dateval = 0; + uint16 numDays = 0, numMins = 0; + int16 timezone = 0; + uint32 numDays32 = 0, numTicks = 0; + Timestamp timestamp = 0; + TimestampTz timestamptz = 0; + Numeric res = 0; + char *decString, temp1, temp2; + uint128 n128 = 0, num = 0; + StringInfoData strbuf; + tsql_datetimeoffset *tdt = (tsql_datetimeoffset *) palloc0(DATETIMEOFFSET_LEN); + + variantBaseType = buf->data[0]; + tempLen = buf->len - buf->cursor; + + pltsql_plugin_handler_ptr->sqlvariant_get_pg_base_type(variantBaseType, &pgBaseType, + tempLen, &dataLen, &variantHeaderLen); + + if (variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_NVARCHAR) + { + initStringInfo(&strbuf); + TdsUTF16toUTF8StringInfo(&strbuf, &buf->data[9], tempLen - 9); + } + + if (dataLen + VARHDRSZ_SHORT <= VARATT_SHORT_MAX) + { + result = (bytea *) palloc0(VARHDRSZ_SHORT + variantHeaderLen + dataLen); + SET_VARSIZE_SHORT(result, VARHDRSZ_SHORT + variantHeaderLen + dataLen); + } + else + { + result = (bytea *) palloc0(VARHDRSZ + variantHeaderLen + dataLen); + SET_VARSIZE(result, VARHDRSZ + variantHeaderLen + dataLen); + } + + if (variantBaseType == VARIANT_TYPE_CHAR || variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_VARCHAR || variantBaseType == VARIANT_TYPE_NVARCHAR) + { + memcpy(&maxLen, &buf->data[7], 2); + if (variantBaseType == VARIANT_TYPE_NCHAR || variantBaseType == VARIANT_TYPE_NVARCHAR) + { + memcpy(READ_DATA(result, variantHeaderLen + 4), strbuf.data, dataLen); + } + else + { + memcpy(READ_DATA(result, variantHeaderLen + 4), &buf->data[9], dataLen); + } + } + else if (variantBaseType == VARIANT_TYPE_BINARY || variantBaseType == VARIANT_TYPE_VARBINARY) + { + memcpy(&maxLen, &buf->data[2], 2); + memcpy(READ_DATA(result, variantHeaderLen), &buf->data[0], dataLen); + } + else if (variantBaseType == VARIANT_TYPE_DATE) + { + memset(&date, 0, sizeof(date)); + memcpy(&date, &buf->data[2], 3); + TdsCheckDateValidity(date); + TdsTimeGetDatumFromDays(date, &dateval); + memcpy(READ_DATA(result, variantHeaderLen), &dateval, sizeof(date)); + } + else if (variantBaseType == VARIANT_TYPE_SMALLDATETIME) + { + memcpy(&numDays, &buf->data[2], 2); + memcpy(&numMins, &buf->data[4], 2); + TdsTimeGetDatumFromSmalldatetime(numDays, numMins, ×tamp); + memcpy(READ_DATA(result, variantHeaderLen), ×tamp, sizeof(timestamp)); + } + else if (variantBaseType == VARIANT_TYPE_DATETIME) + { + memcpy(&numDays32, &buf->data[2], 4); + memcpy(&numTicks, &buf->data[6], 4); + TdsTimeGetDatumFromDatetime(numDays32, numTicks, ×tamp); + memcpy(READ_DATA(result, variantHeaderLen), ×tamp, sizeof(timestamp)); + } + else if (variantBaseType == VARIANT_TYPE_TIME) + { + scale = buf->data[2]; + temp = scale; + /* postgres limitation */ + if (scale > 7 || scale < 0) + scale = 6; + + if (scale <= 2) + dataLen = 3; + else if (scale <= 4) + dataLen = 4; + else if (scale <= 7) + dataLen = 5; + + memset(&numMicro, 0, sizeof(numMicro)); + memcpy(&numMicro, &buf->data[3], dataLen); + + if (temp == 7 || temp == 0xff) + numMicro /= 10; + + while (scale < 6) + { + numMicro *= 10; + scale++; + } + scale = temp; + memcpy(READ_DATA(result, variantHeaderLen), &numMicro, sizeof(numMicro)); + } + else if (variantBaseType == VARIANT_TYPE_DATETIME2) + { + scale = buf->data[2]; + + /* postgres limitation */ + if (scale > 7 || scale == 0xff || scale < 0) + scale = 6; + + if (scale <= 2) + dataLen = 6; + else if (scale <= 4) + dataLen = 7; + else if (scale <= 7) + dataLen = 8; + + memset(&numDays32, 0, sizeof(numDays32)); + memset(&numMicro, 0, sizeof(numMicro)); + memcpy(&numDays32, &buf->data[3], 3); + memcpy(&numMicro, &buf->data[6], dataLen - 3); + TdsGetTimestampFromDayTime(numDays32, numMicro, 0, ×tamp, scale); + memcpy(READ_DATA(result, variantHeaderLen), ×tamp, sizeof(timestamp)); + } + else if (variantBaseType == VARIANT_TYPE_DATETIMEOFFSET) + { + scale = buf->data[2]; + + /* postgres limitation */ + if (scale > 7 || scale == 0xff || scale < 0) + scale = 6; + + if (scale <= 2) + dataLen = 8; + else if (scale <= 4) + dataLen = 9; + else if (scale <= 7) + dataLen = 10; + + memset(&numDays32, 0, sizeof(numDays32)); + memset(&numMicro, 0, sizeof(numMicro)); + memcpy(&numDays32, &buf->data[dataLen - 2], 3); + memcpy(&numMicro, &buf->data[3], dataLen - 5); + memcpy(&timezone, &buf->data[dataLen + 1], 2); + + timezone *= -1; + TdsGetTimestampFromDayTime(numDays32, numMicro, (int)timezone, ×tamptz, scale); + timestamptz -= (timezone * SECS_PER_MINUTE * USECS_PER_SEC); + timestamptz -= (timezone * USECS_PER_SEC); + + tdt->tsql_ts = timestamptz; + tdt->tsql_tz = timezone; + memcpy(READ_DATA(result, variantHeaderLen), tdt, DATETIMEOFFSET_LEN); + } + else if (variantBaseType == VARIANT_TYPE_NUMERIC) + { + precision = buf->data[2]; + scale = buf->data[3]; + sign = buf->data[4]; + tempScale = scale; + + dataLen = 16; + memcpy(&n128, &buf->data[5], dataLen); + num = LEtoh128(n128); + decString = (char *)palloc0(sizeof(char) * 40); + if (num != 0) + Integer2String(num, decString); + else + decString[0] = '0'; + len = strlen(decString); + temp1 = '.'; + if (num != 0) + { + while (tempScale) + { + temp2 = decString[len - tempScale]; + decString[len - tempScale] = temp1; + temp1 = temp2; + tempScale--; + } + decString[len++] = temp1; + } + else + { + decString[len++] = temp1; + while (tempScale) + { + decString[len++] = '0'; + tempScale--; + } + } + + if (sign == 1 && num != 0) + decString++; + res = TdsSetVarFromStrWrapper(decString); + memcpy(READ_DATA(result, variantHeaderLen), &res, sizeof(Numeric)); + } + else + { + /* + * For all other fixed length datatypes + */ + memcpy(READ_DATA(result, variantHeaderLen), &buf->data[2], dataLen); + } + + if (variantBaseType == VARIANT_TYPE_MONEY) + { + /* + * swap positions of 2 nibbles for money type + * to match SQL behaviour + */ + for (i = 0; i < 4; i++) + SwapByte(READ_DATA(result, variantHeaderLen), i, i + 4); + } + + if (variantBaseType == VARIANT_TYPE_UNIQUEIDENTIFIER) + { + /* SWAP to match TSQL behaviour */ + SwapByte(READ_DATA(result, variantHeaderLen), 0, 3); + SwapByte(READ_DATA(result, variantHeaderLen), 1, 2); + SwapByte(READ_DATA(result, variantHeaderLen), 4, 5); + SwapByte(READ_DATA(result, variantHeaderLen), 6, 7); + } + + pltsql_plugin_handler_ptr->sqlvariant_set_metadata(result, + pgBaseType, scale, precision, maxLen); + + buf->cursor += tempLen; + pfree(buf); + PG_RETURN_BYTEA_P(result); +} + +int +TdsSendTypeSqlvariant(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, variantBaseType = 0; + uint8_t pgBaseType = 0; + int dataLen = 0, totalLen = 0, maxLen = 0; + int metadataLen = 0, variantHeaderLen = 0; + bytea *vlena = DatumGetByteaPCopy(value); + char *buf = VARDATA(vlena), *decString = NULL, *out = NULL; + bool isBaseNum = false, isBaseChar = false; + bool isBaseBin = false, isBaseDec = false, isBaseDate = false; + uint32 numDays = 0, numTicks = 0, dateval = 0; + uint16 numMins = 0, numDays16 = 0; + uint64 numMicro = 0; + int16 timezone = 0; + StringInfoData strbuf; + int precision = 0, scale = -1, sign = 1, i = 0, temp = 0; + uint128 num = 0; + Timestamp timestamp = 0; + TimestampTz timestamptz = 0; + + TDSInstrumentation(INSTR_TDS_DATATYPE_SQLVARIANT); + + /* + * First sql variant header byte contains: + * type code ( 5bit ) + MD ver (3bit) + */ + pgBaseType = pltsql_plugin_handler_ptr->sqlvariant_inline_pg_base_type(vlena); + + pltsql_plugin_handler_ptr->sqlvariant_get_metadata(vlena, pgBaseType, + &scale, &precision, &maxLen); + + pltsql_plugin_handler_ptr->sqlvariant_get_variant_base_type(pgBaseType, + &variantBaseType, &isBaseNum, &isBaseChar, + &isBaseDec, &isBaseBin, &isBaseDate, &variantHeaderLen); + + if (variantBaseType == VARIANT_TYPE_NUMERIC) + { + dataLen = VARSIZE_ANY_EXHDR(vlena) - variantHeaderLen; + buf += variantHeaderLen; + dataLen = 16; + + out = OutputFunctionCall(finfo, value); + + if (out && out[0] == '-') + { + sign = 0; + out++; + } + decString = (char *)palloc(sizeof(char) * (strlen(out) + 1)); + precision = 0, scale = -1; + while (out && *out) + { + if (*out == '.') + { + out++; + scale = 0; + continue; + } + decString[precision++] = *out; + out++; + if (scale >= 0) + scale++; + } + if (scale == -1) + scale = 0; + decString[precision] = '\0'; + num = StringToInteger(decString); + } + + dataLen = VARSIZE_ANY_EXHDR(vlena) - variantHeaderLen; + buf += variantHeaderLen; + + if (isBaseNum) + { + /* + * dataformat: totalLen(4B) + baseType(1B) + metadatalen(1B) + + * data(dataLen) + */ + if (variantBaseType == VARIANT_TYPE_TINYINT) + dataLen = 1; + + if (variantBaseType == VARIANT_TYPE_SMALLMONEY) + dataLen = 4; + + if (variantBaseType == VARIANT_TYPE_MONEY) + { + /* + * swap positions of 2 nibbles for money type + * to match SQL behaviour + */ + for (i = 0; i < 4; i++) + SwapByte(buf, i, i + 4); + } + + if (variantBaseType == VARIANT_TYPE_UNIQUEIDENTIFIER) + { + /* SWAP to match TSQL behaviour */ + SwapByte(buf, 0, 3); + SwapByte(buf, 1, 2); + SwapByte(buf, 4, 5); + SwapByte(buf, 6, 7); + } + + totalLen = dataLen + 2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutbytes(buf, dataLen); + } + else if (isBaseChar) + { + /* + * dataformat: totalLen(4B) + baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) + data(dataLen) + */ + dataLen -= 4; + if (variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_NVARCHAR) + { + initStringInfo(&strbuf); + TdsUTF8toUTF16StringInfo(&strbuf, buf + 4, dataLen); + dataLen *= 2; + } + + totalLen = dataLen + 9; + metadataLen = 7; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + /* + * 5B of fixed collation + * TODO: [BABEL-1069] Remove collation related hardcoding + * from sql_variant sender for char class basetypes + */ + rc |= TdsPutInt8(9); + rc |= TdsPutInt8(4); + rc |= TdsPutInt8(208); + rc |= TdsPutInt8(0); + rc |= TdsPutInt8(52); + + rc |= TdsPutUInt16LE(dataLen); + + if (variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_NVARCHAR) + rc |= TdsPutbytes(strbuf.data, dataLen); + else + rc |= TdsPutbytes(buf + 4, dataLen); + } + else if (isBaseBin) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * dataLen(2B) + data(dataLen) + */ + totalLen = dataLen; + metadataLen = 2; + + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutUInt16LE(maxLen); + rc |= TdsPutbytes(buf + 4, maxLen); + } + else if (isBaseDec) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * precision(1B) + scale(1B) + sign(1B) + data(dataLen) + */ + dataLen = 16; + totalLen = dataLen + 5; + metadataLen = 2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutInt8(precision); + rc |= TdsPutInt8(scale); + rc |= TdsPutInt8(sign); + rc |= TdsPutbytes(&num, dataLen); + } + else if (isBaseDate) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * data(3B) + */ + + if (variantBaseType == VARIANT_TYPE_DATE) + { + memset(&dateval, 0, sizeof(dateval)); + memcpy(&dateval, buf, sizeof(dateval)); + numDays = TdsDayDifference(dateval); + dataLen = 3; + totalLen = dataLen + 2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutDate(numDays); + } + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * data(4B) + */ + else if (variantBaseType == VARIANT_TYPE_SMALLDATETIME) + { + memcpy(×tamp, buf, sizeof(timestamp)); + dataLen = 4; + totalLen = dataLen + 2; + TdsTimeDifferenceSmalldatetime(timestamp, &numDays16, &numMins); + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutUInt16LE(numDays16); + rc |= TdsPutUInt16LE(numMins); + } + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * data(8B) + */ + else if (variantBaseType == VARIANT_TYPE_DATETIME) + { + memcpy(×tamp, buf, dataLen); + TdsTimeDifferenceDatetime(timestamp, &numDays, &numTicks); + dataLen = 8; + totalLen = dataLen + 2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutUInt32LE(numDays); + rc |= TdsPutUInt32LE(numTicks); + } + else if (variantBaseType == VARIANT_TYPE_TIME) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * scale(1B) + data(3B-5B) + */ + if (scale == 0xff || scale < 0 || scale > 7) + scale = 6; + + if (scale >= 0 && scale < 3) + dataLen = 3; + else if (scale >= 3 && scale < 5) + dataLen = 4; + else if (scale >= 5 && scale <= 7) + dataLen = 5; + + metadataLen = 1; + memcpy(&numMicro, buf, sizeof(numMicro)); + temp = scale; + if (scale == 7 || scale == 0xff) + numMicro *= 10; + + while (temp < 6) + { + numMicro /= 10; + temp++; + } + totalLen = dataLen + metadataLen + 2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutInt8(scale); + rc = TdsPutbytes(&numMicro, dataLen); + } + else if(variantBaseType == VARIANT_TYPE_DATETIME2) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * scale(1B) + data(6B-8B) + */ + if (scale == 0xff || scale < 0 || scale > 7) + scale = 6; + + if (scale >= 0 && scale < 3) + dataLen = 6; + else if (scale >= 3 && scale < 5) + dataLen = 7; + else if (scale >= 5 && scale <= 7) + dataLen = 8; + + memcpy(×tamp, buf, sizeof(timestamp)); + TdsGetDayTimeFromTimestamp((Timestamp)timestamp, &numDays, + &numMicro, scale); + + metadataLen = 1; + totalLen = dataLen + metadataLen + 2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutInt8(scale); + rc |= TdsPutbytes(&numMicro, dataLen - 3); + rc |= TdsPutDate(numDays); + } + else if (variantBaseType == VARIANT_TYPE_DATETIMEOFFSET) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * scale(1B) + data(8B-10B) + */ + tsql_datetimeoffset *tdt = (tsql_datetimeoffset *)buf; + timestamptz = tdt->tsql_ts; + timezone = tdt->tsql_tz; + timestamptz += (timezone * SECS_PER_MINUTE * USECS_PER_SEC); + + if (scale == 0xff || scale < 0 || scale > 7) + scale = 6; + + if (scale >= 0 && scale < 3) + dataLen = 8; + else if (scale >= 3 && scale < 5) + dataLen = 9; + else if (scale >= 5 && scale <= 7) + dataLen = 10; + + TdsGetDayTimeFromTimestamp((Timestamp)timestamptz, &numDays, + &numMicro, scale); + timezone *= -1; + + metadataLen = 1; + totalLen = dataLen + metadataLen + 2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(metadataLen); + rc |= TdsPutInt8(scale); + rc |= TdsPutbytes(&numMicro, dataLen - 5); + rc |= TdsPutDate(numDays); + rc |= TdsPutInt16LE(timezone); + } + } + return rc; +} + +Datum +TdsRecvTypeDatetimeoffset(const char *message, const ParameterToken token) +{ + int scale = 0, len = 0; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + uint64_t numMicro = 0; + uint32_t numDays = 0; + int16_t timezone = 0; + TimestampTz timestamp; + tsql_datetimeoffset *tdt = (tsql_datetimeoffset *) palloc0(DATETIMEOFFSET_LEN); + + TdsColumnMetaData col = token->paramMeta; + scale = col.metaEntry.type6.scale; + + TDSInstrumentation(INSTR_TDS_DATATYPE_DATETIME_OFFSET); + + /* + * if Datetimeoffset data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (scale == 0xFF) + scale = 6; + + len = token->len; + + memcpy(&numMicro, &buf->data[buf->cursor], len - 5); + buf->cursor += len - 5; + + memcpy(&numDays, &buf->data[buf->cursor], 3); + buf->cursor += 3; + + memcpy(&timezone, &buf->data[buf->cursor], 2); + buf->cursor += 2; + + timezone *= -1; + TdsGetTimestampFromDayTime(numDays, numMicro, (int)timezone, ×tamp, scale); + + timestamp -= (timezone * SECS_PER_MINUTE * USECS_PER_SEC); + /* since reverse is done in tm2timestamp() */ + timestamp -= (timezone * USECS_PER_SEC); + + tdt->tsql_ts = timestamp; + tdt->tsql_tz = timezone; + + pfree(buf); + PG_RETURN_DATETIMEOFFSET(tdt); +} + +int +TdsSendTypeDatetimeoffset(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, length = 0, scale = 0; + uint64 numSec = 0; + uint32 numDays = 0; + int16_t timezone = 0; + TimestampTz timestamp = 0; + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + tsql_datetimeoffset *tdt = (tsql_datetimeoffset *)value; + + if (GetClientTDSVersion() < TDS_VERSION_7_3_A) + /* + * If client being connected is using TDS version lower than 7.3A + * then TSQL treats DATETIMEOFFSET as NVARCHAR. + */ + return TdsSendTypeNVarchar(finfo, value, vMetaData); + + TDSInstrumentation(INSTR_TDS_DATATYPE_DATETIME_OFFSET); + + timestamp = tdt->tsql_ts; + timezone = tdt->tsql_tz; + timestamp += (timezone * SECS_PER_MINUTE * USECS_PER_SEC); + + scale = col->metaEntry.type6.scale; + /* + * if Datetimeoffset data has no specific scale specified in the query, default scale + * to be considered is 7 always. However, setting default scale to 6 since + * postgres supports upto 6 digits after decimal point + */ + if (scale == 0xFF) + scale = 6; + + if (scale >= 0 && scale < 3) + length = 8; + else if (scale >= 3 && scale < 5) + length = 9; + else if (scale >= 5 && scale <= 7) + length = 10; + + + TdsGetDayTimeFromTimestamp((Timestamp)timestamp, &numDays, + &numSec, scale); + timezone *= -1; + if (TdsPutInt8(length) == 0 && + TdsPutbytes(&numSec, length - 5) == 0 && + TdsPutDate(numDays) == 0) + rc = TdsPutUInt16LE(timezone); + + return rc; +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdsutils.c b/contrib/babelfishpg_tds/src/backend/tds/tdsutils.c new file mode 100644 index 00000000000..a60787640d0 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsutils.c @@ -0,0 +1,536 @@ +/*------------------------------------------------------------------------- + * + * tdsutils.c + * TDS Listener utility functions + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdsutils.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "src/include/tds_int.h" +#include "nodes/nodes.h" +#include "nodes/parsenodes.h" +#include "parser/parser.h" +#include "parser/parse_node.h" +#include "utils/elog.h" + +static int FindMatchingParam(List *params, const char *name); +static Node * TransformParamRef(ParseState *pstate, ParamRef *pref); +Node * TdsFindParam(ParseState *pstate, ColumnRef *cref); +void TdsErrorContextCallback(void *arg); + +/* + * GetUTF8CodePoint - extract the next Unicode code point from 1..4 + * bytes at 'in' in UTF-8 encoding. + */ +static inline int32_t +GetUTF8CodePoint(const unsigned char *in, int len, int *consumed_p) +{ + int32_t code; + int consumed; + + if (len == 0) + return EOF; + + if ((in[0] & 0x80) == 0) + { + /* 1 byte - 0xxxxxxx */ + code = in[0]; + consumed = 1; + } + else if ((in[0] & 0xE0) == 0xC0) + { + /* 2 byte - 110xxxxx 10xxxxxx */ + if (len < 2) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("truncated UTF8 byte sequence starting with 0x%02x", + in[0]))); + if ((in[1] & 0xC0) != 0x80) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + code = ((in[0] & 0x1F) << 6) | (in[1] & 0x3F); + consumed = 2; + } + else if ((in[0] & 0xF0) == 0xE0) + { + /* 3 byte - 1110xxxx 10xxxxxx 10xxxxxx */ + if (len < 3) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("truncated UTF8 byte sequence starting with 0x%02x", + in[0]))); + if ((in[1] & 0xC0) != 0x80 || (in[2] & 0xC0) != 0x80) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + code = ((in[0] & 0x0F) << 12) | ((in[1] & 0x3F) << 6) | (in[2] & 0x3F); + consumed = 3; + } + else if ((in[0] & 0xF8) == 0xF0) + { + /* 4 byte - 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (len < 4) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("truncated UTF8 byte sequence starting with 0x%02x", + in[0]))); + if ((in[1] & 0xC0) != 0x80 || (in[2] & 0xC0) != 0x80 || + (in[3] & 0xC0) != 0x80) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + code = ((in[0] & 0x07) << 18) | ((in[1] & 0x3F) << 12) | + ((in[2] & 0x3F) << 6) | (in[3] & 0x3F); + consumed = 4; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 byte sequence starting with 0x%02x", + in[0]))); + } + + if (code > 0x10FFFF || (code >= 0xD800 && code < 0xE000)) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF8 code point 0x%x", code))); + + if (consumed_p) + *consumed_p = consumed; + + return code; +} + +/* -------------------- + * GetUTF16CodePoint - Extract the next UTF-16 code point from a byte sequence + * + * The code point is extracted from 2 or 4 bytes at 'in'. The optional + * 'consumed' pointer will be set to the number of bytes actually used. + * + * Returns: next Unicode code point + * + * Will thrown an ERROR if the encoding sequence is invalid as per Unicode + * specifications. Wiki claims that some Windows clients can produce invalid + * UTF-16 encoding sequences, but any attempt to work around that is a bad + * idea. We would silently mangle the data by converting invalid codes to + * something else, that will be interpreted differently when the application + * gets the data back. It is corrupted (invalid) data we are talking about. + * Forcing a square peg into a round hole with a sledge hammer has never + * worked out well in the PostgreSQL world. + * -------------------- + */ +static inline int32_t +GetUTF16CodePoint(const unsigned char *in, int len, int *consumed) +{ + uint16_t code1; + uint16_t code2; + int32_t result; + + /* Get the first 16 bits */ + code1 = in[1] << 8 | in[0]; + if (code1 < 0xD800 || code1 >= 0xE000) + { + /* + * This is a single 16 bit code point, which is equal to code1. + * PostgreSQL does not support NUL bytes in character data as + * it internally needs the ability to convert any datum to a + * NUL terminated C-string without explicit length information. + */ + if (code1 == 0) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF16 byte sequence - " + "code point 0 not supported"))); + if (consumed) + *consumed = 2; + return (int32_t)code1; + } + + /* This is a surrogate pair - check that it is the high part */ + if (code1 >= 0xDC00) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF16 byte sequence - " + "high part is (0x%02x, 0x%02x)", in[0], in[1]))); + + /* Check that there is a second surrogate half */ + if (len < 4) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF16 byte sequence - " + "only 2 bytes (0x%02x, 0x%02x)", in[0], in[1]))); + + /* Get the second 16 bits (low part) */ + code2 = in[3] << 8 | in[2]; + if (code2 < 0xDC00 || code2 > 0xE000) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF16 byte sequence - " + "low part is (0x%02x, 0x%02x)", in[2], in[3]))); + + /* Valid surrogate pair, convert to code point */ + result = ((code1 & 0x03FF) << 10 | (code2 & 0x03FF)) + 0x10000; + + /* Valid 32 bit surrogate code point */ + if (consumed) + *consumed = 4; + return result; +} + +/* + * AddUTF8ToStringInfo - Add Unicode code point to a StringInfo in UTF-8 + */ +static inline void +AddUTF8ToStringInfo(int32_t code, StringInfo buf) +{ + /* Check that this is a valid code point */ + if ((code > 0xD800 && code < 0xE000) || code < 0x0001 || code > 0x10FFFF) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid Unicode code point 0x%x", code))); + + /* Range U+0000 .. U+007F (7 bit)*/ + if (code <= 0x7F) + { + appendStringInfoChar(buf, code); + return; + } + + /* Range U+0080 .. U+07FF (11 bit) */ + if (code <= 0x7ff) + { + appendStringInfoChar(buf, 0xC0 | (code >> 6)); + appendStringInfoChar(buf, 0x80 | (code & 0x3F)); + return; + } + + /* Range U+0800 .. U+FFFF (16 bit) */ + if (code <= 0xFFFF) + { + appendStringInfoChar(buf, 0xE0 | (code >> 12)); + appendStringInfoChar(buf, 0x80 | ((code >> 6) & 0x3F)); + appendStringInfoChar(buf, 0x80 | (code & 0x3F)); + return; + } + + /* Range U+10000 .. U+10FFFF (21 bit) */ + appendStringInfoChar(buf, 0xF0 | (code >> 18)); + appendStringInfoChar(buf, 0x80 | ((code >> 12) & 0x3F)); + appendStringInfoChar(buf, 0x80 | ((code >> 6) & 0x3F)); + appendStringInfoChar(buf, 0x80 | (code & 0x3F)); +} + +/* + * AddUTF16ToStringInfo - Add Unicode code point to a StringInfo in UTF-16 + */ +static inline void +AddUTF16ToStringInfo(int32_t code, StringInfo buf) +{ + union { + uint16_t value; + uint8_t half[2]; + } temp16; + + /* Check that this is a valid code point */ + if ((code > 0xD800 && code < 0xE000) || code < 0x0001 || code > 0x10FFFF) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid Unicode code point 0x%x", code))); + + /* Handle single 16-bit code point */ + if (code <= 0xFFFF) + { + appendStringInfoChar(buf, code & 0xFF); + appendStringInfoChar(buf, (code >> 8) & 0xFF); + return; + } + + temp16.value = 0xD800 + (((code - 0x010000) >> 10) & 0x03FF); + appendStringInfoChar(buf, temp16.half[0]); + appendStringInfoChar(buf, temp16.half[1]); + temp16.value = 0xDC00 + ((code - 0x010000) & 0x03FF); + appendStringInfoChar(buf, temp16.half[0]); + appendStringInfoChar(buf, temp16.half[1]); +} + +/* + * TdsUTF16toUTF8StringInfo - convert UTF16 data into UTF8 and + * add it to a StringInfo. + */ +void +TdsUTF16toUTF8StringInfo(StringInfo out, void *vin, int len) +{ + unsigned char *in = vin; + int i; + int consumed; + int32_t code; + + /* UTF16 data allways comes in 16-bit units */ + if ((len & 0x0001) != 0) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid UTF16 byte sequence - " + "input data has odd number of bytes"))); + + for (i = 0; i < len;) + { + code = GetUTF16CodePoint(&in[i], len - i, &consumed); + AddUTF8ToStringInfo(code, out); + i += consumed; + } +} + +/* + * TdsUTF8toUTF16StringInfo - convert UTF8 data into UTF16 and + * add it to a StringInfo. + */ +void +TdsUTF8toUTF16StringInfo(StringInfo out, const void *vin, size_t len) +{ + const unsigned char *in = vin; + size_t i; + int consumed; + int32_t code; + + for (i = 0; i < len;) + { + code = GetUTF8CodePoint(&in[i], len - i, &consumed); + AddUTF16ToStringInfo(code, out); + i += consumed; + } +} + +/* + * TdsUTF8LengthInUTF16 - compute the length of a UTF8 string in number of + * 16-bit units if we were to convert it into + * UTF16 with TdsUTF8toUTF16StringInfo() + * */ +int +TdsUTF8LengthInUTF16(const void *vin, int len) +{ + const unsigned char *in = vin; + int result = 0; + int i; + int consumed; + int32_t code; + + for (i = 0; i < len;) + { + code = GetUTF8CodePoint(&in[i], len - i, &consumed); + + /* Check that this is a valid code point */ + if ((code > 0xD800 && code < 0xE000) || code < 0x0001 || code > 0x10FFFF) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("invalid Unicode code point 0x%x", code))); + + if (code <= 0xFFFF) + /* This code point would result in a single 16-bit output */ + result += 1; + else + /* This code point would result in a 16-bit surrogate pair */ + result += 2; + + i += consumed; + } + + return result; +} + +/* Process the stream headers for message */ +int32_t +ProcessStreamHeaders(const StringInfo message) +{ + int32_t header_len; + /* We expect at least the packet type and header length */ + if (message->len < 4) + elog(FATAL, "corrupted TDS_QUERY packet - len=%d", + message->len); + + /* Skip the headers */ + memcpy(&header_len, &(message->data[0]), 4); + if (header_len > message->len) + elog(FATAL, "corrupted TDS_QUERY packet - " + "header length beyond packet end"); + return header_len; +} + +/* + * Returns the parameter number to associate with the given + * parameter name, or zero if the given name is not found. + * + * NOTE: parameter numbers start at 1, not zero, so we + * add 1 to the array index below. + */ +static int +FindMatchingParam(List *params, const char *name) +{ + ListCell *cell; + int i = 0; + + foreach(cell, params) + { + TdsParamName item = lfirst(cell); + + if (pg_strcasecmp(name, item->name) == 0) + return i + 1; + i++; + } + + return 0; +} + +/* + * Transforms the given ColumnRef to a ParamRef if the name + * of the column matches the name of one of the parameters + * found in parameter list returned by TdsGetParamNames(). + * + * If a match is found, this function returns a new ParamRef + * node, otherwise it returns NULL and the given ColumnRef + * should be treated as a ColumnRef. + */ +Node * +TdsFindParam(ParseState *pstate, ColumnRef *cref) +{ + extern int sql_dialect; + List *params = NULL; + + if (sql_dialect != SQL_DIALECT_TSQL) + return NULL; + + if (!TdsGetParamNames(¶ms)) + return NULL; + + if (pstate->p_paramref_hook == NULL) + return NULL; + + if (list_length(cref->fields) != 1) + return NULL; + else + { + char *colname = strVal(linitial(cref->fields)); + int paramNo = 0; + ParamRef *pref; + + if (params != NULL) + { + paramNo = FindMatchingParam(params, colname); + } + else + { + paramNo = TdsGetAndSetParamIndex(colname); + } + + if (paramNo == 0) + return NULL; + + pref = makeNode(ParamRef); + + pref->number = paramNo; + pref->location = cref->location; + + return TransformParamRef(pstate, pref); + } +} + +static Node * +TransformParamRef(ParseState *pstate, ParamRef *pref) +{ + Node *result; + + /* + * The core parser knows nothing about Params. If a hook is supplied, + * call it. If not, or if the hook returns NULL, throw a generic error. + */ + if (pstate->p_paramref_hook != NULL) + result = pstate->p_paramref_hook(pstate, pref); + else + result = NULL; + + if (result == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_PARAMETER), + errmsg("there is no parameter $%d", pref->number), + parser_errposition(pstate, pref->location))); + + return result; +} + +/* + * TDS Error context callback to let us supply a call-stack traceback. + */ +void +TdsErrorContextCallback(void *arg) +{ + TdsErrorContextData *tdsErrorContext = (TdsErrorContextData *) arg; + + /* + * err_text should not be NULL. Initialise to Empty String + * if it need's to be ignored. + */ + Assert(tdsErrorContext != NULL && tdsErrorContext->err_text != NULL); + + switch (tdsErrorContext->reqType) + { + case TDS_LOGIN7: /* Login7 request */ + { + errcontext("TDS Protocol: Message Type: TDS Login7, Phase: Login. %s", + tdsErrorContext->err_text); + } + break; + case TDS_PRELOGIN: /* Pre-login Request*/ + { + errcontext("TDS Protocol: Message Type: TDS Pre-Login, Phase: Login. %s", + tdsErrorContext->err_text); + } + break; + case TDS_QUERY: /* Simple SQL BATCH */ + { + errcontext("TDS Protocol: Message Type: SQL BATCH, Phase: %s. %s", + tdsErrorContext->phase, + tdsErrorContext->err_text); + } + break; + case TDS_RPC: /* Remote procedure call */ + { + errcontext("TDS Protocol: Message Type: RPC, SP Type: %s, Phase: %s. %s", + tdsErrorContext->spType, + tdsErrorContext->phase, + tdsErrorContext->err_text); + } + break; + case TDS_TXN: /* Transaction management request */ + { + errcontext("TDS Protocol: Message Type: Txn Manager, Txn Type: %s, Phase: %s. %s", + tdsErrorContext->txnType, + tdsErrorContext->phase, + tdsErrorContext->err_text); + } + break; + case TDS_ATTENTION: /* Attention request */ + { + errcontext("TDS Protocol: Message Type: Attention, Phase: %s. %s", + tdsErrorContext->phase, + tdsErrorContext->err_text); + } + break; + default: + errcontext("TDS Protocol: %s", + tdsErrorContext->err_text); + } +} diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdsxact.c b/contrib/babelfishpg_tds/src/backend/tds/tdsxact.c new file mode 100644 index 00000000000..d74be104694 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsxact.c @@ -0,0 +1,428 @@ +/*------------------------------------------------------------------------- + * + * tdsxact.c + * TDS Listener functions for handling Transaction requests + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * contrib/babelfishpg_tds/src/backend/tds/tdsxact.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/transam.h" +#include "nodes/parsenodes.h" +#include "storage/proc.h" + +#include "src/include/tds_instr.h" +#include "src/include/tds_int.h" +#include "src/include/tds_request.h" +#include "src/include/tds_response.h" + +/* Transaction management request */ + +/* Transaction command types */ +#define TDS_TM_BEGIN_XACT 5 +#define TDS_TM_COMMIT_XACT 7 +#define TDS_TM_ROLLBACK_XACT 8 +#define TDS_TM_SAVEPOINT_XACT 9 + +/* Transaction isolation level */ +#define TDS_ISOLATION_LEVEL_NONE 0 +#define TDS_ISOLATION_LEVEL_READ_UNCOMMITTED 1 +#define TDS_ISOLATION_LEVEL_READ_COMMITTED 2 +#define TDS_ISOLATION_LEVEL_REPEATABLE_READ 3 +#define TDS_ISOLATION_LEVEL_SERIALIZABLE 4 +#define TDS_ISOLATION_LEVEL_SNAPSHOT 5 + +/* [A-Za-z\200-\377_\#] */ +static bool +IsValidIdentFirstChar(char ch) +{ + if ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= 0x80 && ch <= 0xff) || + (ch == '_') || (ch == '#')) + return true; + + return false; +} + +/* [A-Za-z\200-\377_0-9\$\#] */ +static bool +IsValidIdentChar(char ch) +{ + if (IsValidIdentFirstChar(ch) || + (ch >= '0' && ch <= '9') || + (ch == '$')) + return true; + + return false; +} + +static bool +IsValidTxnName(char *txnName, int len) +{ + if (len > 0 && IsValidIdentFirstChar(txnName[0])) + { + for(int i=1; i < len; ++i) + if (!IsValidIdentChar(txnName[i])) + return false; + return true; + } + return false; +} + +/* Get transaction name from transaction management request */ +static int +GetTxnName(const StringInfo message, TDSRequestTxnMgmt request, int offset) +{ + uint8_t len; + memcpy(&len, message->data + offset, sizeof(len)); + offset += sizeof(len); + + if (len != 0) + { + if (len > TSQL_TXN_NAME_LIMIT) + ereport(ERROR, + (errcode(ERRCODE_NAME_TOO_LONG), + errmsg("Transaction name length %u above limit %u", + len, TSQL_TXN_NAME_LIMIT))); + + initStringInfo(&(request->txnName)); + TdsUTF16toUTF8StringInfo(&(request->txnName), + message->data + offset, + len); + if (!IsValidTxnName(request->txnName.data, request->txnName.len)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("Transaction savepoint name is not valid"))); + + offset += len; + } + return offset; +} + +/* A new transaction request -> isolation level + txn name */ +static int +GetNewTxnRequest(const StringInfo message, + TDSRequestTxnMgmt request, + int offset) +{ + /* Transaction isolation level */ + memcpy(&(request->isolationLevel), + message->data + offset, + sizeof(request->isolationLevel)); + offset += sizeof(request->isolationLevel); + + if (request->isolationLevel > TDS_ISOLATION_LEVEL_SNAPSHOT) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Invalid isolation level %u for transaction request", + request->isolationLevel))); + + return GetTxnName(message, request, offset); +} + +static const char * +GetIsolationLevelStr(uint8_t isolationLevel) +{ + switch(isolationLevel) + { + case TDS_ISOLATION_LEVEL_READ_UNCOMMITTED: + return "READ UNCOMMITTED "; + case TDS_ISOLATION_LEVEL_READ_COMMITTED: + return "READ COMMITTED "; + case TDS_ISOLATION_LEVEL_REPEATABLE_READ: + return "REPEATABLE READ "; + case TDS_ISOLATION_LEVEL_SERIALIZABLE: + return "SERIALIZABLE "; + case TDS_ISOLATION_LEVEL_SNAPSHOT: + return "SNAPSHOT "; + default: + return "UNKNOWN "; + } +} + +static void +BuildTxnMgmtRequestQuery(TDSRequest requestParam, StringInfo cmdStr) +{ + TDSRequestTxnMgmt request = (TDSRequestTxnMgmt) requestParam; + switch (request->txnReqType) + { + case TDS_TM_BEGIN_XACT: + { + appendStringInfoString(cmdStr, "BEGIN TRANSACTION "); + if (request->txnName.len != 0) + appendStringInfoString(cmdStr, request->txnName.data); + if (request->isolationLevel != TDS_ISOLATION_LEVEL_NONE) + { + appendStringInfoString(cmdStr, "; SET TRANSACTION ISOLATION LEVEL "); + appendStringInfoString(cmdStr, + GetIsolationLevelStr( + request->isolationLevel)); + } + } + break; + case TDS_TM_COMMIT_XACT: + case TDS_TM_ROLLBACK_XACT: + { + if (request->txnReqType == TDS_TM_COMMIT_XACT) + appendStringInfoString(cmdStr, "COMMIT TRANSACTION "); + else + appendStringInfoString(cmdStr, "ROLLBACK TRANSACTION "); + if (request->txnName.len != 0) + appendStringInfoString(cmdStr, request->txnName.data); + if (request->nextTxn != NULL) + { + appendStringInfoString(cmdStr, "; BEGIN TRANSACTION "); + if (request->nextTxn->txnName.len != 0) + appendStringInfoString(cmdStr, + request->nextTxn->txnName.data); + if (request->nextTxn->isolationLevel != + TDS_ISOLATION_LEVEL_NONE) + { + appendStringInfoString(cmdStr, "; SET TRANSACTION ISOLATION LEVEL "); + appendStringInfoString(cmdStr, + GetIsolationLevelStr( + request->nextTxn->isolationLevel)); + } + } + } + break; + case TDS_TM_SAVEPOINT_XACT: + { + appendStringInfoString(cmdStr, "SAVE TRANSACTION "); + appendStringInfoString(cmdStr, request->txnName.data); + } + break; + default: + break; + } +} + +TDSRequest +GetTxnMgmtRequest(const StringInfo message) +{ + TDSRequestTxnMgmt request; + int txnReqOffset = 0; + uint8_t flags; + uint32_t tdsVersion = GetClientTDSVersion(); + + TDSInstrumentation(INSTR_TDS_TM_REQUEST); + + TdsErrorContext->err_text = "Fetching Transaction Management Request"; + /* + * In the ALL_HEADERS rule, the Query Notifications header and the Transaction + * Descriptor header were introduced in TDS 7.2. We need to to Process them only + * for TDS versions more than or equal to 7.2, otherwise we do not increment the offset. + */ + if (tdsVersion > TDS_VERSION_7_1_1) + txnReqOffset = ProcessStreamHeaders(message); + + /* Build return structure */ + request = palloc0(sizeof(TDSRequestTxnMgmtData)); + request->reqType = TDS_REQUEST_TXN_MGMT; + + /* Transaction request type */ + memcpy(&(request->txnReqType), + message->data + txnReqOffset, + sizeof(request->txnReqType)); + txnReqOffset += sizeof(request->txnReqType); + + switch (request->txnReqType) + { + case TDS_TM_BEGIN_XACT: + { + TdsErrorContext->txnType = "TM_BEGIN_XACT"; + txnReqOffset = GetNewTxnRequest(message, + request, + txnReqOffset); + TDS_DEBUG(TDS_DEBUG1, "message_type: Transaction Management Request (14) txn_request_type: TM_BEGIN_XACT"); + } + break; + case TDS_TM_COMMIT_XACT: + case TDS_TM_ROLLBACK_XACT: + { + if (request->txnReqType == TDS_TM_COMMIT_XACT) + { + TdsErrorContext->txnType = "TM_COMMIT_XACT"; + TDS_DEBUG(TDS_DEBUG1, "message_type: Transaction Management Request (14) txn_request_type: TM_COMMIT_XACT"); + } + else + { + TdsErrorContext->txnType = "TM_ROLLBACK_XACT"; + TDS_DEBUG(TDS_DEBUG1, "message_type: Transaction Management Request (14) txn_request_type: TM_ROLLBACK_XACT"); + } + txnReqOffset = GetTxnName(message, request, txnReqOffset); + + /* Transaction request flags */ + memcpy(&flags, message->data + txnReqOffset, sizeof(flags)); + txnReqOffset += sizeof(flags); + + /* Next transaction request */ + if (flags & 0x1) + { + request->nextTxn = palloc0(sizeof(TDSRequestTxnMgmtData)); + txnReqOffset = GetNewTxnRequest(message, + request->nextTxn, + txnReqOffset); + } + } + break; + case TDS_TM_SAVEPOINT_XACT: + { + TdsErrorContext->txnType = "TM_SAVEPOINT_XACT"; + txnReqOffset = GetTxnName(message, request, txnReqOffset); + if (request->txnName.len == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Savepoint request with empty name"))); + TDS_DEBUG(TDS_DEBUG1, "message_type: Transaction Management Request (14) txn_request_type: TM_SAVEPOINT_XACT"); + } + break; + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Transaction management request %u not supported", + request->txnReqType))); + break; + } + + if (txnReqOffset > message->len) + elog(FATAL, + "Transaction management request is corrupt," + "request length: %u request offset: %u", + message->len, txnReqOffset); + + /* Build the internal query corresponding to the txn request */ + initStringInfo(&(request->query)); + BuildTxnMgmtRequestQuery((TDSRequest)request, &(request->query)); + + pfree(message->data); + + return (TDSRequest)request; + +} + +void +ProcessTxnMgmtRequest(TDSRequest request) +{ + uint64_t txnId = (uint64_t) MyProc->lxid; + TDSRequestTxnMgmt req; + InlineCodeBlock *codeblock = makeNode(InlineCodeBlock); + int cmd_type = TDS_CMD_UNKNOWN; + LOCAL_FCINFO(fcinfo,1); + + TdsErrorContext->err_text = "Processing Transaction Management Request"; + req = (TDSRequestTxnMgmt)request; + + /* Only source text matters to handler */ + codeblock->source_text = req->query.data; + codeblock->langOid = 0; /* TODO does it matter */ + codeblock->langIsTrusted = true; + codeblock->atomic = false; + + /* Just to satisfy argument requirement */ + MemSet(fcinfo, 0, SizeForFunctionCallInfo(1)); + fcinfo->args[0].value = PointerGetDatum(codeblock); + fcinfo->args[0].isnull = false; + + pltsql_plugin_handler_ptr->sp_executesql_callback (fcinfo); + MemoryContextSwitchTo(MessageContext); + + /* + * XXX: For BEGIN, COMMIT AND ROLLBACK transaction commands, we send + * environment change tokens. Ideally, the tokens should be sent from + * pltsql extension itself so that even when we execute the above commands + * as SQL batch, the tokens are sent correctly. + */ + switch (req->txnReqType) + { + case TDS_TM_BEGIN_XACT: + { + cmd_type = TDS_CMD_BEGIN; + + /* + * Client expects new transaction id as part of ENV change + * token but BEGIN does not generate new id until a write + * command is executed. So BEGIN returns 0 as new transaction + * id. This is OK as transaction id has value in the context + * of MARS only (client sends it as part of transaction stream + * header). To support MARS, fix it. + */ + TdsSendEnvChangeBinary(TDS_ENVID_BEGINTXN, + &txnId, sizeof(uint64_t), + NULL, 0); + } + break; + case TDS_TM_COMMIT_XACT: + { + cmd_type = TDS_CMD_COMMIT; + + /* + * As BEGIN commands sends 0 as new transaction id, COMMIT + * has to do the same thing. + */ + TdsSendEnvChangeBinary(TDS_ENVID_COMMITTXN, NULL, 0, + &txnId, sizeof(uint64_t)); + if(req->nextTxn != NULL) + { + txnId = (uint64_t) MyProc->lxid; + TdsSendEnvChangeBinary(TDS_ENVID_BEGINTXN, + &txnId, sizeof(uint64_t), + NULL, 0); + } + } + break; + case TDS_TM_ROLLBACK_XACT: + { + cmd_type = TDS_CMD_ROLLBACK; + + /* + * As BEGIN commands sends 0 as new transaction id, ROLLBACK + * has to do the same thing. But, we don't send the token for + * ROLLBACK TO SAVEPOINT command. So if we've rolled back the + * top transaction, send the token. + */ + if (GetTopTransactionIdIfAny() == InvalidTransactionId) + TdsSendEnvChangeBinary(TDS_ENVID_ROLLBACKTXN, NULL, 0, + &txnId, sizeof(uint64_t)); + if(req->nextTxn != NULL) + { + txnId = (uint64_t) MyProc->lxid; + TdsSendEnvChangeBinary(TDS_ENVID_BEGINTXN, + &txnId, sizeof(uint64_t), + NULL, 0); + } + } + break; + default: + break; + } + + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_FINAL, cmd_type, 0); + pfree(codeblock); +} + +int +TestTxnMgmtRequest(TDSRequest request, const char *expectedStr) +{ + int res = 0; + StringInfoData cmdStr; + + Assert(request->reqType == TDS_REQUEST_TXN_MGMT); + initStringInfo(&cmdStr); + BuildTxnMgmtRequestQuery(request, &cmdStr); + res = strncmp(cmdStr.data, + expectedStr, + Min(cmdStr.len, strlen(expectedStr))); + pfree(cmdStr.data); + + return res; +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/adt/README b/contrib/babelfishpg_tds/src/backend/utils/adt/README new file mode 100644 index 00000000000..d35db6e88e4 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/adt/README @@ -0,0 +1,62 @@ +contrib/babelfishpg_tds/backend/utils/adt/README + +We have copied some files from backend PostgreSQL code to utilize +some of the APIs defined in them. We have created our own wrapper +functions in TDS extension that make use of these APIs. + +1. numeric.c + + a. init_var + + Allocate memory to NumericVar variable. We utilize this in + converting the input string to numeric value in TDS receiver + side. + + b. set_var_from_str + + Parse a string and put the number into a numeric variable. + We utilize this in converting the input string to numeric + value in TDS receiver side. + + c. make_result + + Create the packed db numeric format in palloc()'d memory from + a variable (that we got from set_var_from_str()). We utilize + this in converting the input string to numeric value in TDS + receiver side. + + d. numeric_get_typmod + + Get precision and scale from numeric value. We need to send + both the precision and scale as part of column meta data + for numeric and decimal datatypes. + + e. free_var + + Free up memory used by NumericVar variable. We utilize this in + converting the input string to numeric value in TDS receiver + side. + +2. varchar.c + + a. varchar_input + + Convert input C string to SQL varchar(n). Used at TDS side to + convert NVarchar, NChar and NText values into Datum. + +3. xml.c + + a. xmlFreeDoc + + Free up structures used by a XML document when we convert + XML data to Datum at TDS side. + + b. xml_parse + + Check if input is well-formed XML data when we convert XML + data to Datum at TDS side. + + c. parse_xml_decl + + Parse XML decalaration when we convert XML data to Datum + at TDS side. \ No newline at end of file diff --git a/contrib/babelfishpg_tds/src/backend/utils/adt/numeric.c b/contrib/babelfishpg_tds/src/backend/utils/adt/numeric.c new file mode 100644 index 00000000000..3903e515a30 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/adt/numeric.c @@ -0,0 +1,786 @@ +/*------------------------------------------------------------------------- + * + * numeric.c + * An exact numeric data type for the Postgres database system + * + * Original coding 1998, Jan Wieck. Heavily revised 2003, Tom Lane. + * + * Many of the algorithmic ideas are borrowed from David M. Smith's "FM" + * multiple-precision math library, most recently published as Algorithm + * 786: Multiple-Precision Complex Arithmetic and Functions, ACM + * Transactions on Mathematical Software, Vol. 24, No. 4, December 1998, + * pages 359-367. + * + * Copyright (c) 1998-2018, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/utils/adt/numeric.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include +#include +#include +#include + +#include "access/hash.h" +#include "catalog/pg_type.h" +#include "common/int.h" +#include "funcapi.h" +#include "lib/hyperloglog.h" +#include "libpq/pqformat.h" +#include "miscadmin.h" +#include "nodes/nodeFuncs.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/int8.h" +#include "utils/numeric.h" +#include "utils/sortsupport.h" + +#include "src/include/tds_int.h" + +/* ---------- + * Uncomment the following to enable compilation of dump_numeric() + * and dump_var() and to get a dump of any result produced by make_result(). + * ---------- +#define NUMERIC_DEBUG + */ + + +/* ---------- + * Local data types + * + * Numeric values are represented in a base-NBASE floating point format. + * Each "digit" ranges from 0 to NBASE-1. The type NumericDigit is signed + * and wide enough to store a digit. We assume that NBASE*NBASE can fit in + * an int. Although the purely calculational routines could handle any even + * NBASE that's less than sqrt(INT_MAX), in practice we are only interested + * in NBASE a power of ten, so that I/O conversions and decimal rounding + * are easy. Also, it's actually more efficient if NBASE is rather less than + * sqrt(INT_MAX), so that there is "headroom" for mul_var and div_var_fast to + * postpone processing carries. + * + * Values of NBASE other than 10000 are considered of historical interest only + * and are no longer supported in any sense; no mechanism exists for the client + * to discover the base, so every client supporting binary mode expects the + * base-10000 format. If you plan to change this, also note the numeric + * abbreviation code, which assumes NBASE=10000. + * ---------- + */ + +#if 0 +#define NBASE 10 +#define HALF_NBASE 5 +#define DEC_DIGITS 1 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 4 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 8 + +typedef signed char NumericDigit; +#endif + +#if 0 +#define NBASE 100 +#define HALF_NBASE 50 +#define DEC_DIGITS 2 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 3 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 6 + +typedef signed char NumericDigit; +#endif + +#if 1 +#define NBASE 10000 +#define HALF_NBASE 5000 +#define DEC_DIGITS 4 /* decimal digits per NBASE digit */ +#define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */ +#define DIV_GUARD_DIGITS 4 + +typedef int16 NumericDigit; +#endif + +/* + * The Numeric type as stored on disk. + * + * If the high bits of the first word of a NumericChoice (n_header, or + * n_short.n_header, or n_long.n_sign_dscale) are NUMERIC_SHORT, then the + * numeric follows the NumericShort format; if they are NUMERIC_POS or + * NUMERIC_NEG, it follows the NumericLong format. If they are NUMERIC_NAN, + * it is a NaN. We currently always store a NaN using just two bytes (i.e. + * only n_header), but previous releases used only the NumericLong format, + * so we might find 4-byte NaNs on disk if a database has been migrated using + * pg_upgrade. In either case, when the high bits indicate a NaN, the + * remaining bits are never examined. Currently, we always initialize these + * to zero, but it might be possible to use them for some other purpose in + * the future. + * + * In the NumericShort format, the remaining 14 bits of the header word + * (n_short.n_header) are allocated as follows: 1 for sign (positive or + * negative), 6 for dynamic scale, and 7 for weight. In practice, most + * commonly-encountered values can be represented this way. + * + * In the NumericLong format, the remaining 14 bits of the header word + * (n_long.n_sign_dscale) represent the display scale; and the weight is + * stored separately in n_weight. + * + * NOTE: by convention, values in the packed form have been stripped of + * all leading and trailing zero digits (where a "digit" is of base NBASE). + * In particular, if the value is zero, there will be no digits at all! + * The weight is arbitrary in that case, but we normally set it to zero. + */ + +struct NumericShort +{ + uint16 n_header; /* Sign + display scale + weight */ + NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ +}; + +struct NumericLong +{ + uint16 n_sign_dscale; /* Sign + display scale */ + int16 n_weight; /* Weight of 1st digit */ + NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ +}; + +union NumericChoice +{ + uint16 n_header; /* Header word */ + struct NumericLong n_long; /* Long form (4-byte header) */ + struct NumericShort n_short; /* Short form (2-byte header) */ +}; + +struct NumericData +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + union NumericChoice choice; /* choice of format */ +}; + + +/* + * Interpretation of high bits. + */ + +#define NUMERIC_SIGN_MASK 0xC000 +#define NUMERIC_POS 0x0000 +#define NUMERIC_NEG 0x4000 +#define NUMERIC_SHORT 0x8000 +#define NUMERIC_NAN 0xC000 + +#define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK) +#define NUMERIC_IS_NAN(n) (NUMERIC_FLAGBITS(n) == NUMERIC_NAN) +#define NUMERIC_IS_SHORT(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT) + +#define NUMERIC_HDRSZ (VARHDRSZ + sizeof(uint16) + sizeof(int16)) +#define NUMERIC_HDRSZ_SHORT (VARHDRSZ + sizeof(uint16)) + +/* + * If the flag bits are NUMERIC_SHORT or NUMERIC_NAN, we want the short header; + * otherwise, we want the long one. Instead of testing against each value, we + * can just look at the high bit, for a slight efficiency gain. + */ +#define NUMERIC_HEADER_IS_SHORT(n) (((n)->choice.n_header & 0x8000) != 0) +#define NUMERIC_HEADER_SIZE(n) \ + (VARHDRSZ + sizeof(uint16) + \ + (NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16))) + +/* + * Short format definitions. + */ + +#define NUMERIC_SHORT_SIGN_MASK 0x2000 +#define NUMERIC_SHORT_DSCALE_MASK 0x1F80 +#define NUMERIC_SHORT_DSCALE_SHIFT 7 +#define NUMERIC_SHORT_DSCALE_MAX \ + (NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT) +#define NUMERIC_SHORT_WEIGHT_SIGN_MASK 0x0040 +#define NUMERIC_SHORT_WEIGHT_MASK 0x003F +#define NUMERIC_SHORT_WEIGHT_MAX NUMERIC_SHORT_WEIGHT_MASK +#define NUMERIC_SHORT_WEIGHT_MIN (-(NUMERIC_SHORT_WEIGHT_MASK+1)) + +/* + * Extract sign, display scale, weight. + */ + +#define NUMERIC_DSCALE_MASK 0x3FFF + +#define NUMERIC_SIGN(n) \ + (NUMERIC_IS_SHORT(n) ? \ + (((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \ + NUMERIC_NEG : NUMERIC_POS) : NUMERIC_FLAGBITS(n)) +#define NUMERIC_DSCALE(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ + ((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \ + >> NUMERIC_SHORT_DSCALE_SHIFT \ + : ((n)->choice.n_long.n_sign_dscale & NUMERIC_DSCALE_MASK)) +#define NUMERIC_WEIGHT(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ + (((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \ + ~NUMERIC_SHORT_WEIGHT_MASK : 0) \ + | ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \ + : ((n)->choice.n_long.n_weight)) + +/* ---------- + * NumericVar is the format we use for arithmetic. The digit-array part + * is the same as the NumericData storage format, but the header is more + * complex. + * + * The value represented by a NumericVar is determined by the sign, weight, + * ndigits, and digits[] array. + * + * Note: the first digit of a NumericVar's value is assumed to be multiplied + * by NBASE ** weight. Another way to say it is that there are weight+1 + * digits before the decimal point. It is possible to have weight < 0. + * + * buf points at the physical start of the palloc'd digit buffer for the + * NumericVar. digits points at the first digit in actual use (the one + * with the specified weight). We normally leave an unused digit or two + * (preset to zeroes) between buf and digits, so that there is room to store + * a carry out of the top digit without reallocating space. We just need to + * decrement digits (and increment weight) to make room for the carry digit. + * (There is no such extra space in a numeric value stored in the database, + * only in a NumericVar in memory.) + * + * If buf is NULL then the digit buffer isn't actually palloc'd and should + * not be freed --- see the constants below for an example. + * + * dscale, or display scale, is the nominal precision expressed as number + * of digits after the decimal point (it must always be >= 0 at present). + * dscale may be more than the number of physically stored fractional digits, + * implying that we have suppressed storage of significant trailing zeroes. + * It should never be less than the number of stored digits, since that would + * imply hiding digits that are present. NOTE that dscale is always expressed + * in *decimal* digits, and so it may correspond to a fractional number of + * base-NBASE digits --- divide by DEC_DIGITS to convert to NBASE digits. + * + * rscale, or result scale, is the target precision for a computation. + * Like dscale it is expressed as number of *decimal* digits after the decimal + * point, and is always >= 0 at present. + * Note that rscale is not stored in variables --- it's figured on-the-fly + * from the dscales of the inputs. + * + * While we consistently use "weight" to refer to the base-NBASE weight of + * a numeric value, it is convenient in some scale-related calculations to + * make use of the base-10 weight (ie, the approximate log10 of the value). + * To avoid confusion, such a decimal-units weight is called a "dweight". + * + * NB: All the variable-level functions are written in a style that makes it + * possible to give one and the same variable as argument and destination. + * This is feasible because the digit buffer is separate from the variable. + * ---------- + */ +typedef struct NumericVar +{ + int ndigits; /* # of digits in digits[] - can be 0! */ + int weight; /* weight of first digit */ + int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */ + int dscale; /* display scale */ + NumericDigit *buf; /* start of palloc'd space for digits[] */ + NumericDigit *digits; /* base-NBASE digits */ +} NumericVar; + + +/* ---------- + * Data for generate_series + * ---------- + */ +typedef struct +{ + NumericVar current; + NumericVar stop; + NumericVar step; +} generate_series_numeric_fctx; + + +/* ---------- + * Sort support. + * ---------- + */ +typedef struct +{ + void *buf; /* buffer for short varlenas */ + int64 input_count; /* number of non-null values seen */ + bool estimating; /* true if estimating cardinality */ + + hyperLogLogState abbr_card; /* cardinality estimator */ +} NumericSortSupport; + + +/* ---------- + * Fast sum accumulator. + * + * NumericSumAccum is used to implement SUM(), and other standard aggregates + * that track the sum of input values. It uses 32-bit integers to store the + * digits, instead of the normal 16-bit integers (with NBASE=10000). This + * way, we can safely accumulate up to NBASE - 1 values without propagating + * carry, before risking overflow of any of the digits. 'num_uncarried' + * tracks how many values have been accumulated without propagating carry. + * + * Positive and negative values are accumulated separately, in 'pos_digits' + * and 'neg_digits'. This is simpler and faster than deciding whether to add + * or subtract from the current value, for each new value (see sub_var() for + * the logic we avoid by doing this). Both buffers are of same size, and + * have the same weight and scale. In accum_sum_final(), the positive and + * negative sums are added together to produce the final result. + * + * When a new value has a larger ndigits or weight than the accumulator + * currently does, the accumulator is enlarged to accommodate the new value. + * We normally have one zero digit reserved for carry propagation, and that + * is indicated by the 'have_carry_space' flag. When accum_sum_carry() uses + * up the reserved digit, it clears the 'have_carry_space' flag. The next + * call to accum_sum_add() will enlarge the buffer, to make room for the + * extra digit, and set the flag again. + * + * To initialize a new accumulator, simply reset all fields to zeros. + * + * The accumulator does not handle NaNs. + * ---------- + */ +typedef struct NumericSumAccum +{ + int ndigits; + int weight; + int dscale; + int num_uncarried; + bool have_carry_space; + int32 *pos_digits; + int32 *neg_digits; +} NumericSumAccum; + + +/* + * We define our own macros for packing and unpacking abbreviated-key + * representations for numeric values in order to avoid depending on + * USE_FLOAT8_BYVAL. The type of abbreviation we use is based only on + * the size of a datum, not the argument-passing convention for float8. + */ +#define NUMERIC_ABBREV_BITS (SIZEOF_DATUM * BITS_PER_BYTE) +#if SIZEOF_DATUM == 8 +#define NumericAbbrevGetDatum(X) ((Datum) (X)) +#define DatumGetNumericAbbrev(X) ((int64) (X)) +#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT64_MIN) +#else +#define NumericAbbrevGetDatum(X) ((Datum) (X)) +#define DatumGetNumericAbbrev(X) ((int32) (X)) +#define NUMERIC_ABBREV_NAN NumericAbbrevGetDatum(PG_INT32_MIN) +#endif + +/* ---------- + * Local functions + * ---------- + */ + +#ifdef NUMERIC_DEBUG +static void dump_numeric(const char *str, Numeric num); +static void dump_var(const char *str, NumericVar *var); +#else +#define dump_numeric(s,n) +#define dump_var(s,v) +#endif + +#define digitbuf_alloc(ndigits) \ + ((NumericDigit *) palloc((ndigits) * sizeof(NumericDigit))) +#define digitbuf_free(buf) \ + do { \ + if ((buf) != NULL) \ + pfree(buf); \ + } while (0) + +#define init_var(v) MemSetAligned(v, 0, sizeof(NumericVar)) + +#define NUMERIC_DIGITS(num) (NUMERIC_HEADER_IS_SHORT(num) ? \ + (num)->choice.n_short.n_data : (num)->choice.n_long.n_data) +#define NUMERIC_NDIGITS(num) \ + ((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit)) +#define NUMERIC_CAN_BE_SHORT(scale,weight) \ + ((scale) <= NUMERIC_SHORT_DSCALE_MAX && \ + (weight) <= NUMERIC_SHORT_WEIGHT_MAX && \ + (weight) >= NUMERIC_SHORT_WEIGHT_MIN) + +static void alloc_var(NumericVar *var, int ndigits); +static void free_var(NumericVar *var); +static const char *set_var_from_str(const char *str, const char *cp, + NumericVar *dest); +static Numeric make_result(const NumericVar *var); +static void strip_var(NumericVar *var); + +/* ---------------------------------------------------------------------- + * + * Local functions follow + * + * In general, these do not support NaNs --- callers must eliminate + * the possibility of NaN first. (make_result() is an exception.) + * + * ---------------------------------------------------------------------- + */ + + +/* + * alloc_var() - + * + * Allocate a digit buffer of ndigits digits (plus a spare digit for rounding) + */ +static void +alloc_var(NumericVar *var, int ndigits) +{ + digitbuf_free(var->buf); + var->buf = digitbuf_alloc(ndigits + 1); + var->buf[0] = 0; /* spare digit for rounding */ + var->digits = var->buf + 1; + var->ndigits = ndigits; +} + + +/* + * free_var() - + * + * Return the digit buffer of a variable to the free pool + */ +static void +free_var(NumericVar *var) +{ + digitbuf_free(var->buf); + var->buf = NULL; + var->digits = NULL; + var->sign = NUMERIC_NAN; +} + +/* + * set_var_from_str() + * + * Parse a string and put the number into a variable + * + * This function does not handle leading or trailing spaces, and it doesn't + * accept "NaN" either. It returns the end+1 position so that caller can + * check for trailing spaces/garbage if deemed necessary. + * + * cp is the place to actually start parsing; str is what to use in error + * reports. (Typically cp would be the same except advanced over spaces.) + */ +static const char * +set_var_from_str(const char *str, const char *cp, NumericVar *dest) +{ + bool have_dp = false; + int i; + unsigned char *decdigits; + int sign = NUMERIC_POS; + int dweight = -1; + int ddigits; + int dscale = 0; + int weight; + int ndigits; + int offset; + NumericDigit *digits; + + /* + * We first parse the string to extract decimal digits and determine the + * correct decimal weight. Then convert to NBASE representation. + */ + switch (*cp) + { + case '+': + sign = NUMERIC_POS; + cp++; + break; + + case '-': + sign = NUMERIC_NEG; + cp++; + break; + } + + if (*cp == '.') + { + have_dp = true; + cp++; + } + + if (!isdigit((unsigned char) *cp)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + + decdigits = (unsigned char *) palloc(strlen(cp) + DEC_DIGITS * 2); + + /* leading padding for digit alignment later */ + memset(decdigits, 0, DEC_DIGITS); + i = DEC_DIGITS; + + while (*cp) + { + if (isdigit((unsigned char) *cp)) + { + decdigits[i++] = *cp++ - '0'; + if (!have_dp) + dweight++; + else + dscale++; + } + else if (*cp == '.') + { + if (have_dp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + have_dp = true; + cp++; + } + else + break; + } + + ddigits = i - DEC_DIGITS; + /* trailing padding for digit alignment later */ + memset(decdigits + i, 0, DEC_DIGITS - 1); + + /* Handle exponent, if any */ + if (*cp == 'e' || *cp == 'E') + { + long exponent; + char *endptr; + + cp++; + exponent = strtol(cp, &endptr, 10); + if (endptr == cp) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "numeric", str))); + cp = endptr; + + /* + * At this point, dweight and dscale can't be more than about + * INT_MAX/2 due to the MaxAllocSize limit on string length, so + * constraining the exponent similarly should be enough to prevent + * integer overflow in this function. If the value is too large to + * fit in storage format, make_result() will complain about it later; + * for consistency use the same ereport errcode/text as make_result(). + */ + if (exponent >= INT_MAX / 2 || exponent <= -(INT_MAX / 2)) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + dweight += (int) exponent; + dscale -= (int) exponent; + if (dscale < 0) + dscale = 0; + } + + /* + * Okay, convert pure-decimal representation to base NBASE. First we need + * to determine the converted weight and ndigits. offset is the number of + * decimal zeroes to insert before the first given digit to have a + * correctly aligned first NBASE digit. + */ + if (dweight >= 0) + weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1; + else + weight = -((-dweight - 1) / DEC_DIGITS + 1); + offset = (weight + 1) * DEC_DIGITS - (dweight + 1); + ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS; + + alloc_var(dest, ndigits); + dest->sign = sign; + dest->weight = weight; + dest->dscale = dscale; + + i = DEC_DIGITS - offset; + digits = dest->digits; + + while (ndigits-- > 0) + { +#if DEC_DIGITS == 4 + *digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 + + decdigits[i + 2]) * 10 + decdigits[i + 3]; +#elif DEC_DIGITS == 2 + *digits++ = decdigits[i] * 10 + decdigits[i + 1]; +#elif DEC_DIGITS == 1 + *digits++ = decdigits[i]; +#else +#error unsupported NBASE +#endif + i += DEC_DIGITS; + } + + pfree(decdigits); + + /* Strip any leading/trailing zeroes, and normalize weight if zero */ + strip_var(dest); + + /* Return end+1 position for caller */ + return cp; +} + +/* + * make_result() - + * + * Create the packed db numeric format in palloc()'d memory from + * a variable. + */ +static Numeric +make_result(const NumericVar *var) +{ + Numeric result; + NumericDigit *digits = var->digits; + int weight = var->weight; + int sign = var->sign; + int n; + Size len; + + if (sign == NUMERIC_NAN) + { + result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT); + + SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT); + result->choice.n_header = NUMERIC_NAN; + /* the header word is all we need */ + + dump_numeric("make_result()", result); + return result; + } + + n = var->ndigits; + + /* truncate leading zeroes */ + while (n > 0 && *digits == 0) + { + digits++; + weight--; + n--; + } + /* truncate trailing zeroes */ + while (n > 0 && digits[n - 1] == 0) + n--; + + /* If zero result, force to weight=0 and positive sign */ + if (n == 0) + { + weight = 0; + sign = NUMERIC_POS; + } + + /* Build the result */ + if (NUMERIC_CAN_BE_SHORT(var->dscale, weight)) + { + len = NUMERIC_HDRSZ_SHORT + n * sizeof(NumericDigit); + result = (Numeric) palloc(len); + SET_VARSIZE(result, len); + result->choice.n_short.n_header = + (sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK) + : NUMERIC_SHORT) + | (var->dscale << NUMERIC_SHORT_DSCALE_SHIFT) + | (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0) + | (weight & NUMERIC_SHORT_WEIGHT_MASK); + } + else + { + len = NUMERIC_HDRSZ + n * sizeof(NumericDigit); + result = (Numeric) palloc(len); + SET_VARSIZE(result, len); + result->choice.n_long.n_sign_dscale = + sign | (var->dscale & NUMERIC_DSCALE_MASK); + result->choice.n_long.n_weight = weight; + } + + Assert(NUMERIC_NDIGITS(result) == n); + if (n > 0) + memcpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit)); + + /* Check for overflow of int16 fields */ + if (NUMERIC_WEIGHT(result) != weight || + NUMERIC_DSCALE(result) != var->dscale) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("value overflows numeric format"))); + + dump_numeric("make_result()", result); + return result; +} + +/* + * strip_var + * + * Strip any leading and trailing zeroes from a numeric variable + */ +static void +strip_var(NumericVar *var) +{ + NumericDigit *digits = var->digits; + int ndigits = var->ndigits; + + /* Strip leading zeroes */ + while (ndigits > 0 && *digits == 0) + { + digits++; + var->weight--; + ndigits--; + } + + /* Strip trailing zeroes */ + while (ndigits > 0 && digits[ndigits - 1] == 0) + ndigits--; + + /* If it's zero, normalize the sign and weight */ + if (ndigits == 0) + { + var->sign = NUMERIC_POS; + var->weight = 0; + } + + var->digits = digits; + var->ndigits = ndigits; +} + +/* + * Converts input string to numeric value in TDS receiver side + */ +Numeric +TdsSetVarFromStrWrapper(const char *str) +{ + NumericVar value; + Numeric res; + init_var(&value); + set_var_from_str(str, str, &value); + res = make_result(&value); + free_var(&value); + return res; +} + +/* + * Get Precision & Scale from Numeric Value + */ +int32_t +numeric_get_typmod(Numeric num) +{ + int32_t scale = NUMERIC_DSCALE(num); + int32_t weight = NUMERIC_WEIGHT(num); + int32_t precision; + + if (weight >= 0) + { + static const int32 timescales[DEC_DIGITS] = { + 1000, + 100, + 10, + 1, + }; + int leading_digits = NUMERIC_DIGITS(num)[0]; + precision = weight * DEC_DIGITS + scale; + + for(int i = 0; i < DEC_DIGITS; i++) + { + if(leading_digits >= timescales[i]) + { + precision += (4-i); + break; + } + } + } + else + /* weight < 0 means the integral part of the number is 0 */ + precision = 1 + scale; + + return (((precision & 0xFFFF) << 16 ) | (scale & 0xFFFF)) + VARHDRSZ; +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/adt/varchar.c b/contrib/babelfishpg_tds/src/backend/utils/adt/varchar.c new file mode 100644 index 00000000000..e9cab2f44b0 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/adt/varchar.c @@ -0,0 +1,109 @@ +/*------------------------------------------------------------------------- + * + * varchar.c + * Functions for the built-in types char(n) and varchar(n). + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/utils/adt/varchar.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + + +#include "access/hash.h" +#include "catalog/pg_collation.h" +#include "libpq/pqformat.h" +#include "nodes/nodeFuncs.h" +#include "parser/parser.h" /* only needed for GUC variables */ +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/varlena.h" +#include "mb/pg_wchar.h" + +#include "src/include/tds_int.h" + +static inline void +CheckUTF16Length(const char *utf8_str, size_t len, size_t maxlen, + char *varstr) +{ + int i; + + if (sql_dialect == SQL_DIALECT_TSQL) + { + for (i = len; i > 0; i--) + if (utf8_str[i - 1] != ' ') + break; + if (TdsUTF8LengthInUTF16(utf8_str, i) > maxlen) + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character%s(%d) " + "as UTF16 output", + varstr, (int)maxlen))); + } +} + +/***************************************************************************** + * varchar - varchar(n) + * + * Note: varchar piggybacks on type text for most operations, and so has no + * C-coded functions except for I/O and typmod checking. + *****************************************************************************/ + +/* + * varchar_input -- common guts of varcharin and varcharrecv + * + * s is the input text of length len (may not be null-terminated) + * atttypmod is the typmod value to apply + * + * Note that atttypmod is measured in characters, which + * is not necessarily the same as the number of bytes. + * + * If the input string is too long, raise an error, unless the extra + * characters are spaces, in which case they're truncated. (per SQL) + * + * Uses the C string to text conversion function, which is only appropriate + * if VarChar and text are equivalent types. + */ +static VarChar * +varchar_input(const char *s, size_t len, int32 atttypmod) +{ + VarChar *result; + size_t maxlen; + + maxlen = atttypmod - VARHDRSZ; + + if (atttypmod >= (int32) VARHDRSZ && len > maxlen) + { + /* Verify that extra characters are spaces, and clip them off */ + size_t mbmaxlen = pg_mbcharcliplen(s, len, maxlen); + size_t j; + + for (j = mbmaxlen; j < len; j++) + { + if (s[j] != ' ') + ereport(ERROR, + (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), + errmsg("value too long for type character varying(%d)", + (int) maxlen))); + } + + len = mbmaxlen; + } + + if (atttypmod >= (int32) VARHDRSZ) + CheckUTF16Length(s, len, maxlen, " varying"); + + result = (VarChar *) cstring_to_text_with_len(s, len); + return result; +} + +void * +tds_varchar_input(const char *s, size_t len, int32 atttypmod) +{ + return varchar_input(s, len, atttypmod); +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/adt/xml.c b/contrib/babelfishpg_tds/src/backend/utils/adt/xml.c new file mode 100644 index 00000000000..ad1a6899929 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/adt/xml.c @@ -0,0 +1,738 @@ +/*------------------------------------------------------------------------- + * + * xml.c + * XML data type support. + * + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/backend/utils/adt/xml.c + * + *------------------------------------------------------------------------- + */ + +/* + * Generally, XML type support is only available when libxml use was + * configured during the build. But even if that is not done, the + * type and all the functions are available, but most of them will + * fail. For one thing, this avoids having to manage variant catalog + * installations. But it also has nice effects such as that you can + * dump a database containing XML type data even if the server is not + * linked with libxml. Thus, make sure xml_out() works even if nothing + * else does. + */ + +/* + * Notes on memory management: + * + * Sometimes libxml allocates global structures in the hope that it can reuse + * them later on. This makes it impractical to change the xmlMemSetup + * functions on-the-fly; that is likely to lead to trying to pfree() chunks + * allocated with malloc() or vice versa. Since libxml might be used by + * loadable modules, eg libperl, our only safe choices are to change the + * functions at postmaster/backend launch or not at all. Since we'd rather + * not activate libxml in sessions that might never use it, the latter choice + * is the preferred one. However, for debugging purposes it can be awfully + * handy to constrain libxml's allocations to be done in a specific palloc + * context, where they're easy to track. Therefore there is code here that + * can be enabled in debug builds to redirect libxml's allocations into a + * special context LibxmlContext. It's not recommended to turn this on in + * a production build because of the possibility of bad interactions with + * external modules. + */ +/* #define USE_LIBXMLCONTEXT */ + +#include "postgres.h" + +#ifdef USE_LIBXML +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/include/tds_int.h" + +/* + * We used to check for xmlStructuredErrorContext via a configure test; but + * that doesn't work on Windows, so instead use this grottier method of + * testing the library version number. + */ +#if LIBXML_VERSION >= 20704 +#define HAVE_XMLSTRUCTUREDERRORCONTEXT 1 +#endif +#endif /* USE_LIBXML */ + +#include "access/htup_details.h" +#include "catalog/namespace.h" +#include "catalog/pg_class.h" +#include "catalog/pg_type.h" +#include "commands/dbcommands.h" +#include "executor/spi.h" +#include "executor/tablefunc.h" +#include "fmgr.h" +#include "lib/stringinfo.h" +#include "libpq/pqformat.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "nodes/execnodes.h" +#include "nodes/nodeFuncs.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/date.h" +#include "utils/datetime.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/xml.h" + +/* GUC variables */ +int xmlbinary; +int xmloption; + +#ifdef USE_LIBXML + +/* random number to identify PgXmlErrorContext */ +#define ERRCXT_MAGIC 68275028 + +struct PgXmlErrorContext +{ + int magic; + /* strictness argument passed to pg_xml_init */ + PgXmlStrictness strictness; + /* current error status and accumulated message, if any */ + bool err_occurred; + StringInfoData err_buf; + /* previous libxml error handling state (saved by pg_xml_init) */ + xmlStructuredErrorFunc saved_errfunc; + void *saved_errcxt; + /* previous libxml entity handler (saved by pg_xml_init) */ + xmlExternalEntityLoader saved_entityfunc; +}; + +static void xml_ereport_by_code(int level, int sqlcode, + const char *msg, int errcode); + +#ifdef USE_LIBXMLCONTEXT + +static MemoryContext LibxmlContext = NULL; + +static void xml_memory_init(void); +static void *xml_palloc(size_t size); +static void *xml_repalloc(void *ptr, size_t size); +static void xml_pfree(void *ptr); +static char *xml_pstrdup(const char *string); +#endif /* USE_LIBXMLCONTEXT */ + +static xmlChar *xml_text2xmlChar(text *in); +static int parse_xml_decl(const xmlChar *str, size_t *lenp, + xmlChar **version, xmlChar **encoding, int *standalone); +static bool xml_doctype_in_content(const xmlChar *str); +static xmlDocPtr xml_parse(text *data, XmlOptionType xmloption_arg, + bool preserve_whitespace, int encoding); +#endif /* USE_LIBXML */ + +/* XMLTABLE support */ +#ifdef USE_LIBXML +/* random number to identify XmlTableContext */ +#define XMLTABLE_CONTEXT_MAGIC 46922182 +typedef struct XmlTableBuilderData +{ + int magic; + int natts; + long int row_count; + PgXmlErrorContext *xmlerrcxt; + xmlParserCtxtPtr ctxt; + xmlDocPtr doc; + xmlXPathContextPtr xpathcxt; + xmlXPathCompExprPtr xpathcomp; + xmlXPathObjectPtr xpathobj; + xmlXPathCompExprPtr *xpathscomp; +} XmlTableBuilderData; +#endif + +#define NO_XML_SUPPORT() \ + ereport(ERROR, \ + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \ + errmsg("unsupported XML feature"), \ + errdetail("This functionality requires the server to be built with libxml support."), \ + errhint("You need to rebuild PostgreSQL using --with-libxml."))) + + +/* from SQL/XML:2008 section 4.9 */ +#define NAMESPACE_XSD "http://www.w3.org/2001/XMLSchema" +#define NAMESPACE_XSI "http://www.w3.org/2001/XMLSchema-instance" +#define NAMESPACE_SQLXML "http://standards.iso.org/iso/9075/2003/sqlxml" + +#ifdef USE_LIBXML + +/* + * SQL/XML allows storing "XML documents" or "XML content". "XML + * documents" are specified by the XML specification and are parsed + * easily by libxml. "XML content" is specified by SQL/XML as the + * production "XMLDecl? content". But libxml can only parse the + * "content" part, so we have to parse the XML declaration ourselves + * to complete this. + */ + +#define CHECK_XML_SPACE(p) \ + do { \ + if (!xmlIsBlank_ch(*(p))) \ + return XML_ERR_SPACE_REQUIRED; \ + } while (0) + +#define SKIP_XML_SPACE(p) \ + while (xmlIsBlank_ch(*(p))) (p)++ + +/* Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender */ +/* Beware of multiple evaluations of argument! */ +#define PG_XMLISNAMECHAR(c) \ + (xmlIsBaseChar_ch(c) || xmlIsIdeographicQ(c) \ + || xmlIsDigit_ch(c) \ + || c == '.' || c == '-' || c == '_' || c == ':' \ + || xmlIsCombiningQ(c) \ + || xmlIsExtender_ch(c)) + +/* pnstrdup, but deal with xmlChar not char; len is measured in xmlChars */ +static xmlChar * +xml_pnstrdup(const xmlChar *str, size_t len) +{ + xmlChar *result; + + result = (xmlChar *) palloc((len + 1) * sizeof(xmlChar)); + memcpy(result, str, len * sizeof(xmlChar)); + result[len] = 0; + return result; +} + +/* + * str is the null-terminated input string. Remaining arguments are + * output arguments; each can be NULL if value is not wanted. + * version and encoding are returned as locally-palloc'd strings. + * Result is 0 if OK, an error code if not. + */ +static int +parse_xml_decl(const xmlChar *str, size_t *lenp, + xmlChar **version, xmlChar **encoding, int *standalone) +{ + const xmlChar *p; + const xmlChar *save_p; + size_t len; + int utf8char; + int utf8len; + + /* + * Only initialize libxml. We don't need error handling here, but we do + * need to make sure libxml is initialized before calling any of its + * functions. Note that this is safe (and a no-op) if caller has already + * done pg_xml_init(). + */ + pg_xml_init_library(); + + /* Initialize output arguments to "not present" */ + if (version) + *version = NULL; + if (encoding) + *encoding = NULL; + if (standalone) + *standalone = -1; + + p = str; + + if (xmlStrncmp(p, (xmlChar *) " + * rather than an XMLDecl, so we have done what we came to do and found no + * XMLDecl. + * + * We need an input length value for xmlGetUTF8Char, but there's no need + * to count the whole document size, so use strnlen not strlen. + */ + utf8len = strnlen((const char *) (p + 5), MAX_MULTIBYTE_CHAR_LEN); + utf8char = xmlGetUTF8Char(p + 5, &utf8len); + if (PG_XMLISNAMECHAR(utf8char)) + goto finished; + + p += 5; + + /* version */ + CHECK_XML_SPACE(p); + SKIP_XML_SPACE(p); + if (xmlStrncmp(p, (xmlChar *) "version", 7) != 0) + return XML_ERR_VERSION_MISSING; + p += 7; + SKIP_XML_SPACE(p); + if (*p != '=') + return XML_ERR_VERSION_MISSING; + p += 1; + SKIP_XML_SPACE(p); + + if (*p == '\'' || *p == '"') + { + const xmlChar *q; + + q = xmlStrchr(p + 1, *p); + if (!q) + return XML_ERR_VERSION_MISSING; + + if (version) + *version = xml_pnstrdup(p + 1, q - p - 1); + p = q + 1; + } + else + return XML_ERR_VERSION_MISSING; + + /* encoding */ + save_p = p; + SKIP_XML_SPACE(p); + if (xmlStrncmp(p, (xmlChar *) "encoding", 8) == 0) + { + CHECK_XML_SPACE(save_p); + p += 8; + SKIP_XML_SPACE(p); + if (*p != '=') + return XML_ERR_MISSING_ENCODING; + p += 1; + SKIP_XML_SPACE(p); + + if (*p == '\'' || *p == '"') + { + const xmlChar *q; + + q = xmlStrchr(p + 1, *p); + if (!q) + return XML_ERR_MISSING_ENCODING; + + if (encoding) + *encoding = xml_pnstrdup(p + 1, q - p - 1); + p = q + 1; + } + else + return XML_ERR_MISSING_ENCODING; + } + else + { + p = save_p; + } + + /* standalone */ + save_p = p; + SKIP_XML_SPACE(p); + if (xmlStrncmp(p, (xmlChar *) "standalone", 10) == 0) + { + CHECK_XML_SPACE(save_p); + p += 10; + SKIP_XML_SPACE(p); + if (*p != '=') + return XML_ERR_STANDALONE_VALUE; + p += 1; + SKIP_XML_SPACE(p); + if (xmlStrncmp(p, (xmlChar *) "'yes'", 5) == 0 || + xmlStrncmp(p, (xmlChar *) "\"yes\"", 5) == 0) + { + if (standalone) + *standalone = 1; + p += 5; + } + else if (xmlStrncmp(p, (xmlChar *) "'no'", 4) == 0 || + xmlStrncmp(p, (xmlChar *) "\"no\"", 4) == 0) + { + if (standalone) + *standalone = 0; + p += 4; + } + else + return XML_ERR_STANDALONE_VALUE; + } + else + { + p = save_p; + } + + SKIP_XML_SPACE(p); + if (xmlStrncmp(p, (xmlChar *) "?>", 2) != 0) + return XML_ERR_XMLDECL_NOT_FINISHED; + p += 2; + +finished: + len = p - str; + + for (p = str; p < str + len; p++) + if (*p > 127) + return XML_ERR_INVALID_CHAR; + + if (lenp) + *lenp = len; + + return XML_ERR_OK; +} + +/* + * Test whether an input that is to be parsed as CONTENT contains a DTD. + * + * The SQL/XML:2003 definition of CONTENT ("XMLDecl? content") is not + * satisfied by a document with a DTD, which is a bit of a wart, as it means + * the CONTENT type is not a proper superset of DOCUMENT. SQL/XML:2006 and + * later fix that, by redefining content with reference to the "more + * permissive" Document Node of the XQuery/XPath Data Model, such that any + * DOCUMENT value is indeed also a CONTENT value. That definition is more + * useful, as CONTENT becomes usable for parsing input of unknown form (think + * pg_restore). + * + * As used below in parse_xml when parsing for CONTENT, libxml does not give + * us the 2006+ behavior, but only the 2003; it will choke if the input has + * a DTD. But we can provide the 2006+ definition of CONTENT easily enough, + * by detecting this case first and simply doing the parse as DOCUMENT. + * + * A DTD can be found arbitrarily far in, but that would be a contrived case; + * it will ordinarily start within a few dozen characters. The only things + * that can precede it are an XMLDecl (here, the caller will have called + * parse_xml_decl already), whitespace, comments, and processing instructions. + * This function need only return true if it sees a valid sequence of such + * things leading to must follow */ + p = xmlStrstr(p + 2, (xmlChar *) "--"); + if (!p || p[2] != '>') + return false; + /* advance over comment, and keep scanning */ + p += 3; + continue; + } + + /* otherwise, if it's not a PI , fail */ + if (*p != '?') + return false; + p++; + + /* find end of PI (the string ?> is forbidden within a PI) */ + e = xmlStrstr(p, (xmlChar *) "?>"); + if (!e) + return false; + + /* advance over PI, keep scanning */ + p = e + 2; + } +} + + +/* + * Convert a C string to XML internal representation + * + * Note: it is caller's responsibility to xmlFreeDoc() the result, + * else a permanent memory leak will ensue! + * + * TODO maybe libxml2's xmlreader is better? (do not construct DOM, + * yet do not use SAX - see xmlreader.c) + */ +static xmlDocPtr +xml_parse(text *data, XmlOptionType xmloption_arg, bool preserve_whitespace, + int encoding) +{ + int32 len; + xmlChar *string; + xmlChar *utf8string; + PgXmlErrorContext *xmlerrcxt; + volatile xmlParserCtxtPtr ctxt = NULL; + volatile xmlDocPtr doc = NULL; + + len = VARSIZE_ANY_EXHDR(data); /* will be useful later */ + string = xml_text2xmlChar(data); + + utf8string = pg_do_encoding_conversion(string, + len, + encoding, + PG_UTF8); + + /* Start up libxml and its parser */ + xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_WELLFORMED); + + /* Use a TRY block to ensure we clean up correctly */ + PG_TRY(); + { + bool parse_as_document = false; + int res_code; + size_t count = 0; + xmlChar *version = NULL; + int standalone = 0; + + xmlInitParser(); + + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY, + "could not allocate parser context"); + + /* Decide whether to parse as document or content */ + if (xmloption_arg == XMLOPTION_DOCUMENT) + parse_as_document = true; + else + { + /* Parse and skip over the XML declaration, if any */ + res_code = parse_xml_decl(utf8string, + &count, &version, NULL, &standalone); + if (res_code != 0) + xml_ereport_by_code(ERROR, ERRCODE_INVALID_XML_CONTENT, + "invalid XML content: invalid XML declaration", + res_code); + + /* Is there a DOCTYPE element? */ + if (xml_doctype_in_content(utf8string + count)) + parse_as_document = true; + } + + if (parse_as_document) + { + /* + * Note, that here we try to apply DTD defaults + * (XML_PARSE_DTDATTR) according to SQL/XML:2008 GR 10.16.7.d: + * 'Default values defined by internal DTD are applied'. As for + * external DTDs, we try to support them too, (see SQL/XML:2008 GR + * 10.16.7.e) + */ + doc = xmlCtxtReadDoc(ctxt, utf8string, + NULL, + "UTF-8", + XML_PARSE_NOENT | XML_PARSE_DTDATTR + | (preserve_whitespace ? 0 : XML_PARSE_NOBLANKS)); + if (doc == NULL || xmlerrcxt->err_occurred) + { + /* Use original option to decide which error code to throw */ + if (xmloption_arg == XMLOPTION_DOCUMENT) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT, + "invalid XML document"); + else + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_CONTENT, + "invalid XML content"); + } + } + else + { + doc = xmlNewDoc(version); + Assert(doc->encoding == NULL); + doc->encoding = xmlStrdup((const xmlChar *) "UTF-8"); + doc->standalone = standalone; + + /* allow empty content */ + if (*(utf8string + count)) + { + res_code = xmlParseBalancedChunkMemory(doc, NULL, NULL, 0, + utf8string + count, NULL); + if (res_code != 0 || xmlerrcxt->err_occurred) + xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_CONTENT, + "invalid XML content"); + } + } + } + PG_CATCH(); + { + if (doc != NULL) + xmlFreeDoc(doc); + if (ctxt != NULL) + xmlFreeParserCtxt(ctxt); + + pg_xml_done(xmlerrcxt, true); + + PG_RE_THROW(); + } + PG_END_TRY(); + + xmlFreeParserCtxt(ctxt); + + pg_xml_done(xmlerrcxt, false); + + return doc; +} + + +/* + * xmlChar<->text conversions + */ +static xmlChar * +xml_text2xmlChar(text *in) +{ + return (xmlChar *) text_to_cstring(in); +} + + +#ifdef USE_LIBXMLCONTEXT + +/* + * Manage the special context used for all libxml allocations (but only + * in special debug builds; see notes at top of file) + */ +static void +xml_memory_init(void) +{ + /* Create memory context if not there already */ + if (LibxmlContext == NULL) + LibxmlContext = AllocSetContextCreate(TopMemoryContext, + MC_Libxml_context, + ALLOCSET_DEFAULT_SIZES); + + /* Re-establish the callbacks even if already set */ + xmlMemSetup(xml_pfree, xml_palloc, xml_repalloc, xml_pstrdup); +} + +/* + * Wrappers for memory management functions + */ +static void * +xml_palloc(size_t size) +{ + return MemoryContextAlloc(LibxmlContext, size); +} + + +static void * +xml_repalloc(void *ptr, size_t size) +{ + return repalloc(ptr, size); +} + + +static void +xml_pfree(void *ptr) +{ + /* At least some parts of libxml assume xmlFree(NULL) is allowed */ + if (ptr) + pfree(ptr); +} + + +static char * +xml_pstrdup(const char *string) +{ + return MemoryContextStrdup(LibxmlContext, string); +} +#endif /* USE_LIBXMLCONTEXT */ + + +/* + * Wrapper for "ereport" function for XML-related errors. The "msg" + * is the SQL-level message; some can be adopted from the SQL/XML + * standard. This function uses "code" to create a textual detail + * message. At the moment, we only need to cover those codes that we + * may raise in this file. + */ +static void +xml_ereport_by_code(int level, int sqlcode, + const char *msg, int code) +{ + const char *det; + + switch (code) + { + case XML_ERR_INVALID_CHAR: + det = gettext_noop("Invalid character value."); + break; + case XML_ERR_SPACE_REQUIRED: + det = gettext_noop("Space required."); + break; + case XML_ERR_STANDALONE_VALUE: + det = gettext_noop("standalone accepts only 'yes' or 'no'."); + break; + case XML_ERR_VERSION_MISSING: + det = gettext_noop("Malformed declaration: missing version."); + break; + case XML_ERR_MISSING_ENCODING: + det = gettext_noop("Missing encoding in text declaration."); + break; + case XML_ERR_XMLDECL_NOT_FINISHED: + det = gettext_noop("Parsing XML declaration: '?>' expected."); + break; + default: + det = gettext_noop("Unrecognized libxml error code: %d."); + break; + } + + ereport(level, + (errcode(sqlcode), + errmsg_internal("%s", msg), + errdetail(det, code))); +} +#endif /* USE_LIBXML */ + +/* + * support functions for XMLTABLE + * + */ +#ifdef USE_LIBXML + +/* + * Returns private data from executor state. Ensure validity by check with + * MAGIC number. + */ +static inline XmlTableBuilderData * +GetXmlTableBuilderPrivateData(TableFuncScanState *state, const char *fname) +{ + XmlTableBuilderData *result; + + if (!IsA(state, TableFuncScanState)) + elog(ERROR, "%s called with invalid TableFuncScanState", fname); + result = (XmlTableBuilderData *) state->opaque; + if (result->magic != XMLTABLE_CONTEXT_MAGIC) + elog(ERROR, "%s called with invalid TableFuncScanState", fname); + + return result; +} +#endif + +void * +tds_xml_parse(text *data, int xmloption_arg, bool preserve_whitespace, + int encoding) +{ + return xml_parse(data, xmloption_arg, preserve_whitespace, encoding); +} + +void +tds_xmlFreeDoc(void *doc) +{ + return xmlFreeDoc(doc); +} + +int +tds_parse_xml_decl(const xmlChar *str, size_t *lenp, + xmlChar **version, xmlChar **encoding, int *standalone) +{ + return parse_xml_decl(str, lenp, version, encoding, standalone); +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/mb/README b/contrib/babelfishpg_tds/src/backend/utils/mb/README new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/babelfishpg_tds/src/backend/utils/mb/conv.c b/contrib/babelfishpg_tds/src/backend/utils/mb/conv.c new file mode 100644 index 00000000000..814fed55ba5 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/mb/conv.c @@ -0,0 +1,361 @@ +/*------------------------------------------------------------------------- + * + * Utility functions for conversion procs. + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/mb/conv.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "mb/pg_wchar.h" + +#include "src/include/tds_int.h" + +/* + * comparison routine for bsearch() + * this routine is intended for combined UTF8 -> local code + */ +static int +compare3(const void *p1, const void *p2) +{ + uint32 s1, + s2, + d1, + d2; + + s1 = *(const uint32 *) p1; + s2 = *((const uint32 *) p1 + 1); + d1 = ((const pg_utf_to_local_combined *) p2)->utf1; + d2 = ((const pg_utf_to_local_combined *) p2)->utf2; + return (s1 > d1 || (s1 == d1 && s2 > d2)) ? 1 : ((s1 == d1 && s2 == d2) ? 0 : -1); +} + +/* + * store 32bit character representation into multibyte stream + */ +static inline unsigned char * +store_coded_char(unsigned char *dest, uint32 code) +{ + if (code & 0xff000000) + *dest++ = code >> 24; + if (code & 0x00ff0000) + *dest++ = code >> 16; + if (code & 0x0000ff00) + *dest++ = code >> 8; + if (code & 0x000000ff) + *dest++ = code; + return dest; +} + +/* + * Convert a character using a conversion radix tree. + * + * 'l' is the length of the input character in bytes, and b1-b4 are + * the input character's bytes. + */ +static inline uint32 +pg_mb_radix_conv(const pg_mb_radix_tree *rt, + int l, + unsigned char b1, + unsigned char b2, + unsigned char b3, + unsigned char b4) +{ + if (l == 4) + { + /* 4-byte code */ + + /* check code validity */ + if (b1 < rt->b4_1_lower || b1 > rt->b4_1_upper || + b2 < rt->b4_2_lower || b2 > rt->b4_2_upper || + b3 < rt->b4_3_lower || b3 > rt->b4_3_upper || + b4 < rt->b4_4_lower || b4 > rt->b4_4_upper) + return 0; + + /* perform lookup */ + if (rt->chars32) + { + uint32 idx = rt->b4root; + + idx = rt->chars32[b1 + idx - rt->b4_1_lower]; + idx = rt->chars32[b2 + idx - rt->b4_2_lower]; + idx = rt->chars32[b3 + idx - rt->b4_3_lower]; + return rt->chars32[b4 + idx - rt->b4_4_lower]; + } + else + { + uint16 idx = rt->b4root; + + idx = rt->chars16[b1 + idx - rt->b4_1_lower]; + idx = rt->chars16[b2 + idx - rt->b4_2_lower]; + idx = rt->chars16[b3 + idx - rt->b4_3_lower]; + return rt->chars16[b4 + idx - rt->b4_4_lower]; + } + } + else if (l == 3) + { + /* 3-byte code */ + + /* check code validity */ + if (b2 < rt->b3_1_lower || b2 > rt->b3_1_upper || + b3 < rt->b3_2_lower || b3 > rt->b3_2_upper || + b4 < rt->b3_3_lower || b4 > rt->b3_3_upper) + return 0; + + /* perform lookup */ + if (rt->chars32) + { + uint32 idx = rt->b3root; + + idx = rt->chars32[b2 + idx - rt->b3_1_lower]; + idx = rt->chars32[b3 + idx - rt->b3_2_lower]; + return rt->chars32[b4 + idx - rt->b3_3_lower]; + } + else + { + uint16 idx = rt->b3root; + + idx = rt->chars16[b2 + idx - rt->b3_1_lower]; + idx = rt->chars16[b3 + idx - rt->b3_2_lower]; + return rt->chars16[b4 + idx - rt->b3_3_lower]; + } + } + else if (l == 2) + { + /* 2-byte code */ + + /* check code validity - first byte */ + if (b3 < rt->b2_1_lower || b3 > rt->b2_1_upper || + b4 < rt->b2_2_lower || b4 > rt->b2_2_upper) + return 0; + + /* perform lookup */ + if (rt->chars32) + { + uint32 idx = rt->b2root; + + idx = rt->chars32[b3 + idx - rt->b2_1_lower]; + return rt->chars32[b4 + idx - rt->b2_2_lower]; + } + else + { + uint16 idx = rt->b2root; + + idx = rt->chars16[b3 + idx - rt->b2_1_lower]; + return rt->chars16[b4 + idx - rt->b2_2_lower]; + } + } + else if (l == 1) + { + /* 1-byte code */ + + /* check code validity - first byte */ + if (b4 < rt->b1_lower || b4 > rt->b1_upper) + return 0; + + /* perform lookup */ + if (rt->chars32) + return rt->chars32[b4 + rt->b1root - rt->b1_lower]; + else + return rt->chars16[b4 + rt->b1root - rt->b1_lower]; + } + return 0; /* shouldn't happen */ +} + +/* + * UTF8 ---> local code + * + * utf: input string in UTF8 encoding (need not be null-terminated) + * len: length of input string (in bytes) + * iso: pointer to the output area (must be large enough!) + (output string will be null-terminated) + * map: conversion map for single characters + * cmap: conversion map for combined characters + * (optional, pass NULL if none) + * cmapsize: number of entries in the conversion map for combined characters + * (optional, pass 0 if none) + * conv_func: algorithmic encoding conversion function + * (optional, pass NULL if none) + * encoding: PG identifier for the local encoding + * + * For each character, the cmap (if provided) is consulted first; if no match, + * the map is consulted next; if still no match, the conv_func (if provided) + * is applied. An error is raised if no match is found. + * + * See pg_wchar.h for more details about the data structures used here. + */ +void +tds_UtfToLocal(const unsigned char *utf, int len, + unsigned char *iso, + const pg_mb_radix_tree *map, + const pg_utf_to_local_combined *cmap, int cmapsize, + utf_local_conversion_func conv_func, + int encoding) +{ + uint32 iutf; + int l; + const pg_utf_to_local_combined *cp; + + if (!PG_VALID_ENCODING(encoding)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid encoding number: %d", encoding))); + + for (; len > 0; len -= l) + { + unsigned char b1 = 0; + unsigned char b2 = 0; + unsigned char b3 = 0; + unsigned char b4 = 0; + + /* "break" cases all represent errors */ + if (*utf == '\0') + break; + + l = pg_utf_mblen(utf); + if (len < l) + break; + + if (!pg_utf8_islegal(utf, l)) + break; + + if (l == 1) + { + /* ASCII case is easy, assume it's one-to-one conversion */ + *iso++ = *utf++; + continue; + } + + /* collect coded char of length l */ + if (l == 2) + { + b3 = *utf++; + b4 = *utf++; + } + else if (l == 3) + { + b2 = *utf++; + b3 = *utf++; + b4 = *utf++; + } + else if (l == 4) + { + b1 = *utf++; + b2 = *utf++; + b3 = *utf++; + b4 = *utf++; + } + else + { + elog(ERROR, "unsupported character length %d", l); + iutf = 0; /* keep compiler quiet */ + } + iutf = (b1 << 24 | b2 << 16 | b3 << 8 | b4); + + /* First, try with combined map if possible */ + if (cmap && len > l) + { + const unsigned char *utf_save = utf; + int len_save = len; + int l_save = l; + + /* collect next character, same as above */ + len -= l; + + l = pg_utf_mblen(utf); + if (len < l) + break; + + if (!pg_utf8_islegal(utf, l)) + break; + + /* We assume ASCII character cannot be in combined map */ + if (l > 1) + { + uint32 iutf2; + uint32 cutf[2]; + + if (l == 2) + { + iutf2 = *utf++ << 8; + iutf2 |= *utf++; + } + else if (l == 3) + { + iutf2 = *utf++ << 16; + iutf2 |= *utf++ << 8; + iutf2 |= *utf++; + } + else if (l == 4) + { + iutf2 = *utf++ << 24; + iutf2 |= *utf++ << 16; + iutf2 |= *utf++ << 8; + iutf2 |= *utf++; + } + else + { + elog(ERROR, "unsupported character length %d", l); + iutf2 = 0; /* keep compiler quiet */ + } + + cutf[0] = iutf; + cutf[1] = iutf2; + + cp = bsearch(cutf, cmap, cmapsize, + sizeof(pg_utf_to_local_combined), compare3); + + if (cp) + { + iso = store_coded_char(iso, cp->code); + continue; + } + } + + /* fail, so back up to reprocess second character next time */ + utf = utf_save; + len = len_save; + l = l_save; + } + + /* Now check ordinary map */ + if (map) + { + uint32 converted = pg_mb_radix_conv(map, l, b1, b2, b3, b4); + + if (converted) + { + iso = store_coded_char(iso, converted); + continue; + } + } + + /* if there's a conversion function, try that */ + if (conv_func) + { + uint32 converted = (*conv_func) (iutf); + + if (converted) + { + iso = store_coded_char(iso, converted); + continue; + } + } + + /* + * TSQL puts question mark '?' + * if it can not recognize the UTF8 byte or byte sequence + */ + iso = store_coded_char(iso, '?'); + } + + /* if we broke out of loop early, must be invalid input */ + if (len > 0) + report_invalid_encoding(PG_UTF8, (const char *) utf, len); + + *iso = '\0'; +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_big5/utf8_and_big5.c b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_big5/utf8_and_big5.c new file mode 100644 index 00000000000..c32423f7c6e --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_big5/utf8_and_big5.c @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * BIG5 <--> UTF8 + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/mb/conversion_procs/utf8_and_big5/utf8_and_big5.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "fmgr.h" +#include "mb/pg_wchar.h" +#include "src/backend/utils/mb/Unicode/big5_to_utf8.map" +#include "src/backend/utils/mb/Unicode/utf8_to_big5.map" + +#include "src/include/tds_int.h" + +/* ---------- + * conv_proc( + * INTEGER, -- source encoding id + * INTEGER, -- destination encoding id + * CSTRING, -- source string (null terminated C string) + * CSTRING, -- destination string (null terminated C string) + * INTEGER -- source string length + * ) returns VOID; + * ---------- + */ +void +utf8_to_big5(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *dest, int len) +{ + tds_UtfToLocal(src, len, dest, + &big5_from_unicode_tree, + NULL, 0, + NULL, + PG_BIG5); + + return; +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_gbk/utf8_and_gbk.c b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_gbk/utf8_and_gbk.c new file mode 100644 index 00000000000..bb18072588b --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_gbk/utf8_and_gbk.c @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * GBK <--> UTF8 + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/mb/conversion_procs/utf8_and_gbk/utf8_and_gbk.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "fmgr.h" +#include "mb/pg_wchar.h" +#include "src/backend/utils/mb/Unicode/gbk_to_utf8.map" +#include "src/backend/utils/mb/Unicode/utf8_to_gbk.map" + +#include "src/include/tds_int.h" + +/* ---------- + * conv_proc( + * INTEGER, -- source encoding id + * INTEGER, -- destination encoding id + * CSTRING, -- source string (null terminated C string) + * CSTRING, -- destination string (null terminated C string) + * INTEGER -- source string length + * ) returns VOID; + * ---------- + */ +void +utf8_to_gbk(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *dest, int len) +{ + tds_UtfToLocal(src, len, dest, + &gbk_from_unicode_tree, + NULL, 0, + NULL, + PG_GBK); + + return; +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_sjis/utf8_and_sjis.c b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_sjis/utf8_and_sjis.c new file mode 100644 index 00000000000..1c421fdb618 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_sjis/utf8_and_sjis.c @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * SJIS <--> UTF8 + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/mb/conversion_procs/utf8_and_sjis/utf8_and_sjis.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "fmgr.h" +#include "mb/pg_wchar.h" +#include "src/backend/utils/mb/Unicode/sjis_to_utf8.map" +#include "src/backend/utils/mb/Unicode/utf8_to_sjis.map" + +#include "src/include/tds_int.h" + +/* ---------- + * conv_proc( + * INTEGER, -- source encoding id + * INTEGER, -- destination encoding id + * CSTRING, -- source string (null terminated C string) + * CSTRING, -- destination string (null terminated C string) + * INTEGER -- source string length + * ) returns VOID; + * ---------- + */ +void +utf8_to_sjis(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *dest, int len) +{ + tds_UtfToLocal(src, len, dest, + &sjis_from_unicode_tree, + NULL, 0, + NULL, + PG_SJIS); + + return; +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_uhc/utf8_and_uhc.c b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_uhc/utf8_and_uhc.c new file mode 100644 index 00000000000..f910c0e10ba --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_uhc/utf8_and_uhc.c @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * UHC <--> UTF8 + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/mb/conversion_procs/utf8_and_uhc/utf8_and_uhc.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "fmgr.h" +#include "mb/pg_wchar.h" +#include "src/backend/utils/mb/Unicode/uhc_to_utf8.map" +#include "src/backend/utils/mb/Unicode/utf8_to_uhc.map" + +#include "src/include/tds_int.h" + +/* ---------- + * conv_proc( + * INTEGER, -- source encoding id + * INTEGER, -- destination encoding id + * CSTRING, -- source string (null terminated C string) + * CSTRING, -- destination string (null terminated C string) + * INTEGER -- source string length + * ) returns VOID; + * ---------- + */ +void +utf8_to_uhc(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *dest, int len) +{ + tds_UtfToLocal(src, len, dest, + &uhc_from_unicode_tree, + NULL, 0, + NULL, + PG_UHC); + + return; +} diff --git a/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_win/utf8_and_win.c b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_win/utf8_and_win.c new file mode 100644 index 00000000000..c9e913107fc --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_win/utf8_and_win.c @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------- + * + * WIN <--> UTF8 + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/mb/conversion_procs/utf8_and_win/utf8_and_win.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "fmgr.h" +#include "mb/pg_wchar.h" +#include "src/backend/utils/mb/Unicode/utf8_to_win1250.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1251.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1252.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1253.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1254.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1255.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1256.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1257.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win1258.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win866.map" +#include "src/backend/utils/mb/Unicode/utf8_to_win874.map" +#include "src/backend/utils/mb/Unicode/win1250_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1251_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1252_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1253_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1254_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1255_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1256_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1257_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win866_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win874_to_utf8.map" +#include "src/backend/utils/mb/Unicode/win1258_to_utf8.map" + +#include "src/include/tds_int.h" + +/* ---------- + * conv_proc( + * INTEGER, -- source encoding id + * INTEGER, -- destination encoding id + * CSTRING, -- source string (null terminated C string) + * CSTRING, -- destination string (null terminated C string) + * INTEGER -- source string length + * ) returns VOID; + * ---------- + */ + +typedef struct +{ + pg_enc encoding; + const pg_mb_radix_tree *map1; /* to UTF8 map name */ + const pg_mb_radix_tree *map2; /* from UTF8 map name */ +} pg_conv_map; + +static const pg_conv_map maps[] = { + {PG_WIN866, &win866_to_unicode_tree, &win866_from_unicode_tree}, + {PG_WIN874, &win874_to_unicode_tree, &win874_from_unicode_tree}, + {PG_WIN1250, &win1250_to_unicode_tree, &win1250_from_unicode_tree}, + {PG_WIN1251, &win1251_to_unicode_tree, &win1251_from_unicode_tree}, + {PG_WIN1252, &win1252_to_unicode_tree, &win1252_from_unicode_tree}, + {PG_WIN1253, &win1253_to_unicode_tree, &win1253_from_unicode_tree}, + {PG_WIN1254, &win1254_to_unicode_tree, &win1254_from_unicode_tree}, + {PG_WIN1255, &win1255_to_unicode_tree, &win1255_from_unicode_tree}, + {PG_WIN1256, &win1256_to_unicode_tree, &win1256_from_unicode_tree}, + {PG_WIN1257, &win1257_to_unicode_tree, &win1257_from_unicode_tree}, + {PG_WIN1258, &win1258_to_unicode_tree, &win1258_from_unicode_tree}, +}; + +void +utf8_to_win(int src_encoding, int dest_encoding, const unsigned char *src,unsigned char *dest, int len) +{ + int i; + + for (i = 0; i < lengthof(maps); i++) + { + if (dest_encoding == maps[i].encoding) + { + tds_UtfToLocal(src, len, dest, + maps[i].map2, + NULL, 0, + NULL, + dest_encoding); + return ; + } + } + + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("unexpected encoding ID %d for WIN character sets", + dest_encoding))); + + return ; +} diff --git a/contrib/babelfishpg_tds/src/include/.gitignore b/contrib/babelfishpg_tds/src/include/.gitignore new file mode 100644 index 00000000000..9317aeef477 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/.gitignore @@ -0,0 +1 @@ +/error_mapping.h diff --git a/contrib/babelfishpg_tds/src/include/err_handler.h b/contrib/babelfishpg_tds/src/include/err_handler.h new file mode 100644 index 00000000000..ebdd0d061c2 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/err_handler.h @@ -0,0 +1,51 @@ +#include "utils/elog.h" + +/* Function in err_handler.c */ +extern void emit_tds_log(ErrorData *edata); +extern void load_error_mapping(void); +extern bool get_tsql_error_details(ErrorData *edata, + int *tsql_error_code, + int *tsql_error_severity, + int *tsql_error_state, + char *error_context); +extern void reset_error_mapping_cache(void); +extern int* get_mapped_error_code_list(void); + +/* + * Structure to store key information for error mapping. + * Hash of error message along with sqlerrorcode is key here. + */ +typedef struct error_map_key{ + uint32 message_hash; /* Hash of error message */ + int sqlerrcode; /* encoded ERRSTATE of error code */ +}error_map_key; + +/* + * This linked list will be used during second level of lookup. + * i.e., when given PG error code and error message_id (untranslated error message) is not enough + * to uniquely identify the correct tsql error details. + */ +typedef struct error_map_node{ + char *error_msg_keywords; /* Unique keywords from error message to identify the correct tsql error. */ + int tsql_error_code; /* TSQL error code */ + int tsql_error_severity; /* TSQL error severity */ + struct error_map_node *next; +}error_map_node; + +/* + * Structure to store list of tsql error details for given key. + */ +typedef struct error_map{ + error_map_key key; + error_map_node *head; +}error_map; + +typedef struct error_map_details{ + char sql_state[5]; + const char *error_message; + int tsql_error_code; + int tsql_error_severity; + char *error_msg_keywords; +}error_map_details; + +typedef error_map *error_map_info; diff --git a/contrib/babelfishpg_tds/src/include/faultinjection.h b/contrib/babelfishpg_tds/src/include/faultinjection.h new file mode 100644 index 00000000000..7f1f853ff6f --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/faultinjection.h @@ -0,0 +1,81 @@ +/*------------------------------------------------------------------------- + * + * faultinjection.h + * This file contains definitions for structures and externs used + * internally by the Fault Injection Framework + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * contrib/babelfish_tds/src/include/faultinjection.h + * + *------------------------------------------------------------------------- + */ +#include "nodes/pg_list.h" + +#define FAULT_NAME_MAX_LENGTH 100 +#define INVALID_TAMPER_BYTE -1 + +typedef enum FaultInjectorType_e { + TestType = 0, + ParseHeaderType, + PreParsingType, + ParseRpcType, + PostParsingType, + InvalidType +} FaultInjectorType_e; + +typedef struct FaultInjectionType { + FaultInjectorType_e type; + char faultTypeName[FAULT_NAME_MAX_LENGTH]; + List *injected_entries; +} FaultInjectionType; + +extern FaultInjectionType FaultInjectionTypes[]; + +typedef struct FaultInjectorEntry_s { + char faultName[FAULT_NAME_MAX_LENGTH]; /* name of the fault */ + FaultInjectorType_e type; + int num_occurrences; /* 0 when diabled */ + void (*fault_callback) (void *arg); +} FaultInjectorEntry_s; + +extern const FaultInjectorEntry_s Faults[]; + +extern int tamperByte; + +#define TEST_LIST const FaultInjectorEntry_s Faults[] +#define TEST_TYPE_LIST FaultInjectionType FaultInjectionTypes[] + +/* + * Example of defining a Test Type + * + * TEST_TYPE_LIST = { + * {TestType, "Test", NIL} + * }; + */ + +/* + * Example of defining a test of previously defined type + * + * static void + * test_fault1(void *arg) + * { + * ... + * } + * + * TEST_LIST = { + * {"test_fault1", TestType, 0, &test_fault1}, + * {"", InvalidType, 0, NULL} -- keep this as last + * }; + */ + +extern bool trigger_fault_injection; +extern void TriggerFault(FaultInjectorType_e type, void *arg); + +#ifdef FAULT_INJECTOR +#define FAULT_INJECT(type, arg) TriggerFault(type, (void *) (arg)) +#else +#define FAULT_INJECT(type, arg) ((void)0) +#endif diff --git a/contrib/babelfishpg_tds/src/include/guc.h b/contrib/babelfishpg_tds/src/include/guc.h new file mode 100644 index 00000000000..82f69b80374 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/guc.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * guc.h + * This file contains extern declarations for GUCs + * used by the TDS listener. + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/guc.h + * + *------------------------------------------------------------------------- + */ + +extern int pe_port; +extern char *pe_listen_addrs; +extern char *pe_unix_socket_directories; +extern int pe_unix_socket_permissions; +extern char *pe_unix_socket_group; +extern bool tds_ssl_encrypt; +extern int tds_default_numeric_precision; +extern int tds_default_numeric_scale; +extern int32_t tds_default_protocol_version; +extern int32_t tds_default_packet_size; +extern int tds_debug_log_level; +extern bool tds_enable_db_session_property; +extern char *default_server_name; \ No newline at end of file diff --git a/contrib/babelfishpg_tds/src/include/tds.h b/contrib/babelfishpg_tds/src/include/tds.h new file mode 100644 index 00000000000..4ba140287ce --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * tds_instr.h + * + * Header file for BabelFish tds. Defining structures used to talk between the extensions + * + * Copyright (c) 2021, Amazon Web Services, Inc. or its affiliates. All Rights Reserved + *------------------------------------------------------------------------- + */ + + +/* + * When we load instrumentation extension, we create a rendezvous variable named + * "TdsInstrPlugin" that points to an instance of type TdsInstrPlugin. + * + * We use this rendezvous variable to safely share information with + * the engine even before the extension is loaded. If you call + * find_rendezvous_variable("TdsInstrPlugin") and find that *result + * is NULL, then the extension has not been loaded. If you find + * that *result is non-NULL, it points to an instance of the + * TdsInstrPlugin struct shown here. + */ +typedef struct TdsInstrPlugin +{ + /* Function pointers set up by the plugin */ + void (*tds_instr_increment_metric) (int metric); +} TdsInstrPlugin; + +extern TdsInstrPlugin **tds_instr_plugin_ptr; + +#define TDSInstrumentation(metric) \ +({ if ((tds_instr_plugin_ptr && (*tds_instr_plugin_ptr) && (*tds_instr_plugin_ptr)->tds_instr_increment_metric)) \ + (*tds_instr_plugin_ptr)->tds_instr_increment_metric(metric); \ +}) + diff --git a/contrib/babelfishpg_tds/src/include/tds_debug.h b/contrib/babelfishpg_tds/src/include/tds_debug.h new file mode 100644 index 00000000000..363a9bc0595 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_debug.h @@ -0,0 +1,113 @@ +/*------------------------------------------------------------------------- + * + * tds_debug.h + * Debugging functions/macros used internally in the TDS Listener + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tds_debug.h + * + *------------------------------------------------------------------------- + */ +#ifndef TDS_DEBUG_H +#define TDS_DEBUG_H + +#include + +#include + +#define DebugPrintBytes(_c, _s, _len) \ +do \ +{ \ + int i; \ + StringInfoData h; \ + StringInfoData a; \ + if (!TDS_DEBUG_ENABLED(TDS_DEBUG3)) \ + break; \ + initStringInfo(&h); \ + initStringInfo(&a); \ + for(i = 0; i < _len; i++) \ + { \ + if (i % 16 == 0) \ + { \ + if (i != 0) \ + { \ + /* append characters and start new line */ \ + appendStringInfo(&h, " %s\n ", a.data); \ + resetStringInfo(&a); \ + } \ + /* print the offset */ \ + appendStringInfo(&h, " %04x:", i); \ + } \ + appendStringInfo(&h, " %02x", (unsigned char)(_s)[i]); \ + if (isascii((_s)[i]) && (_s)[i] >= ' ') \ + appendStringInfoChar(&a, (_s)[i]); \ + else \ + appendStringInfoChar(&a, '.'); \ + } \ + if (i % 16 != 0) \ + { \ + while (i++ % 16 != 0) \ + appendStringInfoString(&h, " "); \ + appendStringInfo(&h, " %s", a.data); \ + } \ + if (h.len == 0) \ + appendStringInfo(&h, ""); \ + elog(LOG, "MESSAGE: %s\n %s", (_c), h.data); \ + pfree(h.data); \ + pfree(a.data); \ +} while(0) + +#define DebugPrintMessage(_c, _m) \ +do \ +{ \ + DebugPrintBytes((_c), (_m)->data, (_m->len)); \ +} while(0) + +#define DebugPrintMessageData(_c, _m) \ +do \ +{ \ + DebugPrintBytes((_c), (_m).data, (_m.len)); \ +} while(0) + +#define DebugPrintLoginMessage(_r) \ +do \ +{ \ + StringInfoData s; \ + Assert((_r) != NULL); \ + if (!TDS_DEBUG_ENABLED(TDS_DEBUG3)) \ + break; \ + initStringInfo(&s); \ + appendStringInfo(&s, "\n Login (_r) {\n"); \ + appendStringInfo(&s, " length: %u\n", (_r)->length); \ + appendStringInfo(&s, " tdsVersion: 0x%08x\n", (_r)->tdsVersion); \ + appendStringInfo(&s, " packetSize: %d\n", (_r)->packetSize); \ + appendStringInfo(&s, " optionFlags1: 0x%02x\n", (_r)->optionFlags1); \ + appendStringInfo(&s, " optionFlags2: 0x%02x\n", (_r)->optionFlags2); \ + appendStringInfo(&s, " typeFlags: 0x%02x\n", (_r)->typeFlags); \ + appendStringInfo(&s, " timezone: 0x%08x\n", (_r)->clientTimezone); \ + appendStringInfo(&s, " lcid: 0x%08x\n", (_r)->clientLcid); \ + if ((_r)->hostname != NULL) \ + appendStringInfo(&s, " hostname: %s\n", (_r)->hostname); \ + if ((_r)->username != NULL) \ + appendStringInfo(&s, " username: %s\n", (_r)->username); \ + if ((_r)->appname != NULL) \ + appendStringInfo(&s, " appname: %s\n", (_r)->appname); \ + if ((_r)->servername != NULL) \ + appendStringInfo(&s, " servername: %s\n", (_r)->servername); \ + if ((_r)->library != NULL) \ + appendStringInfo(&s, " library: %s\n", (_r)->library); \ + if ((_r)->language != NULL) \ + appendStringInfo(&s, " language: %s\n", (_r)->language); \ + if ((_r)->database != NULL) \ + appendStringInfo(&s, " database: %s\n", (_r)->database); \ + appendStringInfo(&s, "}"); \ + elog(LOG, "%s", s.data); \ + pfree(s.data); \ +} while(0) + +#endif /* TDS_DEBUG_H */ + + diff --git a/contrib/babelfishpg_tds/src/include/tds_instr.h b/contrib/babelfishpg_tds/src/include/tds_instr.h new file mode 100644 index 00000000000..1d1db21db7b --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_instr.h @@ -0,0 +1,94 @@ +/*------------------------------------------------------------------------- + * tds_instr.h + * + * Header file for BabelFish Instrumentation + * + * Copyright (c) 2021, Amazon Web Services, Inc. or its affiliates. All Rights Reserved + *------------------------------------------------------------------------- + */ + +#include "src/pltsql_instr.h" +#include "tds.h" + +typedef enum BabelFishTdsInstrMetricType { + INSTR_TDS_LOGIN_SSL = INSTR_TSQL_COUNT, + INSTR_TDS_LOGIN_END_TO_END_ENCRYPT, + INSTR_TDS_LOGIN_ACTIVE_DIRECTORY, + + INSTR_UNSUPPORTED_TDS_PRELOGIN_THREADID, + INSTR_UNSUPPORTED_TDS_PRELOGIN_INSTOPT, + INSTR_UNSUPPORTED_TDS_PRELOGIN_TRACEID, + INSTR_UNSUPPORTED_TDS_PRELOGIN_FEDAUTHREQUIRED, + INSTR_UNSUPPORTED_TDS_PRELOGIN_NONCEOPT, + INSTR_UNSUPPORTED_TDS_LOGIN_CB_SSPI_LONG, + INSTR_UNSUPPORTED_TDS_LOGIN_GSS_S_CONTINUE_NEEDED, + INSTR_UNSUPPORTED_TDS_LOGIN_NTLMSSP, + + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_BYTE_ORDER_68000, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_CHAR_EBCDIC, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_FLOAT_VAX, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_FLOAT_ND5000, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_DUMP_LOAD_OFF, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_USE_DB_ON, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_DATABASE_FATAL, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS1_SET_LANG_ON, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_LANGUAGE_FATAL, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_TRAN_BOUNDARY, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_CACHE_CONNECT, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_USER_TYPE_SERVER, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_USER_TYPE_REMUSER, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_USER_TYPE_SQLREPL, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS2_INT_SECURITY_ON, + INSTR_UNSUPPORTED_TDS_LOGIN_TYPE_FLAGS_SQL_TSQL, + INSTR_UNSUPPORTED_TDS_LOGIN_TYPE_FLAGS_READ_ONLY_INTENT, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_CHANGE_PASSWORD, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_USER_INSTANCE, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_SEND_YUKON_BINARY_XML, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_UNKNOWN_COLLATION_HANDLING, + INSTR_UNSUPPORTED_TDS_LOGIN_OPTION_FLAGS3_EXTENSION, + + INSTR_TDS_VERSION_7_0, + INSTR_TDS_VERSION_7_1, + INSTR_TDS_VERSION_7_1_1, + INSTR_TDS_VERSION_7_2, + INSTR_TDS_VERSION_7_3_A, + INSTR_TDS_VERSION_7_3_B, + INSTR_TDS_VERSION_7_4, + + INSTR_TDS_SP_EXECUTESQL, + INSTR_TDS_SP_PREPARE, + INSTR_TDS_SP_EXECUTE, + INSTR_TDS_SP_PREPEXEC, + INSTR_TDS_USER_CUSTOM_SP, + INSTR_TDS_SP_UNPREPARE, + INSTR_TDS_SP_CURSOR_OPEN, + INSTR_TDS_SP_CURSOR_EXEC, + INSTR_TDS_SP_CURSOR_PREPEXEC, + INSTR_TDS_SP_CURSOR_FETCH, + INSTR_TDS_SP_CURSOR_CLOSE, + INSTR_TDS_SP_CURSOR_UNPREPARE, + INSTR_UNSUPPORTED_TDS_SP_CURSOR, + INSTR_UNSUPPORTED_TDS_SP_CURSOROPTION, + INSTR_UNSUPPORTED_TDS_SP_CURSORPREPARE, + + INSTR_TDS_TM_REQUEST, + + INSTR_TDS_DATATYPE_VARCHAR_MAX, + INSTR_TDS_DATATYPE_NVARCHAR_MAX, + INSTR_TDS_DATATYPE_VARBINARY_MAX, + INSTR_TDS_DATATYPE_MONEY, + INSTR_TDS_DATATYPE_SMALLMONEY, + INSTR_TDS_DATATYPE_XML, + INSTR_TDS_DATATYPE_DATETIME_OFFSET, + INSTR_TDS_DATATYPE_TABLE_VALUED_PARAMETER, + INSTR_TDS_DATATYPE_SQLVARIANT, + INSTR_TDS_DATATYPE_IMAGE, + + INSTR_TDS_TOKEN_NBCROW, + INSTR_TDS_TOKEN_SSPI, + INSTR_TDS_TOKEN_TABNAME, + + INSTR_TDS_UNMAPPED_ERROR, + + INSTR_TDS_COUNT +} BabelFishTdsInstrMetricType; diff --git a/contrib/babelfishpg_tds/src/include/tds_int.h b/contrib/babelfishpg_tds/src/include/tds_int.h new file mode 100644 index 00000000000..fd67417b2a2 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_int.h @@ -0,0 +1,344 @@ +/*------------------------------------------------------------------------- + * + * tds_int.h + * This file contains definitions for structures and externs used + * internally by the TDS listener. + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tds_int.h + * + *------------------------------------------------------------------------- + */ +#ifndef TDS_INT_H +#define TDS_INT_H + +#include "datatype/timestamp.h" +#include "fmgr.h" +#include "lib/stringinfo.h" +#include "libpq/hba.h" +#include "libpq/libpq-be.h" +#include "libpq/pqcomm.h" +#include "nodes/parsenodes.h" +#include "parser/parse_node.h" +#include "nodes/params.h" +#include "tcop/dest.h" +#include "utils/memutils.h" +#include "utils/numeric.h" +#include + +#include "tds_typeio.h" +#include "guc.h" + +#include "../../contrib/babelfishpg_tsql/src/pltsql.h" +#include "../../contrib/babelfishpg_tsql/src/pltsql-2.h" + +#define TDS_PACKET_HEADER_SIZE 8 + +/* + * Default packet size for initial hand-shake - this is used only for prelogin + * SSL handshakes and login packets. + * TODO. This will vary with the SQL clients. We need a to determine the sizes + * of prelogin, SSL handshakes and login packets by inspecting the respective + * packets.. For now, just use the default. + */ +#define TDS_DEFAULT_INIT_PACKET_SIZE 4096 + +/* + * If the client sends the following packet size, we should use server default + * packet size. + */ +#define TDS_USE_SERVER_DEFAULT_PACKET_SIZE 0 + +/* default database for TSQL */ +#define TSQL_DEFAULT_DB "master" + +/* default server name */ +#define TDS_DEFAULT_SERVER_NAME "Microsoft SQL Server" + +/* TDS packet types */ +#define TDS_QUERY 0x01 +#define TDS_RPC 0x03 +#define TDS_RESPONSE 0x04 +#define TDS_TXN 0x0E +#define TDS_LOGIN7 0x10 +#define TDS_PRELOGIN 0x12 +#define TDS_ATTENTION 0x06 + +/* various flags in TDS request packet header */ +#define TDS_PACKET_HEADER_STATUS_EOM 0x01 /* end of message */ +#define TDS_PACKET_HEADER_STATUS_IGNORE 0x02 /* ignore event */ +#define TDS_PACKET_HEADER_STATUS_RESETCON 0x08 /* reset connection */ +#define TDS_PACKET_HEADER_STATUS_RESETCONSKIPTRAN 0x10 /* reset connection but + keep transaction context */ + +/* TDS prelogin option types */ +#define TDS_PRELOGIN_VERSION 0x00 +#define TDS_PRELOGIN_ENCRYPTION 0x01 +#define TDS_PRELOGIN_INSTOPT 0x02 +#define TDS_PRELOGIN_THREADID 0x03 +#define TDS_PRELOGIN_MARS 0x04 +#define TDS_PRELOGIN_TRACEID 0x05 +#define TDS_PRELOGIN_FEDAUTHREQUIRED 0x06 +#define TDS_PRELOGIN_NONCEOPT 0x07 +#define TDS_PRELOGIN_TERMINATOR 0xFF + +/* TDS Encryption values */ +#define TDS_ENCRYPT_OFF 0x00 +#define TDS_ENCRYPT_ON 0x01 +#define TDS_ENCRYPT_NOT_SUP 0x02 +#define TDS_ENCRYPT_REQ 0x03 +#define TDS_ENCRYPT_CLIENT_CERT_ENCRYPT_OFF 0x80 +#define TDS_ENCRYPT_CLIENT_CERT_ENCRYPT_ON 0x81 +#define TDS_ENCRYPT_CLIENT_CERT_ENCRYPT_REQ 0x83 + +/* TDS Environment Change IDs */ +#define TDS_ENVID_DATABASE 0x01 +#define TDS_ENVID_LANGUAGE 0x02 +#define TDS_ENVID_BLOCKSIZE 0x04 +#define TDS_ENVID_COLLATION 0x07 +#define TDS_ENVID_RESETCON 0x12 + +/* TDS Environment Change IDs */ +#define TDS_ENVID_BEGINTXN 0x08 +#define TDS_ENVID_COMMITTXN 0x09 +#define TDS_ENVID_ROLLBACKTXN 0x0a + +/* + * Macros for TDS Versions + * + * If tds_default_protocol_version is set to TDS_DEFAULT_VERSION value + * then we shall use the TDS Version that the client specifies during login. + */ +#define TDS_DEFAULT_VERSION 0 +#define TDS_VERSION_7_0 0x70000000 /* TDS version 7.0 */ +#define TDS_VERSION_7_1 0x71000000 /* TDS version 7.1 */ +#define TDS_VERSION_7_1_1 0x71000001 /* TDS version 7.1 Rev 1 */ +#define TDS_VERSION_7_2 0x72090002 /* TDS version 7.2 */ +#define TDS_VERSION_7_3_A 0x730A0003 /* TDS version 7.3A */ +#define TDS_VERSION_7_3_B 0x730B0003 /* TDS version 7.3B */ +#define TDS_VERSION_7_4 0x74000004 /* TDS version 7.4 */ + +/* + * Macros to explicitly convert host byte order to LITTLE_ENDIAN + * fashioned after the pg_hton16().. family found in port/pg_bswap.h + */ +#ifdef WORDS_BIGENDIAN + +#define htoLE16(x) pg_bswap16(x) +#define htoLE32(x) pg_bswap32(x) +#define htoLE64(x) pg_bswap64(x) +#define htoLE128(x) pg_bswap128(x) + +#define LEtoh16(x) pg_bswap16(x) +#define LEtoh32(x) pg_bswap32(x) +#define LEtoh64(x) pg_bswap64(x) +#define LEtoh128(x) pg_bswap128(x) + +#else + +#define htoLE16(x) (x) +#define htoLE32(x) (x) +#define htoLE64(x) (x) +#define htoLE128(x) (x) + +#define LEtoh16(x) (x) +#define LEtoh32(x) (x) +#define LEtoh64(x) (x) +#define LEtoh128(x) (x) + +#endif + +/* TDS type related */ +#define TDS_MAX_NUM_PRECISION 38 +#define READ_DATA(PTR, SVHDR) (VARDATA_ANY(PTR) + SVHDR) + +/* Globals */ +extern PLtsql_protocol_plugin *pltsql_plugin_handler_ptr; + +/* Globals in backend/tds/tdscomm.c */ +extern MemoryContext TdsMemoryContext; + +/* Global to store default collation info */ +extern int TdsDefaultLcid; +extern int TdsDefaultCollationFlags; +extern uint8_t TdsDefaultSortid; + +#define TDS_DEBUG1 1 +#define TDS_DEBUG2 2 +#define TDS_DEBUG3 3 + +#define TDS_DEBUG_ENABLED(level) (level <= tds_debug_log_level) + +#define TDS_DEBUG(level, ... ) do { \ +if (TDS_DEBUG_ENABLED(level)) \ + elog(LOG, __VA_ARGS__); \ +} while(0); + +#define TdsGetEncoding(collation)\ + (\ + (collation & 0xFFFFF) ? TdsLookupEncodingByLCID(collation & 0xFFFFF) : \ + TdsLookupEncodingByLCID(TdsDefaultLcid) \ + ); + +/* Structures in backend/tds/tdsprotocol.c */ +typedef struct TdsParamNameData +{ + char *name; /* name of the parameter (If there is an upperlimit, + we can use fixed size array) */ + uint8 type; /* 0: IN parameter 1: OUT parameter (TODO: INOUT parameters?) */ +} TdsParamNameData; + +typedef TdsParamNameData *TdsParamName; + +/* XXX: Should be removed */ +/* Stores mapping between TVP and underlying table */ +extern List *tvp_lookup_list; + +typedef struct TdsMessageWrapper +{ + StringInfo message; + uint8_t messageType; + uint64_t offset; +} TdsMessageWrapper; + +/* + * We store the required TDS information to gain more context if we + * encounter an error in TDS. + */ +typedef struct +{ + uint8_t reqType; /* current Tds Request Type*/ + char *phase; /* current TDS_REQUEST_PHASE_* (see above) */ + char *spType; + char *txnType; + char *err_text; /* additional errorstate info */ + +} TdsErrorContextData; + +extern TdsErrorContextData *TdsErrorContext; + + +/* Socket functions */ +typedef ssize_t (*TdsSecureSocketApi)(Port *port, void *ptr, size_t len); + +/* Functions in backend/tds/tdscomm.c */ +extern void TdsSetMessageType(uint8_t msgType); +extern void TdsCommInit(uint32_t bufferSize, + TdsSecureSocketApi secure_read, + TdsSecureSocketApi secure_write); +extern void TdsSetMessageType(uint8_t msgType); +extern void TdsCommReset(void); +extern void TdsCommShutdown(void); +extern int TdsPeekbyte(void); +extern int TdsReadNextBuffer(void); +extern int TdsSocketFlush(void); +extern int TdsGetbytes(char *s, size_t len); +extern int TdsDiscardbytes(size_t len); +extern int TdsPutbytes(void *s, size_t len); +extern int TdsPutInt8(int8_t value); +extern int TdsPutUInt8(uint8_t value); +extern int TdsPutInt16LE(int16_t value); +extern int TdsPutUInt16LE(uint16_t value); +extern int TdsPutInt32LE(int32_t value); +extern int TdsPutUInt32LE(uint32_t value); +extern int TdsPutInt64LE(int64_t value); +extern int TdsPutFloat4LE(float4 value); +extern int TdsPutFloat8LE(float8 value); +extern bool TdsCheckMessageType(uint8_t messageType); +extern int TdsReadNextRequest(StringInfo message, uint8_t *status, uint8_t *messageType); +extern int TdsReadMessage(StringInfo message, uint8_t messageType); +extern int TdsWriteMessage(StringInfo message, uint8_t messageType); +extern int TdsHandleTestQuery(StringInfo message); +extern int TdsTestProtocol(void); +extern int TdsPutUInt16LE(uint16_t value); +extern int TdsPutUInt64LE(uint64_t value); +extern int TdsPutDate(uint32_t value); + +/* Functions in backend/tds/tdslogin.c */ +extern void TdsSetBufferSize(uint32_t newSize); +extern void TdsClientAuthentication(Port *port); +extern void TdsClientInit(void); +extern void TdsSetBufferSize(uint32_t newSize); +extern int TdsProcessLogin(Port *port, bool LoadSsl); +extern void TdsSendLoginAck(Port *port); +extern uint32_t GetClientTDSVersion(void); +extern char* get_tds_login_domainname(void); + +/* Functions in backend/tds/tdsprotocol.c */ +extern int TdsSocketBackend(void); +extern void TdsProtocolInit(void); +extern void TdsProtocolFinish(void); +extern int TestGetTdsRequest(uint8_t reqType, const char* expectedStr); + +/* Functions in backend/tds/tdsrpc.c */ +extern bool TdsIsSPPrepare(void); +extern void TdsFetchInParamValues(ParamListInfo params); +extern bool TdsGetParamNames(List **); +extern int TdsGetAndSetParamIndex(const char *pname); +extern void TDSLogDuration(char *query); + +/* Functions in backend/tds/tdsutils.c */ +extern int TdsUTF8LengthInUTF16(const void *in, int len); +extern void TdsUTF16toUTF8StringInfo(StringInfo out, void *in, int len); +extern void TdsUTF8toUTF16StringInfo(StringInfo out, + const void *in, + size_t len); +extern int32_t ProcessStreamHeaders(const StringInfo message); +extern Node * TdsFindParam(ParseState *pstate, ColumnRef *cref); +extern void TdsErrorContextCallback(void *arg); + +/* Functions in backend/tds/guc.c */ +extern void TdsDefineGucs(void); + +/* Functions in backend/tds/tdspostgres.c */ +extern void TDSPostgresMain(int argc, char *argv[], + const char *dbname, const Oid dboid, + const char *username) pg_attribute_noreturn(); + +/* Functions in backend/tds/tdspostinit.c */ +extern void TDSInitPostgres(const char *in_dbname, Oid dboid, const char *username, + Oid useroid, char *out_dbname, bool override_allow_connections); + +/* Functions in backend/tds/tdspostmaster.c */ +extern void TDSBackendRun(Port *port, bool loadedSSL, char *extraOptions); + +/* Functions in backend/tds/tds_srv.c */ +extern void pe_init(void); +extern void pe_fin(void); + +/* Functions in encoding/encoding_utils.c */ +extern char *server_to_any(const char *s, int len, int encoding); + +/* Functions in backend/utils/mb/conv.c */ +extern void tds_UtfToLocal(const unsigned char *utf, int len, + unsigned char *iso, + const pg_mb_radix_tree *map, + const pg_utf_to_local_combined *cmap, int cmapsize, + utf_local_conversion_func conv_func, + int encoding); + +/* Functions in backend/utils/mb/conversion_procs */ +extern void utf8_to_win(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *result, int len); +extern void utf8_to_big5(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *result, int len); +extern void utf8_to_gbk(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *result, int len); +extern void utf8_to_uhc(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *result, int len); +extern void utf8_to_sjis(int src_encoding, int dest_encoding, const unsigned char *src, unsigned char *result, int len); + +/* Functions in backend/utils/adt/numeric.c */ +extern Numeric TdsSetVarFromStrWrapper(const char *str); +extern int32_t numeric_get_typmod(Numeric num); + +/* Functions in backend/utils/adt/varchar.c */ +extern void *tds_varchar_input(const char *s, size_t len, int32 atttypmod); + +/* Functions in backend/utils/adt/xml.c */ +extern void tds_xmlFreeDoc(void *doc); +extern void *tds_xml_parse(text *data, int xmloption_arg, bool preserve_whitespace, + int encoding); +extern int tds_parse_xml_decl(const xmlChar *str, size_t *lenp, + xmlChar **version, xmlChar **encoding, int *standalone); + +#endif /* TDS_INT_H */ diff --git a/contrib/babelfishpg_tds/src/include/tds_iofuncmap.h b/contrib/babelfishpg_tds/src/include/tds_iofuncmap.h new file mode 100644 index 00000000000..75f101fced6 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_iofuncmap.h @@ -0,0 +1,156 @@ +/*------------------------------------------------------------------------- + * + * tds_iofuncmap.h + * TDS Listener Type Input Output function numbers + * + * !!! Do not add anything but simple #define TOKEN value + * constructs to this file. It is used in the SQL input + * for the babelfishpg_tsql extension. Anything you + * might want to add here belongs into tds_typeio.h. + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/tds_iofuncmap.h + * + *------------------------------------------------------------------------- + */ +#ifndef TDS_IOFUNCMAP_H +#define TDS_IOFUNCMAP_H + +#define TDS_SEND_INVALID 0 + +#define TDS_SEND_BIT 1 +#define TDS_SEND_TINYINT 2 +#define TDS_SEND_SMALLINT 3 +#define TDS_SEND_INTEGER 4 +#define TDS_SEND_BIGINT 5 +#define TDS_SEND_FLOAT4 6 +#define TDS_SEND_FLOAT8 7 +#define TDS_SEND_CHAR 8 +#define TDS_SEND_NVARCHAR 9 +#define TDS_SEND_VARCHAR 10 +#define TDS_SEND_DATE 11 +#define TDS_SEND_DATETIME 12 +#define TDS_SEND_MONEY 13 +#define TDS_SEND_SMALLMONEY 14 +#define TDS_SEND_NCHAR 15 +#define TDS_SEND_TEXT 16 +#define TDS_SEND_NTEXT 17 +#define TDS_SEND_NUMERIC 18 +#define TDS_SEND_SMALLDATETIME 19 +#define TDS_SEND_BINARY 20 +#define TDS_SEND_VARBINARY 21 +#define TDS_SEND_UNIQUEIDENTIFIER 22 +#define TDS_SEND_TIME 23 +#define TDS_SEND_DATETIME2 24 +#define TDS_SEND_IMAGE 25 +#define TDS_SEND_XML 26 +#define TDS_SEND_SQLVARIANT 28 +#define TDS_SEND_DATETIMEOFFSET 29 + +#define TDS_RECV_INVALID 0 +#define TDS_RECV_BIT 1 +#define TDS_RECV_TINYINT 2 +#define TDS_RECV_SMALLINT 3 +#define TDS_RECV_INTEGER 4 +#define TDS_RECV_BIGINT 5 +#define TDS_RECV_FLOAT4 6 +#define TDS_RECV_FLOAT8 7 +#define TDS_RECV_CHAR 8 +#define TDS_RECV_NVARCHAR 9 +#define TDS_RECV_VARCHAR 10 +#define TDS_RECV_DATE 11 +#define TDS_RECV_DATETIME 12 +#define TDS_RECV_MONEY 13 +#define TDS_RECV_SMALLMONEY 14 +#define TDS_RECV_NCHAR 15 +#define TDS_RECV_TEXT 16 +#define TDS_RECV_NTEXT 17 +#define TDS_RECV_NUMERIC 18 +#define TDS_RECV_SMALLDATETIME 19 +#define TDS_RECV_BINARY 20 +#define TDS_RECV_VARBINARY 21 +#define TDS_RECV_UNIQUEIDENTIFIER 22 +#define TDS_RECV_TIME 23 +#define TDS_RECV_DATETIME2 24 +#define TDS_RECV_IMAGE 25 +#define TDS_RECV_XML 26 +#define TDS_RECV_TABLE 27 +#define TDS_RECV_SQLVARIANT 28 +#define TDS_RECV_DATETIMEOFFSET 29 + +/* + * Supported TDS data types + * + * Caution: these must be specified in decimal to be processed by + * contrib/babelfishpg_tsql/sql/datatype.sql + */ +#define TDS_TYPE_TEXT 35 /* 0x23 */ +#define TDS_TYPE_UNIQUEIDENTIFIER 36 /* 0x24 */ +#define TDS_TYPE_INTEGER 38 /* 0x26 */ +#define TDS_TYPE_NTEXT 99 /* 0x63 */ +#define TDS_TYPE_BIT 104 /* 0x68 */ +#define TDS_TYPE_FLOAT 109 /* 0x6D */ +#define TDS_TYPE_VARCHAR 167 /* 0xA7 */ +#define TDS_TYPE_NVARCHAR 231 /* 0xE7 */ +#define TDS_TYPE_NCHAR 239 /* 0xEF */ +#define TDS_TYPE_MONEYN 110 /* 0x6E */ +#define TDS_TYPE_CHAR 175 /* 0xAF */ +#define TDS_TYPE_DATE 40 /* 0x28 */ +#define TDS_TYPE_DATETIMEN 111 /* 0x6F */ +#define TDS_TYPE_NUMERICN 108 /* 0x6C */ +#define TDS_TYPE_XML 241 /* 0xf1 */ +#define TDS_TYPE_DECIMALN 106 /* 0x6A */ +#define TDS_TYPE_VARBINARY 165 /* 0xA5 */ +#define TDS_TYPE_BINARY 173 /* 0xAD */ +#define TDS_TYPE_IMAGE 34 /* 0x22 */ +#define TDS_TYPE_TIME 41 /* 0x29 */ +#define TDS_TYPE_DATETIME2 42 /* 0x2A */ +#define TDS_TYPE_TABLE 243 /* 0xF3 */ +#define TDS_TYPE_SQLVARIANT 98 /* 0x62 */ +#define TDS_TYPE_DATETIMEOFFSET 43 /* 0x2B */ + +/* + * macros for supporting sqlvariant datatype on TDS side + */ +#define VARIANT_HEADER 12 +#define VARIANT_TYPE_TINYINT 48 +#define VARIANT_TYPE_BIT 50 +#define VARIANT_TYPE_SMALLINT 52 +#define VARIANT_TYPE_INT 56 +#define VARIANT_TYPE_BIGINT 127 +#define VARIANT_TYPE_REAL 59 +#define VARIANT_TYPE_FLOAT 62 +#define VARIANT_TYPE_NUMERIC 108 +#define VARIANT_TYPE_MONEY 60 +#define VARIANT_TYPE_SMALLMONEY 122 +#define VARIANT_TYPE_DATE 40 +#define VARIANT_TYPE_CHAR 175 +#define VARIANT_TYPE_VARCHAR 167 +#define VARIANT_TYPE_NCHAR 239 +#define VARIANT_TYPE_NVARCHAR 231 +#define VARIANT_TYPE_BINARY 173 +#define VARIANT_TYPE_VARBINARY 165 +#define VARIANT_TYPE_UNIQUEIDENTIFIER 36 +#define VARIANT_TYPE_TIME 41 +#define VARIANT_TYPE_SMALLDATETIME 58 +#define VARIANT_TYPE_DATETIME 61 +#define VARIANT_TYPE_DATETIME2 42 +#define VARIANT_TYPE_DATETIMEOFFSET 43 + +/* + * TDS Data types' max len + */ +#define TDS_MAXLEN_TINYINT 1 +#define TDS_MAXLEN_SMALLINT 2 +#define TDS_MAXLEN_INT 4 +#define TDS_MAXLEN_BIGINT 8 +#define TDS_MAXLEN_BIT 1 +#define TDS_MAXLEN_FLOAT4 4 +#define TDS_MAXLEN_FLOAT8 8 +#define TDS_MAXLEN_NUMERIC 17 +#define TDS_MAXLEN_UNIQUEIDENTIFIER 16 + +#endif /* TDS_IOFUNCMAP_H */ diff --git a/contrib/babelfishpg_tds/src/include/tds_protocol.h b/contrib/babelfishpg_tds/src/include/tds_protocol.h new file mode 100644 index 00000000000..96f7c74b980 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_protocol.h @@ -0,0 +1,81 @@ +/*------------------------------------------------------------------------- + * + * tds_protocol.h + * Definitions for TDS protocol related structures and functions + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tds_protocol.h + * + *------------------------------------------------------------------------- + */ + +#ifndef TDS_PROTOCOL_H +#define TDS_PROTOCOL_H + +#include "src/include/tds_request.h" + +/* + * Once the TDS login handshake is done, the backend should be in any of the + * following phasees: + * + * TDS_REQUEST_PHASE_INIT -- initial state + * TDS_REQUEST_PHASE_FETCH -- fetch a TDS packet and generate a TDSRequest + * TDS_REQUEST_PHASE_PROCESS -- process the request + * TDS_REQUEST_PHASE_FLUSH -- flush the response + * TDS_REQUEST_PHASE_ERROR -- handle error + * + * Once the login handshake is done, the backend enters the initial state. After + * that it goes into the following loop: + * + * Step INIT: Initializations. Currently, it's a no-op. + * Goto step FETCH + * + * Step Fetch: Fetch and parse a new TDS packet and generate a TDSRequest. + * Goto step ERROR in case of any error via elog() + * Goto step PROCESS otherwise + * + * Step PROCESS: Process the request and generate next libpq request (if any) + * that will be sent to the TCOP loop. + * Remain in step PROCESS if a libpq request is generated and return + * to TCOP loop + * Goto step ERROR in case of any error via elog() + * Goto step FLUSH if processing of the request is complete + * + * Step FLUSH: Flush the response (call TdsFlush), reset the request + * context and perform other cleanups (if any). Any error + * in this step will violate the TDS protocol. + * Goto step ERROR in case of any error via elog() (should be FATAL?) + * Goto step Fetch + * + * Step ERROR: Must have generated at least one error token before going + * into this phase. Generate more error tokens if required. + * Goto step FLUSH + */ +#define TDS_REQUEST_PHASE_INIT 0 +#define TDS_REQUEST_PHASE_FETCH 1 +#define TDS_REQUEST_PHASE_PROCESS 2 +#define TDS_REQUEST_PHASE_FLUSH 3 +#define TDS_REQUEST_PHASE_ERROR 4 + +/* + * We store the information required to process each phase in the following + * structure. + */ +typedef struct +{ + MemoryContext requestContext; /* temporary request context */ + TDSRequest request; /* current request in-progress */ + uint8_t phase; /* current TDS_REQUEST_PHASE_* (see above) */ + uint8_t status; /* current status of the request */ + + /* denotes whether we've sent at least one done token */ + bool isEmptyResponse; + +} TdsRequestCtrlData; + +extern TdsRequestCtrlData *TdsRequestCtrl; + +#endif /* TDS_PROTOCOL_H */ diff --git a/contrib/babelfishpg_tds/src/include/tds_request.h b/contrib/babelfishpg_tds/src/include/tds_request.h new file mode 100644 index 00000000000..a4e9d5648bb --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_request.h @@ -0,0 +1,865 @@ +/*------------------------------------------------------------------------- + * + * tds_request.h + * This file contains definitions for structures and externs used + * for processing a TDS request. + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tds_request.h + * + *------------------------------------------------------------------------- + */ +#ifndef TDS_REQUEST_H +#define TDS_REQUEST_H + +#include "postgres.h" + +#include "lib/stringinfo.h" +#include "miscadmin.h" + +#include "src/include/tds_int.h" +#include "src/include/tds_typeio.h" +#include "src/collation.h" + +/* Different TDS request types returned by GetTDSRequest() */ +typedef enum TDSRequestType +{ + TDS_REQUEST_SQL_BATCH = 1, /* a simple SQL batch */ + TDS_REQUEST_SP_NUMBER = 2, /* numbered SP like sp_execute */ + TDS_REQUEST_TXN_MGMT = 3, /* transaction management request */ + TDS_REQUEST_ATTN /* attention request */ +} TDSRequestType; + +/* Simple SQL batch */ +typedef struct TDSRequestSQLBatchData +{ + TDSRequestType reqType; + StringInfoData query; +} TDSRequestSQLBatchData; +typedef TDSRequestSQLBatchData *TDSRequestSQLBatch; + +/* + * TODO: Use below values as an ENUM, rather than MACRO + * Enum will flag out compile time error if any condition is missed + */ +#define SP_CURSOR 1 +#define SP_CURSOROPEN 2 +#define SP_CURSORPREPARE 3 +#define SP_CURSOREXEC 4 +#define SP_CURSORPREPEXEC 5 +#define SP_CURSORUNPREPARE 6 +#define SP_CURSORFETCH 7 +#define SP_CURSOROPTION 8 +#define SP_CURSORCLOSE 9 +#define SP_EXECUTESQL 10 +#define SP_PREPARE 11 +#define SP_EXECUTE 12 +#define SP_PREPEXEC 13 +#define SP_PREPEXECRPC 14 +#define SP_UNPREPARE 15 +#define SP_CUSTOMTYPE 16 + +/* Check if retStatus Not OK */ +#define CheckPLPStatusNotOKForTVP(temp, retStatus) \ +do \ +{ \ + if (retStatus != STATUS_OK) \ + { \ + ereport(ERROR, \ + (errcode(ERRCODE_PROTOCOL_VIOLATION), \ + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " \ + "Table-valued parameter %d (\"%s\"), row %d, column %d: The chunking format is incorrect for a " \ + "large object parameter of data type 0x%02X.", \ + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->tvpInfo->rowCount, \ + i + 1, temp->tvpInfo->colMetaData[i].columnTdsType))); \ + } \ +} while(0) + +int ReadPlp(ParameterToken temp, StringInfo message, uint64_t *mainOffset); +/* Numbered Stored Procedure like sp_prepexec, sp_execute */ +typedef struct TDSRequestSPData +{ + TDSRequestType reqType; + uint16_t spType; + uint16_t spFlags; + + StringInfoData name; + + uint32 handle; /* handle corresponding to this SP request */ + uint32 cursorHandle; /* cursor handle corresponding to this SP_CURSOR* request */ + + /* cursor prepared handle corresponding to SP_CURSOR[prepare/prepexec/exec] request */ + uint32_t cursorPreparedHandle; + + /* + * parameter points to the head of the ParameterToken linked List. + * Each ParameterToken contains all the data pertaining to the parameter. + */ + ParameterToken parameter; + + /* + * Pointer to request data, while parsing we don't copy the actual data + * but just store the dataOffset and len fields in the Parametertoken + * structure + */ + char *messageData; + uint64_t batchSeparatorOffset; + int messageLen; + + /* + * Below three fields are just a place holder for keeping the addresses + * of Parameter query & data token separate, so that during processing + * this can be used directly + */ + ParameterToken queryParameter; + ParameterToken dataParameter; + ParameterToken handleParameter; + + StringInfo metaDataParameterValue; + + /* + * cursor parameters - all parameters except cursorHandleParameter have different + * meanings w.r.t the type of the cursor request (check GetTDSRequest() for their + * respective meaning). + */ + ParameterToken cursorPreparedHandleParameter; + ParameterToken cursorHandleParameter; + ParameterToken cursorExtraArg1; + ParameterToken cursorExtraArg2; + ParameterToken cursorExtraArg3; + + /* number of total dataParameters */ + uint16 nTotalParams; + + /* number of OUT dataParameters */ + uint16 nOutParams; + + /* + * cursor scorll option and concurrency control options (only valid for + * sp_cursoropen packet) + */ + int scrollopt; + int ccopt; + + /* + * TODO: Use as local variable rather than part of the structure + */ + Datum *boundParamsData; + char *boundParamsNullList; + Oid *boundParamsOidList; + + uint16 nTotalBindParams; + + /* True, if this is a stored procedure */ + bool isStoredProcedure; + + /* + * we store the OUT dataParameter pointers in the following array so that + * they can be accessed directly given their index. + */ + ParameterToken *idxOutParams; + + /* + * In case when parameter names aren't specified by the application, + * then use paramIndex for maintaining the paramIndex which is used + * by Engine + */ + int paramIndex; +} TDSRequestSPData; +typedef TDSRequestSPData *TDSRequestSP; + +/* Default handle value for a RPC request which doesn't use any handle */ +/* + * TODO: Check and correct the values for SP_HANDLE_INVALID + * and SP_CURSOR_HANDLE_INVALID + */ +#define SP_HANDLE_INVALID 0 +#define SP_CURSOR_PREPARED_HANDLE_START 1073741824 +#define SP_CURSOR_PREPARED_HANDLE_INVALID 0xFFFFFFFF +#define SP_CURSOR_HANDLE_INVALID 180150000 + +/* During parse, we should always send the base type OID if it exists. */ +#define SetParamMetadataCommonInfo(paramMeta, finfo) \ +do { \ + (paramMeta)->pgTypeOid = (finfo->ttmbasetypeid != InvalidOid) ? \ + finfo->ttmbasetypeid : finfo->ttmtypeid; \ + (paramMeta)->sendFunc = finfo->sendFuncPtr; \ +} while(0); + +#define GetPgOid(pgTypeOid, finfo) \ +do { \ + pgTypeOid = (finfo->ttmbasetypeid != InvalidOid) ? \ + finfo->ttmbasetypeid : finfo->ttmtypeid; \ +} while(0); + +/* Macro used to check if Next RPC Packet Exists. */ +#define RPCBatchExists(sp) (sp.batchSeparatorOffset < sp.messageLen) + +/* Transaction management request */ +typedef struct TDSRequestTxnMgmtData +{ + TDSRequestType reqType; + uint16_t txnReqType; + StringInfoData txnName; + uint8_t isolationLevel; + StringInfoData query; + + /* Commit/rollback requests can have optional begin transaction */ + struct TDSRequestTxnMgmtData *nextTxn; + +} TDSRequestTxnMgmtData; +typedef TDSRequestTxnMgmtData *TDSRequestTxnMgmt; + +typedef union TDSRequestData +{ + TDSRequestType reqType; + TDSRequestSQLBatchData sqlBatch; + TDSRequestSPData sp; + TDSRequestTxnMgmtData txnMgmt; +} TDSRequestData; +typedef TDSRequestData *TDSRequest; + +/* COLMETADATA flags */ +#define TDS_COLMETA_NULLABLE 0x01 +#define TDS_COLMETA_CASESEN 0x02 +#define TDS_COLMETA_UPD_RO 0x00 +#define TDS_COLMETA_UPD_RW 0x04 +#define TDS_COLMETA_UPD_UNKNOWN 0x08 +#define TDS_COLMETA_IDENTITY 0x10 +#define TDS_COLMETA_COMPUTED 0x20 + +#define TDS_COL_METADATA_DEFAULT_FLAGS TDS_COLMETA_NULLABLE | \ + TDS_COLMETA_UPD_UNKNOWN + +/* Macro for TVP tokens. */ +#define TVP_ROW_TOKEN 0x01 +#define TVP_NULL_TOKEN 0xFFFF +#define TVP_ORDER_UNIQUE_TOKEN 0x10 +#define TVP_COLUMN_ORDERING_TOKEN 0x11 +#define TVP_END_TOKEN 0x00 + +static inline void +SetTvpRowData(ParameterToken temp, const StringInfo message, uint64_t *offset) +{ + TvpColMetaData *colmetadata = temp->tvpInfo->colMetaData; + TvpRowData *rowData = NULL; + char *messageData = message->data; + int retStatus = 0; + temp->tvpInfo->rowCount = 0; + while(messageData[*offset] == TVP_ROW_TOKEN) /* Loop over each row. */ + { + int i = 0; /* Current Column Number. */ + + if (rowData == NULL) /* First Row. */ + { + rowData = palloc0(sizeof(TvpRowData)); + temp->tvpInfo->rowData = rowData; + } + else + { + TvpRowData *temp = palloc0(sizeof(TvpRowData)); + rowData->nextRow = temp; + rowData = temp; + } + + rowData->columnValues = palloc0(temp->tvpInfo->colCount * sizeof(StringInfoData)); + rowData->isNull = palloc0(temp->tvpInfo->colCount); + (*offset)++; + + while(i != temp->tvpInfo->colCount) /* Loop over each column. */ + { + initStringInfo(&rowData->columnValues[i]); + rowData->isNull[i] = 'f'; + temp->tvpInfo->rowCount += 1; + switch(colmetadata[i].columnTdsType) + { + case TDS_TYPE_INTEGER: + case TDS_TYPE_BIT: + case TDS_TYPE_FLOAT: + case TDS_TYPE_TIME: + case TDS_TYPE_DATE: + case TDS_TYPE_DATETIME2: + case TDS_TYPE_DATETIMEN: + case TDS_TYPE_MONEYN: + case TDS_TYPE_UNIQUEIDENTIFIER: + { + rowData->columnValues[i].len = messageData[(*offset)++]; + if (rowData->columnValues[i].len == 0) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + if (rowData->columnValues[i].len > colmetadata[i].maxLen) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X has an invalid data length or metadata length.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->tvpInfo->rowCount, i + 1, colmetadata[i].columnTdsType))); + memcpy(rowData->columnValues[i].data, &messageData[*offset], rowData->columnValues[i].len); + *offset += rowData->columnValues[i].len; + } + break; + case TDS_TYPE_NUMERICN: + case TDS_TYPE_DECIMALN: + { + if (colmetadata[i].scale > colmetadata[i].precision) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"): row %d, column %d: The supplied value is not a valid instance of data type Numeric/Decimal. " + "Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->tvpInfo->rowCount, i + 1))); + rowData->columnValues[i].len = messageData[(*offset)++]; + if (rowData->columnValues[i].len == 0) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + if (rowData->columnValues[i].len > colmetadata[i].maxLen) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X has an invalid data length or metadata length.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->tvpInfo->rowCount, i + 1, colmetadata[i].columnTdsType))); + + memcpy(rowData->columnValues[i].data, &messageData[*offset], rowData->columnValues[i].len); + *offset += rowData->columnValues[i].len; + } + break; + + case TDS_TYPE_CHAR: + case TDS_TYPE_VARCHAR: + case TDS_TYPE_NCHAR: + case TDS_TYPE_NVARCHAR: + case TDS_TYPE_BINARY: + case TDS_TYPE_VARBINARY: + { + if (colmetadata[i].maxLen != 0xffff) + { + memcpy(&rowData->columnValues[i].len, &messageData[*offset], sizeof(short)); + *offset += sizeof(short); + rowData->columnValues[i].maxlen = colmetadata[i].maxLen; + if (rowData->columnValues[i].len != 0xffff) + { + char * value; + + if (rowData->columnValues[i].len > rowData->columnValues[i].maxlen) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X has an invalid data length or metadata length.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->tvpInfo->rowCount, i + 1, colmetadata[i].columnTdsType))); + value = palloc(rowData->columnValues[i].len); + memcpy(value, &messageData[*offset], rowData->columnValues[i].len); + rowData->columnValues[i].data = value; + *offset += rowData->columnValues[i].len; + if (colmetadata[i].columnTdsType == TDS_TYPE_NVARCHAR) + { + StringInfo tempStringInfo = palloc( sizeof(StringInfoData)); + initStringInfo(tempStringInfo); + TdsUTF16toUTF8StringInfo(tempStringInfo, value,rowData->columnValues[i].len); + rowData->columnValues[i] = *tempStringInfo; + } + } + else + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + } + else + { + retStatus = ReadPlp(temp, message, offset); + CheckPLPStatusNotOKForTVP(temp, retStatus); + if (temp->isNull) + { + rowData->isNull[i] = 'n'; + } + rowData->columnValues[i] = *(TdsGetPlpStringInfoBufferFromToken(messageData, temp)); + if (colmetadata[i].columnTdsType == TDS_TYPE_NVARCHAR) + { + StringInfo tempStringInfo = palloc(sizeof(StringInfoData)); + initStringInfo(tempStringInfo); + TdsUTF16toUTF8StringInfo(tempStringInfo, rowData->columnValues[i].data,rowData->columnValues[i].len); + rowData->columnValues[i] = *tempStringInfo; + } + temp->isNull = false; + } + } + break; + case TDS_TYPE_XML: + { + retStatus = ReadPlp(temp, message, offset); + CheckPLPStatusNotOKForTVP(temp, retStatus); + if (temp->isNull) + { + rowData->isNull[i] = 'n'; + i++; + temp->isNull = false; + continue; + } + rowData->columnValues[i] = *(TdsGetPlpStringInfoBufferFromToken(messageData, temp)); + } + break; + case TDS_TYPE_SQLVARIANT: + { + memcpy(&rowData->columnValues[i].len, &messageData[*offset], sizeof(uint32_t)); + *offset += sizeof(uint32_t); + + if (rowData->columnValues[i].len == 0) + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + if (rowData->columnValues[i].len > colmetadata[i].maxLen) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X has an invalid data length or metadata length.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->tvpInfo->rowCount, i + 1, colmetadata[i].columnTdsType))); + memcpy(rowData->columnValues[i].data, &messageData[*offset], rowData->columnValues[i].len); + *offset += rowData->columnValues[i].len; + } + break; + } + i++; + } + } + if (messageData[*offset] != TVP_END_TOKEN) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type) " + "unexpected token encountered processing a table-valued parameter.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, temp->tvpInfo->rowCount, temp->tvpInfo->colCount, temp->type))); + (*offset)++; +} +static inline void +SetColMetadataForTvp(ParameterToken temp,const StringInfo message, uint64_t *offset) +{ + uint8_t len; + uint16 colCount; + uint16 isTvpNull; + char *tempString; + int i = 0; + char *messageData = message->data; + StringInfo tempStringInfo = palloc( sizeof(StringInfoData)); + temp->tvpInfo->tvpTypeName = " "; + + /* Database-Name.Schema-Name.TableType-Name */ + for(; i < 3; i++) + { + len = messageData[(*offset)++]; + if (len != 0) + { + /* Database name not allowed in a TVP */ + if (i ==0) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type) " + "has a non-zero length database name specified. Database name is not allowed with a table-valued parameter, " + "only schema name and type name are valid.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, 1, 1, temp->type))); + initStringInfo(tempStringInfo); + + tempString = palloc0(len * 2); + memcpy(tempString, &messageData[*offset], len * 2); + TdsUTF16toUTF8StringInfo(tempStringInfo, tempString,len * 2); + + *offset += len * 2; + temp->len += len; + + temp->tvpInfo->tvpTypeName = psprintf("%s.%s", temp->tvpInfo->tvpTypeName, tempStringInfo->data); + } + else if (i == 2) + { + /* Throw error if TabelType-Name is not provided */ + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d, to a parameterized string has no table type defined.", + temp->paramOrdinal + 1))); + } + } + temp->tvpInfo->tableName = tempStringInfo->data; + i = 0; + + temp->tvpInfo->tvpTypeName += 2; + + memcpy(&isTvpNull, &messageData[*offset], sizeof(uint16)); + if (isTvpNull != TVP_NULL_TOKEN) + { + /* + * TypeColumnMetaData = UserType Flags TYPE_INFO ColName ; + */ + TvpColMetaData *colmetadata; + memcpy(&colCount, &messageData[*offset], sizeof(uint16)); + colmetadata = palloc0(colCount * sizeof(TvpColMetaData)); + temp->tvpInfo->colCount = colCount; + *offset += sizeof(uint16); + + temp->isNull = false; + + while(i != colCount) + { + if (((*offset) + sizeof(uint32_t) > message->len)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X " + "(user-defined table type) has an invalid column count specified.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, 1, i + 1, temp->type))); + + /* UserType */ + memcpy(&colmetadata[i].userType, &messageData[*offset], sizeof(uint32_t)); + *offset += sizeof(uint32_t); + /* Flags */ + memcpy(&colmetadata[i].flags, &messageData[*offset], sizeof(uint32)); + *offset += sizeof(uint16); + + /* TYPE_INFO */ + colmetadata[i].columnTdsType = messageData[(*offset)++]; + switch(colmetadata[i].columnTdsType) + { + case TDS_TYPE_INTEGER: + case TDS_TYPE_BIT: + case TDS_TYPE_FLOAT: + case TDS_TYPE_MONEYN: + case TDS_TYPE_DATETIMEN: + case TDS_TYPE_UNIQUEIDENTIFIER: + colmetadata[i].maxLen = messageData[(*offset)++]; + break; + case TDS_TYPE_DECIMALN: + case TDS_TYPE_NUMERICN: + colmetadata[i].maxLen = messageData[(*offset)++]; + colmetadata[i].precision = messageData[(*offset)++]; + colmetadata[i].scale = messageData[(*offset)++]; + break; + // case DATETIMEOFFSET: /* TODO, not implemented yet */ + case TDS_TYPE_CHAR: + case TDS_TYPE_VARCHAR: + case TDS_TYPE_NCHAR: + case TDS_TYPE_NVARCHAR: + { + memcpy(&colmetadata[i].maxLen, &messageData[*offset], sizeof(uint16)); + *offset += sizeof(uint16); + if (colmetadata[i].maxLen == 0xffff) + { + memcpy(&colmetadata[i].collation, &messageData[*offset], sizeof(uint32_t)); + *offset += sizeof(uint32_t); + colmetadata[i].sortId = messageData[(*offset)++]; + } + else + { + memcpy(&colmetadata[i].collation, &messageData[*offset], sizeof(uint32_t)); + *offset += sizeof(uint32_t); + colmetadata[i].sortId = messageData[(*offset)++]; + } + } + break; + case TDS_TYPE_XML: + { + colmetadata[i].maxLen = messageData[(*offset)++]; + } + break; + case TDS_TYPE_DATETIME2: + { + colmetadata[i].scale = messageData[(*offset)++]; + colmetadata[i].maxLen = 8; + } + break; + case TDS_TYPE_TIME: + { + colmetadata[i].scale = messageData[(*offset)++]; + colmetadata[i].maxLen = 5; + } + break; + case TDS_TYPE_BINARY: + case TDS_TYPE_VARBINARY: + { + uint16 plp; + memcpy(&plp, &messageData[*offset], sizeof(uint16)); + *offset += sizeof(uint16); + colmetadata[i].maxLen = plp; + } + break; + case TDS_TYPE_DATE: + colmetadata[i].maxLen = 3; + break; + case TDS_TYPE_SQLVARIANT: + memcpy(&colmetadata[i].maxLen, &messageData[*offset], sizeof(uint32_t)); + *offset += sizeof(uint32_t); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X is unknown.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, 1, i + 1, colmetadata[i].columnTdsType))); + } + + if ((colmetadata[i].flags & TDS_COLMETA_COMPUTED) && ((messageData[*offset] == TVP_ORDER_UNIQUE_TOKEN) || + (messageData[*offset] == TVP_COLUMN_ORDERING_TOKEN))) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type). " + "The specified column is computed or default and has ordering or uniqueness set. Ordering and uniqueness " + "can only be set on columns that have client supplied data.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, 1, i + 1, colmetadata[i].columnTdsType))); + if (messageData[*offset] != TVP_END_TOKEN) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type) " + "unexpected token encountered processing a table-valued parameter.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, 1, i + 1, colmetadata[i].columnTdsType))); + i++; + (*offset)++; + } + temp->tvpInfo->colMetaData = colmetadata; /* Setting the column metadata in paramtoken. */ + + /* TODO Optional Metadata token:- [TVP_ORDER_UNIQUE] */ + if (messageData[*offset] == TVP_ORDER_UNIQUE_TOKEN) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Order unique token for TVP is not currently supported in Babelfish"))); + + /* TODO Optional Metadata token:- [TVP_COLUMN_ORDERING_TOKEN] */ + if (messageData[*offset] == TVP_COLUMN_ORDERING_TOKEN) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Column ordering token for TVP is not currently supported in Babelfish"))); + + if (messageData[*offset] != TVP_END_TOKEN) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. " + "Table-valued parameter %d (\"%s\"), row %d, column %d: Data type 0x%02X (user-defined table type) " + "unexpected token encountered processing a table-valued parameter.", + temp->paramOrdinal + 1, temp->paramMeta.colName.data, 1, i + 1, colmetadata[i].columnTdsType))); + (*offset)++; + } + else + { + temp->isNull = true; /* If TVP is NULL. */ + (*offset) += 2; + } + SetTvpRowData(temp, message, offset); +} + +static inline void +SetColMetadataForFixedType(TdsColumnMetaData *col, uint8_t tdsType, uint8_t maxSize) +{ + col->sizeLen = 1; + col->metaLen = sizeof(col->metaEntry.type1); + col->metaEntry.type1.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type1.tdsTypeId = tdsType; + col->metaEntry.type1.maxSize = maxSize; +} + +static inline void +SetColMetadataForCharType(TdsColumnMetaData *col, uint8_t tdsType, uint32_t codePage, + pg_enc encoding, uint16_t codeFlags, uint8_t sortId, + uint16_t maxSize) +{ + col->sizeLen = 2; + col->metaLen = sizeof(col->metaEntry.type2); + col->metaEntry.type2.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type2.tdsTypeId = tdsType; + col->metaEntry.type2.maxSize = maxSize; + + col->metaEntry.type2.collationInfo = codePage | (codeFlags << 20); + col->metaEntry.type2.charSet = sortId; + col->encoding = encoding; +} + +static inline void +SetColMetadataForTextType(TdsColumnMetaData *col, uint8_t tdsType, uint32_t codePage, + pg_enc encoding, uint16_t codeFlags, uint8_t sortId, + uint32_t maxSize) +{ + col->sizeLen = 3; + col->sendTableName = true; + col->metaLen = sizeof(col->metaEntry.type3); + col->metaEntry.type3.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type3.tdsTypeId = tdsType; + /* TODO: Remove the hardcoding :- BABEL-298 */ + col->metaEntry.type3.maxSize = 0x7fffffff; + + col->metaEntry.type3.collationInfo = codePage | (codeFlags << 20); + col->metaEntry.type3.charSet = sortId; + col->encoding = encoding; +} + +static inline void +SetColMetadataForImageType(TdsColumnMetaData *col, uint8_t tdsType) +{ + col->sizeLen = 1; + if (tdsType == TDS_TYPE_IMAGE) + { + col->sendTableName = true; + col->metaEntry.type8.maxSize = 0x7fffffff; + } + else if (tdsType == TDS_TYPE_SQLVARIANT) + { + col->sendTableName = false; + /* + * varchar(max), nvarchar(max), varbinary(max) can not be supported + * by sql_variant, this is a datatype restriction, hence, maxLen supported + * for varchar, nvarchar, varbinary would be <= 8K + */ + col->metaEntry.type8.maxSize = 0x00001f49; + } + col->metaLen = sizeof(col->metaEntry.type8); + col->metaEntry.type8.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type8.tdsTypeId = tdsType; +} + +static inline void +SetColMetadataForDateType(TdsColumnMetaData *col, uint8_t tdsType) +{ + col->sizeLen = 1; + col->metaLen = sizeof(col->metaEntry.type4); + col->metaEntry.type4.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type4.tdsTypeId = tdsType; +} + +static inline void +SetColMetadataForNumericType(TdsColumnMetaData *col, uint8_t tdsType, + uint8_t maxSize, uint8_t precision, uint8_t scale) +{ + col->sizeLen = 1; + col->metaLen = sizeof(col->metaEntry.type5); + col->metaEntry.type5.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type5.tdsTypeId = tdsType; + col->metaEntry.type5.maxSize = maxSize; + col->metaEntry.type5.precision = precision; + col->metaEntry.type5.scale = scale; +} + +static inline void +SetColMetadataForBinaryType(TdsColumnMetaData *col, uint8_t tdsType, uint16_t maxSize) +{ + col->sizeLen = 1; + col->metaLen = sizeof(col->metaEntry.type7); + col->metaEntry.type7.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type7.tdsTypeId = tdsType; + col->metaEntry.type7.maxSize = maxSize; +} + +static inline void +SetColMetadataForTimeType(TdsColumnMetaData *col, uint8_t tdsType, uint8_t scale) +{ + col->sizeLen = 1; + col->metaLen = sizeof(col->metaEntry.type6); + col->metaEntry.type6.flags = TDS_COL_METADATA_DEFAULT_FLAGS; + col->metaEntry.type6.tdsTypeId = tdsType; + col->metaEntry.type6.scale = scale; +} + +/* + * SetColMetadataForCharTypeHelper - set the collation for tds char datatypes by + * doing lookup on the hashtable setup by babelfishpg_tsql extension, which maps + * postgres collation to TSQL. If no entry is found then set the default + * TSQL collation values. + */ +static inline void +SetColMetadataForCharTypeHelper(TdsColumnMetaData *col, uint8_t tdsType, + Oid collation, int32 atttypmod) +{ + coll_info_t cinfo; + pg_enc enc; + + cinfo = (pltsql_plugin_handler_ptr)->lookup_collation_table_callback(collation); + + /* + * TODO: Remove the NULL condition once all the Postgres collations are mapped + * to TSQL + */ + if (cinfo.oid == InvalidOid) + { + enc = TdsLookupEncodingByLCID(TdsDefaultLcid); + SetColMetadataForCharType(col, tdsType, + TdsDefaultLcid, /* collation lcid */ + enc, + TdsDefaultCollationFlags, /* collation flags */ + TdsDefaultSortid, /* sort id */ + atttypmod); + } + else + { + enc = TdsLookupEncodingByLCID(cinfo.lcid); + SetColMetadataForCharType(col, tdsType, + cinfo.lcid, + enc, + cinfo.collateflags, + cinfo.sortid, + atttypmod); + } +} + +/* + * SetColMetadataForTextTypeHelper - set the collation for tds text datatypes by + * doing lookup on the hashtable setup by babelfishpg_tsql extension, which maps + * postgres collation to TSQL. If no entry is found then set the default + * TSQL collation values. + */ +static inline void +SetColMetadataForTextTypeHelper(TdsColumnMetaData *col, uint8_t tdsType, + Oid collation, int32 atttypmod) +{ + coll_info_t cinfo; + pg_enc enc; + + cinfo = (pltsql_plugin_handler_ptr)->lookup_collation_table_callback(collation); + + /* + * TODO: Remove the NULL condition once all the Postgres collations are mapped + * to TSQL + */ + if (cinfo.oid == InvalidOid) + { + enc = TdsLookupEncodingByLCID(TdsDefaultLcid); + SetColMetadataForTextType(col, tdsType, + TdsDefaultLcid, /* collation lcid */ + enc, + TdsDefaultCollationFlags, /* collation flags */ + TdsDefaultSortid, /* sort id */ + atttypmod); + } + else + { + enc = TdsLookupEncodingByLCID(cinfo.lcid); + SetColMetadataForTextType(col, tdsType, + cinfo.lcid, + enc, + cinfo.collateflags, + cinfo.sortid, + atttypmod); + } +} + +/* Functions in tdssqlbatch.c */ +extern TDSRequest GetSQLBatchRequest(StringInfo message); +extern void ProcessSQLBatchRequest(TDSRequest request); +extern void ExecuteSQLBatch(char *query); + +/* Funtions in tdsrpc.c */ +extern TDSRequest GetRPCRequest(StringInfo message); +extern void RestoreRPCBatch(StringInfo message, uint8_t *status, uint8_t *messageType); +extern void ProcessRPCRequest(TDSRequest request); + +/* Functions in tdsxact.c */ +extern TDSRequest GetTxnMgmtRequest(const StringInfo message); +extern void ProcessTxnMgmtRequest(TDSRequest request); +extern int TestTxnMgmtRequest(TDSRequest request, const char *expectedStr); + +#endif /* TDS_REQUEST_H */ diff --git a/contrib/babelfishpg_tds/src/include/tds_response.h b/contrib/babelfishpg_tds/src/include/tds_response.h new file mode 100644 index 00000000000..9d698dfb135 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_response.h @@ -0,0 +1,102 @@ +/*------------------------------------------------------------------------- + * + * tds_response.h + * This file contains definitions for structures and externs used + * by the response module of TDS listener. + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/tds_response.h + * + *------------------------------------------------------------------------- + */ +#ifndef TDS_H +#define TDS_H + +#include "nodes/pg_list.h" + +#include "tds_int.h" + +/* TDS token types */ +#define TDS_TOKEN_COLMETADATA 0x81 +#define TDS_TOKEN_COLINFO 0xA5 +#define TDS_TOKEN_ERROR 0xAA +#define TDS_TOKEN_INFO 0xAB +#define TDS_TOKEN_LOGINACK 0xAD +#define TDS_TOKEN_ROW 0xD1 +#define TDS_TOKEN_NBCROW 0xD2 +#define TDS_TOKEN_ENVCHANGE 0xE3 +#define TDS_TOKEN_SSPI 0xED +#define TDS_TOKEN_DONE 0xFD +#define TDS_TOKEN_DONEINPROC 0xFF +#define TDS_TOKEN_DONEPROC 0xFE +#define TDS_TOKEN_RETURNSTATUS 0x79 +#define TDS_TOKEN_RETURNVALUE 0xAC +#define TDS_TOKEN_TABNAME 0xA4 + +/* TDS done codes */ +#define TDS_DONE_FINAL 0x00 +#define TDS_DONE_MORE 0x01 +#define TDS_DONE_ERROR 0x02 +#define TDS_DONE_INXACT 0x04 +#define TDS_DONE_COUNT 0x10 +#define TDS_DONE_ATTN 0x20 + +/* TDS command types in DONE token */ +#define TDS_CMD_UNKNOWN 0x02 +#define TDS_CMD_SET 0xBE +#define TDS_CMD_SELECT 0xC1 +#define TDS_CMD_INSERT 0xC3 +#define TDS_CMD_DELETE 0xC4 +#define TDS_CMD_UPDATE 0xC5 +#define TDS_CMD_ROLLBACK 0xD2 +#define TDS_CMD_BEGIN 0xD4 +#define TDS_CMD_COMMIT 0xD5 +#define TDS_CMD_EXECUTE 0xE0 +#define TDS_CMD_INFO 0xF7 + +/* Functions in tdsresponse.c */ +extern void InitTDSResponse(void); +extern void TdsResponseReset(void); +extern ParameterToken MakeEmptyParameterToken(char *name, int atttypid, + int32 atttypmod, int attcollation); +extern int32 GetTypModForToken(ParameterToken token); +extern void TdsSendInfo(int number, int state, int class, + char *message, int line_no); +extern void TdsSendDone(int tag, int status, + int curcmd, uint64_t nprocessed); +extern void SendColumnMetadataToken(int natts, bool sendRowStat); +extern void SendTabNameToken(void); +extern void SendColInfoToken(int natts, bool sendRowStat); +extern void PrepareRowDescription(TupleDesc typeinfo, List *targetlist, int16 *formats, + bool extendedInfo, bool fetchPkeys); +extern void SendReturnValueTokenInternal(ParameterToken token, uint8 status, + FmgrInfo *finfo, Datum datum, bool isNull, + bool forceCoercion); +extern void TdsSendEnvChange(int envid, const char *new_val, const char *old_val); +extern void TdsSendInfoOrError(int token, int number, int state, int class, + char *message, char *server_name, + char *proc_name, int line_no); +extern void TdsPrepareReturnValueMetaData(TupleDesc typeinfo); +extern void TdsSendEnvChangeBinary(int envid, + void *new, int new_nbytes, + void *old, int old_nbytes); +extern void TdsSendReturnStatus(int status); +extern void TdsSendHandle(void); +extern void TdsSendRowDescription(TupleDesc typeinfo, + List *targetlist, int16 *formats); +extern bool TdsPrintTup(TupleTableSlot *slot, DestReceiver *self); +extern void TdsPrintTupShutdown(void); +extern void TdsSendError(int number, int state, int class, + char *message, int lineNo); +extern int TdsFlush(void); +extern void TDSStatementBeginCallback(PLtsql_execstate *estate, PLtsql_stmt *stmt); +extern void TDSStatementEndCallback(PLtsql_execstate *estate, PLtsql_stmt *stmt); +extern void TDSStatementExceptionCallback(PLtsql_execstate *estate, PLtsql_stmt *stmt, + bool terminate_batch); +extern void SendColumnMetadata(TupleDesc typeinfo, List *targetlist, int16 *formats); +extern bool GetTdsEstateErrorData(int *number, int *severity, int *state); + +#endif /* TDS_H */ diff --git a/contrib/babelfishpg_tds/src/include/tds_secure.h b/contrib/babelfishpg_tds/src/include/tds_secure.h new file mode 100644 index 00000000000..cfd9bcf6168 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_secure.h @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- + * + * tds_secure.h + * This file contains definitions for functions to register + * read and write TLS functions for Tds listener + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tds_secure.h + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#ifdef HAVE_NETINET_TCP_H +#include +#include +#endif + +#ifdef USE_OPENSSL +#include +#include +#include +#endif +#ifndef OPENSSL_NO_ECDH +#include +#endif + +#include "libpq/libpq.h" +#include "port/pg_bswap.h" + +#ifdef USE_SSL +#ifndef HAVE_BIO_GET_DATA +#define BIO_get_data(bio) (bio->ptr) +#define BIO_set_data(bio, data) (bio->ptr = data) +#endif +#endif + +BIO_METHOD *TdsBioSecureSocket(BIO_METHOD *my_bio_methods); + +extern int tds_ssl_min_protocol_version; +extern int tds_ssl_max_protocol_version; + +/* TDS specific function defined in tds-secure-openssl.c (modified copy of be-secure-openssl.c) */ +int Tds_be_tls_init(bool isServerStart); +void Tds_be_tls_destroy(void); /* TODO: call through our signal handler(SIGHUP_handler)/PG_TDS_fin */ +int Tds_be_tls_open_server(Port *port); +extern void Tds_be_tls_close(Port *port); +ssize_t Tds_be_tls_read(Port *port, void *ptr, size_t len, int *waitfor); +ssize_t Tds_be_tls_write(Port *port, void *ptr, size_t len, int *waitfor); + +/* function defined in tdssecure.c and called from tdscomm.c */ +ssize_t +tds_secure_read(Port *port, void *ptr, size_t len); +ssize_t +tds_secure_write(Port *port, void *ptr, size_t len); + +/* function defined in tdssecure.c and called from tdslogin.c */ +void TdsFreeSslStruct(Port *port); diff --git a/contrib/babelfishpg_tds/src/include/tds_timestamp.h b/contrib/babelfishpg_tds/src/include/tds_timestamp.h new file mode 100644 index 00000000000..69c97ed2674 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_timestamp.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + * + * tds_timestamp.h + * Definitions of handler functions for TDS timestamp datatype + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tds_timestamp.h + * + *------------------------------------------------------------------------- + */ +#include "utils/date.h" +#include "utils/timestamp.h" + +extern void TdsGetTimestampFromDayTime(uint32 numDays, uint64 numMicro, int tz, + Timestamp *timestamp, int scale); +extern void TdsGetDayTimeFromTimestamp(Timestamp value, uint32 *numDays, + uint64 *numSec, int scale); + +extern void TdsTimeDifferenceSmalldatetime(Datum value, uint16 *numDays, + uint16 *numMins); +extern void TdsTimeGetDatumFromSmalldatetime(uint16 numDays, uint16 numMins, + Timestamp *timestamp); +extern uint32 TdsDayDifference(Datum value); +extern void TdsTimeDifferenceDatetime(Datum value, uint32 *numDays, + uint32 *numTicks); +extern void TdsCheckDateValidity(DateADT result); +extern void TdsTimeGetDatumFromDays(uint32 numDays, uint64 *val); +extern void TdsTimeGetDatumFromDatetime(uint32 numDays, uint32 numTicks, + Timestamp *timestamp); +extern uint32 TdsGetDayDifferenceHelper(int day, int mon, int year, bool isDateType); + +/* + * structure for datatimeoffset support with separate time zone field + */ +typedef struct tsql_datetimeoffset +{ + int64 tsql_ts; + int16 tsql_tz; +} tsql_datetimeoffset; + +/* datetimeoffset macros */ +#define DATETIMEOFFSET_LEN MAXALIGN(sizeof(tsql_datetimeoffset)) +/* datetimeoffset default value in internal representation */ +#define DatetimeoffsetGetDatum(X) PointerGetDatum(X) +#define PG_RETURN_DATETIMEOFFSET(X) return DatetimeoffsetGetDatum(X) diff --git a/contrib/babelfishpg_tds/src/include/tds_typecode.h b/contrib/babelfishpg_tds/src/include/tds_typecode.h new file mode 100644 index 00000000000..427d3749200 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_typecode.h @@ -0,0 +1,57 @@ +#ifndef TYPECODE_H +#define TYPECODE_H + +/* Persistent Type Code for SQL Variant Type */ +/* WARNING: EXISTING VALUES MUST NOT BE CHANGED */ + +#define SQLVARIANT_T 0 +#define DATETIMEOFFSET_T 1 +#define DATETIME2_T 2 +#define DATETIME_T 3 +#define SMALLDATETIME_T 4 +#define DATE_T 5 +#define TIME_T 6 +#define FLOAT_T 7 +#define REAL_T 8 +#define NUMERIC_T 9 +#define MONEY_T 10 +#define SMALLMONEY_T 11 +#define BIGINT_T 12 +#define INT_T 13 +#define SMALLINT_T 14 +#define TINYINT_T 15 +#define BIT_T 16 +#define NVARCHAR_T 17 +#define NCHAR_T 18 +#define VARCHAR_T 19 +#define CHAR_T 20 +#define VARBINARY_T 21 +#define BINARY_T 22 +#define UNIQUEIDENTIFIER_T 23 + +#define TIMESTAMP_L 8 +#define DATE_L 4 +#define DATETIMEOFFSET_L DATETIMEOFFSET_LEN +#define TIME_L 8 +#define FLOAT_L 8 +#define REAL_L 4 +#define FIXEDDECIMAL_L 8 +#define BIGINT_L 8 +#define INT_L 4 +#define SMALLINT_L 2 +#define BIT_L 1 +#define UNIQUEIDENTIFIER_L 16 + +#define IS_STRING_TYPE(t) \ + ( ((t) == NVARCHAR_T) || ((t) == NCHAR_T) \ + || ((t) == VARCHAR_T) || ((t) == CHAR_T)) + +#define IS_MONEY_TYPE(t) (((t) == MONEY_T) || ((t) == SMALLMONEY_T)) +#define IS_BINARY_TYPE(t) (((t) == VARBINARY_T) || ((t) == BINARY_T)) + +/* MACRO from fixed decimal */ +#ifndef FIXEDDECIMAL_MULTIPLIER +#define FIXEDDECIMAL_MULTIPLIER 10000LL +#endif + +#endif diff --git a/contrib/babelfishpg_tds/src/include/tds_typeio.h b/contrib/babelfishpg_tds/src/include/tds_typeio.h new file mode 100644 index 00000000000..239c1d3663f --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_typeio.h @@ -0,0 +1,424 @@ +/*------------------------------------------------------------------------- + * + * tds_typeio.h + * Definitions for PG-Datum <-> TDS-protocol conversion + * + * Portions Copyright (c) 2020, AWS + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tds_typeio.h + * + *------------------------------------------------------------------------- + */ + +#ifndef TDS_TYPEIO_H +#define TDS_TYPEIO_H + +#include "fmgr.h" + +#include "tds_iofuncmap.h" + +/* Prototypes for Send and Receive IO functions */ + +/* Partial Length Prefixecd-bytes tokens */ +#define PLP_TERMINATOR 0x00000000 +#define PLP_NULL 0xFFFFFFFFFFFFFFFF +#define PLP_UNKNOWN_LEN 0xFFFFFFFFFFFFFFFE +#define PLP_CHUNCK_LEN 32000 + +/* + * TODO: Using a void * for the column meta data is an ugly hack. + * This is needed here now because there are circular + * dependencies with tds_int.h that I rather untangle + * in a separate CR than muddling it up into this one. + * -- Jan + * Circular dependency for parameter token needs to be handled + * in a similar way as that of column meta data + */ +typedef int (*TdsSendTypeFunction)(FmgrInfo *finfo, Datum value, + void *vMetaData); + +/* COLMETADATA entry for types like INTEGER and SMALLINT */ +typedef struct __attribute__((packed)) ColMetaEntry1 +{ + uint16_t flags; + uint8_t tdsTypeId; + uint8_t maxSize; +} ColMetaEntry1; + +/* COLMETADATA entry for types like NVARCHAR */ +typedef struct __attribute__((packed)) ColMetaEntry2 +{ + uint16_t flags; + uint8_t tdsTypeId; + uint16_t maxSize; + /* + * collationInfo(32 bits): LCID/CodePage (20 bits) + + * collationFlags(8 bits) + version (4 bits) + */ + uint32_t collationInfo; + uint8_t charSet; /* sortID */ +} ColMetaEntry2; + + +/* COLMETADATA entry for types like TEXT */ +typedef struct __attribute__((packed)) ColMetaEntry3 +{ + uint16_t flags; + uint8_t tdsTypeId; + uint32_t maxSize; + /* + * collationInfo(32 bits): LCID/CodePage (20 bits) + + * collationFlags(8 bits) + version (4 bits) + */ + uint32_t collationInfo; + uint8_t charSet; /* sortID */ +} ColMetaEntry3; + +/* COLMETADATA entry for type like DATE */ +typedef struct __attribute__((packed)) ColMetaEntry4 +{ + uint16_t flags; + uint8_t tdsTypeId; +} ColMetaEntry4; + +/* COLMETADATA entry for type NUMERIC */ +typedef struct __attribute__((packed)) ColMetaEntry5 +{ + uint16_t flags; + uint8_t tdsTypeId; + uint8_t maxSize; + uint8_t precision; + uint8_t scale; +} ColMetaEntry5; + +/* COLMETADATA entry for type like TIME, DATETIME2, DATETIMEOFFSET */ +typedef struct __attribute__((packed)) ColMetaEntry6 +{ + uint16_t flags; + uint8_t tdsTypeId; + uint8_t scale; +} ColMetaEntry6; + +/* COLMETADATA entry for types like BINARY VARBINARY */ +typedef struct __attribute__((packed)) ColMetaEntry7 +{ + uint16_t flags; + uint8_t tdsTypeId; + uint16_t maxSize; +} ColMetaEntry7; + +/* COLMETADATA entry for type like IMAGE */ +typedef struct __attribute__((packed)) ColMetaEntry8 +{ + uint16_t flags; + uint8_t tdsTypeId; + uint32_t maxSize; +} ColMetaEntry8; + +typedef union ColMetaEntry +{ + ColMetaEntry1 type1; + ColMetaEntry2 type2; + ColMetaEntry3 type3; + ColMetaEntry4 type4; + ColMetaEntry5 type5; + ColMetaEntry6 type6; + ColMetaEntry7 type7; + ColMetaEntry8 type8; +} ColMetaEntry; + +/* + * It stores the relation related information corresponding to + * a TdsColumnMetaData entry. We need this information to + * construct the COLMETADATA, TABNAME and COLINFO tokens. + */ +typedef struct TdsRelationMetaData +{ + Oid relOid; /* relation oid */ + AttrNumber *keyattrs; /* primary keys for this relation */ + int16 numkeyattrs; /* number of attributes in pk */ + + /* + * We store the fully qualified name of the relation. This information is + * needed on TABNAME token. + * + * partName[0] - relation name + * partName[1] - schema name + * partName[2] - database name + * partName[3] - object name + */ + char *partName[4]; + + /* + * A 1-based index for this relation which is used while sending the + * COLINFO token. + */ + uint8 tableNum; +} TdsRelationMetaDataInfoData; + +typedef TdsRelationMetaDataInfoData *TdsRelationMetaDataInfo; + +typedef struct TdsColumnMetaData +{ + Oid pgTypeOid; /* type identifier in PostgreSQL */ + StringInfoData colName; /* column name */ + int sizeLen; /* size of the type's data length */ + int metaLen; /* size of ColMetaEntry used */ + TdsSendTypeFunction sendFunc; + ColMetaEntry metaEntry; + bool sendTableName; + pg_enc encoding; + + /* + * Following information are only needed if we need to send TABNAME and COLINFO + * tokens. + */ + char *baseColName; /* actual column name if any alias is used */ + Oid relOid; /* relation that this column belongs to (0 if + an expression column */ + AttrNumber attrNum; /* attribute number in the relation */ + TdsRelationMetaDataInfo relinfo; +} TdsColumnMetaData; + +/* Partial Length Prefixed-bytes */ +typedef struct PlpData +{ + unsigned long offset; + unsigned long len; + struct PlpData *next; +} PlpData; +typedef PlpData *Plp; + +typedef struct TvpColMetaData +{ + int userType; + uint16 flags; + uint8_t columnTdsType; + + /* For numeric and decimal. */ + uint8_t scale; + uint8_t precision; + + uint32_t collation; + uint8_t sortId; + + uint32_t maxLen; +} TvpColMetaData; + +typedef struct TvpRowData +{ + /* Array of length col count, holds value of each column in that row. */ + StringInfo columnValues; + + char *isNull; + struct TvpRowData *nextRow; +} TvpRowData; + +typedef struct TvpData +{ + char *tvpTypeName; + char *tableName; + int colCount; + int rowCount; + + TvpColMetaData *colMetaData; /* Array of each column's metadata. */ + TvpRowData *rowData; /* Linked List holding each row. */ +} TvpData; + +/* Map TVP to its underlying table, either by relid or by table name. */ +typedef struct TvpLookupItem +{ + char *name; + Oid tableRelid; + char *tableName; +} TvpLookupItem; + +/* parameter token in RPC */ +typedef struct ParameterTokenData +{ + uint8_t type; + uint8_t flags; + + /* + * maxlen and len fields are 4 bytes for some + * datatypes(text, ntext) while 2 bytes for + * (nvarchar, others?) and 1 byte for others. + */ + uint32_t maxLen; + uint32_t len; + bool isNull; + + Plp plp; + + /* + * dataOffset points to the offset in the request message + * from where the data bytes actually start. + * Using, dataOffset + len we can fetch the entire data + * from the request message, when we want to use it. + */ + int dataOffset; + + uint16 paramOrdinal; + + /* + * Upon receiving a parameter for a RPC packet, we fill the following + * structure with the meta information about that parameter. We also + * store the corresponding PG type OID, receiver function and sender + * function. For IN parameters, we use the receiver functions to + * convert the parameter from TDS wire format to Datum. For OUT + * parameters, we use the sender functions to convert the Datums to + * TDS wire format and include them in the return value tokens. + */ + TdsColumnMetaData paramMeta; + + /* + * If this is an OUT parameter, it points to the column number in the + * result set. + */ + int outAttNo; + + TvpData *tvpInfo; + struct ParameterTokenData *next; +} ParameterTokenData; + +typedef ParameterTokenData *ParameterToken; + + +typedef Datum (*TdsRecvTypeFunction)(const char *, const ParameterToken); + +/* + * TdsCollationData - hash table structure for + * mapping Postgres - TSQL Collation + */ +typedef struct TdsCollationData +{ + Oid collationOid; + int32_t codePage; + int32_t collateFlags; + int32_t sortId; +} TdsCollationData; + +typedef TdsCollationData *TdsCollationInfo; + +/* + * TdsLCIDToEncodingMap - hash table structure to + * store LCID - Encoding pair + */ +typedef struct TdsLCIDToEncodingMap +{ + int lcid; + int enc; +} TdsLCIDToEncodingMap; + +typedef TdsLCIDToEncodingMap *TdsLCIDToEncodingMapInfo; +/* + * TdsIoFunctionData - hash table entry for IO function cache + * TdsIoFunctionRawData - Raw Table data entry for TdsIoFunctionData + */ + +typedef struct TdsIoFunctionRawData +{ + const char *typnsp; + const char *typname; + int32_t ttmtdstypeid; + int32_t ttmtdstypelen; + int32_t ttmtdslenbytes; + int32_t ttmsendfunc; + int32_t ttmrecvfunc; +} TdsIoFunctionRawData; + +typedef struct TdsIoFunctionData +{ + Oid ttmtypeid; + Oid ttmbasetypeid; + int32_t ttmtdstypeid; + int32_t ttmtdstypelen; + int32_t ttmtdslenbytes; + int32_t sendFuncId; + int32_t recvFuncId; + TdsSendTypeFunction sendFuncPtr; + TdsRecvTypeFunction recvFuncPtr; +} TdsIoFunctionData; + +typedef struct TdsIoFunctionData *TdsIoFunctionInfo; + +/* Functions in tdstypeio.c */ +extern void TdsResetCache(void); +extern void TdsLoadTypeFunctionCache(void); +extern TdsIoFunctionInfo TdsLookupTypeFunctionsByOid(Oid typeId, int32* typmod); +extern TdsIoFunctionInfo TdsLookupTypeFunctionsByTdsId(int32_t typeId, + int32_t typeLen); + +extern StringInfo TdsGetStringInfoBufferFromToken(const char *message, + const ParameterToken token); +extern StringInfo TdsGetPlpStringInfoBufferFromToken(const char *message, + const ParameterToken token); +extern void TdsReadUnicodeDataFromTokenCommon(const char *message, + const ParameterToken token, + StringInfo temp); +TdsCollationInfo TdsLookupCollationByOid(Oid cId); +extern void TdsLoadEncodingLCIDCache(void); +extern int TdsLookupEncodingByLCID(int LCID); +extern int TdsSendTypeBit(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeTinyint(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeSmallint(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeInteger(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeBigint(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeFloat4(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeFloat8(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeVarchar(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeNVarchar(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeMoney(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeSmallmoney(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeChar(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeNChar(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeSmalldatetime(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeText(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeNText(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeDate(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeDatetime(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeNumeric(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeSmalldatetime(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeImage(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeBinary(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeVarbinary(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeUniqueIdentifier(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeTime(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeDatetime2(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeXml(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeSqlvariant(FmgrInfo *finfo, Datum value, void *vMetaData); +extern int TdsSendTypeDatetimeoffset(FmgrInfo *finfo, Datum value, void *vMetaData); + +extern Datum TdsRecvTypeBit(const char *, const ParameterToken); +extern Datum TdsRecvTypeTinyInt(const char *, const ParameterToken); +extern Datum TdsRecvTypeSmallInt(const char *, const ParameterToken); +extern Datum TdsRecvTypeInteger(const char *, const ParameterToken); +extern Datum TdsRecvTypeBigInt(const char *, const ParameterToken); +extern Datum TdsRecvTypeFloat4(const char *, const ParameterToken); +extern Datum TdsRecvTypeFloat8(const char *, const ParameterToken); +extern Datum TdsRecvTypeVarchar(const char *, const ParameterToken); +extern Datum TdsRecvTypeNVarchar(const char *, const ParameterToken); +extern Datum TdsRecvTypeMoney(const char *, const ParameterToken); +extern Datum TdsRecvTypeSmallmoney(const char *, const ParameterToken); +extern Datum TdsRecvTypeChar(const char *, const ParameterToken); +extern Datum TdsRecvTypeNChar(const char *, const ParameterToken); +extern Datum TdsRecvTypeText(const char *message, const ParameterToken); +extern Datum TdsRecvTypeNText(const char *message, const ParameterToken); +extern Datum TdsRecvTypeDate(const char *message, const ParameterToken); +extern Datum TdsRecvTypeDatetime(const char *message, const ParameterToken); +extern Datum TdsRecvTypeNumeric(const char *message, const ParameterToken); +extern Datum TdsRecvTypeSmalldatetime(const char *, const ParameterToken); +extern Datum TdsRecvTypeImage(const char *, const ParameterToken); +extern Datum TdsRecvTypeBinary(const char *message, const ParameterToken); +extern Datum TdsRecvTypeVarbinary(const char *message, const ParameterToken); +extern Datum TdsRecvTypeUniqueIdentifier(const char *, const ParameterToken); +extern Datum TdsRecvTypeTime(const char *message, const ParameterToken); +extern Datum TdsRecvTypeDatetime2(const char *message, const ParameterToken); +extern Datum TdsRecvTypeXml(const char *, const ParameterToken); +extern Datum TdsRecvTypeTable(const char *, const ParameterToken); +extern Datum TdsRecvTypeSqlvariant(const char *message, const ParameterToken); +extern Datum TdsRecvTypeDatetimeoffset(const char *message, const ParameterToken); + +#endif /* TDS_TYPEIO_H */ diff --git a/contrib/babelfishpg_tds/src/include/tdsprinttup.h b/contrib/babelfishpg_tds/src/include/tdsprinttup.h new file mode 100644 index 00000000000..78fdf82d9d0 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tdsprinttup.h @@ -0,0 +1,15 @@ +/*------------------------------------------------------------------------- + * + * tdsprinttup.h + * + * + * + * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/tds/tdsprinttup.h + * + *------------------------------------------------------------------------- + */ + + diff --git a/contrib/babelfishpg_tsql/Makefile b/contrib/babelfishpg_tsql/Makefile new file mode 100644 index 00000000000..c834a9ff757 --- /dev/null +++ b/contrib/babelfishpg_tsql/Makefile @@ -0,0 +1,236 @@ +# Note: this Makefile uses pg_config to find various header +# files and built tools. +include Version.config + +EXTENSION = babelfishpg_tsql +EXTVERSION = $(PGTSQL_MAJOR_VERSION).$(PGTSQL_MINOR_VERSION).$(PGTSQL_MICRO_VERSION) + +#subdir = contrib/babelfishpg_tsql + +# Note: +# Set PREV_EXTVERSION after release, i.e after release of 3.0.0, set PREV_EXTVERSION to 3.0.0 +# babel_upgrade test target should at the top of the src/test/regress/babel_schedule +# src/test/regress/sql/babel_upgrade.sql should be modified to include the PREV_EXTVERSION to test the upgrade path +# contrib/babelfishpg_tsql/sql/upgrades/$(EXTENSION)--$(PREV_EXTVERSION).sql should be present to test the upgrade path +#PREV_EXTVERSION = 1.0.0 +MODULEPATH = $$libdir/$(EXTENSION)-$(PGTSQL_MAJOR_VERSION) +MODULE_big = $(EXTENSION) + +# $(OBJS) should contain the name of each .o file that +# we link into the extension +OBJS = src/pl_gram.o src/pl_handler.o src/pl_comp.o src/pl_exec.o +OBJS += src/pl_funcs.o src/pl_scanner.o $(WIN32RES) +OBJS += src/pl_comp-2.o +OBJS += src/properties.o +OBJS += src/databasepropertyex.o +OBJS += src/plan_inval.o +OBJS += src/procedures.o +OBJS += src/cursor.o +OBJS += src/applock.o +OBJS += src/pltsql_coerce.o +OBJS += runtime/functions.o +OBJS += src/err_handler.o +OBJS += src/pltsql_function_probin_handler.o +OBJS += src/pltsql_utils.o +OBJS += src/tablecmds.o +OBJS += src/stmt_walker.o +OBJS += src/codegen.o +OBJS += src/dynavec.o +OBJS += src/dynastack.o +OBJS += src/analyzer.o +OBJS += src/prepare.o +OBJS += src/compile_context.o +OBJS += src/collation.o src/string.o +OBJS += src/forxml.o +OBJS += src/pltsql_identity.o +OBJS += src/collationproperty.o +OBJS += src/rolecmds.o +OBJS += src/backend_parser/keywords.o +OBJS += src/backend_parser/parser.o +OBJS += src/backend_parser/scan-backend.o +OBJS += src/backend_parser/gram-backend.o +OBJS += src/backend_parser/gram_hook.o +OBJS += src/dbcmds.o +OBJS += src/session.o +OBJS += src/guc.o +OBJS += src/catalog.o +OBJS += src/schemacmds.o +OBJS += src/hooks.o +OBJS += src/tsqlNodes.o +OBJS += src/tsqlHandler.o +OBJS += src/tsqlUnsupportedFeatureHandler.o +OBJS += src/tsqlIface.o +OBJS += antlr/libantlr_tsql.a +OBJS += src/multidb.o + +export ANTLR4_JAVA_BIN=java +export ANTLR4_RUNTIME_LIB=-lantlr4-runtime +export ANTLR4_RUNTIME_INCLUDE_DIR=/usr/local/include/antlr4-runtime +export ANTLR4_RUNTIME_LIB_DIR=/usr/local/lib + +PG_CXXFLAGS += -g -Werror +PG_CXXFLAGS += -Wno-deprecated -Wno-error=attributes -Wno-suggest-attribute=format # disable some warnings from ANTLR runtime header +PG_CXXFLAGS += -Wno-undef -Wall -Wcpp +PG_CXXFLAGS += -Wno-register # otherwise C++17 gags on PostgreSQL headers +PG_CXXFLAGS += -I$(ANTLR4_RUNTIME_INCLUDE_DIR) +PG_CFLAGS += -g +PG_CPPFLAGS += -I$(TSQLSRC) -I$(PG_SRC) -DFAULT_INJECTOR + +SHLIB_LINK += -L$(ANTLR4_RUNTIME_LIB_DIR) $(ANTLR4_RUNTIME_LIB) -lcrypto + +UPGRADES = $(patsubst sql/upgrades/%.sql,sql/%.sql,$(wildcard sql/upgrades/*.sql)) + +# We need the previous version to test extension upgrade scripts +ifdef PREV_EXTVERSION +DATA = sql/$(EXTENSION)--$(PREV_EXTVERSION).sql +endif + +DATA_built = \ + $(EXTENSION).control \ + sql/$(EXTENSION)--$(EXTVERSION).sql $(UPGRADES) + +#include ../Makefile.common + +# Get Postgres version, as well as major (9.4, etc) version. Remove '.' from MAJORVER. +VERSION = $(shell $(PG_CONFIG) --version | awk '{print $$2}' | sed -e 's/devel$$//') +MAJORVER = $(shell echo $(VERSION) | cut -d . -f1,2 | tr -d .) + +# Function for testing a condition +test = $(shell test $(1) $(2) $(3) && echo yes || echo no) + +GE91 = $(call test, $(MAJORVER), -ge, 91) + +ifdef USE_PGXS +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/babelfishpg_tsql +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + + +ifeq ($(GE91),yes) +all: sql/$(EXTENSION)--$(EXTVERSION).sql $(UPGRADES) +endif + +$(EXTENSION).control: $(EXTENSION).control.in + cat $< \ + | sed -e 's|@EXTVERSION@|$(EXTVERSION)|g' \ + | sed -e 's|@EXTENSION@|$(EXTENSION)|g' \ + | sed -e 's|@MODULEPATH@|$(MODULEPATH)|g' \ + > $@ + +sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).in + cpp $< | sed 's/^# /-- /g' > $@ + +sql/%.sql: sql/upgrades/%.sql + cp $< $@ + + +# $(KEYWORDS) should contain the name of each keyword +# file + +#KEYWORDS = src/pl_reserved_kwlist.h +KEYWORDS = src/pl_reserved_kwlist_d.h +#KEYWORDS += src/pl_unreserved_kwlist.h +KEYWORDS += src/pl_unreserved_kwlist_d.h + +#PERL := perl + +# where to find gen_keywordlist.pl and subsidiary files +TOOLSDIR = $(PG_SRC)/src/tools +GEN_KEYWORDLIST = $(PERL) -I $(TOOLSDIR) $(TOOLSDIR)/gen_keywordlist.pl +GEN_KEYWORDLIST_DEPS = $(TOOLSDIR)/gen_keywordlist.pl $(TOOLSDIR)/PerfectHash.pm +TSQLSRC = . + +antlr/Makefile: antlr/CMakeLists.txt antlr/TSqlLexer.g4 antlr/TSqlLexer.g4 + cd antlr && $(cmake) . && cd .. + +.PHONY: antlr/libantlr_tsql.a # to allow CMake's make check the build +antlr/libantlr_tsql.a: antlr/Makefile + $(MAKE) -C $(@D) all + +# See notes in src/backend/parser/Makefile about the following two rules +src/pl_gram.h: src/pl_gram.c + touch $@ + +src/pl_gram.c: BISONFLAGS += -d -v + +# generate plerrcodes.h: from src/backend/utils/errcodes.txt +src/plerrcodes.h: $(PG_SRC)/src/backend/utils/errcodes.txt src/generate-plerrcodes.pl + echo $(top_srcdir) + $(PERL) src/generate-plerrcodes.pl $< > $@ + +# generate keyword headers for the scanner +src/pl_reserved_kwlist_d.h: src/pl_reserved_kwlist.h $(GEN_KEYWORDLIST_DEPS) + $(GEN_KEYWORDLIST) --varname ReservedPLKeywords $< + +src/pl_unreserved_kwlist_d.h: src/pl_unreserved_kwlist.h $(GEN_KEYWORDLIST_DEPS) + $(GEN_KEYWORDLIST) --varname UnreservedPLKeywords $< + +src/pl_scanner.o: src/pl_scanner.c $(KEYWORDS) + +src/pl_comp.o: src/plerrcodes.h + +src/tsqlIface.o: src/tsqlIface.cpp + +src/tsqlUnsupportedFeatureHandler.o: src/tsqlUnsupportedFeatureHandler.cpp + +## extend backend parser +BEPARSERDIR=src/backend_parser + +src/backend_parser/kwlist_d.h: src/backend_parser/kwlist.h $(GEN_KEYWORDLIST_DEPS) + $(GEN_KEYWORDLIST) --output src/backend_parser --varname pgtsql_ScanKeywords $< + +src/backend_parser/keywords.o: src/backend_parser/keywords.c src/backend_parser/kwlist_d.h + +src/backend_parser/gram-backend.y: $(PG_SRC)/src/backend/parser/gram.y $(BEPARSERDIR)/gram-tsql-prologue.y.h $(BEPARSERDIR)/gram-tsql-decl.y $(BEPARSERDIR)/gram-tsql-rule.y $(BEPARSERDIR)/gram-tsql-epilogue.y.c $(BEPARSERDIR)/include.pl + $(PERL) $(BEPARSERDIR)/include.pl $(BEPARSERDIR) gram.y < $< > $@ + +src/backend_parser/gram-backend.c: BISONFLAGS += -d + +src/backend_parser/gram-backend.c: src/backend_parser/gram-backend.y + +src/backend_parser/gram-backend.h: src/backend_parser/gram-backend.c + touch $@ + +src/backend_parser/scan-backend.l: $(PG_SRC)/src/backend/parser/scan.l $(BEPARSERDIR)/scan-tsql-prologue-top.l.h $(BEPARSERDIR)/scan-tsql-prologue.l.h $(BEPARSERDIR)/scan-tsql-decl.l $(BEPARSERDIR)/scan-tsql-rule.l $(BEPARSERDIR)/scan-tsql-epilogue.l.c + $(PERL) $(BEPARSERDIR)/include.pl $(BEPARSERDIR) scan.l < $< > $@ + +src/backend_parser/scan-backend.c: FLEXFLAGS = -CF -p -p +src/backend_parser/scan-backend.c: FLEX_NO_BACKUP=yes +src/backend_parser/scan-backend.c: FLEX_FIX_WARNING=yes + +ifneq ("$(wildcard $(FLEX))","") + FLEX_EXEC := $(FLEX) +else + FLEX_EXEC := flex # if $(FLEX) doens't exists (postgres and extension can be built in different nodes), use built-in flex +endif + +src/backend_parser/scan-backend.c: src/backend_parser/scan-backend.l + $(FLEX_EXEC) $(if $(FLEX_NO_BACKUP),-b) $(FLEXFLAGS) -o'$@' $< + @$(if $(FLEX_NO_BACKUP),if [ `wc -l &2; exit 1; fi) + $(if $(FLEX_FIX_WARNING),$(PERL) $(TOOLSDIR)/fix-old-flex-code.pl '$@') + +# Force these dependencies to be known even without dependency info built: +src/backend_parser/gram-backend.o src/backend_parser/scan-backend.o src/backend_parser/parser.o: src/backend_parser/gram-backend.h + +.DEFAULT_GOAL := all +.PHONY: sql/$(EXTENSION)--$(EXTVERSION).sql + +################################################################################ +## ANTLR Parser rules +################################################################################ +# ANTLR = antlr4 +# ANTLRFLAGS = -Dlanguage=Cpp -listener +# +# TSqlLexer.o : CXXFLAGS = -O -g -fpic -I/usr/local/include/antlr4-runtime -Wno-attributes -Wno-deprecated +# TSqlParser.o : CXXFLAGS = -O -g -fpic -I/usr/local/include/antlr4-runtime -Wno-attributes -Wno-deprecated +# +# TSqlParser.cpp: TSqlParser.g4 TSqlLexer.g4 +# $(ANTLR) $(ANTLRFLAGS) $< +# +# TSqlLexer.cpp: TSqlLexer.g4 +# $(ANTLR) $(ANTLRFLAGS) $< diff --git a/contrib/babelfishpg_tsql/Release.md b/contrib/babelfishpg_tsql/Release.md new file mode 100644 index 00000000000..98ad2f0c7ad --- /dev/null +++ b/contrib/babelfishpg_tsql/Release.md @@ -0,0 +1,49 @@ +How to release + +Date: 2020-12-23 + +Versioning Scheme +----------------- + +Goals +==== +* Provide frequent (weekly/bi-weekly) extension patches for pre-prod instances when we want to alter the existing database without loosing the data +* Test upgrades between patches + +Notes +==== +PgTsql release version is composed by PGTSQL_MAJOR_VERSION, +PGTSQL_MINOR_VERSION and PGTSQL_MICRO_VERSION components, all +set in Version.config. + +By default only *PGTSQL_MICRO_VERSION* is incremented between internal releases. + + +Internal release procedure by example +==== + +#### Preconditions +- Latest released version is `3.0.0`, i.e `PREV_EXTVERSION` is set to 3.0.0 +- Current development version is `3.0.1` i.e `Version.config` contains `PGTSQL_MAJOR_VERSION=3, PGTSQL_MINOR_VERSION=0 PGTSQL_MICRO_VERSION=1` +- There is an upgrade path `sql/upgrades/pgtsql--3.0.0--3.0.1.sql` +- There is an install script `sql/pgtsql--$(PREV_EXTVERSION).sql` to test upgrade path script +- babel_upgrade test is at the top of src/test/regress/babel_schedule +- `src/test/regress/sql/babel_upgrade.sql` is modified to include the `PREV_EXTVERSION` to test the upgrade path + +#### Development Procedure +- Developers alter `sql/upgrades/pgtsql--3.0.0--3.0.1.sql` with upgrade scripts. + +#### Release Procedure +- Release existing build +- set `PREV_EXTVERSION` to 3.0.1 +- Copy sql install script to sql/pgtsql--3.0.1.sql +- Alter `src/test/regress/sql/babel_upgrade.sql` with a new upgrade path test from `3.0.1` to `3.0.2` +- Increment PGTSQL_MICRO_VERSION to 2 +- Create an empty upgrade path in `sql/upgrades` from a previously released minor to a new development minor `sql/upgrades/pgtsql--3.0.1--3.0.2.sql` + + +Testing +==== + +bb installcheck-babel +* Extension upgrade test in `src/test/regress/sql/babel_upgrade.sql` \ No newline at end of file diff --git a/contrib/babelfishpg_tsql/Version.config b/contrib/babelfishpg_tsql/Version.config new file mode 100644 index 00000000000..3596582bc70 --- /dev/null +++ b/contrib/babelfishpg_tsql/Version.config @@ -0,0 +1,7 @@ +# Version numbering central repository, to be included from various +# places during the build process + +PGTSQL_MAJOR_VERSION=1 +PGTSQL_MINOR_VERSION=0 +PGTSQL_MICRO_VERSION=0 + diff --git a/contrib/babelfishpg_tsql/antlr/CMakeLists.txt b/contrib/babelfishpg_tsql/antlr/CMakeLists.txt new file mode 100644 index 00000000000..4c464755328 --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/CMakeLists.txt @@ -0,0 +1,33 @@ +# minimum required CMAKE version +CMAKE_MINIMUM_REQUIRED(VERSION 3.7 FATAL_ERROR) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake-dir) + +# compiler must be 11 or 14 +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -O2 -ggdb -w -Wno-deprecated") +set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -fPIC -O2 -ggdb -w -Wno-deprecated") + +message(STATUS "CMAKE_C_FLAGS=${CMAKE_C_FLAGS}") + +# add antrl4cpp artifacts to project environment +#include_directories($ENV{ANTLR4_RUNTIME_INCLUDE_DIR}) +SET (MYDIR /usr/local/include/antlr4-runtime/) +include_directories(${MYDIR}) + +# set variable pointing to the antlr tool that supports C++ +# this is not required if the jar file can be found under PATH environment +set(ANTLR_EXECUTABLE ${PROJECT_SOURCE_DIR}/thirdparty/antlr/antlr-4.9.3-complete.jar) +# add macros to generate ANTLR Cpp code from grammar +find_package(ANTLR REQUIRED) + +antlr_target(SampleGrammarLexer TSqlLexer.g4 LEXER) +antlr_target(SampleGrammarParser TSqlParser.g4 PARSER LISTENER VISITOR + DEPENDS_ANTLR SampleGrammarLexer + COMPILE_FLAGS -lib ${ANTLR_SampleGrammarLexer_OUTPUT_DIR}) + +# include generated files in project environment +include_directories(${ANTLR_SampleGrammarLexer_OUTPUT_DIR}) +include_directories(${ANTLR_SampleGrammarParser_OUTPUT_DIR}) + +add_library(antlr_tsql STATIC ${ANTLR_SampleGrammarLexer_CXX_OUTPUTS} ${ANTLR_SampleGrammarParser_CXX_OUTPUTS}) diff --git a/contrib/babelfishpg_tsql/antlr/TSqlLexer.g4 b/contrib/babelfishpg_tsql/antlr/TSqlLexer.g4 new file mode 100644 index 00000000000..43fd969b9fe --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/TSqlLexer.g4 @@ -0,0 +1,1287 @@ +/* +T-SQL (Transact-SQL, MSSQL) grammar. +The MIT License (MIT). +Copyright (c) 2017, Mark Adams (madams51703@gmail.com) +Copyright (c) 2015-2017, Ivan Kochurkin (kvanttt@gmail.com), Positive Technologies. +Copyright (c) 2016, Scott Ure (scott@redstormsoftware.com). +Copyright (c) 2016, Rui Zhang (ruizhang.ccs@gmail.com). +Copyright (c) 2016, Marcus Henriksson (kuseman80@gmail.com). +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +lexer grammar TSqlLexer; + +@header +{ + extern bool pltsql_quoted_identifier; +} + +//Keywords +ABORT: A B O R T; +ABORT_AFTER_WAIT: A B O R T UNDERLINE A F T E R UNDERLINE W A I T; +ABSENT: A B S E N T; +ABSOLUTE: A B S O L U T E; +ACCENT_SENSITIVITY: A C C E N T UNDERLINE S E N S I T I V I T Y; +ACCESS: A C C E S S; +ACTION: A C T I O N; +ACTIVATION: A C T I V A T I O N; +ACTIVE: A C T I V E; +ADD: A D D; +ADDRESS: A D D R E S S; +ADMINISTER: A D M I N I S T E R; +AES: A E S; +AES_128: A E S UNDERLINE '128'; +AES_192: A E S UNDERLINE '192'; +AES_256: A E S UNDERLINE '256'; +AFFINITY: A F F I N I T Y; +AFTER: A F T E R; +AGGREGATE: A G G R E G A T E; +ALGORITHM: A L G O R I T H M; +ALL: A L L; +ALLOWED: A L L O W E D; +ALLOW_CONNECTIONS: A L L O W UNDERLINE C O N N E C T I O N S; +ALLOW_ENCRYPTED_VALUE_MODIFICATIONS: A L L O W UNDERLINE E N C R Y P T E D UNDERLINE V A L U E UNDERLINE M O D I F I C A T I O N S; +ALLOW_MULTIPLE_EVENT_LOSS: A L L O W UNDERLINE M U L T I P L E UNDERLINE E V E N T UNDERLINE L O S S; +ALLOW_SINGLE_EVENT_LOSS: A L L O W UNDERLINE S I N G L E UNDERLINE E V E N T UNDERLINE L O S S; +ALLOW_SNAPSHOT_ISOLATION: A L L O W UNDERLINE S N A P S H O T UNDERLINE I S O L A T I O N; +ALTER: A L T E R; +ALWAYS: A L W A Y S; +AND: A N D; +ANONYMOUS: A N O N Y M O U S; +ANSI_DEFAULTS: A N S I UNDERLINE D E F A U L T S; +ANSI_NULLS: A N S I UNDERLINE N U L L S; +ANSI_NULL_DEFAULT: A N S I UNDERLINE N U L L UNDERLINE D E F A U L T; +ANSI_NULL_DFLT_OFF: A N S I UNDERLINE N U L L UNDERLINE D F L T UNDERLINE O F F; +ANSI_NULL_DFLT_ON: A N S I UNDERLINE N U L L UNDERLINE D F L T UNDERLINE O N; +ANSI_PADDING: A N S I UNDERLINE P A D D I N G; +ANSI_WARNINGS: A N S I UNDERLINE W A R N I N G S; +ANY: A N Y; +APPEND: A P P E N D; +APPLICATION: A P P L I C A T I O N; +APPLICATION_LOG: A P P L I C A T I O N UNDERLINE L O G; +APPLY: A P P L Y; +ARITHABORT: A R I T H A B O R T; +ARITHIGNORE: A R I T H I G N O R E; +AS: A S; +ASC: A S C; +ASSEMBLY: A S S E M B L Y; +ASYMMETRIC: A S Y M M E T R I C; +ASYNCHRONOUS_COMMIT: A S Y N C H R O N O U S UNDERLINE C O M M I T; +ATOMIC: A T O M I C; +AT_KEYWORD: A T; +AUDIT: A U D I T; +AUDIT_GUID: A U D I T UNDERLINE G U I D; +AUTHENTICATE: A U T H E N T I C A T E; +AUTHENTICATION: A U T H E N T I C A T I O N; +AUTHORIZATION: A U T H O R I Z A T I O N; +AUTO: A U T O; +AUTOCOMMIT: A U T O C O M M I T; +AUTOGROW_ALL_FILES: A U T O G R O W UNDERLINE A L L UNDERLINE F I L E S; +AUTOGROW_SINGLE_FILE: A U T O G R O W UNDERLINE S I N G L E UNDERLINE F I L E; +AUTOMATED_BACKUP_PREFERENCE: A U T O M A T E D UNDERLINE B A C K U P UNDERLINE P R E F E R E N C E; +AUTOMATIC: A U T O M A T I C; +AUTO_CLEANUP: A U T O UNDERLINE C L E A N U P; +AUTO_CLOSE: A U T O UNDERLINE C L O S E; +AUTO_CREATE_STATISTICS: A U T O UNDERLINE C R E A T E UNDERLINE S T A T I S T I C S; +AUTO_SHRINK: A U T O UNDERLINE S H R I N K; +AUTO_UPDATE_STATISTICS: A U T O UNDERLINE U P D A T E UNDERLINE S T A T I S T I C S; +AUTO_UPDATE_STATISTICS_ASYNC: A U T O UNDERLINE U P D A T E UNDERLINE S T A T I S T I C S UNDERLINE A S Y N C; +AVAILABILITY: A V A I L A B I L I T Y; +AVAILABILITY_MODE: A V A I L A B I L I T Y UNDERLINE M O D E; +AVG: A V G; +BACKUP: B A C K U P; +BACKUP_PRIORITY: B A C K U P UNDERLINE P R I O R I T Y; +BEFORE: B E F O R E; +BEGIN: B E G I N; +BEGIN_DIALOG: B E G I N UNDERLINE D I A L O G; +BETWEEN: B E T W E E N; +BIGINT: B I G I N T; +BASE64: B A S E '64'; +BINARY_CHECKSUM: B I N A R Y UNDERLINE C H E C K S U M; +BINARY_KEYWORD: B I N A R Y; +BINDING: B I N D I N G; +BLOB_STORAGE: B L O B UNDERLINE S T O R A G E; +BLOCK: B L O C K; +BLOCKERS: B L O C K E R S; +BLOCKING_HIERARCHY: B L O C K I N G UNDERLINE H I E R A R C H Y; +BLOCKSIZE: B L O C K S I Z E; +BOUNDING_BOX: B O U N D I N G UNDERLINE B O X; +BREAK: B R E A K; +BROKER: B R O K E R; +BROKER_INSTANCE: B R O K E R UNDERLINE I N S T A N C E; +BROWSE: B R O W S E; +BUFFER: B U F F E R; +BUFFERCOUNT: B U F F E R C O U N T; +BULK: B U L K; +BULK_LOGGED: B U L K UNDERLINE L O G G E D; +BY: B Y; +CACHE: C A C H E; +CALLED: C A L L E D; +CALLER: C A L L E R; +CAP_CPU_PERCENT: C A P UNDERLINE C P U UNDERLINE P E R C E N T; +CASCADE: C A S C A D E; +CASE: C A S E; +CAST: C A S T; +CATALOG: C A T A L O G; +CATALOG_COLLATION: C A T A L O G UNDERLINE C O L L A T I O N; +CATCH: C A T C H; +CELLS_PER_OBJECT: C E L L S UNDERLINE P E R UNDERLINE O B J E C T; +CERTIFICATE: C E R T I F I C A T E; +CHANGE: C H A N G E; +CHANGES: C H A N G E S; +CHANGETABLE: C H A N G E T A B L E; +CHANGE_RETENTION: C H A N G E UNDERLINE R E T E N T I O N; +CHANGE_TRACKING: C H A N G E UNDERLINE T R A C K I N G; +CHECK: C H E C K; +CHECKPOINT: C H E C K P O I N T; +CHECKSUM: C H E C K S U M; +CHECKSUM_AGG: C H E C K S U M UNDERLINE A G G; +CHECK_EXPIRATION: C H E C K UNDERLINE E X P I R A T I O N; +CHECK_POLICY: C H E C K UNDERLINE P O L I C Y; +CLASSIFIER_FUNCTION: C L A S S I F I E R UNDERLINE F U N C T I O N; +CLEANUP: C L E A N U P; +CLEANUP_POLICY: C L E A N U P UNDERLINE P O L I C Y; +CLEAR: C L E A R; +CLOSE: C L O S E; +CLUSTER: C L U S T E R; +CLUSTERED: C L U S T E R E D; +COALESCE: C O A L E S C E; +COLLATE: C O L L A T E; +COLLECTION: C O L L E C T I O N; +COLUMN: C O L U M N; +COLUMNS: C O L U M N S; +COLUMNSTORE: C O L U M N S T O R E; +COLUMN_MASTER_KEY: C O L U M N UNDERLINE M A S T E R UNDERLINE K E Y; +COMMIT: C O M M I T; +COMMITTED: C O M M I T T E D; +COMPATIBILITY_LEVEL: C O M P A T I B I L I T Y UNDERLINE L E V E L; +COMPRESSION: C O M P R E S S I O N; +COMPUTE: C O M P U T E; +CONCAT: C O N C A T; +CONCAT_NULL_YIELDS_NULL: C O N C A T UNDERLINE N U L L UNDERLINE Y I E L D S UNDERLINE N U L L; +CONFIGURATION: C O N F I G U R A T I O N; +CONNECT: C O N N E C T; +CONNECTION: C O N N E C T I O N; +CONSTRAINT: C O N S T R A I N T; +CONTAINED: C O N T A I N E D; +CONTAINMENT: C O N T A I N M E N T; +CONTAINS: C O N T A I N S; +CONTAINSTABLE: C O N T A I N S T A B L E; +CONTENT: C O N T E N T; +CONTEXT: C O N T E X T; +CONTINUE: C O N T I N U E; +CONTINUE_AFTER_ERROR: C O N T I N U E UNDERLINE A F T E R UNDERLINE E R R O R; +CONTRACT: C O N T R A C T; +CONTRACT_NAME: C O N T R A C T UNDERLINE N A M E; +CONTROL: C O N T R O L; +CONVERSATION: C O N V E R S A T I O N; +CONVERT: C O N V E R T; +COOKIE: C O O K I E; +COPY_ONLY: C O P Y UNDERLINE O N L Y; +COUNT: C O U N T; +COUNTER: C O U N T E R; +COUNT_BIG: C O U N T UNDERLINE B I G; +CPU: C P U; +CREATE: C R E A T E; +CREATE_NEW: C R E A T E UNDERLINE N E W; +CREATION_DISPOSITION: C R E A T I O N UNDERLINE D I S P O S I T I O N; +CREDENTIAL: C R E D E N T I A L; +CROSS: C R O S S; +CRYPTOGRAPHIC: C R Y P T O G R A P H I C; +CUBE: C U B E; +CUME_DIST: C U M E UNDERLINE D I S T; +CURRENT: C U R R E N T; +CURRENT_DATE: C U R R E N T UNDERLINE D A T E; +CURRENT_TIME: C U R R E N T UNDERLINE T I M E; +CURRENT_TIMESTAMP: C U R R E N T UNDERLINE T I M E S T A M P; +CURRENT_USER: C U R R E N T UNDERLINE U S E R; +CURSOR: C U R S O R; +CURSOR_CLOSE_ON_COMMIT: C U R S O R UNDERLINE C L O S E UNDERLINE O N UNDERLINE C O M M I T; +CURSOR_DEFAULT: C U R S O R UNDERLINE D E F A U L T; +CUSTOM: C U S T O M; +CYCLE: C Y C L E; +D: [Dd]; +DATA: D A T A; +DATABASE: D A T A B A S E; +DATABASE_MIRRORING: D A T A B A S E UNDERLINE M I R R O R I N G; +DATA_COMPRESSION: D A T A UNDERLINE C O M P R E S S I O N; +DATA_CONSISTENCY_CHECK: D A T A UNDERLINE C O N S I S T E N C Y UNDERLINE C H E C K; +DATA_FLUSH_INTERVAL_SECONDS: D A T A UNDERLINE F L U S H UNDERLINE I N T E R V A L UNDERLINE S E C O N D S; +DATA_SOURCE: D A T A UNDERLINE S O U R C E; +DATASPACE: D A T A S P A C E; +DATEADD: D A T E A D D; +DATEDIFF: D A T E D I F F; +DATEFIRST: D A T E F I R S T; +DATEFORMAT: D A T E F O R M A T; +DATE_FORMAT: D A T E UNDERLINE F O R M A T; +DATENAME: D A T E N A M E; +DATEPART: D A T E P A R T; +DATE_CORRELATION_OPTIMIZATION: D A T E UNDERLINE C O R R E L A T I O N UNDERLINE O P T I M I Z A T I O N; +DAY: D A Y; +DAYS: D A Y S; +DBCC: D B C C; +DB_CHAINING: D B UNDERLINE C H A I N I N G; +DB_FAILOVER: D B UNDERLINE F A I L O V E R; +DDL: D D L; +DEALLOCATE: D E A L L O C A T E; +DECLARE: D E C L A R E; +DECRYPTION: D E C R Y P T I O N; +DEFAULT: D E F A U L T; +DEFAULT_DOUBLE_QUOTE: ["] D E F A U L T ["]; +DEFAULT_DATABASE: D E F A U L T UNDERLINE D A T A B A S E; +DEFAULT_FULLTEXT_LANGUAGE: D E F A U L T UNDERLINE F U L L T E X T UNDERLINE L A N G U A G E; +DEFAULT_LANGUAGE: D E F A U L T UNDERLINE L A N G U A G E; +DEFAULT_SCHEMA: D E F A U L T UNDERLINE S C H E M A; +DEFINITION: D E F I N I T I O N; +DELAY: D E L A Y; +DELAYED_DURABILITY: D E L A Y E D UNDERLINE D U R A B I L I T Y; +DELETE: D E L E T E; +DELETED: D E L E T E D; +DENSE_RANK: D E N S E UNDERLINE R A N K; +DENY: D E N Y; +DEPENDENTS: D E P E N D E N T S; +DES: D E S; +DESC: D E S C; +DESCRIPTION: D E S C R I P T I O N; +DESX: D E S X; +DHCP: D H C P; +DIAGNOSTICS: D I A G N O S T I C S; +DIALOG: D I A L O G; +DIFFERENTIAL: D I F F E R E N T I A L; +DIRECTORY_NAME: D I R E C T O R Y UNDERLINE N A M E; +DISABLE: D I S A B L E; +DISABLED: D I S A B L E D; +DISABLE_BROKER: D I S A B L E UNDERLINE B R O K E R; +DISK: D I S K; +DISK_DRIVE: [A-Z][:]; +DISTINCT: D I S T I N C T; +DISTRIBUTED: D I S T R I B U T E D; +DISTRIBUTED_AGG: D I S T R I B U T E D UNDERLINE A G G; +DOCUMENT: D O C U M E N T; +DOLLAR_ACTION: DOLLAR A C T I O N; +DOLLAR_EDGE_ID: DOLLAR E D G E UNDERLINE I D; // graph +DOLLAR_FROM_ID: DOLLAR F R O M UNDERLINE I D; // graph +DOLLAR_IDENTITY: DOLLAR I D E N T I T Y; +DOLLAR_NODE_ID: DOLLAR N O D E UNDERLINE I D; // graph +DOLLAR_PARTITION: DOLLAR P A R T I T I O N; +DOLLAR_ROWGUID: DOLLAR R O W G U I D; +DOLLAR_TO_ID: DOLLAR T O UNDERLINE I D; // graph +DOUBLE: D O U B L E; +DROP: D R O P; +DTC_SUPPORT: D T C UNDERLINE S U P P O R T; +DUMP: D U M P; +DYNAMIC: D Y N A M I C; +EDGE: E D G E; +ELEMENTS: E L E M E N T S; +ELSE: E L S E; +EMERGENCY: E M E R G E N C Y; +EMPTY: E M P T Y; +ENABLE: E N A B L E; +ENABLED: E N A B L E D; +ENABLE_BROKER: E N A B L E UNDERLINE B R O K E R; +ENCRYPTED_VALUE: E N C R Y P T E D UNDERLINE V A L U E; +ENCRYPTION: E N C R Y P T I O N; +ENCODING: E N C O D I N G; +END: E N D; +ENDPOINT: E N D P O I N T; +ENDPOINT_URL: E N D P O I N T UNDERLINE U R L; +ERRLVL: E R R L V L; +ERROR: E R R O R; +ERROR_BROKER_CONVERSATIONS: E R R O R UNDERLINE B R O K E R UNDERLINE C O N V E R S A T I O N S; +ESCAPE: E S C A P E; +EVENT: E V E N T; +EVENTDATA: E V E N T D A T A '(' ')'; +EVENT_RETENTION_MODE: E V E N T UNDERLINE R E T E N T I O N UNDERLINE M O D E; +EXCEPT: E X C E P T; +EXCLUSIVE: E X C L U S I V E; +EXEC: E X E C; +EXECUTE: E X E C U T E; +EXECUTABLE: E X E C U T A B L E; +EXECUTABLE_FILE: E X E C U T A B L E UNDERLINE F I L E; +EXECUTION_COUNT: E X E C U T I O N UNDERLINE C O U N T; +EXIST: E X I S T; +EXISTS: E X I S T S; +EXIT: E X I T; +EXPAND: E X P A N D; +EXPIREDATE: E X P I R E D A T E; +EXPIRY_DATE: E X P I R Y UNDERLINE D A T E; +EXPLICIT: E X P L I C I T; +EXTENSION: E X T E N S I O N; +EXTERNAL: E X T E R N A L; +EXTERNALPUSHDOWN: E X T E R N A L P U S H D O W N; +EXTERNAL_ACCESS: E X T E R N A L UNDERLINE A C C E S S; +EXTRACT: E X T R A C T; +FAILOVER: F A I L O V E R; +FAILOVER_MODE: F A I L O V E R UNDERLINE M O D E; +FAILURE: F A I L U R E; +FAILURECONDITIONLEVEL: F A I L U R E C O N D I T I O N L E V E L; +FAILURE_CONDITION_LEVEL: F A I L U R E UNDERLINE C O N D I T I O N UNDERLINE L E V E L; +FAIL_OPERATION: F A I L UNDERLINE O P E R A T I O N; +FAIL_UNSUPPORTED: F A I L UNDERLINE U N S U P P O R T E D; +FAN_IN: F A N UNDERLINE I N; +FALSE: F A L S E; +FAST: F A S T; +FAST_FORWARD: F A S T UNDERLINE F O R W A R D; +FETCH: F E T C H; +FIELD_TERMINATOR: F I E L D UNDERLINE T E R M I N A T O R; +FILE: F I L E; +FILEGROUP: F I L E G R O U P; +FILEGROWTH: F I L E G R O W T H; +FILENAME: F I L E N A M E; +FILEPATH: F I L E P A T H; +FILESTREAM: F I L E S T R E A M; +FILESTREAM_ON: F I L E S T R E A M UNDERLINE O N; +FILETABLE: F I L E T A B L E; +FILE_SNAPSHOT: F I L E UNDERLINE S N A P S H O T; +FILTER: F I L T E R; +FIPS_FLAGGER: F I P S UNDERLINE F L A G G E R; +FIRST: F I R S T; +FIRST_ROW: F I R S T UNDERLINE R O W; +FIRST_VALUE: F I R S T UNDERLINE V A L U E; +FMTONLY: F M T O N L Y; +FN: F N; +FOLLOWING: F O L L O W I N G; +FOR: F O R; +FORCE: F O R C E; +FORCED: F O R C E D; +FORCEPLAN: F O R C E P L A N; +FORCESEEK: F O R C E S E E K; +FORCE_FAILOVER_ALLOW_DATA_LOSS: F O R C E UNDERLINE F A I L O V E R UNDERLINE A L L O W UNDERLINE D A T A UNDERLINE L O S S; +FORCE_SERVICE_ALLOW_DATA_LOSS: F O R C E UNDERLINE S E R V I C E UNDERLINE A L L O W UNDERLINE D A T A UNDERLINE L O S S; +FOREIGN: F O R E I G N; +FORMAT: F O R M A T; +FORWARD_ONLY: F O R W A R D UNDERLINE O N L Y; +FORMAT_OPTIONS: F O R M A T UNDERLINE O P T I O N S; +FORMAT_TYPE: F O R M A T UNDERLINE T Y P E; +FREETEXT: F R E E T E X T; +FREETEXTTABLE: F R E E T E X T T A B L E; +FROM: F R O M; +FULL: F U L L; +FULLSCAN: F U L L S C A N; +FULLTEXT: F U L L T E X T; +FUNCTION: F U N C T I O N; +GB: G B; +GENERATED: G E N E R A T E D; +GEOGRAPHY_AUTO_GRID: G E O G R A P H Y UNDERLINE A U T O UNDERLINE G R I D; +GEOGRAPHY_GRID: G E O G R A P H Y UNDERLINE G R I D; +GEOMETRY_AUTO_GRID: G E O M E T R Y UNDERLINE A U T O UNDERLINE G R I D; +GEOMETRY_GRID: G E O M E T R Y UNDERLINE G R I D; +GET: G E T; +GETANCESTOR: G E T A N C E S T O R; +GETDATE: G E T D A T E; +GETDESCENDANT: G E T D E S C E N D A N T; +GETLEVEL: G E T L E V E L; +GETREPARENTEDVALUE: G E T R E P A R E N T E D V A L U E; +GETROOT: G E T R O O T; +GETUTCDATE: G E T U T C D A T E; +GLOBAL: G L O B A L; +GOTO: G O T O; +GOVERNOR: G O V E R N O R; +GRANT: G R A N T; +GRIDS: G R I D S; +GROUP: G R O U P; +GROUPING: G R O U P I N G; +GROUPING_ID: G R O U P I N G UNDERLINE I D; +GROUP_MAX_REQUESTS: G R O U P UNDERLINE M A X UNDERLINE R E Q U E S T S; +GUID: G U I D; +HADR: H A D R; +HASH: H A S H; +HASHED: H A S H E D; +HAVING: H A V I N G; +HEALTHCHECKTIMEOUT: H E A L T H C H E C K T I M E O U T; +HEALTH_CHECK_TIMEOUT: H E A L T H UNDERLINE C H E C K UNDERLINE T I M E O U T; +HIDDEN_RENAMED: H I D D E N; +HIGH: H I G H; +HINT: H I N T; +HISTORY_RETENTION_PERIOD: H I S T O R Y UNDERLINE R E T E N T I O N UNDERLINE P E R I O D; +HISTORY_TABLE: H I S T O R Y UNDERLINE T A B L E; +HOLDLOCK: H O L D L O C K; +HONOR_BROKER_PRIORITY: H O N O R UNDERLINE B R O K E R UNDERLINE P R I O R I T Y; +HOUR: H O U R; +HOURS: H O U R S; +IDENTITY: I D E N T I T Y; +IDENTITYCOL: I D E N T I T Y C O L; +IDENTITY_INSERT: I D E N T I T Y UNDERLINE I N S E R T; +IDENTITY_VALUE: I D E N T I T Y UNDERLINE V A L U E; +IF: I F; +IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX: I G N O R E UNDERLINE N O N C L U S T E R E D UNDERLINE C O L U M N S T O R E UNDERLINE I N D E X; +IIF: I I F; +IMMEDIATE: I M M E D I A T E; +IMPERSONATE: I M P E R S O N A T E; +IMPLICIT_TRANSACTIONS: I M P L I C I T UNDERLINE T R A N S A C T I O N S; +IMPORTANCE: I M P O R T A N C E; +IN: I N; +INCLUDE: I N C L U D E; +INCLUDE_NULL_VALUES: I N C L U D E UNDERLINE N U L L UNDERLINE V A L U E S; +INCREMENT: I N C R E M E N T; +INCREMENTAL: I N C R E M E N T A L; +INDEX: I N D E X; +INFINITE: I N F I N I T E; +INIT: I N I T; +INITIATOR: I N I T I A T O R; +INNER: I N N E R; +INPUT: I N P U T; +INSENSITIVE: I N S E N S I T I V E; +INSERT: I N S E R T; +INSERTED: I N S E R T E D; +INSTEAD: I N S T E A D; +INT: I N T; +INTERSECT: I N T E R S E C T; +INTERVAL: I N T E R V A L; +INTERVAL_LENGTH_MINUTES: I N T E R V A L UNDERLINE L E N G T H UNDERLINE M I N U T E S; +INTO: I N T O; +IO: I O; +IP: I P; +IS: I S; +ISDESCENDANTOF: I S D E S C E N D A N T O F; +ISNULL: I S N U L L; +ISOLATION: I S O L A T I O N; +JOB: J O B; +JOIN: J O I N; +JSON: J S O N; +KB: K B; +KEEP: K E E P; +KEEPFIXED: K E E P F I X E D; +KEEP_CDC: K E E P UNDERLINE C D C; +KEEP_REPLICATION: K E E P UNDERLINE R E P L I C A T I O N; +KERBEROS: K E R B E R O S; +KEY: K E Y; +KEYS: K E Y S; +KEYSET: K E Y S E T; +KEY_PATH: K E Y UNDERLINE P A T H; +KEY_SOURCE: K E Y UNDERLINE S O U R C E; +KEY_STORE_PROVIDER_NAME: K E Y UNDERLINE S T O R E UNDERLINE P R O V I D E R UNDERLINE N A M E; +KILL: K I L L; +LAG: L A G; +LANGUAGE: L A N G U A G E; +LAST: L A S T; +LAST_VALUE: L A S T UNDERLINE V A L U E; +LEAD: L E A D; +LEDGER: L E D G E R; +LEFT: L E F T; +LEVEL: L E V E L; +LIBRARY: L I B R A R Y; +LIFETIME: L I F E T I M E; +LIKE: L I K E; +LINENO: L I N E N O; +LINKED: L I N K E D; +LINUX: L I N U X; +LIST: L I S T; +LISTENER: L I S T E N E R; +LISTENER_IP: L I S T E N E R UNDERLINE I P; +LISTENER_PORT: L I S T E N E R UNDERLINE P O R T; +LISTENER_URL: L I S T E N E R UNDERLINE U R L; +LOAD: L O A D; +LOB_COMPACTION: L O B UNDERLINE C O M P A C T I O N; +LOCAL: L O C A L; +LOCAL_SERVICE_NAME: L O C A L UNDERLINE S E R V I C E UNDERLINE N A M E; +LOCATION: L O C A T I O N; +LOCK: L O C K; +LOCK_ESCALATION: L O C K UNDERLINE E S C A L A T I O N; +LOG: L O G; +LOGIN: L O G I N; +LOOP: L O O P; +LOW: L O W; +MANUAL: M A N U A L; +MARK: M A R K; +MASK: M A S K; +MASKED: M A S K E D; +MASTER: M A S T E R; +MATCHED: M A T C H E D; +MATERIALIZED: M A T E R I A L I Z E D; +MAX: M A X; +MAXDOP: M A X D O P; +MAXRECURSION: M A X R E C U R S I O N; +MAXSIZE: M A X S I Z E; +MAXTRANSFER: M A X T R A N S F E R; +MAXVALUE: M A X V A L U E; +MAX_CPU_PERCENT: M A X UNDERLINE C P U UNDERLINE P E R C E N T; +MAX_DISPATCH_LATENCY: M A X UNDERLINE D I S P A T C H UNDERLINE L A T E N C Y; +MAX_DOP: M A X UNDERLINE D O P; +MAX_DURATION: M A X UNDERLINE D U R A T I O N; +MAX_EVENT_SIZE: M A X UNDERLINE E V E N T UNDERLINE S I Z E; +MAX_FILES: M A X UNDERLINE F I L E S; +MAX_GRANT_PERCENT: M A X UNDERLINE G R A N T UNDERLINE P E R C E N T; +MAX_IOPS_PER_VOLUME: M A X UNDERLINE I O P S UNDERLINE P E R UNDERLINE V O L U M E; +MAX_MEMORY: M A X UNDERLINE M E M O R Y; +MAX_MEMORY_PERCENT: M A X UNDERLINE M E M O R Y UNDERLINE P E R C E N T; +MAX_OUTSTANDING_IO_PER_VOLUME: M A X UNDERLINE O U T S T A N D I N G UNDERLINE I O UNDERLINE P E R UNDERLINE V O L U M E; +MAX_PLANS_PER_QUERY: M A X UNDERLINE P L A N S UNDERLINE P E R UNDERLINE Q U E R Y; +MAX_PROCESSES: M A X UNDERLINE P R O C E S S E S; +MAX_QUEUE_READERS: M A X UNDERLINE Q U E U E UNDERLINE R E A D E R S; +MAX_ROLLOVER_FILES: M A X UNDERLINE R O L L O V E R UNDERLINE F I L E S; +MAX_SIZE: M A X UNDERLINE S I Z E; +MAX_SIZE_MB: M A X UNDERLINE S I Z E UNDERLINE M B; +MAX_STORAGE_SIZE_MB: M A X UNDERLINE S T O R A G E UNDERLINE S I Z E UNDERLINE M B; +MB: M B; +MEDIADESCRIPTION: M E D I A D E S C R I P T I O N; +MEDIANAME: M E D I A N A M E; +MEDIUM: M E D I U M; +MEMBER: M E M B E R; +MEMORY_OPTIMIZED_DATA: M E M O R Y UNDERLINE O P T I M I Z E D UNDERLINE D A T A; +MEMORY_PARTITION_MODE: M E M O R Y UNDERLINE P A R T I T I O N UNDERLINE M O D E; +MERGE: M E R G E; +MESSAGE: M E S S A G E; +MESSAGE_FORWARDING: M E S S A G E UNDERLINE F O R W A R D I N G; +MESSAGE_FORWARD_SIZE: M E S S A G E UNDERLINE F O R W A R D UNDERLINE S I Z E; +MIN: M I N; +MINUTE: M I N U T E; +MINUTES: M I N U T E S; +MINVALUE: M I N V A L U E; +MIN_ACTIVE_ROWVERSION: M I N UNDERLINE A C T I V E UNDERLINE R O W V E R S I O N; +MIN_CPU_PERCENT: M I N UNDERLINE C P U UNDERLINE P E R C E N T; +MIN_GRANT_PERCENT: M I N UNDERLINE G R A N T UNDERLINE P E R C E N T; +MIN_IOPS_PER_VOLUME: M I N UNDERLINE I O P S UNDERLINE P E R UNDERLINE V O L U M E; +MIN_MEMORY_PERCENT: M I N UNDERLINE M E M O R Y UNDERLINE P E R C E N T; +MIRROR: M I R R O R; +MIRROR_ADDRESS: M I R R O R UNDERLINE A D D R E S S; +MIXED_PAGE_ALLOCATION: M I X E D UNDERLINE P A G E UNDERLINE A L L O C A T I O N; +MODE: M O D E; +MODEL: M O D E L; +MODIFY: M O D I F Y; +MONTH: M O N T H; +MONTHS: M O N T H S; +MOVE: M O V E; +MULTI_USER: M U L T I UNDERLINE U S E R; +MUST_CHANGE: M U S T UNDERLINE C H A N G E; +NAME: N A M E; +NATIONAL: N A T I O N A L; +NATIVE_COMPILATION: N A T I V E UNDERLINE C O M P I L A T I O N; +NEGOTIATE: N E G O T I A T E; +NESTED_TRIGGERS: N E S T E D UNDERLINE T R I G G E R S; +NEW_ACCOUNT: N E W UNDERLINE A C C O U N T; +NEW_BROKER: N E W UNDERLINE B R O K E R; +NEW_PASSWORD: N E W UNDERLINE P A S S W O R D; +NEXT: N E X T; +NO: N O; +NOCHECK: N O C H E C K; +NOCOMPUTE: N O C O M P U T E; +NOCOUNT: N O C O U N T; +NODE: N O D E; +NODES: N O D E S; +NOEXEC: N O E X E C; +NOEXPAND: N O E X P A N D; +NOFORMAT: N O F O R M A T; +NOINIT: N O I N I T; +NONCLUSTERED: N O N C L U S T E R E D; +NONE: N O N E; +NON_TRANSACTED_ACCESS: N O N UNDERLINE T R A N S A C T E D UNDERLINE A C C E S S; +NORECOMPUTE: N O R E C O M P U T E; +NORECOVERY: N O R E C O V E R Y; +NOREWIND: N O R E W I N D; +NOSKIP: N O S K I P; +NOT: N O T; +NOTIFICATION: N O T I F I C A T I O N; +NOTIFICATIONS: N O T I F I C A T I O N S; +NOUNLOAD: N O U N L O A D; +NOWAIT: N O W A I T; +NO_CHECKSUM: N O UNDERLINE C H E C K S U M; +NO_COMPRESSION: N O UNDERLINE C O M P R E S S I O N; +NO_EVENT_LOSS: N O UNDERLINE E V E N T UNDERLINE L O S S; +NO_PERFORMANCE_SPOOL: N O UNDERLINE P E R F O R M A N C E UNDERLINE S P O O L; +NO_TRUNCATE: N O UNDERLINE T R U N C A T E; +NO_WAIT: N O UNDERLINE W A I T; +NTILE: N T I L E; +NTLM: N T L M; +NULL_P: N U L L; +NULLIF: N U L L I F; +NUMANODE: N U M A N O D E; +NUMBER: N U M B E R; +NUMERIC_ROUNDABORT: N U M E R I C UNDERLINE R O U N D A B O R T; +OBJECT: O B J E C T; +OF: O F; +OFF: O F F; +OFFLINE: O F F L I N E; +OFFSET: O F F S E T; +OFFSETS: O F F S E T S; +OLD_ACCOUNT: O L D UNDERLINE A C C O U N T; +OLD_PASSWORD: O L D UNDERLINE P A S S W O R D; +ON: O N; +ONLINE: O N L I N E; +ONLY: O N L Y; +ON_FAILURE: O N UNDERLINE F A I L U R E; +OPEN: O P E N; +OPENDATASOURCE: O P E N D A T A S O U R C E; +OPENJSON: O P E N J S O N; +OPENQUERY: O P E N Q U E R Y; +OPENROWSET: O P E N R O W S E T; +OPENXML: O P E N X M L; +OPEN_EXISTING: O P E N UNDERLINE E X I S T I N G; +OPERATIONS: O P E R A T I O N S; +OPERATION_MODE: O P E R A T I O N UNDERLINE M O D E; +OPTIMISTIC: O P T I M I S T I C; +OPTIMIZE: O P T I M I Z E; +OPTION: O P T I O N; +OR: O R; +ORDER: O R D E R; +OUT: O U T; +OUTER: O U T E R; +OUTPUT: O U T P U T; +OVER: O V E R; +OVERRIDE: O V E R R I D E; +OWNER: O W N E R; +OWNERSHIP: O W N E R S H I P; +PAGE: P A G E; +PAGECOUNT: P A G E C O U N T; +PAGE_VERIFY: P A G E UNDERLINE V E R I F Y; +PARAM: P A R A M; +PARAMETERIZATION: P A R A M E T E R I Z A T I O N; +PARAM_NODE: P A R A M UNDERLINE N O D E; +PARSE: P A R S E; +PARSEONLY: P A R S E O N L Y; +PARTIAL: P A R T I A L; +PARTITION: P A R T I T I O N; +PARTITIONS: P A R T I T I O N S; +PARTNER: P A R T N E R; +PASSWORD: P A S S W O R D; +PATH: P A T H; +PAUSE: P A U S E; +PERCENT: P E R C E N T; +PERCENTILE_CONT: P E R C E N T I L E UNDERLINE C O N T; +PERCENTILE_DISC: P E R C E N T I L E UNDERLINE D I S C; +PERCENT_RANK: P E R C E N T UNDERLINE R A N K; +PERIOD: P E R I O D; +PERMISSION_SET: P E R M I S S I O N UNDERLINE S E T; +PERSISTED: P E R S I S T E D; +PERSIST_SAMPLE_PERCENT: P E R S I S T UNDERLINE S A M P L E UNDERLINE P E R C E N T; +PERSISTENT_LOG_BUFFER: P E R S I S T E N T UNDERLINE L O G UNDERLINE B U F F E R; +PER_CPU: P E R UNDERLINE C P U; +PER_DB: P E R UNDERLINE D B; +PER_NODE: P E R UNDERLINE N O D E; +PIVOT: P I V O T; +PLAN: P L A N; +PLATFORM: P L A T F O R M; +POISON_MESSAGE_HANDLING: P O I S O N UNDERLINE M E S S A G E UNDERLINE H A N D L I N G; +POLICY: P O L I C Y; +POOL: P O O L; +POPULATION: P O P U L A T I O N; +PORT: P O R T; +POSITION: P O S I T I O N; +PRECEDING: P R E C E D I N G; +PRECISION: P R E C I S I O N; +PREDICATE: P R E D I C A T E; +PREDICT: P R E D I C T; +PRIMARY: P R I M A R Y; +PRIMARY_ROLE: P R I M A R Y UNDERLINE R O L E; +PRINT: P R I N T; +PRIOR: P R I O R; +PRIORITY: P R I O R I T Y; +PRIORITY_LEVEL: P R I O R I T Y UNDERLINE L E V E L; +PRIVATE: P R I V A T E; +PRIVATE_KEY: P R I V A T E UNDERLINE K E Y; +PRIVILEGES: P R I V I L E G E S; +PROC: P R O C; +PROCEDURE: P R O C E D U R E; +PROCEDURE_CACHE: P R O C E D U R E UNDERLINE C A C H E; +PROCEDURE_NAME: P R O C E D U R E UNDERLINE N A M E; +PROCESS: P R O C E S S; +PROFILE: P R O F I L E; +PROPERTY: P R O P E R T Y; +PROVIDER: P R O V I D E R; +PROVIDER_KEY_NAME: P R O V I D E R UNDERLINE K E Y UNDERLINE N A M E; +PUBLIC: P U B L I C; +PYTHON: P Y T H O N; +QUERY: Q U E R Y; +QUERYTRACEON: Q U E R Y T R A C E O N; +QUERY_CAPTURE_MODE: Q U E R Y UNDERLINE C A P T U R E UNDERLINE M O D E; +QUERY_CAPTURE_POLICY: Q U E R Y UNDERLINE C A P T U R E UNDERLINE P O L I C Y; +QUERY_STORE: Q U E R Y UNDERLINE S T O R E; +QUEUE: Q U E U E; +QUEUE_DELAY: Q U E U E UNDERLINE D E L A Y; +QUOTED_IDENTIFIER: Q U O T E D UNDERLINE I D E N T I F I E R; +R: [Rr]; +RAISERROR: R A I S E R R O R; +RANGE: R A N G E; +RANK: R A N K; +RAW: R A W; +RC2: R C '2'; +RC4: R C '4'; +RC4_128: R C '4' UNDERLINE '128'; +READ: R E A D; +READONLY: R E A D O N L Y; +READTEXT: R E A D T E X T; +READWRITE: R E A D W R I T E; +READ_COMMITTED_SNAPSHOT: R E A D UNDERLINE C O M M I T T E D UNDERLINE S N A P S H O T; +READ_ONLY: R E A D UNDERLINE O N L Y; +READ_ONLY_ROUTING_LIST: R E A D UNDERLINE O N L Y UNDERLINE R O U T I N G UNDERLINE L I S T; +READ_WRITE: R E A D UNDERLINE W R I T E; +READ_WRITE_FILEGROUPS: R E A D UNDERLINE W R I T E UNDERLINE F I L E G R O U P S; +REBUILD: R E B U I L D; +RECEIVE: R E C E I V E; +RECOMPILE: R E C O M P I L E; +RECONFIGURE: R E C O N F I G U R E; +RECOVERY: R E C O V E R Y; +RECURSIVE_TRIGGERS: R E C U R S I V E UNDERLINE T R I G G E R S; +REDISTRIBUTE: R E D I S T R I B U T E; +REDUCE: R E D U C E; +REFERENCES: R E F E R E N C E S; +REGENERATE: R E G E N E R A T E; +RELATED_CONVERSATION: R E L A T E D UNDERLINE C O N V E R S A T I O N; +RELATED_CONVERSATION_GROUP: R E L A T E D UNDERLINE C O N V E R S A T I O N UNDERLINE G R O U P; +RELATIVE: R E L A T I V E; +REMOTE: R E M O T E; +REMOTE_PROC_TRANSACTIONS: R E M O T E UNDERLINE P R O C UNDERLINE T R A N S A C T I O N S; +REMOTE_SERVICE_NAME: R E M O T E UNDERLINE S E R V I C E UNDERLINE N A M E; +REMOVE: R E M O V E; +REORGANIZE: R E O R G A N I Z E; +REPEATABLE: R E P E A T A B L E; +REPLACE: R E P L A C E; +REPLICA: R E P L I C A; +REPLICATE: R E P L I C A T E; +REPLICATION: R E P L I C A T I O N; +REQUEST_MAX_CPU_TIME_SEC: R E Q U E S T UNDERLINE M A X UNDERLINE C P U UNDERLINE T I M E UNDERLINE S E C; +REQUEST_MAX_MEMORY_GRANT_PERCENT: R E Q U E S T UNDERLINE M A X UNDERLINE M E M O R Y UNDERLINE G R A N T UNDERLINE P E R C E N T; +REQUEST_MEMORY_GRANT_TIMEOUT_SEC: R E Q U E S T UNDERLINE M E M O R Y UNDERLINE G R A N T UNDERLINE T I M E O U T UNDERLINE S E C; +REQUIRED: R E Q U I R E D; +REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT: R E Q U I R E D UNDERLINE S Y N C H R O N I Z E D UNDERLINE S E C O N D A R I E S UNDERLINE T O UNDERLINE C O M M I T; +RESAMPLE: R E S A M P L E; +RESERVE_DISK_SPACE: R E S E R V E UNDERLINE D I S K UNDERLINE S P A C E; +RESET: R E S E T; +RESOURCE: R E S O U R C E; +RESOURCES: R E S O U R C E S; +RESOURCE_MANAGER_LOCATION: R E S O U R C E UNDERLINE M A N A G E R UNDERLINE L O C A T I O N; +RESTART: R E S T A R T; +RESTORE: R E S T O R E; +RESTRICT: R E S T R I C T; +RESTRICTED_USER: R E S T R I C T E D UNDERLINE U S E R; +RESULT: R E S U L T; +RESUME: R E S U M E; +RETAINDAYS: R E T A I N D A Y S; +RETENTION: R E T E N T I O N; +RETURN: R E T U R N; +RETURNS: R E T U R N S; +REVERT: R E V E R T; +REVOKE: R E V O K E; +REWIND: R E W I N D; +RIGHT: R I G H T; +ROBUST: R O B U S T; +ROLE: R O L E; +ROLLBACK: R O L L B A C K; +ROLLUP: R O L L U P; +ROOT: R O O T; +ROUTE: R O U T E; +ROW: R O W; +ROWCOUNT: R O W C O U N T; +ROWGUID: R O W G U I D; +ROWGUIDCOL: R O W G U I D C O L; +ROWS: R O W S; +ROW_NUMBER: R O W UNDERLINE N U M B E R; +RSA_1024: R S A UNDERLINE '1024'; +RSA_2048: R S A UNDERLINE '2048'; +RSA_3072: R S A UNDERLINE '3072'; +RSA_4096: R S A UNDERLINE '4096'; +RSA_512: R S A UNDERLINE '512'; +RULE: R U L E; +RUNTIME: R U N T I M E; +SAFE: S A F E; +SAFETY: S A F E T Y; +SAMPLE: S A M P L E; +SAVE: S A V E; +SCALEOUTEXECUTION: S C A L E O U T E X E C U T I O N; +SCHEDULER: S C H E D U L E R; +SCHEMA: S C H E M A; +SCHEMABINDING: S C H E M A B I N D I N G; +SCHEME: S C H E M E; +SCOPED: S C O P E D; +SCRIPT: S C R I P T; +SCROLL: S C R O L L; +SCROLL_LOCKS: S C R O L L UNDERLINE L O C K S; +SEARCH: S E A R C H; +SECOND: S E C O N D; +SECONDARY: S E C O N D A R Y; +SECONDARY_ONLY: S E C O N D A R Y UNDERLINE O N L Y; +SECONDARY_ROLE: S E C O N D A R Y UNDERLINE R O L E; +SECONDS: S E C O N D S; +SECRET: S E C R E T; +SECURABLES: S E C U R A B L E S; +SECURITY: S E C U R I T Y; +SECURITYAUDIT: S E C U R I T Y A U D I T; +SECURITY_LOG: S E C U R I T Y UNDERLINE L O G; +SEEDING_MODE: S E E D I N G UNDERLINE M O D E; +SELECT: S E L E C T; +SELECTIVE: S E L E C T I V E; +SELF: S E L F; +SEMANTICKEYPHRASETABLE: S E M A N T I C K E Y P H R A S E T A B L E; +SEMANTICSIMILARITYDETAILSTABLE: S E M A N T I C S I M I L A R I T Y D E T A I L S T A B L E; +SEMANTICSIMILARITYTABLE: S E M A N T I C S I M I L A R I T Y T A B L E; +SEMI_SENSITIVE: S E M I UNDERLINE S E N S I T I V E; +SEND: S E N D; +SENT: S E N T; +SEQUENCE: S E Q U E N C E; +SEQUENCE_NUMBER: S E Q U E N C E UNDERLINE N U M B E R; +SERIALIZABLE: S E R I A L I Z A B L E; +SERVER: S E R V E R; +SERVICE: S E R V I C E; +SERVICE_BROKER: S E R V I C E UNDERLINE B R O K E R; +SERVICE_NAME: S E R V I C E UNDERLINE N A M E; +SESSION: S E S S I O N; +SESSION_TIMEOUT: S E S S I O N UNDERLINE T I M E O U T; +SESSION_USER: S E S S I O N UNDERLINE U S E R; +SET: S E T; +SETERROR: S E T E R R O R; +SETS: S E T S; +SETTINGS: S E T T I N G S; +SETUSER: S E T U S E R; +SHARE: S H A R E; +SHOWPLAN: S H O W P L A N; +SHOWPLAN_ALL: S H O W P L A N UNDERLINE A L L; +SHOWPLAN_TEXT: S H O W P L A N UNDERLINE T E X T; +SHOWPLAN_XML: S H O W P L A N UNDERLINE X M L; +SHRINKLOG: S H R I N K L O G; +SHUTDOWN: S H U T D O W N; +SID: S I D; +SIGNATURE: S I G N A T U R E; +SIMPLE: S I M P L E; +SINGLE_USER: S I N G L E UNDERLINE U S E R; +SINGLETON: S I N G L E T O N; +SIZE: S I Z E; +SIZE_BASED_CLEANUP_MODE: S I Z E UNDERLINE B A S E D UNDERLINE C L E A N U P UNDERLINE M O D E; +SKIP_KEYWORD: S K I P; +SMALLINT: S M A L L I N T; +SNAPSHOT: S N A P S H O T; +SOFTNUMA: S O F T N U M A; +SOME: S O M E; +SOURCE: S O U R C E; +SPARSE: S P A R S E; +SPATIAL: S P A T I A L; +SPATIAL_WINDOW_MAX_CELLS: S P A T I A L UNDERLINE W I N D O W UNDERLINE M A X UNDERLINE C E L L S; +SPECIFICATION: S P E C I F I C A T I O N; +SPLIT: S P L I T; +SQL: S Q L; +SQLDUMPERFLAGS: S Q L D U M P E R F L A G S; +SQLDUMPERPATH: S Q L D U M P E R P A T H; +SQLDUMPERTIMEOUT: S Q L D U M P E R T I M E O U T S; +STALE_CAPTURE_POLICY_THRESHOLD: S T A L E UNDERLINE C A P T U R E UNDERLINE P O L I C Y UNDERLINE T H R E S H O L D; +STALE_QUERY_THRESHOLD_DAYS: S T A L E UNDERLINE Q U E R Y UNDERLINE T H R E S H O L D UNDERLINE D A Y S; +STANDBY: S T A N D B Y; +START: S T A R T; +STARTED: S T A R T E D; +STARTUP_STATE: S T A R T U P UNDERLINE S T A T E; +START_DATE: S T A R T UNDERLINE D A T E; +STATE: S T A T E; +STATEMENT: S T A T E M E N T; +STATIC: S T A T I C; +STATISTICAL_SEMANTICS: S T A T I S T I C A L UNDERLINE S E M A N T I C S; +STATISTICS: S T A T I S T I C S; +STATS: S T A T S; +STATS_STREAM: S T A T S UNDERLINE S T R E A M; +STATUS: S T A T U S; +STATUSONLY: S T A T U S O N L Y; +STDEV: S T D E V; +STDEVP: S T D E V P; +STOP: S T O P; +STOPAT: S T O P A T; +STOPATMARK: S T O P A T M A R K; +STOPBEFOREMARK: S T O P B E F O R E M A R K; +STOPLIST: S T O P L I S T; +STOPPED: S T O P P E D; +STOP_ON_ERROR: S T O P UNDERLINE O N UNDERLINE E R R O R; +STRING_AGG: S T R I N G UNDERLINE A G G; +STRING_DELIMITER: S T R I N G UNDERLINE D E L I M I T E R; +STUFF: S T U F F; +SUBJECT: S U B J E C T; +SUBSCRIBE: S U B S C R I B E; +SUBSCRIPTION: S U B S C R I P T I O N; +SUM: S U M; +SUPPORTED: S U P P O R T E D; +SUSPEND: S U S P E N D; +SWITCH: S W I T C H; +SYMMETRIC: S Y M M E T R I C; +SYNCHRONOUS_COMMIT: S Y N C H R O N O U S UNDERLINE C O M M I T; +SYNONYM: S Y N O N Y M; +SYSTEM: S Y S T E M; +SYSTEM_TIME: S Y S T E M UNDERLINE T I M E; +SYSTEM_USER: S Y S T E M UNDERLINE U S E R; +SYSTEM_VERSIONING: S Y S T E M UNDERLINE V E R S I O N I N G; +TABLE: T A B L E; +TABLESAMPLE: T A B L E S A M P L E; +TAKE: T A K E; +TAPE: T A P E; +TARGET: T A R G E T; +TARGET_RECOVERY_TIME: T A R G E T UNDERLINE R E C O V E R Y UNDERLINE T I M E; +T: [Tt]; +TB: T B; +TCP: T C P; +TEXTIMAGE_ON: T E X T I M A G E UNDERLINE O N; +TEXTSIZE: T E X T S I Z E; +THEN: T H E N; +THROW: T H R O W; +TIES: T I E S; +TIME: T I M E; +TIMEOUT: T I M E O U T; +TIMER: T I M E R; +TINYINT: T I N Y I N T; +TO: T O; +TOP: T O P; +TORN_PAGE_DETECTION: T O R N UNDERLINE P A G E UNDERLINE D E T E C T I O N; +TOSTRING: T O S T R I N G; +TOTAL_COMPILE_CPU_TIME_MS: T O T A L UNDERLINE C O M P I L E UNDERLINE C P U UNDERLINE T I M E UNDERLINE M S; +TOTAL_EXECUTION_CPU_TIME_MS: T O T A L UNDERLINE E X E C U T I O N UNDERLINE C P U UNDERLINE T I M E UNDERLINE M S; +TRACE: T R A C E; +TRACKING: T R A C K I N G; +TRACK_CAUSALITY: T R A C K UNDERLINE C A U S A L I T Y; +TRACK_COLUMNS_UPDATED: T R A C K UNDERLINE C O L U M N S UNDERLINE U P D A T E D; +TRAN: T R A N; +TRANSACTION: T R A N S A C T I O N; +TRANSACTION_ID: T R A N S A C T I O N UNDERLINE I D; +TRANSFER: T R A N S F E R; +TRANSFORM_NOISE_WORDS: T R A N S F O R M UNDERLINE N O I S E UNDERLINE W O R D S; +TRIGGER: T R I G G E R; +TRIM: T R I M; +TRIPLE_DES: T R I P L E UNDERLINE D E S; +TRIPLE_DES_3KEY: T R I P L E UNDERLINE D E S UNDERLINE '3' K E Y; +TRUE: T R U E; +TRUNCATE: T R U N C A T E; +TRUSTWORTHY: T R U S T W O R T H Y; +TRY: T R Y; +TRY_CAST: T R Y UNDERLINE C A S T; +TRY_CONVERT: T R Y UNDERLINE C O N V E R T; +TRY_PARSE: T R Y UNDERLINE P A R S E; +TS: T S; +TSEQUAL: T S E Q U A L; +TSQL: T S Q L; +TWO_DIGIT_YEAR_CUTOFF: T W O UNDERLINE D I G I T UNDERLINE Y E A R UNDERLINE C U T O F F; +TYPE: T Y P E; +TYPE_WARNING: T Y P E UNDERLINE W A R N I N G; +UNBOUNDED: U N B O U N D E D; +UNCHECKED: U N C H E C K E D; +UNCOMMITTED: U N C O M M I T T E D; +UNDEFINED: U N D E F I N E D; +UNION: U N I O N; +UNIQUE: U N I Q U E; +UNKNOWN: U N K N O W N; +UNLIMITED: U N L I M I T E D; +UNLOCK: U N L O C K; +UNMASK: U N M A S K; +UNPIVOT: U N P I V O T; +UNSAFE: U N S A F E; +UOW: U O W; +UPDATE: U P D A T E; +UPDATETEXT: U P D A T E T E X T; +URL: U R L; +USE: U S E; +USE_TYPE_DEFAULT: U S E UNDERLINE T Y P E UNDERLINE D E F A U L T; +USED: U S E D; +USER: U S E R; +USING: U S I N G; +VALIDATION: V A L I D A T I O N; +VALID_XML: V A L I D UNDERLINE X M L; +VALUE: V A L U E; +VALUES: V A L U E S; +VAR: V A R; +VARBINARY_KEYWORD: V A R B I N A R Y; +VARP: V A R P; +VARYING: V A R Y I N G; +VERBOSELOGGING: V E R B O S E L O G G I N G; +VERSION: V E R S I O N; +VIEW: V I E W; +VIEWS: V I E W S; +VIEW_METADATA: V I E W UNDERLINE M E T A D A T A; +VISIBILITY: V I S I B I L I T Y; +WAIT: W A I T; +WAITFOR: W A I T F O R; +WAIT_AT_LOW_PRIORITY: W A I T UNDERLINE A T UNDERLINE L O W UNDERLINE P R I O R I T Y; +WAIT_STATS_CAPTURE_MODE: W A I T UNDERLINE S T A T S UNDERLINE C A P T U R E UNDERLINE M O D E; +WEEK: W E E K; +WEEKS: W E E K S; +WELL_FORMED_XML: W E L L UNDERLINE F O R M E D UNDERLINE X M L; +WHEN: W H E N; +WHEN_SUPPORTED: W H E N UNDERLINE S U P P O R T E D; +WHERE: W H E R E; +WHILE: W H I L E; +WINDOWS: W I N D O W S; +WITH: W I T H; +WITHIN: W I T H I N; +WITHOUT: W I T H O U T; +WITHOUT_ARRAY_WRAPPER: W I T H O U T UNDERLINE A R R A Y UNDERLINE W R A P P E R; +WITNESS: W I T N E S S; +WORK: W O R K; +WORKLOAD: W O R K L O A D; +WRITETEXT: W R I T E T E X T; +XACT_ABORT: X A C T UNDERLINE A B O R T; +XMAX: X M A X; +XMIN: X M I N; +XML: X M L; +XMLDATA: X M L D A T A; +XMLNAMESPACES: X M L N A M E S P A C E S; +XMLSCHEMA: X M L S C H E M A; +XSINIL: X S I N I L; +XQUERY: X Q U E R Y; +YEAR: Y E A R; +YEARS: Y E A R S; +YMAX: Y M A X; +YMIN: Y M I N; +ZONE: Z O N E; + +//Build-ins: +VARCHAR: V A R C H A R; +NVARCHAR: N V A R C H A R; + + +SPACE: [ \t\r\n]+ -> skip; + +// the following are ignored by Tsql +CHAR_XA0_NBSP: '\u00a0' -> skip; // non-breaking space +CHAR_X08_BS: '\u0008' -> skip; // backspace +CHAR_X0B_VT: '\u000b' -> skip; // vertical tab +CHAR_X0C_FF: '\u000c' -> skip; // form feed + + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/slash-star-comment-transact-sql +COMMENT: '/*' (COMMENT | .)*? '*/' -> skip; +LINE_COMMENT: '--' ~[\r\n]* -> skip; + +// The next two rules are mutually exclusive - which rule we choose depends on the +// value of QUOTED_IDENTIFIER guc, which reflects the SET QUOTED_IDENTFIER statements encountered. +// The first rule chooses to return a DOUBLE_QUOTE_ID if QUOTED_IDENTIFIER guc is true. +// The second rule chooses to return a STRING if QUOTED_IDENTIFIER guc is false +// NB: for performance reasons, put the QUOTED_IDENTIFIER guc condition at the end, not at the start. +DOUBLE_QUOTE_ID: '"' (~'"' | '""' )* '"' {pltsql_quoted_identifier == true}?; +STRING: 'N'? ('\'' (~'\'' | '\'\'')* '\'' | '"' (~'"' | '""')* '"' {pltsql_quoted_identifier == false}? ); + +SINGLE_QUOTE: '\''; +SQUARE_BRACKET_ID: '[' (~']' | ']' ']')* ']'; +LOCAL_ID: '@' ([_$@#0-9] | LETTER )*; + +DECIMAL: DEC_DIGIT+; +ID: ( [_#] | LETTER) ( [_#$@0-9] | LETTER)*; +BINARY: '0' [Xx] HEX_DIGIT*; +FLOAT: DEC_DOT_DEC; +REAL: (DECIMAL | DEC_DOT_DEC) ([Ee] ([+-]? DEC_DIGIT+)?); + +MONEY: CURRENCY_SYMBOL [ ]* ('+'|'-')? (DECIMAL | DEC_DOT_DEC); + +IPV4_ADDR: DECIMAL DOT DECIMAL DOT DECIMAL DOT DECIMAL; + +EQUAL: '='; + +GREATER: '>'; +LESS: '<'; +EXCLAMATION: '!'; + +PLUS_ASSIGN: '+='; +MINUS_ASSIGN: '-='; +MULT_ASSIGN: '*='; +DIV_ASSIGN: '/='; +MOD_ASSIGN: '%='; +AND_ASSIGN: '&='; +XOR_ASSIGN: '^='; +OR_ASSIGN: '|='; + +DOT: '.'; +UNDERLINE: '_'; +AT: '@'; +SHARP: '#'; +DOLLAR: '$'; +LR_BRACKET: '('; +RR_BRACKET: ')'; +L_CURLY: '{'; +R_CURLY: '}'; +COMMA: ','; +SEMI: ';'; +COLON: ':'; +STAR: '*'; +DIVIDE: '/'; +PERCENT_SIGN: '%'; +PLUS: '+'; +MINUS: '-'; +BIT_NOT: '~'; +BIT_OR: '|'; +BIT_AND: '&'; +BIT_XOR: '^'; + +BACKSLASH: '\\'; +DOUBLE_BACK_SLASH: '\\\\'; +DOUBLE_FORWARD_SLASH: '//'; + +fragment DEC_DOT_DEC: (DEC_DIGIT+ '.' DEC_DIGIT+ | DEC_DIGIT+ '.' | '.' DEC_DIGIT+); +fragment HEX_DIGIT: [0-9A-Fa-f]; +fragment DEC_DIGIT: [0-9]; + +// case-insensitive letters +fragment A: ('A'|'a'); +fragment B: ('B'|'b'); +fragment C: ('C'|'c'); +// fragment D: ('D'|'d'); // redundant, since already defined as token above +fragment E: ('E'|'e'); +fragment F: ('F'|'f'); +fragment G: ('G'|'g'); +fragment H: ('H'|'h'); +fragment I: ('I'|'i'); +fragment J: ('J'|'j'); +fragment K: ('K'|'k'); +fragment L: ('L'|'l'); +fragment M: ('M'|'m'); +fragment N: ('N'|'n'); +fragment O: ('O'|'o'); +fragment P: ('P'|'p'); +fragment Q: ('Q'|'q'); +// fragment R: ('R'|'r'); // redundant, since already defined as token above +fragment S: ('S'|'s'); +// fragment T: ('T'|'t'); // redundant, since already defined as token above +fragment U: ('U'|'u'); +fragment V: ('V'|'v'); +fragment W: ('W'|'w'); +fragment X: ('X'|'x'); +fragment Y: ('Y'|'y'); +fragment Z: ('Z'|'z'); + +fragment CURRENCY_SYMBOL + : '$' // Dollar + | '\u20AC' // Euro + | '\u00A2' // Cent + | '\u00A3' // Pound + | '\u00A4' // Currency Sign + | '\u00A5' // Yen / Yuan + | '\u09f2' // Bengali Rupee Mark + | '\u09f3' // Bengali Rupee Sign + | '\u20a8' // Rupee + | '\u0e3f' // Thai Baht + | '\u17db' // Khmer Riel + | '\u20a0' // Euro Currency Sign + | '\u20a1' // Colon + | '\u20a2' // Cruzeiro + | '\u20a3' // French Franc + | '\u20a4' // Lira + | '\u20a5' // Mill + | '\u20a6' // Naira + | '\u20a7' // Peseta + | '\u20a9' // Won + | '\u20aa' // New Sheqel + | '\u20ab' // Dong + | '\u20ad' // Kip + | '\u20ae' // Tugrik + | '\u20af' // Drachma + | '\u20b0' // German Penny + | '\u20b1' // Peso + | '\ufdfc' // Rial + | '\ufe69' // Small Dollar + | '\uff04' // Fullwidth Dollar + | '\uffe0' // Fullwidth Cent + | '\uffe1' // Fullwidth Pound + | '\uffe5' // Fullwidth Yen + | '\uffe6' // Fullwidth Won + ; + +// use standard alphabet + extended Latin only; add more later if desired. +fragment LETTER + : '\u0041'..'\u005a' // A-Z + | '\u0061'..'\u007a' // a-z + | '\u00c0'..'\u00d6' // Latin-1 Supplement + | '\u00d8'..'\u00f6' + | '\u00f8'..'\u00ff' + | '\u0100'..'\u017f' // Latin Extended-A + | '\u0180'..'\u024f' // Latin Extended-B +// | '\u0250'..'\u02ad' // IPA extensions +// | '\u0386' // Greek +// | '\u0388'..'\u038a' +// | '\u038c' +// | '\u038e'..'\u03a1' +// | '\u03a3'..'\u03ce' +// | '\u03d0'..'\u03d7' +// | '\u03da'..'\u03f3' +// | '\u0400'..'\u0481' // Cyrillic +// | '\u048c'..'\u04c4' +// | '\u04c7'..'\u04c8' +// | '\u04cb'..'\u04cc' +// | '\u04d0'..'\u04f5' +// | '\u04f8'..'\u04f9' +// | '\u05d0'..'\u05ea' // Hebrew +// | '\u0621'..'\u063a' // Arabic +// | '\u0641'..'\u064a' +// | '\u0660'..'\u0669' +// | '\u0671'..'\u06d3' +// | '\u06d5' +// | '\u06f0'..'\u06f9' +// | '\u06fa'..'\u06fc' +// | '\u0e01'..'\u0e5b' // Thai +// | '\u1100'..'\u1159' // Hangul/Korean +// | '\u1161'..'\u11a2' +// | '\u11a8'..'\u11f9' +// | '\u1e00'..'\u1e9b' // Latin Extended Additional +// | '\u1ea0'..'\u1ef9' +// | '\u1f00'..'\u1f15' // Greek Extended +// | '\u1f18'..'\u1f1d' +// | '\u1f20'..'\u1f45' +// | '\u1f48'..'\u1f4d' +// | '\u1f50'..'\u1f57' +// | '\u1f59' +// | '\u1f5b' +// | '\u1f5d' +// | '\u1f5f'..'\u1f7d' +// | '\u1f80'..'\u1fb4' +// | '\u1fb6'..'\u1fbc' +// | '\u1fc2'..'\u1fc4' +// | '\u1fc6'..'\u1fcc' +// | '\u1fd0'..'\u1fd3' +// | '\u1fd6'..'\u1fdb' +// | '\u1fe0'..'\u1fec' +// | '\u1ff2'..'\u1ff4' +// | '\u1ff6'..'\u1ffc' +// | '\u210a'..'\u2113' // Letter-like symbols +// | '\u2118'..'\u211d' +// | '\u212a'..'\u212d' +// | '\u212f'..'\u2131' +// | '\u2133'..'\u2138' +// | '\u2160'..'\u2183' // Roman Numeral +// | '\u2460'..'\u24ea' // Enclosed Alphanumerics +// | '\u2e80'..'\u2ef3' // CJK Radicals Supplement +// | '\u2f00'..'\u2fd5' // Kangxi Radicals +// | '\u3021'..'\u3029' // CJK +// | '\u3031'..'\u3035' +// | '\u3038'..'\u303a' +// | '\u3041'..'\u3094' // Hiragana +// | '\u309d'..'\u309e' +// | '\u30a1'..'\u30fa' // Katakana +// | '\u30fc'..'\u30fe' +// | '\u3105'..'\u312c' // Bopomofo +// | '\u3131'..'\u318e' // Hangul Compatability Jamo +// | '\u31a0'..'\u31b7' // Bopomofo Extended +// | '\ua000'..'\ua48c' // Yi Syllables +// | '\uac00' // Hangul Syllables +// | '\ud7a3' +// | '\uf900'..'\ufa2d' // CJK Compatibility Ideographs +// | '\ufb00'..'\ufb06' // Alphabetic Presentation Forms +// | '\ufb13'..'\ufb17' +// | '\ufb1d' +// | '\ufb1f'..'\ufb28' +// | '\ufb2a'..'\ufb36' +// | '\ufb38'..'\ufb3c' +// | '\ufb3e' +// | '\ufb40'..'\ufb41' +// | '\ufb43'..'\ufb44' +// | '\ufb46'..'\ufb4f' +// | '\ufb50'..'\ufbb1' // Arabic Presentation Forms-A +// | '\ufbd3'..'\ufd3d' +// | '\ufd50'..'\ufd8f' +// | '\ufd92'..'\ufdc7' +// | '\ufdf0'..'\ufdfb' +// | '\ufe70'..'\ufe72' // Arabic Presentation Forms-B +// | '\ufe74' +// | '\ufe76'..'\ufefc' +// | '\uff21'..'\uff3a' // Halfwidth and Fullwidth Forms +// | '\uff41'..'\uff5a' +// | '\uff66'..'\uffbe' +// | '\uffc2'..'\uffc7' +// | '\uffca'..'\uffcf' +// | '\uffd2'..'\uffd7' +// | '\uffda'..'\uffdc' +// | '\u10000'..'\u1F9FF' //not supporting 4-byte chars +// | '\u20000'..'\u2FA1F' + ; + +UNMATCHED_CHARACTER: . ; diff --git a/contrib/babelfishpg_tsql/antlr/TSqlParser.g4 b/contrib/babelfishpg_tsql/antlr/TSqlParser.g4 new file mode 100644 index 00000000000..a80eafc8e84 --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/TSqlParser.g4 @@ -0,0 +1,5006 @@ +// tests todo: +// roll back changes for: constant, constant_expression, primitive_expression + +//testG: +// removed primitive_expression +// removed function_call from constant_expression + + +/* +T-SQL (Transact-SQL, MSSQL) grammar. +The MIT License (MIT). +Copyright (c) 2017, Mark Adams (madams51703@gmail.com) +Copyright (c) 2015-2017, Ivan Kochurkin (kvanttt@gmail.com), Positive Technologies. +Copyright (c) 2016, Scott Ure (scott@redstormsoftware.com). +Copyright (c) 2016, Rui Zhang (ruizhang.ccs@gmail.com). +Copyright (c) 2016, Marcus Henriksson (kuseman80@gmail.com). +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +parser grammar TSqlParser; + +options { + tokenVocab = TSqlLexer; +} + +tsql_file + : batch_level_statement SEMI? EOF + // some sql_cluases may start with non-reserved keyword (i.e. THROW) + // so we should try sql_clauses first and then execute_body_batch if sql_clauses fails. + | sql_clauses* EOF + | execute_body_batch sql_clauses* EOF + ; + +batch_level_statement + : create_or_alter_function + | create_or_alter_procedure + | create_or_alter_trigger + | create_or_alter_view + | SEMI + ; + +sql_clauses + : ( dml_statement + | cfl_statement + | another_statement + | ddl_statement + | dbcc_statement + | backup_statement + | restore_statement + | checkpoint_statement + | readtext_statement + | writetext_statement + | updatetext_statement ) SEMI? + | SEMI + ; + +// Data Manipulation Language: https://msdn.microsoft.com/en-us/library/ff848766(v=sql.120).aspx +dml_statement + : merge_statement + | delete_statement + | insert_statement + | bulk_insert_statement + | select_statement_standalone + | update_statement + ; + +// Data Definition Language: https://msdn.microsoft.com/en-us/library/ff848799.aspx) +ddl_statement + : add_signature_statement + | alter_application_role + | alter_assembly + | alter_asymmetric_key + | alter_authorization + | alter_availability_group + | alter_certificate + | alter_column_encryption_key + | alter_credential + | alter_cryptographic_provider + | alter_database + | alter_database_scoped_configuration + | alter_db_role + | alter_external_data_source + | alter_external_library + | alter_external_resource_pool + | alter_fulltext_catalog + | alter_fulltext_index + | alter_fulltext_stoplist + | alter_index + | alter_login + | alter_master_key + | alter_message_type + | alter_partition_function + | alter_partition_scheme + | alter_remote_service_binding + | alter_resource_governor + | alter_schema + | alter_sequence + | alter_server_audit + | alter_server_audit_specification + | alter_server_configuration + | alter_server_role + | alter_server_role_pdw + | alter_service + | alter_service_master_key + | alter_symmetric_key + | alter_table + | alter_user + | alter_workload_group + | alter_xml_schema_collection + | create_aggregate + | create_application_role + | create_assembly + | create_asymmetric_key + | create_column_encryption_key + | create_column_master_key + | create_credential + | create_cryptographic_provider + | create_database + | create_default + | create_db_role + | create_diagnostic_session + | create_event_notification + | create_external_data_source + | create_external_file_format + | create_external_library + | create_external_resource_pool + | create_external_table + | create_fulltext_catalog + | create_fulltext_index + | create_fulltext_stoplist + | create_index + | create_login + | create_master_key + | create_or_alter_broker_priority + | create_or_alter_database_audit_specification + | create_or_alter_endpoint + | create_or_alter_event_session + | create_partition_function + | create_partition_scheme + | create_remote_service_binding + | create_resource_pool + | create_route + | create_rule + | create_schema + | create_search_property_list + | create_security_policy + | create_sequence + | create_server_audit + | create_server_audit_specification + | create_server_role + | create_service + | create_spatial_index + | create_statistics + | create_symmetric_key + | create_synonym + | create_table + | create_type + | create_user + | create_user_azure_sql_dw + | create_workload_group + | create_xml_index + | create_selective_xml_index + | create_xml_schema_collection + | drop_aggregate + | drop_application_role + | drop_assembly + | drop_asymmetric_key + | drop_availability_group + | drop_broker_priority + | drop_certificate + | drop_column_encryption_key + | drop_column_master_key + | drop_contract + | drop_credential + | drop_cryptograhic_provider + | drop_database + | drop_database_audit_specification + | drop_database_encryption_key + | drop_database_scoped_credential + | drop_db_role + | drop_default + | drop_diagnostic_session + | drop_endpoint + | drop_event_notifications + | drop_event_session + | drop_external_data_source + | drop_external_file_format + | drop_external_library + | drop_external_resource_pool + | drop_external_table + | drop_fulltext_catalog + | drop_fulltext_index + | drop_fulltext_stoplist + | drop_function + | drop_index + | drop_login + | drop_master_key + | drop_message_type + | drop_partition_function + | drop_partition_scheme + | drop_procedure + | drop_queue + | drop_remote_service_binding + | drop_resource_pool + | drop_route + | drop_rule + | drop_schema + | drop_search_property_list + | drop_security_policy + | drop_sequence + | drop_server_audit + | drop_server_audit_specification + | drop_server_role + | drop_service + | drop_signature_statement + | drop_statistics + | drop_symmetric_key + | drop_synonym + | drop_table + | drop_trigger + | drop_type + | drop_user + | drop_view + | drop_workload_group + | drop_xml_schema_collection + | disable_trigger + | enable_trigger + | lock_table + | truncate_table + | update_statistics + ; + +backup_statement + : backup_database + | backup_log + | backup_certificate + | backup_master_key + | backup_service_master_key + ; + +restore_statement + : restore_database + ; + +// Control-of-Flow Language: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/control-of-flow +cfl_statement + : block_statement + | break_statement + | continue_statement + | goto_statement + | if_statement + | return_statement + | throw_statement + | try_catch_statement + | waitfor_statement + | while_statement + | print_statement + | raiseerror_statement + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/begin-end-transact-sql +block_statement + : BEGIN SEMI? sql_clauses* END SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/break-transact-sql +break_statement + : BREAK SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/continue-transact-sql +continue_statement + : CONTINUE SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/goto-transact-sql +goto_statement + : GOTO id SEMI? + | id COLON SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/return-transact-sql +return_statement + : RETURN expression? SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/if-else-transact-sql +if_statement + : IF search_condition sql_clauses (ELSE sql_clauses)? SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/throw-transact-sql +throw_statement + : THROW (throw_error_number COMMA throw_message COMMA throw_state)? SEMI? + ; + +throw_error_number + : DECIMAL | LOCAL_ID + ; + +throw_message + : STRING | LOCAL_ID + ; + +throw_state + : DECIMAL | LOCAL_ID + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/try-catch-transact-sql +try_catch_statement + : try_block catch_block SEMI? + ; + +try_block + : BEGIN TRY SEMI? try_clauses=sql_clauses+ END TRY + ; + +catch_block + : BEGIN CATCH SEMI? catch_clauses=sql_clauses* END CATCH + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql +waitfor_statement + : WAITFOR (DELAY | TIME) expression SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/while-transact-sql +while_statement + : WHILE search_condition (sql_clauses | BREAK SEMI? | CONTINUE SEMI?) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/print-transact-sql +print_statement + : PRINT (expression | DOUBLE_QUOTE_ID) (COMMA LOCAL_ID)* SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/raiserror-transact-sql +raiseerror_statement + : RAISERROR LR_BRACKET msg=(DECIMAL | STRING | LOCAL_ID) COMMA severity=constant_LOCAL_ID COMMA + state=constant_LOCAL_ID (COMMA argument+=constant_LOCAL_ID)* RR_BRACKET (WITH raiseerror_option (COMMA raiseerror_option)* )? SEMI? + ; + +raiseerror_option + : (LOG | SETERROR | NOWAIT) + ; + + +empty_statement + : SEMI + ; + +another_statement + : declare_statement + | declare_xmlnamespaces_statement + | execute_statement + | cursor_statement + | conversation_statement + | create_contract + | create_queue + | alter_queue + | kill_statement + | message_statement + | security_statement + | set_statement + | transaction_statement + | use_statement + | setuser_statement + | reconfigure_statement + | shutdown_statement + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-application-role-transact-sql +alter_application_role + : ALTER APPLICATION ROLE appliction_role=id WITH (COMMA? NAME EQUAL new_application_role_name=id)? (COMMA? PASSWORD EQUAL application_role_password=STRING)? (COMMA? DEFAULT_SCHEMA EQUAL app_role_default_schema=id)? + ; + +create_application_role + : CREATE APPLICATION ROLE appliction_role=id WITH (COMMA? PASSWORD EQUAL application_role_password=STRING)? (COMMA? DEFAULT_SCHEMA EQUAL app_role_default_schema=id)? + ; + +create_aggregate + : CREATE AGGREGATE func_proc_name_schema LR_BRACKET procedure_param (COMMA procedure_param)* RR_BRACKET + RETURNS data_type external_name SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-aggregate-transact-sql +drop_aggregate + : DROP AGGREGATE if_exists? ( schema_name=id DOT )? aggregate_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-application-role-transact-sql +drop_application_role + : DROP APPLICATION ROLE rolename=id + ; + +alter_assembly + : ALTER ASSEMBLY assembly_name=id alter_assembly_clause + ; + +alter_assembly_clause + : (FROM expression)? (WITH assembly_option (COMMA assembly_option)*)? alter_assembly_drop_clause? alter_assembly_add_clause? + ; + +alter_assembly_drop_clause + : DROP FILE ((STRING|id) (COMMA (STRING|id))* | ALL) + ; + +alter_assembly_add_clause + : ADD FILE FROM alter_assembly_client_file_clause (COMMA alter_assembly_client_file_clause)* + ; + +alter_assembly_client_file_clause + : (expression|id) (AS (id|STRING))? + ; + +assembly_option + : PERMISSION_SET EQUAL (SAFE|EXTERNAL_ACCESS|UNSAFE) + | VISIBILITY EQUAL (ON | OFF) + | UNCHECKED DATA + ; + +network_file_share + : DOUBLE_BACK_SLASH computer_name=id file_path + ; + +file_path + : BACKSLASH file_path + | id + ; + +local_file + : local_drive file_path + ; + +local_drive + : DISK_DRIVE + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-assembly-transact-sql +create_assembly + : CREATE ASSEMBLY assembly_name=id (AUTHORIZATION owner_name=id)? + FROM (COMMA? (STRING|BINARY) )+ + (WITH PERMISSION_SET EQUAL (SAFE|EXTERNAL_ACCESS|UNSAFE) )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-assembly-transact-sql +drop_assembly + : DROP ASSEMBLY if_exists? (COMMA? assembly_name=id)+ + ( WITH NO DEPENDENTS )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-asymmetric-key-transact-sql +alter_asymmetric_key + : ALTER ASYMMETRIC KEY Asym_Key_Name=id (asymmetric_key_option | REMOVE PRIVATE KEY ) + ; + +asymmetric_key_option + : WITH PRIVATE KEY LR_BRACKET asymmetric_key_password_change_option ( COMMA asymmetric_key_password_change_option)? RR_BRACKET + ; + +asymmetric_key_password_change_option + : (ENCRYPTION|DECRYPTION) BY PASSWORD EQUAL STRING + ; + +//https://docs.microsoft.com/en-us/sql/t-sql/statements/create-asymmetric-key-transact-sql +create_asymmetric_key + : CREATE ASYMMETRIC KEY Asym_Key_Nam=id + (AUTHORIZATION database_principal_name=id)? + ( FROM (FILE EQUAL STRING |EXECUTABLE_FILE EQUAL STRING|ASSEMBLY Assembly_Name=id | PROVIDER Provider_Name=id) )? + (WITH (ALGORITHM EQUAL ( RSA_4096 | RSA_3072 | RSA_2048 | RSA_1024 | RSA_512) |PROVIDER_KEY_NAME EQUAL provider_key_name=STRING | CREATION_DISPOSITION EQUAL (CREATE_NEW|OPEN_EXISTING) ) )? + (ENCRYPTION BY PASSWORD EQUAL asymmetric_key_password=STRING )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-asymmetric-key-transact-sql +drop_asymmetric_key + : DROP ASYMMETRIC KEY key_name=id ( REMOVE PROVIDER KEY )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-authorization-transact-sql +alter_authorization + : ALTER AUTHORIZATION ON (object_type colon_colon)? entity=entity_name TO authorization_grantee + ; + +authorization_grantee + : principal_name=id + | SCHEMA OWNER + ; + +colon_colon + : COLON COLON + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-availability-group-transact-sql +drop_availability_group + : DROP AVAILABILITY GROUP group_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-availability-group-transact-sql +alter_availability_group + : ALTER AVAILABILITY GROUP group_name=id alter_availability_group_options + ; + +alter_availability_group_options + : SET LR_BRACKET ( ( AUTOMATED_BACKUP_PREFERENCE EQUAL ( PRIMARY | SECONDARY_ONLY| SECONDARY | NONE ) | FAILURE_CONDITION_LEVEL EQUAL DECIMAL | HEALTH_CHECK_TIMEOUT EQUAL milliseconds=DECIMAL | DB_FAILOVER EQUAL ( ON | OFF ) | REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT EQUAL DECIMAL ) RR_BRACKET ) + | ADD DATABASE database_name=id + | REMOVE DATABASE database_name=id + | ADD REPLICA ON server_instance=STRING (WITH LR_BRACKET ( (ENDPOINT_URL EQUAL STRING)? (COMMA? AVAILABILITY_MODE EQUAL (SYNCHRONOUS_COMMIT| ASYNCHRONOUS_COMMIT))? (COMMA? FAILOVER_MODE EQUAL (AUTOMATIC|MANUAL) )? (COMMA? SEEDING_MODE EQUAL (AUTOMATIC|MANUAL) )? (COMMA? BACKUP_PRIORITY EQUAL DECIMAL)? ( COMMA? PRIMARY_ROLE LR_BRACKET ALLOW_CONNECTIONS EQUAL ( READ_WRITE | ALL ) RR_BRACKET)? ( COMMA? SECONDARY_ROLE LR_BRACKET ALLOW_CONNECTIONS EQUAL ( READ_ONLY ) RR_BRACKET )? ) +) RR_BRACKET + |SECONDARY_ROLE LR_BRACKET (ALLOW_CONNECTIONS EQUAL (NO|READ_ONLY|ALL) | READ_ONLY_ROUTING_LIST EQUAL ( LR_BRACKET ( ( STRING) ) RR_BRACKET ) ) + |PRIMARY_ROLE LR_BRACKET (ALLOW_CONNECTIONS EQUAL (NO|READ_ONLY|ALL) | READ_ONLY_ROUTING_LIST EQUAL ( LR_BRACKET ( (COMMA? STRING)*|NONE ) RR_BRACKET ) + | SESSION_TIMEOUT EQUAL session_timeout=DECIMAL +) + | MODIFY REPLICA ON server_instance=STRING (WITH LR_BRACKET (ENDPOINT_URL EQUAL STRING| AVAILABILITY_MODE EQUAL (SYNCHRONOUS_COMMIT| ASYNCHRONOUS_COMMIT) | FAILOVER_MODE EQUAL (AUTOMATIC|MANUAL) | SEEDING_MODE EQUAL (AUTOMATIC|MANUAL) | BACKUP_PRIORITY EQUAL DECIMAL ) + |SECONDARY_ROLE LR_BRACKET (ALLOW_CONNECTIONS EQUAL (NO|READ_ONLY|ALL) | READ_ONLY_ROUTING_LIST EQUAL ( LR_BRACKET ( ( STRING) ) RR_BRACKET ) ) + |PRIMARY_ROLE LR_BRACKET (ALLOW_CONNECTIONS EQUAL (NO|READ_ONLY|ALL) | READ_ONLY_ROUTING_LIST EQUAL ( LR_BRACKET ( (COMMA? STRING)*|NONE ) RR_BRACKET ) + | SESSION_TIMEOUT EQUAL session_timeout=DECIMAL +) ) RR_BRACKET + | REMOVE REPLICA ON STRING + | JOIN + | JOIN AVAILABILITY GROUP ON (COMMA? ag_name=STRING WITH LR_BRACKET ( LISTENER_URL EQUAL STRING COMMA AVAILABILITY_MODE EQUAL (SYNCHRONOUS_COMMIT|ASYNCHRONOUS_COMMIT) COMMA FAILOVER_MODE EQUAL MANUAL COMMA SEEDING_MODE EQUAL (AUTOMATIC|MANUAL) RR_BRACKET ) )+ + | MODIFY AVAILABILITY GROUP ON (COMMA? ag_name_modified=STRING WITH LR_BRACKET (LISTENER_URL EQUAL STRING (COMMA? AVAILABILITY_MODE EQUAL (SYNCHRONOUS_COMMIT|ASYNCHRONOUS_COMMIT) )? (COMMA? FAILOVER_MODE EQUAL MANUAL )? (COMMA? SEEDING_MODE EQUAL (AUTOMATIC|MANUAL))? RR_BRACKET ) )+ + |GRANT CREATE ANY DATABASE + | DENY CREATE ANY DATABASE + | FAILOVER + | FORCE_FAILOVER_ALLOW_DATA_LOSS + | ADD LISTENER listener_name=STRING LR_BRACKET ( WITH DHCP (ON LR_BRACKET ip_v4_failover ip_v4_failover RR_BRACKET ) | WITH IP LR_BRACKET ( (COMMA? LR_BRACKET ( ip_v4_failover COMMA ip_v4_failover | ip_v6_failover ) RR_BRACKET)+ RR_BRACKET (COMMA PORT EQUAL DECIMAL)? ) ) RR_BRACKET + | MODIFY LISTENER (ADD IP LR_BRACKET (ip_v4_failover ip_v4_failover | ip_v6_failover) RR_BRACKET | PORT EQUAL DECIMAL ) + |RESTART LISTENER STRING + |REMOVE LISTENER STRING + |OFFLINE + | WITH LR_BRACKET DTC_SUPPORT EQUAL PER_DB RR_BRACKET + ; + +ip_v4_failover + : STRING + ; + +ip_v6_failover + : STRING + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-broker-priority-transact-sql +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-broker-priority-transact-sql +create_or_alter_broker_priority + : (CREATE | ALTER) BROKER PRIORITY ConversationPriorityName=id FOR CONVERSATION + SET LR_BRACKET + ( CONTRACT_NAME EQUAL ( ( id) | ANY ) COMMA? )? + ( LOCAL_SERVICE_NAME EQUAL (DOUBLE_FORWARD_SLASH? id | ANY ) COMMA? )? + ( REMOTE_SERVICE_NAME EQUAL (RemoteServiceName=STRING | ANY ) COMMA? )? + ( PRIORITY_LEVEL EQUAL ( PriorityValue=DECIMAL | DEFAULT ) )? + RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-broker-priority-transact-sql +drop_broker_priority + : DROP BROKER PRIORITY ConversationPriorityName=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-certificate-transact-sql +alter_certificate + : ALTER CERTIFICATE certificate_name=id (REMOVE PRIVATE_KEY | WITH PRIVATE KEY LR_BRACKET ( FILE EQUAL STRING COMMA? | DECRYPTION BY PASSWORD EQUAL STRING COMMA?| ENCRYPTION BY PASSWORD EQUAL STRING COMMA?)+ RR_BRACKET | WITH ACTIVE FOR BEGIN_DIALOG EQUAL ( ON | OFF ) ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-column-encryption-key-transact-sql +alter_column_encryption_key + : ALTER COLUMN ENCRYPTION KEY column_encryption_key=id (ADD | DROP) VALUE LR_BRACKET COLUMN_MASTER_KEY EQUAL column_master_key_name=id ( COMMA ALGORITHM EQUAL algorithm_name=STRING COMMA ENCRYPTED_VALUE EQUAL BINARY)? RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-column-encryption-key-transact-sql +create_column_encryption_key + : CREATE COLUMN ENCRYPTION KEY column_encryption_key=id + WITH VALUES + (LR_BRACKET COMMA? COLUMN_MASTER_KEY EQUAL column_master_key_name=id COMMA + ALGORITHM EQUAL algorithm_name=STRING COMMA + ENCRYPTED_VALUE EQUAL encrypted_value=BINARY RR_BRACKET COMMA?)+ + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-certificate-transact-sql +drop_certificate + : DROP CERTIFICATE certificate_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-column-encryption-key-transact-sql +drop_column_encryption_key + : DROP COLUMN ENCRYPTION KEY key_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-column-master-key-transact-sql +drop_column_master_key + : DROP COLUMN MASTER KEY key_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-contract-transact-sql +drop_contract + : DROP CONTRACT dropped_contract_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-credential-transact-sql +drop_credential + : DROP CREDENTIAL credential_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-cryptographic-provider-transact-sql +drop_cryptograhic_provider + : DROP CRYPTOGRAPHIC PROVIDER provider_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-database-transact-sql +drop_database + : DROP DATABASE if_exists? id (COMMA id)* + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-database-audit-specification-transact-sql +drop_database_audit_specification + : DROP DATABASE AUDIT SPECIFICATION audit_specification_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-database-encryption-key-transact-sql?view=sql-server-ver15 +drop_database_encryption_key + : DROP DATABASE ENCRYPTION KEY + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-database-scoped-credential-transact-sql +drop_database_scoped_credential + : DROP DATABASE SCOPED CREDENTIAL credential_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-default-transact-sql +drop_default + : DROP DEFAULT if_exists? simple_name (COMMA simple_name)* + ; + +drop_diagnostic_session + : DROP DIAGNOSTICS SESSION session_name=ID SEMI + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-endpoint-transact-sql +drop_endpoint + : DROP ENDPOINT endPointName=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-external-data-source-transact-sql +drop_external_data_source + : DROP EXTERNAL DATA SOURCE external_data_source_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-external-file-format-transact-sql +drop_external_file_format + : DROP EXTERNAL FILE FORMAT external_file_format_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-external-library-transact-sql +drop_external_library + : DROP EXTERNAL LIBRARY library_name=id +( AUTHORIZATION owner_name=id )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-external-resource-pool-transact-sql +drop_external_resource_pool + : DROP EXTERNAL RESOURCE POOL pool_name=id + ; + +create_external_table + : CREATE EXTERNAL TABLE table_name LR_BRACKET column_definition (COMMA column_definition)* COMMA? RR_BRACKET + WITH LR_BRACKET external_table_option (COMMA external_table_option)* RR_BRACKET + SEMI? + ; + +external_table_option + : id EQUAL expression + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-external-table-transact-sql +drop_external_table + : DROP EXTERNAL TABLE table_name SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-event-notification-transact-sql +drop_event_notifications + : DROP EVENT NOTIFICATION (COMMA? notification_name=id)+ + ON (SERVER|DATABASE|QUEUE queue_name=id) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-event-session-transact-sql +drop_event_session + : DROP EVENT SESSION event_session_name=id + ON SERVER + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-fulltext-catalog-transact-sql +drop_fulltext_catalog + : DROP FULLTEXT CATALOG catalog_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-fulltext-index-transact-sql +drop_fulltext_index + : DROP FULLTEXT INDEX ON table_name + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-fulltext-stoplist-transact-sql +drop_fulltext_stoplist + : DROP FULLTEXT STOPLIST stoplist_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-login-transact-sql +drop_login + : DROP LOGIN login_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-master-key-transact-sql +drop_master_key + : DROP MASTER KEY + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-message-type-transact-sql +drop_message_type + : DROP MESSAGE TYPE message_type_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-partition-function-transact-sql +drop_partition_function + : DROP PARTITION FUNCTION partition_function_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-partition-scheme-transact-sql +drop_partition_scheme + : DROP PARTITION SCHEME partition_scheme_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-queue-transact-sql +drop_queue + : DROP QUEUE (database_name=id DOT)? (schema_name=id DOT)? queue_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-remote-service-binding-transact-sql +drop_remote_service_binding + : DROP REMOTE SERVICE BINDING binding_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-resource-pool-transact-sql +drop_resource_pool + : DROP RESOURCE POOL pool_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-role-transact-sql +drop_db_role + : DROP ROLE if_exists? role_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-route-transact-sql +drop_route + : DROP ROUTE route_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-rule-transact-sql +drop_rule + : DROP RULE if_exists? simple_name (COMMA simple_name)* + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-schema-transact-sql +drop_schema + : DROP SCHEMA if_exists? schema_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-search-property-list-transact-sql +drop_search_property_list + : DROP SEARCH PROPERTY LIST property_list_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-security-policy-transact-sql +drop_security_policy + : DROP SECURITY POLICY if_exists? (schema_name=id DOT )? security_policy_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-sequence-transact-sql +drop_sequence + : DROP SEQUENCE if_exists? full_object_name (COMMA full_object_name)* + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-server-audit-transact-sql +drop_server_audit + : DROP SERVER AUDIT audit_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-server-audit-specification-transact-sql +drop_server_audit_specification + : DROP SERVER AUDIT SPECIFICATION audit_specification_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-server-role-transact-sql +drop_server_role + : DROP SERVER ROLE role_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-service-transact-sql +drop_service + : DROP SERVICE dropped_service_name=id + ; + +add_signature_statement + : ADD COUNTER? SIGNATURE TO (object_type colon_colon)? full_object_name BY signature_item (COMMA signature_item)* SEMI? + ; + +signature_item + : (CERTIFICATE|ASYMMETRIC KEY) name=id (WITH PASSWORD EQUAL STRING | WITH SIGNATURE EQUAL BINARY)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-signature-transact-sql +drop_signature_statement + : DROP COUNTER? SIGNATURE FROM (schema_name=id DOT)? module_name=id + BY (COMMA? CERTIFICATE cert_name=id + | COMMA? ASYMMETRIC KEY Asym_key_name=id + )+ + SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-symmetric-key-transact-sql +drop_symmetric_key + : DROP SYMMETRIC KEY symmetric_key_name=id (REMOVE PROVIDER KEY)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-synonym-transact-sql +drop_synonym + : DROP SYNONYM if_exists? simple_name + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-user-transact-sql +drop_user + : DROP USER if_exists? user_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-workload-group-transact-sql +drop_workload_group + : DROP WORKLOAD GROUP group_name=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-xml-schema-collection-transact-sql +drop_xml_schema_collection + : DROP XML SCHEMA COLLECTION simple_name + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/disable-trigger-transact-sql +disable_trigger + : DISABLE TRIGGER ( ( COMMA? (schema_name=id DOT)? trigger_name=id )+ | ALL) ON ((schema_id=id DOT)? object_name=id|DATABASE|ALL SERVER) + ; + + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/enable-trigger-transact-sql +enable_trigger + : ENABLE TRIGGER ( ( COMMA? (schema_name=id DOT)? trigger_name=id )+ | ALL) ON ( (schema_id=id DOT)? object_name=id|DATABASE|ALL SERVER) + ; + +lock_table + : LOCK TABLE table_name IN (SHARE | EXCLUSIVE) MODE (WAIT seconds=DECIMAL | NOWAIT)? SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql +truncate_table + : TRUNCATE TABLE table_name + ( WITH LR_BRACKET + PARTITIONS LR_BRACKET + (COMMA? ((DECIMAL|LOCAL_ID)|(DECIMAL|LOCAL_ID) TO (DECIMAL|LOCAL_ID)) )+ + RR_BRACKET + RR_BRACKET + )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-column-master-key-transact-sql +create_column_master_key + : CREATE COLUMN MASTER KEY key_name=id + WITH LR_BRACKET + KEY_STORE_PROVIDER_NAME EQUAL key_store_provider_name=STRING COMMA + KEY_PATH EQUAL key_path=STRING + RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-credential-transact-sql +alter_credential + : ALTER CREDENTIAL credential_name=id + WITH IDENTITY EQUAL identity_name=STRING + ( COMMA SECRET EQUAL secret=STRING )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-credential-transact-sql +create_credential + : CREATE CREDENTIAL credential_name=id + WITH IDENTITY EQUAL identity_name=STRING + ( COMMA SECRET EQUAL secret=STRING )? + ( FOR CRYPTOGRAPHIC PROVIDER cryptographic_provider_name=id )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-cryptographic-provider-transact-sql +alter_cryptographic_provider + : ALTER CRYPTOGRAPHIC PROVIDER provider_name=id (FROM FILE EQUAL crypto_provider_ddl_file=STRING)? (ENABLE | DISABLE)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-cryptographic-provider-transact-sql +create_cryptographic_provider + : CREATE CRYPTOGRAPHIC PROVIDER provider_name=id + FROM FILE EQUAL path_of_DLL=STRING + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-event-notification-transact-sql +create_event_notification + : CREATE EVENT NOTIFICATION event_notification_name=id + ON (SERVER|DATABASE|QUEUE queue_name=id) + (WITH FAN_IN)? + FOR (COMMA? event_type_or_group=id)+ + TO SERVICE broker_service=STRING COMMA + broker_service_specifier_or_current_database=STRING + ; + + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-event-session-transact-sql +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-event-session-transact-sql +create_or_alter_event_session + : (CREATE | ALTER) EVENT SESSION event_session_name=id ON SERVER + (COMMA? ADD EVENT ( (event_module_guid=id DOT)? event_package_name=id DOT event_name=id) + (LR_BRACKET + (SET ( COMMA? event_customizable_attributue=id EQUAL (DECIMAL|STRING) )* )? + ( ACTION LR_BRACKET (COMMA? (event_module_guid=id DOT)? event_package_name=id DOT action_name=id)+ RR_BRACKET)+ + (WHERE event_session_predicate_expression)? + RR_BRACKET )* + )* + (COMMA? DROP EVENT (event_module_guid=id DOT)? event_package_name=id DOT event_name=id )* + + ( (ADD TARGET (event_module_guid=id DOT)? event_package_name=id DOT target_name=id ) ( LR_BRACKET SET (COMMA? target_parameter_name=id EQUAL (LR_BRACKET? DECIMAL RR_BRACKET? |STRING) )+ RR_BRACKET )* )* + (DROP TARGET (event_module_guid=id DOT)? event_package_name=id DOT target_name=id )* + + + (WITH + LR_BRACKET + (COMMA? MAX_MEMORY EQUAL max_memory=DECIMAL (KB|MB) )? + (COMMA? EVENT_RETENTION_MODE EQUAL (ALLOW_SINGLE_EVENT_LOSS | ALLOW_MULTIPLE_EVENT_LOSS | NO_EVENT_LOSS ) )? + (COMMA? MAX_DISPATCH_LATENCY EQUAL (max_dispatch_latency_seconds=DECIMAL SECONDS | INFINITE) )? + (COMMA? MAX_EVENT_SIZE EQUAL max_event_size=DECIMAL (KB|MB) )? + (COMMA? MEMORY_PARTITION_MODE EQUAL (NONE | PER_NODE | PER_CPU) )? + (COMMA? TRACK_CAUSALITY EQUAL (ON|OFF) )? + (COMMA? STARTUP_STATE EQUAL (ON|OFF) )? + RR_BRACKET + )? + (STATE EQUAL (START|STOP) )? + + ; + +event_session_predicate_expression + : ( COMMA? (AND|OR)? NOT? ( event_session_predicate_factor | LR_BRACKET event_session_predicate_expression RR_BRACKET) )+ + ; + +event_session_predicate_factor + : event_session_predicate_leaf + | LR_BRACKET event_session_predicate_expression RR_BRACKET + ; + +event_session_predicate_leaf + : (event_field_name=id | (event_field_name=id |( (event_module_guid=id DOT)? event_package_name=id DOT predicate_source_name=id ) ) (EQUAL |(LESS GREATER) | (EXCLAMATION EQUAL) | GREATER | (GREATER EQUAL)| LESS | LESS EQUAL) (DECIMAL | STRING) ) + | (event_module_guid=id DOT)? event_package_name=id DOT predicate_compare_name=id LR_BRACKET (event_field_name=id |( (event_module_guid=id DOT)? event_package_name=id DOT predicate_source_name=id ) COMMA (DECIMAL | STRING) ) RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-external-data-source-transact-sql +alter_external_data_source + : ALTER EXTERNAL DATA SOURCE data_source_name=id SET + ( external_data_source_attribute | CREDENTIAL EQUAL credential_name=id )+ + | ALTER EXTERNAL DATA SOURCE data_source_name=id WITH LR_BRACKET TYPE EQUAL BLOB_STORAGE COMMA LOCATION EQUAL location=STRING (COMMA CREDENTIAL EQUAL credential_name=id )? RR_BRACKET + ; + +create_external_data_source + : CREATE EXTERNAL DATA SOURCE data_source_name=id WITH LR_BRACKET external_data_source_attribute* RR_BRACKET + ; + +external_data_source_attribute + : LOCATION EQUAL location=STRING COMMA? + | RESOURCE_MANAGER_LOCATION EQUAL resource_manager_location=STRING COMMA? + | TYPE EQUAL ID COMMA? + ; + +create_external_file_format + : CREATE EXTERNAL FILE FORMAT external_file_format_name=id WITH LR_BRACKET + FORMAT_TYPE EQUAL id + (COMMA FORMAT_OPTIONS LR_BRACKET external_file_format_option (COMMA external_file_format_option)* RR_BRACKET)? + (COMMA DATA_COMPRESSION EQUAL STRING)? + RR_BRACKET SEMI + ; + +external_file_format_option + : FIELD_TERMINATOR EQUAL STRING + | STRING_DELIMITER EQUAL STRING + | FIRST_ROW EQUAL DECIMAL + | DATE_FORMAT EQUAL STRING + | USE_TYPE_DEFAULT EQUAL (TRUE | FALSE) + | ENCODING EQUAL STRING + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-external-library-transact-sql +alter_external_library + : ALTER EXTERNAL LIBRARY library_name=id (AUTHORIZATION owner_name=id)? + (SET|ADD) ( LR_BRACKET CONTENT EQUAL (client_library=STRING | BINARY | NONE) (COMMA PLATFORM EQUAL (WINDOWS|LINUX)? RR_BRACKET) WITH (COMMA? LANGUAGE EQUAL (R|PYTHON) | DATA_SOURCE EQUAL external_data_source_name=id )+ RR_BRACKET ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-external-library-transact-sql +create_external_library + : CREATE EXTERNAL LIBRARY library_name=id (AUTHORIZATION owner_name=id)? + FROM (COMMA? LR_BRACKET? (CONTENT EQUAL)? (client_library=STRING | BINARY | NONE) (COMMA PLATFORM EQUAL (WINDOWS|LINUX)? RR_BRACKET)? ) ( WITH (COMMA? LANGUAGE EQUAL (R|PYTHON) | DATA_SOURCE EQUAL external_data_source_name=id )+ RR_BRACKET )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-external-resource-pool-transact-sql +alter_external_resource_pool + : ALTER EXTERNAL RESOURCE POOL (pool_name=id | DEFAULT_DOUBLE_QUOTE) WITH LR_BRACKET MAX_CPU_PERCENT EQUAL max_cpu_percent=DECIMAL ( COMMA? AFFINITY CPU EQUAL (AUTO|(COMMA? DECIMAL TO DECIMAL |COMMA DECIMAL )+ ) | NUMANODE EQUAL (COMMA? DECIMAL TO DECIMAL| COMMA? DECIMAL )+ ) (COMMA? MAX_MEMORY_PERCENT EQUAL max_memory_percent=DECIMAL)? (COMMA? MAX_PROCESSES EQUAL max_processes=DECIMAL)? RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-external-resource-pool-transact-sql +create_external_resource_pool + : CREATE EXTERNAL RESOURCE POOL pool_name=id WITH LR_BRACKET MAX_CPU_PERCENT EQUAL max_cpu_percent=DECIMAL ( COMMA? AFFINITY CPU EQUAL (AUTO|(COMMA? DECIMAL TO DECIMAL |COMMA DECIMAL )+ ) | NUMANODE EQUAL (COMMA? DECIMAL TO DECIMAL| COMMA? DECIMAL )+ ) (COMMA? MAX_MEMORY_PERCENT EQUAL max_memory_percent=DECIMAL)? (COMMA? MAX_PROCESSES EQUAL max_processes=DECIMAL)? RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-fulltext-catalog-transact-sql +alter_fulltext_catalog + : ALTER FULLTEXT CATALOG catalog_name=id (REBUILD (WITH ACCENT_SENSITIVITY EQUAL (ON|OFF) )? | REORGANIZE | AS DEFAULT ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-fulltext-catalog-transact-sql +create_fulltext_catalog + : CREATE FULLTEXT CATALOG catalog_name=id + (ON FILEGROUP filegroup=id)? + (IN PATH rootpath=STRING)? + (WITH ACCENT_SENSITIVITY EQUAL (ON|OFF) )? + (AS DEFAULT)? + (AUTHORIZATION owner_name=id)? + ; + +create_fulltext_index + : CREATE FULLTEXT INDEX ON table_name (LR_BRACKET fulltext_index_column (COMMA fulltext_index_column)* RR_BRACKET)? KEY INDEX id (ON catalog_filegroup_option)? (WITH fulltext_with_option (COMMA fulltext_with_option)* )? + ; + +fulltext_index_column + : full_column_name (TYPE COLUMN full_column_name)? (LANGUAGE (STRING|DECIMAL|BINARY))? STATISTICAL_SEMANTICS? + ; + +catalog_filegroup_option + : catalog_name=id (COMMA FILEGROUP filegroup_name=id)? + | FILEGROUP filegroup_name=id (COMMA catalog_name=id)? + ; + +fulltext_with_option + : CHANGE_TRACKING EQUAL? ( MANUAL | AUTO | OFF (COMMA NO POPULATION)? ) + | STOPLIST EQUAL? ( OFF | SYSTEM | stoplist_name=id ) + | SEARCH PROPERTY LIST EQUAL? property_list_name=id + ; + +alter_fulltext_index + : ALTER FULLTEXT INDEX ON table_name alter_fulltext_index_option + ; + +alter_fulltext_index_option + : ( ENABLE | DISABLE ) + | CHANGE_TRACKING EQUAL? ( MANUAL | AUTO | OFF ) + | ADD LR_BRACKET fulltext_index_column (COMMA fulltext_index_column)* RR_BRACKET alter_fulltext_index_no_population? + | ALTER COLUMN full_column_name ( ADD | DROP ) STATISTICAL_SEMANTICS alter_fulltext_index_no_population? + | DROP LR_BRACKET full_column_name (COMMA full_column_name)* RR_BRACKET alter_fulltext_index_no_population? + | START ( FULL | INCREMENTAL | UPDATE ) POPULATION + | ( STOP | PAUSE | RESUME ) POPULATION + | SET STOPLIST EQUAL? ( OFF | SYSTEM | stoplist_name=id ) alter_fulltext_index_no_population? + | SEARCH PROPERTY LIST EQUAL? ( OFF | property_list_name=id ) alter_fulltext_index_no_population? + ; + +alter_fulltext_index_no_population + : WITH NO POPULATION + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-fulltext-stoplist-transact-sql +alter_fulltext_stoplist + : ALTER FULLTEXT STOPLIST stoplist_name=id (ADD stopword=STRING LANGUAGE (STRING|DECIMAL|BINARY) | DROP ( stopword=STRING LANGUAGE (STRING|DECIMAL|BINARY) |ALL (STRING|DECIMAL|BINARY) | ALL ) ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-fulltext-stoplist-transact-sql +create_fulltext_stoplist + : CREATE FULLTEXT STOPLIST stoplist_name=id + (FROM ( (database_name=id DOT)? source_stoplist_name=id |SYSTEM STOPLIST ) )? + (AUTHORIZATION owner_name=id)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-login-transact-sql +alter_login + : ALTER LOGIN login_name=id + ( (ENABLE|DISABLE) + | WITH alter_login_set_option (COMMA alter_login_set_option)* + | (ADD|DROP) CREDENTIAL credential_name=id ) + ; + +alter_login_set_option + : PASSWORD EQUAL ( password=STRING | password_hash=BINARY HASHED ) + ( (MUST_CHANGE|UNLOCK)+ + | OLD_PASSWORD EQUAL old_password=STRING )? + | DEFAULT_DATABASE EQUAL default_database=id + | DEFAULT_LANGUAGE EQUAL (id | STRING | DECIMAL) + | NAME EQUAL login_name=id + | CHECK_POLICY EQUAL (ON|OFF) + | CHECK_EXPIRATION EQUAL (ON|OFF) + | CREDENTIAL EQUAL credential_name=id + | NO CREDENTIAL + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-login-transact-sql +create_login + : CREATE LOGIN login_name=id + ( WITH PASSWORD EQUAL ( password=STRING | password_hash=BINARY HASHED ) MUST_CHANGE? (COMMA create_login_option_list)* + | FROM + ( WINDOWS (WITH create_login_windows_options (COMMA create_login_windows_options)* )? + | CERTIFICATE certname=id + | ASYMMETRIC KEY asym_key_name=id ) ) + ; + +create_login_option_list + : SID EQUAL sid=BINARY + | DEFAULT_DATABASE EQUAL default_database=id + | DEFAULT_LANGUAGE EQUAL (id | STRING | DECIMAL) + | CHECK_EXPIRATION EQUAL (ON|OFF) + | CHECK_POLICY EQUAL (ON|OFF) + | CREDENTIAL EQUAL credential_name=id + ; + +create_login_windows_options + : DEFAULT_DATABASE EQUAL default_database=id + | DEFAULT_LANGUAGE EQUAL (id | STRING | DECIMAL) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-master-key-transact-sql +alter_master_key + : ALTER MASTER KEY ( (FORCE)? REGENERATE WITH ENCRYPTION BY PASSWORD EQUAL password=STRING |(ADD|DROP) ENCRYPTION BY (SERVICE MASTER KEY | PASSWORD EQUAL encryption_password=STRING) ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-master-key-transact-sql +create_master_key + : CREATE MASTER KEY ENCRYPTION BY PASSWORD EQUAL password=STRING + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-message-type-transact-sql +alter_message_type + : ALTER MESSAGE TYPE message_type_name=id VALIDATION EQUAL (NONE | EMPTY | WELL_FORMED_XML | VALID_XML WITH SCHEMA COLLECTION schema_collection_name=id) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-partition-function-transact-sql +alter_partition_function + : ALTER PARTITION FUNCTION partition_function_name=id LR_BRACKET RR_BRACKET (SPLIT|MERGE) RANGE LR_BRACKET DECIMAL RR_BRACKET + ; + +create_partition_function + : CREATE PARTITION FUNCTION partition_function_name=id LR_BRACKET data_type RR_BRACKET AS RANGE (LEFT | RIGHT)? FOR VALUES LR_BRACKET expression_list? RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-partition-scheme-transact-sql +alter_partition_scheme + : ALTER PARTITION SCHEME partition_scheme_name=id NEXT USED (file_group_name=id)? + ; + +create_partition_scheme + : CREATE PARTITION SCHEME partition_scheme_name=id AS PARTITION partition_function_name=id ALL? TO LR_BRACKET id (COMMA id)* RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-remote-service-binding-transact-sql +alter_remote_service_binding + : ALTER REMOTE SERVICE BINDING binding_name=id + WITH (USER EQUAL user_name=id)? + (COMMA ANONYMOUS EQUAL (ON|OFF) )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-remote-service-binding-transact-sql +create_remote_service_binding + : CREATE REMOTE SERVICE BINDING binding_name=id + (AUTHORIZATION owner_name=id)? + TO SERVICE remote_service_name=STRING + WITH (USER EQUAL user_name=id)? + (COMMA ANONYMOUS EQUAL (ON|OFF) )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-resource-pool-transact-sql +create_resource_pool + : CREATE RESOURCE POOL pool_name=id + (WITH + LR_BRACKET + (COMMA? MIN_CPU_PERCENT EQUAL DECIMAL)? + (COMMA? MAX_CPU_PERCENT EQUAL DECIMAL)? + (COMMA? CAP_CPU_PERCENT EQUAL DECIMAL)? + (COMMA? AFFINITY SCHEDULER EQUAL + (AUTO + | LR_BRACKET (COMMA? (DECIMAL|DECIMAL TO DECIMAL) )+ RR_BRACKET + | NUMANODE EQUAL LR_BRACKET (COMMA? (DECIMAL|DECIMAL TO DECIMAL) )+ RR_BRACKET + ) + )? + (COMMA? MIN_MEMORY_PERCENT EQUAL DECIMAL)? + (COMMA? MAX_MEMORY_PERCENT EQUAL DECIMAL)? + (COMMA? MIN_IOPS_PER_VOLUME EQUAL DECIMAL)? + (COMMA? MAX_IOPS_PER_VOLUME EQUAL DECIMAL)? + RR_BRACKET + )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-resource-governor-transact-sql +alter_resource_governor + : ALTER RESOURCE GOVERNOR ( (DISABLE | RECONFIGURE) | WITH LR_BRACKET CLASSIFIER_FUNCTION EQUAL ( schema_name=id DOT function_name=id | NULL_P ) RR_BRACKET | RESET STATISTICS | WITH LR_BRACKET MAX_OUTSTANDING_IO_PER_VOLUME EQUAL max_outstanding_io_per_volume=DECIMAL RR_BRACKET ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-role-transact-sql +alter_db_role + : ALTER ROLE role_name=id + ( (ADD|DROP) MEMBER database_principal=id + | WITH NAME EQUAL new_role_name=id ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-role-transact-sql +create_db_role + : CREATE ROLE role_name=id (AUTHORIZATION owner_name = id)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-route-transact-sql +create_route + : CREATE ROUTE route_name=id + (AUTHORIZATION owner_name=id)? + WITH + (COMMA? SERVICE_NAME EQUAL route_service_name=STRING)? + (COMMA? BROKER_INSTANCE EQUAL broker_instance_identifier=STRING)? + (COMMA? LIFETIME EQUAL DECIMAL)? + COMMA? ADDRESS EQUAL STRING + (COMMA MIRROR_ADDRESS EQUAL STRING )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-rule-transact-sql +create_rule + : CREATE RULE (schema_name=id DOT)? rule_name=id + AS search_condition + ; + +create_default + : CREATE DEFAULT (schema_name=id DOT)? default_name=id + AS expression + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-schema-transact-sql +alter_schema + : ALTER SCHEMA schema_name=id TRANSFER ((OBJECT|TYPE|XML SCHEMA COLLECTION) colon_colon )? id (DOT id)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-schema-transact-sql +create_schema + : CREATE SCHEMA + (schema_name=id + |AUTHORIZATION owner_name=id + | schema_name=id AUTHORIZATION owner_name=id + ) + (create_table + |create_or_alter_view + | grant_statement + | revoke_statement + )* + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-search-property-list-transact-sql +create_search_property_list + : CREATE SEARCH PROPERTY LIST new_list_name=id + (FROM (database_name=id DOT)? source_list_name=id )? + (AUTHORIZATION owner_name=id)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-security-policy-transact-sql +create_security_policy + : CREATE SECURITY POLICY (schema_name=id DOT)? security_policy_name=id + (COMMA? ADD (FILTER|BLOCK)? PREDICATE tvf_schema_name=id DOT security_predicate_function_name=id + LR_BRACKET (COMMA? column_name_or_arguments=id)+ RR_BRACKET + ON table_schema_name=id DOT name=id + (COMMA? AFTER (INSERT|UPDATE) + | COMMA? BEFORE (UPDATE|DELETE) + )* + )+ + (WITH LR_BRACKET + STATE EQUAL (ON|OFF) + (SCHEMABINDING (ON|OFF) )? + RR_BRACKET + )? + for_replication? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-sequence-transact-sql +alter_sequence + : ALTER SEQUENCE (schema_name=id DOT)? sequence_name=id ( RESTART (WITH sign? DECIMAL)? )? (INCREMENT BY sign? DECIMAL )? ( MINVALUE sign? DECIMAL| NO MINVALUE)? (MAXVALUE sign? DECIMAL| NO MAXVALUE)? (CYCLE|NO CYCLE)? (CACHE DECIMAL | NO CACHE)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql +create_sequence + : CREATE SEQUENCE (schema_name=id DOT)? sequence_name=id + (AS data_type )? + (START WITH sign? DECIMAL)? + (INCREMENT BY sign? DECIMAL)? + (MINVALUE sign? DECIMAL? | NO MINVALUE)? + (MAXVALUE sign? DECIMAL? | NO MAXVALUE)? + (CYCLE|NO CYCLE)? + (CACHE DECIMAL? | NO CACHE)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-server-audit-transact-sql +alter_server_audit + : ALTER SERVER AUDIT audit_name=id + ( ( TO + (FILE + ( LR_BRACKET + ( COMMA? FILEPATH EQUAL filepath=STRING + | COMMA? MAXSIZE EQUAL ( DECIMAL (MB|GB|TB) + | UNLIMITED + ) + | COMMA? MAX_ROLLOVER_FILES EQUAL max_rollover_files=(DECIMAL|UNLIMITED) + | COMMA? MAX_FILES EQUAL max_files=DECIMAL + | COMMA? RESERVE_DISK_SPACE EQUAL (ON|OFF) )* + RR_BRACKET ) + | APPLICATION_LOG + | SECURITY_LOG + ) )? + ( WITH LR_BRACKET + (COMMA? QUEUE_DELAY EQUAL queue_delay=DECIMAL + | COMMA? ON_FAILURE EQUAL (CONTINUE | SHUTDOWN|FAIL_OPERATION) + |COMMA? STATE EQUAL (ON|OFF) )* + RR_BRACKET + )? + ( WHERE ( COMMA? (NOT?) event_field_name=id + (EQUAL + |(LESS GREATER) + | (EXCLAMATION EQUAL) + | GREATER + | (GREATER EQUAL) + | LESS + | LESS EQUAL + ) + (DECIMAL | STRING) + | COMMA? (AND|OR) NOT? (EQUAL + |(LESS GREATER) + | (EXCLAMATION EQUAL) + | GREATER + | (GREATER EQUAL) + | LESS + | LESS EQUAL) + (DECIMAL | STRING) ) )? + |REMOVE WHERE + | MODIFY NAME EQUAL new_audit_name=id + ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-server-audit-transact-sql +create_server_audit + : CREATE SERVER AUDIT audit_name=id + ( ( TO + (FILE + ( LR_BRACKET + ( COMMA? FILEPATH EQUAL filepath=STRING + | COMMA? MAXSIZE EQUAL ( DECIMAL (MB|GB|TB) + | UNLIMITED + ) + | COMMA? MAX_ROLLOVER_FILES EQUAL max_rollover_files=(DECIMAL|UNLIMITED) + | COMMA? MAX_FILES EQUAL max_files=DECIMAL + | COMMA? RESERVE_DISK_SPACE EQUAL (ON|OFF) )* + RR_BRACKET ) + | APPLICATION_LOG + | SECURITY_LOG + ) )? + ( WITH LR_BRACKET + (COMMA? QUEUE_DELAY EQUAL queue_delay=DECIMAL + | COMMA? ON_FAILURE EQUAL (CONTINUE | SHUTDOWN|FAIL_OPERATION) + |COMMA? STATE EQUAL (ON|OFF) + |COMMA? AUDIT_GUID EQUAL audit_guid=id + )* + + RR_BRACKET + )? + ( WHERE ( COMMA? (NOT?) event_field_name=id + (EQUAL + |(LESS GREATER) + | (EXCLAMATION EQUAL) + | GREATER + | (GREATER EQUAL) + | LESS + | LESS EQUAL + ) + (DECIMAL | STRING) + | COMMA? (AND|OR) NOT? (EQUAL + |(LESS GREATER) + | (EXCLAMATION EQUAL) + | GREATER + | (GREATER EQUAL) + | LESS + | LESS EQUAL) + (DECIMAL | STRING) ) )? + |REMOVE WHERE + | MODIFY NAME EQUAL new_audit_name=id + ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-server-audit-specification-transact-sql +alter_server_audit_specification + : ALTER SERVER AUDIT SPECIFICATION audit_specification_name=id + (FOR SERVER AUDIT audit_name=id)? + ( (ADD|DROP) LR_BRACKET audit_action_group_name=id RR_BRACKET )* + (WITH LR_BRACKET STATE EQUAL (ON|OFF) RR_BRACKET )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-server-audit-specification-transact-sql +create_server_audit_specification + : CREATE SERVER AUDIT SPECIFICATION audit_specification_name=id + (FOR SERVER AUDIT audit_name=id)? + ( ADD LR_BRACKET audit_action_group_name=id RR_BRACKET )* + (WITH LR_BRACKET STATE EQUAL (ON|OFF) RR_BRACKET )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-server-configuration-transact-sql +alter_server_configuration + : ALTER SERVER CONFIGURATION + SET ( (PROCESS AFFINITY (CPU EQUAL (AUTO | (COMMA? DECIMAL | COMMA? DECIMAL TO DECIMAL)+ ) | NUMANODE EQUAL ( COMMA? DECIMAL |COMMA? DECIMAL TO DECIMAL)+ ) | DIAGNOSTICS LOG (ON|OFF|PATH EQUAL (STRING | DEFAULT) |MAX_SIZE EQUAL (DECIMAL MB |DEFAULT)|MAX_FILES EQUAL (DECIMAL|DEFAULT) ) | FAILOVER CLUSTER PROPERTY (VERBOSELOGGING EQUAL (STRING|DEFAULT) |SQLDUMPERFLAGS EQUAL (STRING|DEFAULT) | SQLDUMPERPATH EQUAL (STRING|DEFAULT) | SQLDUMPERTIMEOUT (STRING|DEFAULT) | FAILURECONDITIONLEVEL EQUAL (STRING|DEFAULT) | HEALTHCHECKTIMEOUT EQUAL (DECIMAL|DEFAULT) ) | HADR CLUSTER CONTEXT EQUAL (STRING|LOCAL) | BUFFER POOL EXTENSION (ON LR_BRACKET FILENAME EQUAL STRING COMMA SIZE EQUAL DECIMAL (KB|MB|GB) RR_BRACKET | OFF ) | SET SOFTNUMA (ON|OFF) ) ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-server-role-transact-sql +alter_server_role + : ALTER SERVER ROLE server_role_name=id + ( (ADD|DROP) MEMBER server_principal=id + | WITH NAME EQUAL new_server_role_name=id + ) + ; +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-server-role-transact-sql +create_server_role + : CREATE SERVER ROLE server_role=id (AUTHORIZATION server_principal=id)? + ; + +alter_server_role_pdw + : ALTER SERVER ROLE server_role_name=id (ADD|DROP) MEMBER login=id + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-service-transact-sql +alter_service + : ALTER SERVICE modified_service_name=id (ON QUEUE (schema_name=id DOT) queue_name=id)? (COMMA? (ADD|DROP) modified_contract_name=id)* + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-service-transact-sql +create_service + : CREATE SERVICE create_service_name=id + (AUTHORIZATION owner_name=id)? + ON QUEUE (schema_name=id DOT)? queue_name=id + ( LR_BRACKET (COMMA? (id|DEFAULT) )+ RR_BRACKET )? + ; + + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-service-master-key-transact-sql + +alter_service_master_key + : ALTER SERVICE MASTER KEY ( FORCE? REGENERATE | (WITH (OLD_ACCOUNT EQUAL acold_account_name=STRING COMMA OLD_PASSWORD EQUAL old_password=STRING | NEW_ACCOUNT EQUAL new_account_name=STRING COMMA NEW_PASSWORD EQUAL new_password=STRING)? ) ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-symmetric-key-transact-sql + +alter_symmetric_key + : ALTER SYMMETRIC KEY key_name=id ( (ADD|DROP) ENCRYPTION BY (CERTIFICATE certificate_name=id | PASSWORD EQUAL password=STRING | SYMMETRIC KEY symmetric_key_name=id | ASYMMETRIC KEY Asym_key_name=id ) ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-symmetric-key-transact-sql +create_symmetric_key + : ALTER SYMMETRIC KEY key_name=id + (AUTHORIZATION owner_name=id)? + (FROM PROVIDER provider_name=id)? + (WITH ( (KEY_SOURCE EQUAL key_pass_phrase=STRING + | ALGORITHM EQUAL (DES | TRIPLE_DES | TRIPLE_DES_3KEY | RC2 | RC4 | RC4_128 | DESX | AES_128 | AES_192 | AES_256) + | IDENTITY_VALUE EQUAL identity_phrase=STRING + | PROVIDER_KEY_NAME EQUAL provider_key_name=STRING + | CREATION_DISPOSITION EQUAL (CREATE_NEW|OPEN_EXISTING) + ) + | ENCRYPTION BY + ( CERTIFICATE certificate_name=id + | PASSWORD EQUAL password=STRING + | SYMMETRIC KEY symmetric_key_name=id + | ASYMMETRIC KEY asym_key_name=id + ) + ) + ) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-synonym-transact-sql +create_synonym + : CREATE SYNONYM (schema_name_1=id DOT )? synonym_name=id FOR full_object_name + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-user-transact-sql +alter_user + : ALTER USER username=id WITH (COMMA? NAME EQUAL newusername=id | COMMA? DEFAULT_SCHEMA EQUAL ( schema_name=id |NULL_P ) | COMMA? LOGIN EQUAL loginame=id | COMMA? PASSWORD EQUAL STRING (OLD_PASSWORD EQUAL STRING)+ | COMMA? DEFAULT_LANGUAGE EQUAL (NONE| lcid=DECIMAL| language_name_or_alias=id) | COMMA? ALLOW_ENCRYPTED_VALUE_MODIFICATIONS EQUAL (ON|OFF) )+ + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-user-transact-sql +create_user + : CREATE USER user_name=id + ( (FOR|FROM) LOGIN login_name=id )? + ( WITH (COMMA? DEFAULT_SCHEMA EQUAL schema_name=id + |COMMA? ALLOW_ENCRYPTED_VALUE_MODIFICATIONS EQUAL (ON|OFF) + )* + )? + | CREATE USER ( windows_principal=id + (WITH + (COMMA? DEFAULT_SCHEMA EQUAL schema_name=id + |COMMA? DEFAULT_LANGUAGE EQUAL (NONE + |DECIMAL + |language_name_or_alias=id ) + |COMMA? SID EQUAL BINARY + |COMMA? ALLOW_ENCRYPTED_VALUE_MODIFICATIONS EQUAL (ON|OFF) + )* + )? + | user_name=id WITH PASSWORD EQUAL password=STRING + (COMMA? DEFAULT_SCHEMA EQUAL schema_name=id + |COMMA? DEFAULT_LANGUAGE EQUAL (NONE + |DECIMAL + |language_name_or_alias=id ) + |COMMA? SID EQUAL BINARY + |COMMA? ALLOW_ENCRYPTED_VALUE_MODIFICATIONS EQUAL (ON|OFF) + )* + | Azure_Active_Directory_principal=id FROM EXTERNAL PROVIDER + ) + | CREATE USER user_name=id + ( WITHOUT LOGIN + (COMMA? DEFAULT_SCHEMA EQUAL schema_name=id + |COMMA? ALLOW_ENCRYPTED_VALUE_MODIFICATIONS EQUAL (ON|OFF) + )* + | (FOR|FROM) CERTIFICATE cert_name=id + | (FOR|FROM) ASYMMETRIC KEY asym_key_name=id + ) + | CREATE USER user_name=id + ; + +create_user_azure_sql_dw + : CREATE USER user_name=id + ( (FOR|FROM) LOGIN login_name=id + | WITHOUT LOGIN + )? + + ( WITH DEFAULT_SCHEMA EQUAL schema_name=id)? + | CREATE USER Azure_Active_Directory_principal=id + FROM EXTERNAL PROVIDER + ( WITH DEFAULT_SCHEMA EQUAL schema_name=id)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-workload-group-transact-sql +alter_workload_group + : ALTER WORKLOAD GROUP + (workload_group_group_name=id + |DEFAULT_DOUBLE_QUOTE + ) + (WITH LR_BRACKET + (IMPORTANCE EQUAL (LOW|MEDIUM|HIGH) + | COMMA? REQUEST_MAX_MEMORY_GRANT_PERCENT EQUAL request_max_memory_grant=DECIMAL + | COMMA? REQUEST_MAX_CPU_TIME_SEC EQUAL request_max_cpu_time_sec=DECIMAL + | REQUEST_MEMORY_GRANT_TIMEOUT_SEC EQUAL request_memory_grant_timeout_sec=DECIMAL + | MAX_DOP EQUAL max_dop=DECIMAL + | GROUP_MAX_REQUESTS EQUAL group_max_requests=DECIMAL)+ + RR_BRACKET )? + (USING (workload_group_pool_name=id | DEFAULT_DOUBLE_QUOTE) )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-workload-group-transact-sql +create_workload_group + : CREATE WORKLOAD GROUP workload_group_group_name=id + (WITH LR_BRACKET + (IMPORTANCE EQUAL (LOW|MEDIUM|HIGH) + | COMMA? REQUEST_MAX_MEMORY_GRANT_PERCENT EQUAL request_max_memory_grant=DECIMAL + | COMMA? REQUEST_MAX_CPU_TIME_SEC EQUAL request_max_cpu_time_sec=DECIMAL + | REQUEST_MEMORY_GRANT_TIMEOUT_SEC EQUAL request_memory_grant_timeout_sec=DECIMAL + | MAX_DOP EQUAL max_dop=DECIMAL + | GROUP_MAX_REQUESTS EQUAL group_max_requests=DECIMAL)+ + RR_BRACKET )? + (USING (workload_group_pool_name=id | DEFAULT_DOUBLE_QUOTE)? + (COMMA? EXTERNAL external_pool_name=id | DEFAULT_DOUBLE_QUOTE)? + )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-xml-schema-collection-transact-sql +create_xml_schema_collection + : CREATE XML SCHEMA COLLECTION simple_name AS (STRING|id|LOCAL_ID) + ; + +alter_xml_schema_collection + : ALTER XML SCHEMA COLLECTION simple_name ADD STRING + ; + +create_queue + : CREATE QUEUE (full_object_name | queue_name=id) queue_settings? + (ON filegroup=id | DEFAULT)? + ; + + +queue_settings + : WITH + (STATUS EQUAL (ON | OFF) COMMA?)? + (RETENTION EQUAL (ON | OFF) COMMA?)? + (ACTIVATION + LR_BRACKET + ( + ( + (STATUS EQUAL (ON | OFF) COMMA? )? + (PROCEDURE_NAME EQUAL func_proc_name_database_schema COMMA?)? + (MAX_QUEUE_READERS EQUAL max_readers=DECIMAL COMMA?)? + ((EXECUTE|EXEC) AS (SELF | user_name=STRING | OWNER) COMMA?)? + ) + | DROP + ) + RR_BRACKET COMMA? + )? + (POISON_MESSAGE_HANDLING + LR_BRACKET + (STATUS EQUAL (ON | OFF)) + RR_BRACKET + )? + ; + +alter_queue + : ALTER QUEUE (full_object_name | queue_name=id) + (queue_settings | queue_action) + ; + +queue_action + : REBUILD ( WITH LR_BRACKET queue_rebuild_options RR_BRACKET)? + | REORGANIZE (WITH LOB_COMPACTION EQUAL (ON | OFF))? + | MOVE TO (id | DEFAULT) + ; +queue_rebuild_options + : maxdop_option + ; + +create_contract + : CREATE CONTRACT contract_name + (AUTHORIZATION owner_name=id)? + LR_BRACKET ((message_type_name=id | DEFAULT) + SENT BY (INITIATOR | TARGET | ANY ) COMMA?)+ + RR_BRACKET + ; + +conversation_statement + : begin_conversation_timer + | begin_conversation_dialog + | end_conversation + | get_conversation + | send_conversation + | waitfor_conversation + | waitfor_receive_statement + | receive_statement + ; + +message_statement + : CREATE MESSAGE TYPE message_type_name=id + (AUTHORIZATION owner_name=id)? + (VALIDATION EQUAL (NONE + | EMPTY + | WELL_FORMED_XML + | VALID_XML WITH SCHEMA COLLECTION schema_collection_name=id)) + ; + +// DML + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql +// note that there is a limit on number of when_matches but it has to be done runtime due to different ordering of statements allowed +merge_statement + : with_expression? + MERGE (TOP LR_BRACKET expression RR_BRACKET PERCENT?)? + INTO? ddl_object with_table_hints? as_table_alias? + USING table_sources + ON search_condition + when_matches+ + output_clause? + option_clause? + SEMI /// semicolon is required! + ; + +when_matches + : (WHEN MATCHED (AND search_condition)? + THEN merge_matched)+ + | (WHEN NOT MATCHED (BY TARGET)? (AND search_condition)? + THEN merge_not_matched) + | (WHEN NOT MATCHED BY SOURCE (AND search_condition)? + THEN merge_matched)+ + ; + +merge_matched + : UPDATE SET update_elem_merge (COMMA update_elem_merge)* + | DELETE + ; + +merge_not_matched + : INSERT ( LR_BRACKET column_name_list RR_BRACKET )? + (table_value_constructor | DEFAULT VALUES) + ; + +// https://msdn.microsoft.com/en-us/library/ms189835.aspx +delete_statement + : with_expression? + DELETE (TOP LR_BRACKET expression RR_BRACKET PERCENT? | TOP DECIMAL)? + FROM? delete_statement_from + with_table_hints? + output_clause? + (FROM table_sources)? + (WHERE (search_condition | CURRENT OF (GLOBAL? cursor_name | cursor_var=LOCAL_ID)))? + option_clause? SEMI? + ; + +delete_statement_from + : ddl_object + | table_alias + | rowset_function + | table_var=LOCAL_ID + ; + +// https://msdn.microsoft.com/en-us/library/ms174335.aspx +insert_statement + : with_expression? + INSERT (TOP LR_BRACKET expression RR_BRACKET PERCENT?)? + INTO? (ddl_object | rowset_function) + with_table_hints? + ( LR_BRACKET insert_column_name_list RR_BRACKET )? + output_clause? + insert_statement_value + option_clause? SEMI? + ; + +insert_statement_value + : table_value_constructor + | derived_table + | execute_statement + | DEFAULT VALUES + ; + +bulk_insert_statement + : BULK INSERT ddl_object FROM STRING ( WITH LR_BRACKET bulk_insert_option (COMMA? bulk_insert_option)* RR_BRACKET )? SEMI? + ; + +bulk_insert_option + : id (EQUAL expression)? + | ORDER LR_BRACKET order_by_expression (COMMA order_by_expression)* RR_BRACKET + ; + +// https://msdn.microsoft.com/en-us/library/ms189499.aspx +select_statement_standalone + : with_expression? + select_statement + ; + +select_statement + : query_expression order_by_clause? option_clause? for_clause? option_clause? + ; + +time + : (LOCAL_ID | constant) + ; + +// https://msdn.microsoft.com/en-us/library/ms177523.aspx +update_statement + : with_expression? + UPDATE (TOP LR_BRACKET expression RR_BRACKET PERCENT?)? + (ddl_object | rowset_function) + with_table_hints? + SET update_elem (COMMA update_elem)* + output_clause? + (FROM table_sources)? + (WHERE (search_condition | CURRENT OF (GLOBAL? cursor_name | cursor_var=LOCAL_ID)))? + option_clause? SEMI? + ; + +// https://msdn.microsoft.com/en-us/library/ms177564.aspx +output_clause + : OUTPUT output_dml_list_elem (COMMA output_dml_list_elem)* + (INTO (LOCAL_ID | table_name) ( LR_BRACKET column_name_list RR_BRACKET )? )? + ; + +output_dml_list_elem + : (output_column_name | expression) as_column_alias? // TODO: scalar_expression + ; + +output_column_name + : (DELETED | INSERTED | table_name) DOT ( STAR | id) + | DOLLAR_ACTION + ; + +readtext_statement + : READTEXT full_column_name text_ptr=(LOCAL_ID|BINARY) offset=(LOCAL_ID|DECIMAL) size=(LOCAL_ID|DECIMAL) HOLDLOCK? + ; + +writetext_statement + : WRITETEXT BULK? full_column_name text_ptr=(LOCAL_ID|BINARY) (WITH LOG)? (LOCAL_ID|STRING) + ; + +updatetext_statement + : UPDATETEXT BULK? full_column_name text_ptr=(LOCAL_ID|BINARY) (NULL_P|DECIMAL|LOCAL_ID) (NULL_P|DECIMAL|LOCAL_ID) (WITH LOG)? ((LOCAL_ID|STRING) | full_column_name text_ptr=(LOCAL_ID|BINARY) )? + ; + +// https://msdn.microsoft.com/en-ie/library/ms176061.aspx +create_database + : CREATE DATABASE (database=id) + ( CONTAINMENT EQUAL ( NONE | PARTIAL ) )? + ( ON PRIMARY? database_file_spec ( COMMA database_file_spec )* )? + ( LOG ON database_file_spec ( COMMA database_file_spec )* )? + ( COLLATE collation_name = id )? + ( WITH create_database_option ( COMMA create_database_option )* )? + ; + +// https://msdn.microsoft.com/en-us/library/ms188783.aspx +create_index + : CREATE UNIQUE? clustered? COLUMNSTORE? INDEX id ON table_name (LR_BRACKET column_name_list_with_order RR_BRACKET)? + (INCLUDE LR_BRACKET column_name_list RR_BRACKET )? + (WHERE where=search_condition)? + with_index_options? + (ON storage_partition_clause)? + SEMI? + ; + +alter_index + : ALTER INDEX (id | ALL) ON table_name alter_index_options + SEMI? + ; + +alter_index_options + : REBUILD ( PARTITION EQUAL (ALL | (DECIMAL|LOCAL_ID)) )? (WITH LR_BRACKET index_option (COMMA index_option)* RR_BRACKET )? + | DISABLE + | REORGANIZE + | SET LR_BRACKET index_option (COMMA index_option)* RR_BRACKET + | RESUME (WITH LR_BRACKET index_option (COMMA index_option)* RR_BRACKET)? + | PAUSE + | ABORT + ; + +create_xml_index + : CREATE PRIMARY? XML INDEX id ON table_name LR_BRACKET id RR_BRACKET + (USING XML INDEX id (FOR (VALUE | PATH | PROPERTY)?)?)? + with_index_options? + SEMI? + ; + +create_selective_xml_index + : CREATE SELECTIVE XML INDEX id ON table_name LR_BRACKET id RR_BRACKET + (WITH XMLNAMESPACES LR_BRACKET STRING AS id (COMMA STRING AS id)* RR_BRACKET)? + FOR LR_BRACKET promoted_node_path (COMMA promoted_node_path)* RR_BRACKET + with_index_options? + SEMI? + ; + +promoted_node_path + : pathname=ID EQUAL STRING (AS (XQUERY STRING | SQL) data_type? SINGLETON?)? + ; + +create_spatial_index + : CREATE SPATIAL INDEX id ON table_name LR_BRACKET id RR_BRACKET + spatial_grid_clause? + spatial_grid_option_clause? + (ON storage_partition_clause)? + SEMI? + ; + +spatial_grid_clause + : USING (GEOMETRY_AUTO_GRID | GEOMETRY_GRID | GEOGRAPHY_AUTO_GRID | GEOGRAPHY_GRID) + ; + +spatial_grid_option_clause + : WITH LR_BRACKET spatial_grid_option (COMMA spatial_grid_option)* RR_BRACKET + ; + +spatial_grid_option + : bounding_box + | tessellation_cells_per_object + | tessellation_grid + | spatial_index_option (COMMA spatial_index_option)* + ; + +bounding_box + : BOUNDING_BOX EQUAL LR_BRACKET (XMIN EQUAL)? expression COMMA (YMIN EQUAL)? expression COMMA (XMAX EQUAL)? expression COMMA (YMAX EQUAL)? expression RR_BRACKET + ; + +tessellation_cells_per_object + : CELLS_PER_OBJECT EQUAL (DECIMAL|LOCAL_ID) + ; + +tessellation_grid + : GRIDS EQUAL LR_BRACKET grid_level_or_size (COMMA grid_level_or_size)* RR_BRACKET + ; + +grid_level_or_size + : (id EQUAL)? (LOW | MEDIUM | HIGH) + ; + +spatial_index_option + : index_option + ; + +// https://msdn.microsoft.com/en-us/library/ms187926(v=sql.120).aspx +create_or_alter_procedure + : ((CREATE (OR ALTER)?) | ALTER) proc=(PROC | PROCEDURE) procName=func_proc_name_schema ( SEMI DECIMAL)? + ( LR_BRACKET? procedure_param (COMMA procedure_param)* RR_BRACKET?)? + (WITH procedure_option (COMMA procedure_option)*)? + for_replication? AS + ( atomic_proc_body | sql_clauses* | external_name ) + ; + +atomic_proc_body + : BEGIN atomic WITH LR_BRACKET atomic_body_options RR_BRACKET sql_clauses* END SEMI? + ; + +atomic + : ATOMIC + ; + +atomic_body_options + : atomic_body_option (COMMA atomic_body_option)* + ; + +atomic_body_option + : LANGUAGE EQUAL STRING + | TRANSACTION ISOLATION LEVEL EQUAL (SNAPSHOT | REPEATABLE READ | SERIALIZABLE) + | DATEFIRST EQUAL DECIMAL + | DATEFORMAT EQUAL id + | DELAYED_DURABILITY EQUAL on_off + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql +create_or_alter_trigger + : create_or_alter_dml_trigger + | create_or_alter_ddl_trigger + ; + +create_or_alter_dml_trigger + : ((CREATE (OR ALTER)?) | ALTER) TRIGGER simple_name + ON table_name + (WITH trigger_option (COMMA trigger_option)* )? + (FOR | AFTER | INSTEAD OF) + dml_trigger_operation (COMMA dml_trigger_operation)* + (WITH APPEND)? + for_replication? + AS (sql_clauses+ | external_name) + ; + +dml_trigger_operation + : (INSERT | UPDATE | DELETE) + ; + +create_or_alter_ddl_trigger + : ((CREATE (OR ALTER)?) | ALTER) TRIGGER simple_name + ON (ALL SERVER | DATABASE) + (WITH trigger_option (COMMA trigger_option)* )? + (FOR | AFTER) ddl_trigger_operation+=ID (COMMA ddl_trigger_operation+=ID)* + AS (sql_clauses+ | external_name) + ; + +trigger_option + : ENCRYPTION + | NATIVE_COMPILATION + | SCHEMABINDING + | execute_as_clause + ; + +// https://msdn.microsoft.com/en-us/library/ms186755.aspx +create_or_alter_function + : ((CREATE (OR ALTER)?) | ALTER) FUNCTION funcName=func_proc_name_schema + (( LR_BRACKET procedure_param (COMMA procedure_param)* RR_BRACKET ) | LR_BRACKET RR_BRACKET ) //must have (), but can be empty + ( func_body_returns_select | func_body_returns_table | func_body_returns_scalar | func_body_returns_table_clr ) SEMI? + ; + +func_body_returns_select + : RETURNS TABLE + (WITH function_option (COMMA function_option)*)? + AS? + func_body_return_select_body + ; + +func_body_return_select_body + : RETURN ( LR_BRACKET select_statement_standalone RR_BRACKET | select_statement_standalone) + ; + +func_body_returns_table + : RETURNS LOCAL_ID table_type_definition + (WITH function_option (COMMA function_option)*)? + AS? + BEGIN sql_clauses* RETURN SEMI? END + ; + +func_body_returns_table_clr + : RETURNS table_type_definition + (WITH function_option (COMMA function_option)*)? + (ORDER LR_BRACKET column_name_list_with_order RR_BRACKET)? + AS? + external_name + ; + +func_body_returns_scalar + : RETURNS data_type + (WITH function_option (COMMA function_option)*)? + AS? + ( BEGIN atomic_func_body? sql_clauses* RETURN ret=expression SEMI? END | external_name ) + ; + +atomic_func_body + : atomic WITH LR_BRACKET atomic_body_options RR_BRACKET + ; + +// CREATE PROC p @p INT NULL --> this appears to be accepted syntax for non-native compiled procs, though formally not allowed +procedure_param + : LOCAL_ID AS? data_type VARYING? (NOT? NULL_P)? (EQUAL default_val=expression)? (OUT | OUTPUT | READONLY)? + ; + +// drop_procedure_param can be used in a DROP FUNCTION or DROP PROCEDURE command +drop_procedure_param + : LOCAL_ID? data_type + ; + +procedure_option + : ENCRYPTION + | NATIVE_COMPILATION + | SCHEMABINDING + | RECOMPILE + | execute_as_clause + ; + +function_option + : ENCRYPTION + | NATIVE_COMPILATION + | SCHEMABINDING + | RETURNS NULL_P ON NULL_P INPUT + | CALLED ON NULL_P INPUT + | execute_as_clause + ; + +// https://msdn.microsoft.com/en-us/library/ms188038.aspx +create_statistics + : CREATE STATISTICS id ON table_name LR_BRACKET column_name_list RR_BRACKET + (WITH (FULLSCAN | SAMPLE DECIMAL (PERCENT | ROWS) | STATS_STREAM) + (COMMA NORECOMPUTE)? (COMMA INCREMENTAL EQUAL on_off)? )? SEMI? + ; + +// UPDATE (INDEX|ALL) STATISTICS is Sybase T-SQL, not MSFT +update_statistics + : UPDATE STATISTICS table_name (table_name | LR_BRACKET column_name_list RR_BRACKET )? + (WITH update_statistics_option (COMMA? update_statistics_option)*)? + SEMI? + ; + +update_statistics_option + : FULLSCAN (COMMA? update_statistics_option_persist_pct)? + | SAMPLE expression (PERCENT|ROWS) (COMMA? update_statistics_option_persist_pct)? + | RESAMPLE (ON PARTITIONS LR_BRACKET ((DECIMAL|LOCAL_ID)|(DECIMAL|LOCAL_ID) TO (DECIMAL|LOCAL_ID)) (COMMA ((DECIMAL|LOCAL_ID)|(DECIMAL|LOCAL_ID) TO (DECIMAL|LOCAL_ID)))* RR_BRACKET )? + | update_statistics_option_stats_stream (COMMA update_statistics_option_stats_stream)* + | (ALL|COLUMNS|INDEX) + | NOCOMPUTE + | INCREMENTAL EQUAL on_off + | maxdop_option + ; + +update_statistics_option_persist_pct + : PERSIST_SAMPLE_PERCENT EQUAL on_off + ; + +update_statistics_option_stats_stream + : STATS_STREAM EQUAL BINARY + | ROWCOUNT EQUAL DECIMAL + | PAGECOUNT EQUAL DECIMAL + ; + +maxdop_option + : MAXDOP EQUAL DECIMAL + ; + +// https://msdn.microsoft.com/en-us/library/ms174979.aspx +create_table + : CREATE TABLE tabname=table_name LR_BRACKET column_def_table_constraints (COMMA? table_indices)* COMMA? RR_BRACKET create_table_options* SEMI? + | CREATE TABLE tabname=table_name (LR_BRACKET (column_definition (COMMA column_definition)*)? column_constraint* COMMA? RR_BRACKET)? graph_clause create_table_options* SEMI? + | CREATE TABLE tabname=table_name AS FILETABLE (WITH LR_BRACKET file_table_option (COMMA file_table_option)* RR_BRACKET)? SEMI? + ; + + +create_table_options + : LOCK ID + | table_options + | ON storage_partition_clause + | TEXTIMAGE_ON storage_partition_clause + | FILESTREAM_ON storage_partition_clause + ; + +graph_clause + : AS (NODE | EDGE) + ; + +table_indices + : INDEX id (UNIQUE | CLUSTERED | NONCLUSTERED)? LR_BRACKET column_name_list_with_order RR_BRACKET + (WHERE where=search_condition)? + with_index_options? + (ON storage_partition_clause)? + ; + +table_options + : WITH (LR_BRACKET index_option_list? system_versioning_options? RR_BRACKET | index_option_list ) + ; + +file_table_option + : id EQUAL expression + ; + +storage_partition_clause + : id (LR_BRACKET id RR_BRACKET)? + | STRING // can be "DEFAULT" + ; + +// https://msdn.microsoft.com/en-us/library/ms187956.aspx +create_or_alter_view + : ((CREATE (OR ALTER)?) | ALTER) VIEW simple_name (LR_BRACKET column_name_list RR_BRACKET)? + (WITH view_attribute (COMMA view_attribute)*)? + AS select_statement_standalone (WITH CHECK OPTION)? SEMI? + ; + +view_attribute + : ENCRYPTION | SCHEMABINDING | VIEW_METADATA + ; + +// https://msdn.microsoft.com/en-us/library/ms190273.aspx +alter_table + : ALTER TABLE tabname=table_name + ( (WITH (NOCHECK | CHECK))? ADD column_def_table_constraints + | ALTER COLUMN column_definition ((ADD | DROP) special_column_option)? (WITH ( LR_BRACKET ONLINE EQUAL on_off RR_BRACKET ))? + | DROP alter_table_drop (COMMA alter_table_drop)* + | (WITH (NOCHECK | CHECK))? (NOCHECK | CHECK) CONSTRAINT ( ALL | id (COMMA id)* ) + | (ENABLE | DISABLE) TRIGGER ( ALL | id (COMMA id)* ) + | (ENABLE | DISABLE) CHANGE_TRACKING (WITH LR_BRACKET TRACK_COLUMNS_UPDATED EQUAL on_off RR_BRACKET)? + | SWITCH (PARTITION (DECIMAL|LOCAL_ID))? TO table_name (PARTITION (DECIMAL|LOCAL_ID))? (WITH LR_BRACKET low_priority_lock_wait RR_BRACKET )? + | SET LR_BRACKET SYSTEM_VERSIONING EQUAL on_off system_versioning_options RR_BRACKET + | SET LR_BRACKET FILESTREAM_ON EQUAL storage_partition_clause RR_BRACKET + | SET LR_BRACKET file_table_option (COMMA file_table_option)* RR_BRACKET + | SET LR_BRACKET LOCK_ESCALATION EQUAL (AUTO | TABLE | DISABLE) RR_BRACKET + | REBUILD table_options + ) + ; + +alter_table_drop + : alter_table_drop_column + | alter_table_drop_constraint + | PERIOD FOR SYSTEM_TIME + ; + +alter_table_drop_column + : COLUMN if_exists? id (COMMA (COLUMN if_exists?)? id)* + ; + +alter_table_drop_constraint + : CONSTRAINT if_exists? alter_table_drop_constraint_id (COMMA (CONSTRAINT if_exists?)? id)* + ; + +alter_table_drop_constraint_id + : id (WITH LR_BRACKET alter_table_drop_constraint_option (COMMA alter_table_drop_constraint_option)* RR_BRACKET)? + ; + +alter_table_drop_constraint_option + : maxdop_option + | ONLINE EQUAL on_off + | MOVE TO storage_partition_clause + ; + +low_priority_lock_wait + : WAIT_AT_LOW_PRIORITY LR_BRACKET MAX_DURATION EQUAL DECIMAL MINUTES? COMMA ABORT_AFTER_WAIT EQUAL (NONE | SELF | BLOCKERS) RR_BRACKET + ; + +// https://msdn.microsoft.com/en-us/library/ms174269.aspx +alter_database + : ALTER DATABASE (database=id | CURRENT) + ( SET database_optionspec (COMMA database_optionspec)* (WITH termination)? + | COLLATE id + | MODIFY NAME EQUAL new_name=id + | ADD FILE file_spec (COMMA file_spec)* ( TO FILEGROUP filegroup=id )? + | ADD LOG FILE file_spec (COMMA file_spec)* + | REMOVE FILE filename=id + | MODIFY FILE file_spec + | ADD FILEGROUP filegroup=id ( CONTAINS FILESTREAM | CONTAINS MEMORY_OPTIMIZED_DATA )? + | REMOVE FILEGROUP filegroup=id + | MODIFY FILEGROUP filegroup=id ( filegroup_updatability_option | DEFAULT | NAME EQUAL filegroup=id | AUTOGROW_SINGLE_FILE | AUTOGROW_ALL_FILES ) + ) + SEMI? + ; + +filegroup_updatability_option + : ( READONLY | READ_ONLY | READWRITE | READ_WRITE ) + ; + +// https://msdn.microsoft.com/en-us/library/bb522682.aspx +// Runtime check. +database_optionspec + : auto_option + | change_tracking_option + | containment_option + | cursor_option + | database_mirroring_option + | date_correlation_optimization_option + | db_encryption_option + | db_state_option + | db_update_option + | db_user_access_option + | delayed_durability_option + | external_access_option + | FILESTREAM database_filestream_option + | hadr_options + | mixed_page_allocation_option + | parameterization_option + | query_store_option + | recovery_option +// | remote_data_archive_option + | service_broker_option + | snapshot_option + | sql_option + | target_recovery_time_option + | termination + ; + +alter_database_scoped_configuration + : ALTER DATABASE SCOPED CONFIGURATION ( FOR SECONDARY )? SET id EQUAL ( on_off | PRIMARY | AUTO | WHEN_SUPPORTED | FAIL_UNSUPPORTED | DECIMAL ) + | ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE BINARY + ; + +auto_option + : AUTO_CLOSE on_off + | AUTO_CREATE_STATISTICS ( OFF | ON ( INCREMENTAL EQUAL ON | OFF )? ) + | AUTO_SHRINK on_off + | AUTO_UPDATE_STATISTICS on_off + | AUTO_UPDATE_STATISTICS_ASYNC (ON | OFF ) + ; + +change_tracking_option + : CHANGE_TRACKING EQUAL ( OFF | ON (change_tracking_option_list (COMMA change_tracking_option_list)*)* ) + ; + +change_tracking_option_list + : AUTO_CLEANUP EQUAL on_off + | CHANGE_RETENTION EQUAL ( DAYS | HOURS | MINUTES ) + ; + +containment_option + : CONTAINMENT EQUAL ( NONE | PARTIAL ) + ; + +cursor_option + : CURSOR_CLOSE_ON_COMMIT on_off + | CURSOR_DEFAULT ( LOCAL | GLOBAL ) + ; + +create_or_alter_database_audit_specification + : (CREATE | ALTER) DATABASE AUDIT SPECIFICATION audit_specification_name=id + FOR SERVER AUDIT server_audit=id + ((ADD | DROP) LR_BRACKET audit_item (COMMA audit_item)* RR_BRACKET)? + WITH LR_BRACKET STATE EQUAL on_off RR_BRACKET + SEMI? + ; + +audit_item + : audit_action_group_name=ID + | audit_action_specification + ; + +audit_action_specification + : permission (COMMA permission)* ON (object_type colon_colon)? entity=entity_name BY principals + ; + +create_diagnostic_session + : CREATE DIAGNOSTICS SESSION session_name=ID AS xml=STRING SEMI + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-endpoint-transact-sql +// https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-endpoint-transact-sql +create_or_alter_endpoint + : (CREATE | ALTER) ENDPOINT endpointname=id (AUTHORIZATION login=id)? + ( STATE EQUAL ( state=STARTED | state=STOPPED | state=DISABLED ) )? + AS TCP LR_BRACKET + LISTENER_PORT EQUAL port=DECIMAL + ( COMMA LISTENER_IP EQUAL + (ALL | LR_BRACKET IPV4_ADDR RR_BRACKET | LR_BRACKET STRING RR_BRACKET) )? + RR_BRACKET + ( FOR TSQL LR_BRACKET RR_BRACKET + | + FOR SERVICE_BROKER LR_BRACKET + AUTHENTICATION EQUAL + ( WINDOWS ( NTLM |KERBEROS | NEGOTIATE )? (CERTIFICATE cert_name=id)? + | CERTIFICATE cert_name=id WINDOWS? ( NTLM |KERBEROS | NEGOTIATE )? + ) + ( COMMA? ENCRYPTION EQUAL ( DISABLED |SUPPORTED | REQUIRED ) + ( ALGORITHM ( AES | RC4 | AES RC4 | RC4 AES ) )? + )? + + ( COMMA? MESSAGE_FORWARDING EQUAL ( ENABLED | DISABLED ) )? + ( COMMA? MESSAGE_FORWARD_SIZE EQUAL DECIMAL)? + RR_BRACKET + | + FOR DATABASE_MIRRORING LR_BRACKET + AUTHENTICATION EQUAL + ( WINDOWS ( NTLM |KERBEROS | NEGOTIATE )? (CERTIFICATE cert_name=id)? + | CERTIFICATE cert_name=id WINDOWS? ( NTLM |KERBEROS | NEGOTIATE )? + ) + + ( COMMA? ENCRYPTION EQUAL ( DISABLED |SUPPORTED | REQUIRED ) + ( ALGORITHM ( AES | RC4 | AES RC4 | RC4 AES ) )? + )? + + COMMA? ROLE EQUAL ( WITNESS | PARTNER | ALL ) + RR_BRACKET + ) + ; + +database_mirroring_option + : mirroring_set_option + ; + +mirroring_set_option + : mirroring_partner partner_option + | mirroring_witness witness_option + ; +mirroring_partner + : PARTNER + ; + +mirroring_witness + : WITNESS + ; + +witness_partner_equal + : EQUAL + ; + +partner_option + : witness_partner_equal partner_server + | FAILOVER + | FORCE_SERVICE_ALLOW_DATA_LOSS + | OFF + | RESUME + | SAFETY (FULL | OFF ) + | SUSPEND + | TIMEOUT DECIMAL + ; + +witness_option + : witness_partner_equal witness_server + | OFF + ; + +witness_server + : partner_server + ; + +partner_server + : partner_server_tcp_prefix host mirroring_host_port_seperator port_number + ; + +mirroring_host_port_seperator + : COLON + ; + +partner_server_tcp_prefix + : TCP COLON DOUBLE_FORWARD_SLASH + ; +port_number + : port=DECIMAL + ; + +host + : id (DOT host?)? + ; + +date_correlation_optimization_option + : DATE_CORRELATION_OPTIMIZATION on_off + ; + +db_encryption_option + : ENCRYPTION on_off + ; +db_state_option + : ( ONLINE | OFFLINE | EMERGENCY ) + ; + +db_update_option + : READ_ONLY | READ_WRITE + ; + +db_user_access_option + : SINGLE_USER | RESTRICTED_USER | MULTI_USER + ; +delayed_durability_option + : DELAYED_DURABILITY EQUAL ( DISABLED | ALLOWED | FORCED ) + ; + +external_access_option + : DB_CHAINING on_off + | TRUSTWORTHY on_off + | DEFAULT_LANGUAGE EQUAL ( id | STRING | DECIMAL ) + | DEFAULT_FULLTEXT_LANGUAGE EQUAL ( id | STRING | DECIMAL ) + | NESTED_TRIGGERS EQUAL ( OFF | ON ) + | TRANSFORM_NOISE_WORDS EQUAL ( OFF | ON ) + | TWO_DIGIT_YEAR_CUTOFF EQUAL DECIMAL + ; + +hadr_options + : HADR + ( ( AVAILABILITY GROUP EQUAL availability_group_name=id | OFF ) |(SUSPEND|RESUME) ) + ; + +mixed_page_allocation_option + : MIXED_PAGE_ALLOCATION ( OFF | ON ) + ; + +parameterization_option + : PARAMETERIZATION ( SIMPLE | FORCED ) + ; + +query_store_option + : QUERY_STORE EQUAL OFF ( LR_BRACKET FORCED RR_BRACKET )? + | QUERY_STORE CLEAR ALL? + | QUERY_STORE ( EQUAL ON )? ( LR_BRACKET query_store_option_item ( COMMA query_store_option_item )* RR_BRACKET )? + ; + +query_store_option_item + : CLEANUP_POLICY EQUAL LR_BRACKET STALE_QUERY_THRESHOLD_DAYS EQUAL thr_days=DECIMAL RR_BRACKET + | DATA_FLUSH_INTERVAL_SECONDS EQUAL DECIMAL + | INTERVAL_LENGTH_MINUTES EQUAL DECIMAL + | MAX_PLANS_PER_QUERY EQUAL DECIMAL + | MAX_SIZE_MB EQUAL DECIMAL + | MAX_STORAGE_SIZE_MB EQUAL DECIMAL + | OPERATION_MODE EQUAL ( READ_WRITE | READ_ONLY ) + | QUERY_CAPTURE_MODE EQUAL ( ALL | AUTO | CUSTOM | NONE ) + | QUERY_CAPTURE_POLICY EQUAL LR_BRACKET query_capture_policy_option ( COMMA query_capture_policy_option )* RR_BRACKET + | SIZE_BASED_CLEANUP_MODE EQUAL ( AUTO | OFF ) + | WAIT_STATS_CAPTURE_MODE EQUAL on_off + ; + +query_capture_policy_option + : EXECUTION_COUNT EQUAL DECIMAL + | STALE_CAPTURE_POLICY_THRESHOLD EQUAL DECIMAL ( DAYS | HOURS ) + | TOTAL_COMPILE_CPU_TIME_MS EQUAL DECIMAL + | TOTAL_EXECUTION_CPU_TIME_MS EQUAL DECIMAL + ; + +recovery_option + : RECOVERY ( FULL | BULK_LOGGED | SIMPLE ) + | TORN_PAGE_DETECTION on_off + | PAGE_VERIFY ( CHECKSUM | TORN_PAGE_DETECTION | NONE ) + ; + +service_broker_option: + ENABLE_BROKER + | DISABLE_BROKER + | NEW_BROKER + | ERROR_BROKER_CONVERSATIONS + | HONOR_BROKER_PRIORITY on_off + ; +snapshot_option + : ALLOW_SNAPSHOT_ISOLATION on_off + | READ_COMMITTED_SNAPSHOT (ON | OFF ) + | MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = (ON | OFF ) + ; + +sql_option + : ANSI_NULL_DEFAULT on_off + | ANSI_NULLS on_off + | ANSI_PADDING on_off + | ANSI_WARNINGS on_off + | ARITHABORT on_off + | COMPATIBILITY_LEVEL EQUAL DECIMAL + | CONCAT_NULL_YIELDS_NULL on_off + | NUMERIC_ROUNDABORT on_off + | QUOTED_IDENTIFIER on_off + | RECURSIVE_TRIGGERS on_off + ; + +target_recovery_time_option + : TARGET_RECOVERY_TIME EQUAL DECIMAL ( SECONDS | MINUTES ) + ; + +termination + : ROLLBACK AFTER seconds = DECIMAL + | ROLLBACK IMMEDIATE + | NO_WAIT + ; + +// https://msdn.microsoft.com/en-us/library/ms176118.aspx +drop_index + : DROP INDEX if_exists? + ( drop_relational_or_xml_or_spatial_index (COMMA drop_relational_or_xml_or_spatial_index)* + | drop_backward_compatible_index (COMMA drop_backward_compatible_index)* + ) + SEMI? + ; + +drop_relational_or_xml_or_spatial_index + : index_name=id ON full_object_name + ; + +drop_backward_compatible_index + : (owner_name=id DOT)? table_or_view_name=id DOT index_name=id + ; + +// https://msdn.microsoft.com/en-us/library/ms174969.aspx +drop_procedure + : DROP proc=(PROC | PROCEDURE) if_exists? func_proc_name_schema (COMMA func_proc_name_schema)* SEMI? + | DROP proc=(PROC | PROCEDURE) if_exists? func_proc_name_schema (( LR_BRACKET drop_procedure_param (COMMA drop_procedure_param)* RR_BRACKET ) | LR_BRACKET RR_BRACKET ) SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-trigger-transact-sql +drop_trigger + : DROP TRIGGER if_exists? simple_name (COMMA simple_name)* (ddl=ON (DATABASE | ALL SERVER))? SEMI? + ; + +// https://msdn.microsoft.com/en-us/library/ms190290.aspx +drop_function + : DROP FUNCTION if_exists? func_proc_name_schema (COMMA func_proc_name_schema)* SEMI? + | DROP FUNCTION if_exists? func_proc_name_schema (( LR_BRACKET drop_procedure_param (COMMA drop_procedure_param)* RR_BRACKET ) | LR_BRACKET RR_BRACKET ) SEMI? +; + +// https://msdn.microsoft.com/en-us/library/ms175075.aspx +drop_statistics + : DROP STATISTICS full_object_name (COMMA full_object_name)* SEMI? + ; + +// https://msdn.microsoft.com/en-us/library/ms173790.aspx +drop_table + : DROP TABLE if_exists? table_name (COMMA table_name)* SEMI? + ; + +// https://msdn.microsoft.com/en-us/library/ms173492.aspx +drop_view + : DROP VIEW if_exists? simple_name (COMMA simple_name)* SEMI? + ; + +create_type + : CREATE TYPE name = simple_name + (FROM data_type null_notnull? default_value=expression?)? + (AS TABLE LR_BRACKET column_def_table_constraints RR_BRACKET table_options? )? + ; + +drop_type: + DROP TYPE if_exists? simple_name + ; + +rowset_function + : open_xml + | open_json + | open_query + | open_datasource + | open_rowset + | change_table + | predict_function + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/functions/openxml-transact-sql +open_xml + : OPENXML LR_BRACKET expression COMMA expression (COMMA expression)? RR_BRACKET + (WITH ( table_name | LR_BRACKET schema_declaration RR_BRACKET) )? + ; + +schema_declaration + : xml_col+=column_declaration (COMMA xml_col+=column_declaration)* + ; + +open_json + : OPENJSON LR_BRACKET expression (COMMA expression)? RR_BRACKET + (WITH LR_BRACKET json_declaration RR_BRACKET )? + ; + +json_declaration + : json_col+=json_column_declaration (COMMA json_col+=json_column_declaration)* + ; + +json_column_declaration + : column_declaration (AS JSON)? + ; + +// https://msdn.microsoft.com/en-us/library/ms188427(v=sql.120).aspx +open_query + : OPENQUERY LR_BRACKET linked_server=id COMMA query=STRING RR_BRACKET + ; + +// https://msdn.microsoft.com/en-us/library/ms179856.aspx +open_datasource + : OPENDATASOURCE LR_BRACKET provider=STRING COMMA init=STRING RR_BRACKET + (DOT id)+ + ; + +// https://msdn.microsoft.com/en-us/library/ms190312.aspx +open_rowset + : OPENROWSET LR_BRACKET provider_name = STRING COMMA connectionString = STRING COMMA sql = STRING RR_BRACKET + | OPENROWSET LR_BRACKET BULK data_file=STRING COMMA (bulk_option (COMMA bulk_option)* | id) RR_BRACKET + ; + +change_table + : CHANGETABLE LR_BRACKET (change_table_changes | change_table_version) RR_BRACKET + ; + +change_table_changes + : CHANGES changetable=table_name COMMA changesid=(NULL_P | DECIMAL | LOCAL_ID) + ; +change_table_version + : VERSION versiontable=table_name COMMA pk_columns=full_column_name_list COMMA pk_values=select_list + ; + +predict_function + : PREDICT LR_BRACKET MODEL EQUAL expression COMMA DATA EQUAL (table_name | function_call) AS table_alias (RUNTIME EQUAL id)? RR_BRACKET + WITH LR_BRACKET column_definition (COMMA column_definition)* RR_BRACKET + ; + +// https://msdn.microsoft.com/en-us/library/ms188927.aspx +declare_statement + : DECLARE LOCAL_ID AS? table_type_definition SEMI? + | DECLARE loc+=declare_local (COMMA loc+=declare_local)* SEMI? + ; + +declare_xmlnamespaces_statement + : WITH XMLNAMESPACES LR_BRACKET xml_dec+=xml_declaration (COMMA xml_dec+=xml_declaration)* RR_BRACKET (select_statement | insert_statement | update_statement | delete_statement | merge_statement )? SEMI? + ; + +xml_declaration + : xml_namespace_uri=STRING AS id + | DEFAULT STRING + ; + +// https://msdn.microsoft.com/en-us/library/ms181441(v=sql.120).aspx +cursor_statement + // https://msdn.microsoft.com/en-us/library/ms175035(v=sql.120).aspx + : CLOSE GLOBAL? cursor_name SEMI? + // https://msdn.microsoft.com/en-us/library/ms188782(v=sql.120).aspx + | DEALLOCATE GLOBAL? CURSOR? cursor_name SEMI? + // https://msdn.microsoft.com/en-us/library/ms180169(v=sql.120).aspx + | declare_cursor + // https://msdn.microsoft.com/en-us/library/ms180152(v=sql.120).aspx + | fetch_cursor + // https://msdn.microsoft.com/en-us/library/ms190500(v=sql.120).aspx + | OPEN GLOBAL? cursor_name SEMI? + ; + +checkpoint_statement + : CHECKPOINT chkptduration=DECIMAL? + ; + +restore_database + : RESTORE DATABASE ( database_name=id | LOCAL_ID ) + (files_or_filegroups (COMMA files_or_filegroups)*)? + FROM (COMMA? backup_device)+ + (WITH restore_options (COMMA? restore_options)* )? + ; + +files_or_filegroups + : READ_WRITE_FILEGROUPS + | (FILE|FILEGROUP) EQUAL (STRING|LOCAL_ID) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/backup-transact-sql +backup_database + : BACKUP DATABASE ( database_name=id | LOCAL_ID ) + (files_or_filegroups (COMMA files_or_filegroups)*)? + TO (COMMA? backup_device)+ + (MIRROR TO (COMMA? backup_device)+)? + (WITH backup_options (COMMA backup_options)* )? + ; + +backup_log + : BACKUP LOG ( database_name=id | LOCAL_ID ) + (TO (COMMA? backup_device)+)? + (MIRROR TO (COMMA? backup_device)+)? + (WITH backup_options (COMMA backup_options)* )? + ; + +backup_device + : (DISK|TAPE|URL) EQUAL (STRING|LOCAL_ID) + | LOCAL_ID + | id + ; + +backup_options + : backup_option + | backup_restore_option + ; + +restore_options + : restore_option + | backup_restore_option + ; + +backup_option + : DIFFERENTIAL + | COPY_ONLY + | (COMPRESSION|NO_COMPRESSION) + | DESCRIPTION EQUAL (STRING|id|LOCAL_ID) + | NAME EQUAL (id|LOCAL_ID) + | CREDENTIAL + | FILE_SNAPSHOT + | (EXPIREDATE EQUAL (STRING|id|LOCAL_ID) | RETAINDAYS EQUAL (DECIMAL|id|LOCAL_ID) ) + | (NOINIT|INIT) + | (NOSKIP|SKIP_KEYWORD) + | (NOFORMAT|FORMAT) + | RESTART + | ENCRYPTION LR_BRACKET ALGORITHM EQUAL ( AES_128 | AES_192 | AES_256 | TRIPLE_DES_3KEY ) + COMMA (SERVER CERTIFICATE EQUAL encryptor_name=id | SERVER ASYMMETRIC KEY EQUAL encryptor_name=id) + RR_BRACKET + // log backup options: + | (NORECOVERY| STANDBY EQUAL (STRING|LOCAL_ID)) + | NO_TRUNCATE + ; + +restore_option + : (RECOVERY | NORECOVERY | STANDBY) EQUAL (STRING |LOCAL_ID) + | MOVE (STRING|LOCAL_ID) TO (STRING|LOCAL_ID) + | REPLACE + | RESTART + | RESTRICTED_USER + | CREDENTIAL + | FILE EQUAL (STRING|LOCAL_ID) + | PASSWORD EQUAL (STRING|LOCAL_ID) + | KEEP_REPLICATION + | KEEP_CDC + | FILESTREAM LR_BRACKET DIRECTORY_NAME EQUAL (STRING|LOCAL_ID) RR_BRACKET + | ENABLE_BROKER + | ERROR_BROKER_CONVERSATIONS + | NEW_BROKER + | STOPAT EQUAL (STRING|LOCAL_ID) + | STOPATMARK EQUAL (STRING) (AFTER (STRING|LOCAL_ID))? + | STOPBEFOREMARK EQUAL (STRING) (AFTER (STRING|LOCAL_ID))? + ; + +backup_restore_option + : MEDIADESCRIPTION EQUAL (STRING|id|LOCAL_ID) + | MEDIANAME EQUAL (STRING|LOCAL_ID) + | BLOCKSIZE EQUAL (DECIMAL|id|LOCAL_ID) + | BUFFERCOUNT EQUAL (DECIMAL|id|LOCAL_ID) + | MAXTRANSFER EQUAL (DECIMAL|id|LOCAL_ID) + | (NO_CHECKSUM|CHECKSUM) + | (STOP_ON_ERROR|CONTINUE_AFTER_ERROR) + | STATS (EQUAL (DECIMAL|LOCAL_ID))? + | (REWIND|NOREWIND) + | (LOAD|NOUNLOAD) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/backup-certificate-transact-sql +backup_certificate + : BACKUP CERTIFICATE certname=id TO FILE EQUAL cert_file=STRING + ( WITH PRIVATE KEY + LR_BRACKET + (COMMA? FILE EQUAL private_key_file=STRING + |COMMA? ENCRYPTION BY PASSWORD EQUAL encryption_password=STRING + |COMMA? DECRYPTION BY PASSWORD EQUAL decryption_pasword=STRING + )+ + RR_BRACKET + )? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/backup-master-key-transact-sql +backup_master_key + : BACKUP MASTER KEY TO FILE EQUAL master_key_backup_file=STRING + ENCRYPTION BY PASSWORD EQUAL encryption_password=STRING + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/statements/backup-service-master-key-transact-sql +backup_service_master_key + : BACKUP SERVICE MASTER KEY TO FILE EQUAL service_master_key_backup_file=STRING + ENCRYPTION BY PASSWORD EQUAL encryption_password=STRING + ; + +kill_statement + : KILL (kill_process | kill_query_notification | kill_stats_job) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/kill-transact-sql +kill_process + : (session_id=(DECIMAL|STRING) | UOW) (WITH STATUSONLY)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/kill-query-notification-subscription-transact-sql +kill_query_notification + : QUERY NOTIFICATION SUBSCRIPTION (ALL | subscription_id=DECIMAL) + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/kill-stats-job-transact-sql +kill_stats_job + : STATS JOB job_id=DECIMAL + ; + +// https://msdn.microsoft.com/en-us/library/ms188332.aspx +execute_statement + : (EXECUTE|EXEC) execute_body SEMI? + ; + +execute_body_batch + : func_proc_name_server_database_schema execute_statement_arg? SEMI? + ; + +execute_body + : (return_status=LOCAL_ID EQUAL)? (func_proc_name_server_database_schema | execute_var_string) execute_statement_arg? (WITH execute_option (COMMA execute_option)* )? + | LR_BRACKET execute_var_string (PLUS execute_var_string)* RR_BRACKET (execute_var_string_option (COMMA execute_var_string_option)* )? + ; + +execute_var_string_option + : AS (LOGIN | USER) EQUAL STRING + | AT_KEYWORD linked_server=id + | AT_KEYWORD DATA_SOURCE id + ; + +execute_statement_arg + : + execute_statement_arg_unnamed (COMMA execute_statement_arg)? //Unnamed params can continue unnamed + | + execute_statement_arg_named (COMMA execute_statement_arg_named)* //Named can only be continued by unnamed + ; + +execute_statement_arg_named + : name=LOCAL_ID EQUAL value=execute_parameter + ; + +execute_statement_arg_unnamed + : value=execute_parameter + ; + +execute_parameter + : constant + | LOCAL_ID (OUTPUT | OUT)? + | id + | DEFAULT + ; + +execute_var_string + : LOCAL_ID + | STRING + ; + +execute_option + : RECOMPILE + | RESULT SETS (NONE |UNDEFINED) + | RESULT SETS LR_BRACKET LR_BRACKET schema_declaration RR_BRACKET RR_BRACKET (AS (OBJECT full_object_name | TYPE full_object_name | FOR XML) )? + ; + +security_statement + : ( execute_as_statement + | revert_statement + | grant_statement + | revoke_statement + | deny_statement + | open_key + | close_key + | create_key + | create_certificate ) SEMI? + ; + +grant_statement + : GRANT (ALL PRIVILEGES? | permissions) (ON permission_object)? TO principals (WITH GRANT OPTION)? (AS principal_id)? + ; + +revoke_statement + : REVOKE (GRANT OPTION FOR)? (ALL PRIVILEGES? | permissions) (ON permission_object)? (TO|FROM) principals CASCADE? (AS principal_id)? + ; + +deny_statement + : DENY (ALL PRIVILEGES? | permissions) (ON permission_object)? TO principals CASCADE? (AS principal_id)? + ; + +permission_object + : (object_type colon_colon)? full_object_name (LR_BRACKET column_name_list RR_BRACKET)? + ; + +principals + : principal_id (COMMA principal_id)* + ; + +permissions + : permission (COMMA permission)* + ; + +permission + : single_permission (LR_BRACKET column_name_list RR_BRACKET)? + ; + +single_permission + : (EXECUTE|EXEC) (ANY EXTERNAL SCRIPT)? + | CREATE ANY? object_type? + | ALTER ANY? object_type? + | SELECT (ALL USER SECURABLES)? + | INSERT + | UPDATE + | DELETE + | REFERENCES + | CONTROL SERVER? + | IMPERSONATE (ANY LOGIN)? + | CHECKPOINT + | CONNECT (REPLICATION | SQL | ANY DATABASE)? + | SEND + | RECEIVE + | VIEW DEFINITION + | TAKE OWNERSHIP + | VIEW ANY? object_type + | AUTHENTICATE SERVER? + | SHOWPLAN + | BACKUP (DATABASE | LOG) + | ADMINISTER DATABASE? BULK OPERATIONS + | EXTERNAL ACCESS ASSEMBLY + | SHUTDOWN + | KILL DATABASE CONNECTION + | SUBSCRIBE QUERY NOTIFICATIONS + | UNMASK + | UNSAFE ASSEMBLY + ; + +object_type + : AGGREGATE + | APPLICATION ROLE + | ASSEMBLY + | ASYMMETRIC KEY + | AVAILABILITY GROUP + | CERTIFICATE + | CHANGE TRACKING + | COLUMN ( ENCRYPTION | MASTER ) KEY DEFINITION? + | CONNECTION + | CONTRACT + | CREDENTIAL + | DATABASE (AUDIT|DDL? EVENT (NOTIFICATION|SESSION)|DDL TRIGGER|SCOPED CONFIGURATION|STATE)? + | DATASPACE + | DDL EVENT NOTIFICATION + | DEFAULT + | ENDPOINT + | EVENT ( NOTIFICATION | SESSION ) + | EXTERNAL (DATA SOURCE | FILE FORMAT | LIBRARY) + | FULLTEXT CATALOG + | FULLTEXT STOPLIST + | FUNCTION + | LINKED SERVER + | LOGIN + | MASK + | MESSAGE TYPE + | OBJECT + | PROCEDURE + | QUEUE + | REMOTE SERVICE BINDING + | RESOURCES + | ROLE + | ROUTE + | RULE + | SCHEMA + | SECURITY POLICY + | SEARCH PROPERTY LIST + | SEQUENCE + | SERVER (AUDIT|ROLE|STATE) + | SERVICE + | SETTINGS + | SYMMETRIC KEY + | SYNONYM + | TABLE + | TRACE (EVENT NOTIFICATION)? + | TYPE + | USER + | VIEW + | XML SCHEMA COLLECTION + ; + +principal_id + : id + | PUBLIC + ; + +create_certificate + : CREATE CERTIFICATE certificate_name=id (AUTHORIZATION user_name=id)? + (FROM existing_keys | generate_new_keys) + (ACTIVE FOR BEGIN DIALOG EQUAL (ON | OFF))? + ; + +existing_keys + : ASSEMBLY assembly_name=id + | EXECUTABLE? FILE EQUAL path_to_file=STRING (WITH PRIVATE KEY LR_BRACKET private_key_options RR_BRACKET )? + ; + +private_key_options + : (FILE | BINARY) EQUAL path=STRING (COMMA (DECRYPTION | ENCRYPTION) BY PASSWORD EQUAL password=STRING)? + ; + +generate_new_keys + : (ENCRYPTION BY PASSWORD EQUAL password=STRING)? + WITH SUBJECT EQUAL certificate_subject_name=STRING (COMMA date_options)* + ; + +date_options + : (START_DATE | EXPIRY_DATE) EQUAL STRING + ; + +open_key + : OPEN SYMMETRIC KEY key_name=id DECRYPTION BY decryption_mechanism + | OPEN MASTER KEY DECRYPTION BY PASSWORD EQUAL password=STRING + ; + +close_key + : CLOSE SYMMETRIC KEY key_name=id + | CLOSE ALL SYMMETRIC KEYS + | CLOSE MASTER KEY + ; + +create_key + : CREATE MASTER KEY ENCRYPTION BY PASSWORD EQUAL password=STRING + | CREATE SYMMETRIC KEY key_name=id + (AUTHORIZATION user_name=id)? + (FROM PROVIDER provider_name=id)? + WITH ((key_options | ENCRYPTION BY encryption_mechanism)COMMA?)+ + ; + +key_options + : KEY_SOURCE EQUAL pass_phrase=STRING + | ALGORITHM EQUAL algorithm + | IDENTITY_VALUE EQUAL identity_phrase=STRING + | PROVIDER_KEY_NAME EQUAL key_name_in_provider=STRING + | CREATION_DISPOSITION EQUAL (CREATE_NEW | OPEN_EXISTING) + ; + +algorithm + : DES + | TRIPLE_DES + | TRIPLE_DES_3KEY + | RC2 + | RC4 + | RC4_128 + | DESX + | AES_128 + | AES_192 + | AES_256 + ; + +encryption_mechanism + : CERTIFICATE certificate_name=id + | ASYMMETRIC KEY asym_key_name=id + | SYMMETRIC KEY decrypting_Key_name=id + | PASSWORD EQUAL STRING + ; + +decryption_mechanism + : CERTIFICATE certificate_name=id (WITH PASSWORD EQUAL STRING)? + | ASYMMETRIC KEY asym_key_name=id (WITH PASSWORD EQUAL STRING)? + | SYMMETRIC KEY decrypting_Key_name=id + | PASSWORD EQUAL STRING + ; + +// https://msdn.microsoft.com/en-us/library/ms190356.aspx +// https://msdn.microsoft.com/en-us/library/ms189484.aspx +set_statement + : SET LOCAL_ID (DOT member_name=id)? EQUAL expression SEMI? + | SET LOCAL_ID assignment_operator expression SEMI? + | SET LOCAL_ID EQUAL + CURSOR declare_cursor_options* FOR select_statement_standalone (FOR (READ ONLY | UPDATE (OF column_name_list)?))? SEMI? + // https://msdn.microsoft.com/en-us/library/ms189837.aspx + | set_special + ; + +// https://msdn.microsoft.com/en-us/library/ms174377.aspx +transaction_statement + // https://msdn.microsoft.com/en-us/library/ms188386.aspx + : BEGIN DISTRIBUTED (TRAN | TRANSACTION) (id | LOCAL_ID)? SEMI? + // https://msdn.microsoft.com/en-us/library/ms188929.aspx + | BEGIN (TRAN | TRANSACTION) ((id | LOCAL_ID) (WITH MARK STRING)?)? SEMI? + // https://msdn.microsoft.com/en-us/library/ms190295.aspx + | COMMIT (TRAN | TRANSACTION) ((id | LOCAL_ID) (WITH LR_BRACKET DELAYED_DURABILITY EQUAL (OFF | ON) RR_BRACKET )?)? SEMI? + // https://msdn.microsoft.com/en-us/library/ms178628.aspx + | COMMIT WORK? SEMI? + | COMMIT id + | ROLLBACK id + // https://msdn.microsoft.com/en-us/library/ms181299.aspx + | ROLLBACK (TRAN | TRANSACTION) (id | LOCAL_ID)? SEMI? + // https://msdn.microsoft.com/en-us/library/ms174973.aspx + | ROLLBACK WORK? SEMI? + // https://msdn.microsoft.com/en-us/library/ms188378.aspx + | SAVE (TRAN | TRANSACTION) (id | LOCAL_ID)? SEMI? + ; + +// https://msdn.microsoft.com/en-us/library/ms188366.aspx +use_statement + : USE dbname=id SEMI? + ; + +setuser_statement + : SETUSER user=STRING? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/reconfigure-transact-sql +reconfigure_statement + : RECONFIGURE (WITH OVERRIDE)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/shutdown-transact-sql +shutdown_statement + : SHUTDOWN (WITH NOWAIT)? + ; + +dbcc_statement + : DBCC name=dbcc_command ( LR_BRACKET expression_list RR_BRACKET )? (WITH dbcc_options)? SEMI? + //These are dbcc commands with strange syntax that doesn't fit the regular dbcc syntax + | DBCC SHRINKLOG ( LR_BRACKET SIZE EQUAL (constant_expression| id | DEFAULT) (KB | MB | GB | TB)? RR_BRACKET )? (WITH dbcc_options)? SEMI? + ; + +dbcc_command + : ID | keyword + ; + +dbcc_options + : ID (COMMA ID)? + ; + +execute_as_clause + : (EXECUTE|EXEC) AS clause=(CALLER | SELF | OWNER | STRING) + ; + +execute_as_statement + : (EXECUTE|EXEC) AS ( CALLER | ( LOGIN | USER ) EQUAL STRING (WITH (NO REVERT | COOKIE INTO LOCAL_ID ))? ) + ; + +revert_statement + : REVERT (LR_BRACKET WITH COOKIE EQUAL LOCAL_ID RR_BRACKET)? + ; + +declare_local + : LOCAL_ID AS? data_type ( EQUAL expression)? + ; + +table_type_definition + : TABLE LR_BRACKET column_def_table_constraints (COMMA? table_type_indices)* RR_BRACKET + ; + +table_type_indices + : (((PRIMARY KEY | INDEX id) (CLUSTERED | NONCLUSTERED)?) | UNIQUE) LR_BRACKET column_name_list_with_order RR_BRACKET + | CHECK LR_BRACKET search_condition RR_BRACKET + ; + +xml_type_definition + : id LR_BRACKET ( CONTENT | DOCUMENT )? xml_schema_collection RR_BRACKET + ; + +xml_schema_collection + : id (DOT id)? + ; + +column_def_table_constraints + : column_def_table_constraint (COMMA? column_def_table_constraint)* + ; + +column_def_table_constraint + : column_definition + | materialized_column_definition + | table_constraint + | period_for_system_time + ; + +// https://msdn.microsoft.com/en-us/library/ms187742.aspx +// emprically found: ROWGUIDCOL can be in various locations +column_definition + : id (data_type system_versioning_column? | AS expression PERSISTED? ) ( special_column_option | (COLLATE id) | null_notnull )* + ( column_constraint? IDENTITY (LR_BRACKET sign? seed=DECIMAL COMMA sign? increment=DECIMAL RR_BRACKET)? )? for_replication? ROWGUIDCOL? + column_constraint* column_inline_index? + ; + +column_inline_index + : INDEX id (CLUSTERED | NONCLUSTERED)? + (WHERE where=search_condition)? + with_index_options? + (ON storage_partition_clause)? + (FILESTREAM_ON storage_partition_clause)? + ; + +materialized_column_definition + : id (COMPUTE | AS) expression (MATERIALIZED | NOT MATERIALIZED)? + ; + +special_column_option + : FILESTREAM + | SPARSE + | ROWGUIDCOL + | HIDDEN_RENAMED + | PERSISTED + | MASKED ( WITH LR_BRACKET FUNCTION EQUAL STRING RR_BRACKET )? + | for_replication + ; + +system_versioning_column + : GENERATED ALWAYS AS (ROW|TRANSACTION_ID|SEQUENCE_NUMBER) (START|END) HIDDEN_RENAMED? + ; + +period_for_system_time + : PERIOD FOR SYSTEM_TIME LR_BRACKET id COMMA id RR_BRACKET + ; + +system_versioning_options + : LR_BRACKET system_versioning_option (COMMA system_versioning_option)* RR_BRACKET + ; + +system_versioning_option + : SYSTEM_VERSIONING EQUAL on_off + | LEDGER EQUAL on_off sub_options? + | DATA_CONSISTENCY_CHECK EQUAL on_off + | HISTORY_RETENTION_PERIOD EQUAL ( INFINITE | DECIMAL (DAY|DAYS|WEEK|WEEKS|MONTH|MONTHS|YEAR|YEARS) ) + ; + +history_table_option + : HISTORY_TABLE EQUAL table_name + | LR_BRACKET history_table_option RR_BRACKET + ; + +for_system_time + : FOR SYSTEM_TIME for_system_time_range + ; + +for_system_time_range + : ALL + | AS OF (STRING|LOCAL_ID) + | BETWEEN (STRING|LOCAL_ID) AND (STRING|LOCAL_ID) + | CONTAINED IN LR_BRACKET (STRING|LOCAL_ID) COMMA (STRING|LOCAL_ID) RR_BRACKET + | FROM (STRING|LOCAL_ID) TO (STRING|LOCAL_ID) + ; + +for_replication + : (NOT? FOR REPLICATION) + ; + +// https://msdn.microsoft.com/en-us/library/ms186712.aspx +column_constraint + :(CONSTRAINT constraint=id)? + ((PRIMARY KEY | UNIQUE) clustered? with_index_options? + | CHECK for_replication? LR_BRACKET search_condition RR_BRACKET + | (FOREIGN KEY)? REFERENCES table_name LR_BRACKET pk = column_name_list RR_BRACKET (on_update | on_delete)* + | DEFAULT expression + | null_notnull + | WITH VALUES + | CONNECTION LR_BRACKET table_name TO table_name (COMMA table_name TO table_name)* RR_BRACKET + ) + ; + +// https://msdn.microsoft.com/en-us/library/ms188066.aspx +table_constraint + : (CONSTRAINT constraint=id)? + ((PRIMARY KEY | UNIQUE) clustered? LR_BRACKET column_name_list_with_order RR_BRACKET with_index_options? (ON storage_partition_clause)? + | CHECK for_replication? LR_BRACKET search_condition RR_BRACKET + | DEFAULT expression (FOR id)? // (COLLATE id)? (WITH VALUES)? + | FOREIGN KEY LR_BRACKET fk = column_name_list RR_BRACKET REFERENCES table_name (LR_BRACKET pk = column_name_list RR_BRACKET)? (on_update | on_delete)* ) for_replication? + ; + +on_update + : ON UPDATE (NO ACTION | CASCADE | SET NULL_P | SET DEFAULT) + ; + +on_delete + : ON DELETE (NO ACTION | CASCADE | SET NULL_P | SET DEFAULT) + ; + +with_index_options + : WITH index_option_list + | WITH LR_BRACKET index_option_list RR_BRACKET + ; + +index_option_list + : index_option (COMMA index_option)* + ; + +// https://msdn.microsoft.com/en-us/library/ms186869.aspx +// Id runtime checking. Id in (PAD_INDEX, FILLFACTOR, IGNORE_DUP_KEY, STATISTICS_NORECOMPUTE, ALLOW_ROW_LOCKS, +// ALLOW_PAGE_LOCKS, SORT_IN_TEMPDB, ONLINE, MAXDOP, DATA_COMPRESSION, ONLINE). +index_option + : (id | keyword) (EQUAL (id | keyword | on_off | DECIMAL))? sub_options? + ; + +sub_options + : LR_BRACKET sub_option (sub_options? | (COMMA sub_option)*) RR_BRACKET + ; + +sub_option + : (id|keyword) EQUAL expression keyword? // keyword is for cases like 'RETENTION_PERIOD = 1 WEEKS' + ; + +// https://msdn.microsoft.com/en-us/library/ms180169.aspx +declare_cursor + : DECLARE cursor_name INSENSITIVE? SCROLL? CURSOR declare_cursor_options* + FOR select_statement_standalone (FOR (READ ONLY | UPDATE (OF full_column_name_list)?))? + ; + +declare_cursor_options + : (LOCAL | GLOBAL) + | (FORWARD_ONLY | SCROLL) + | (STATIC | KEYSET | DYNAMIC | FAST_FORWARD) + | (READ_ONLY | SCROLL_LOCKS | OPTIMISTIC) + | TYPE_WARNING + ; + +fetch_cursor + : FETCH ((NEXT | PRIOR | FIRST | LAST | (ABSOLUTE | RELATIVE) expression)? FROM)? + GLOBAL? cursor_name (INTO LOCAL_ID (COMMA LOCAL_ID)*)? SEMI? + ; + +// https://msdn.microsoft.com/en-us/library/ms190356.aspx +// Runtime check. +set_special + : SET set_on_off_option (COMMA set_on_off_option)* on_off + | SET id (id | constant_LOCAL_ID | on_off) SEMI? + | SET STATISTICS (IO | TIME | XML | PROFILE) on_off SEMI? + | SET ROWCOUNT (LOCAL_ID | DECIMAL) SEMI? + // https://msdn.microsoft.com/en-us/library/ms173763.aspx + | SET (TRAN | TRANSACTION) ISOLATION LEVEL (READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLE | DECIMAL) SEMI? + // https://msdn.microsoft.com/en-us/library/ms188059.aspx + | SET IDENTITY_INSERT table_name on_off SEMI? + | SET TEXTSIZE DECIMAL SEMI? + | SET xml_modify_method + ; + +set_on_off_option + : ANSI_DEFAULTS + | ANSI_NULLS + | ANSI_NULL_DFLT_OFF + | ANSI_NULL_DFLT_ON + | ANSI_PADDING + | ANSI_WARNINGS + | ARITHABORT + | ARITHIGNORE + | AUTOCOMMIT + | CONCAT_NULL_YIELDS_NULL + | CURSOR_CLOSE_ON_COMMIT + | FIPS_FLAGGER + | FMTONLY + | FORCEPLAN + | IMPLICIT_TRANSACTIONS + | NOCOUNT + | NOEXEC + | NUMERIC_ROUNDABORT + | OFFSETS set_offsets_keyword (COMMA set_offsets_keyword)* + | PARSEONLY + | QUOTED_IDENTIFIER + | REMOTE_PROC_TRANSACTIONS + | SHOWPLAN_ALL + | SHOWPLAN_TEXT + | SHOWPLAN_XML + | STATISTICS set_statistics_keyword (COMMA set_statistics_keyword)* + | XACT_ABORT + ; + +set_statistics_keyword + : IO + | PROFILE + | TIME + | XML + ; + +set_offsets_keyword + : SELECT + | FROM + | ORDER + | TABLE + | PROCEDURE + | STATEMENT + | PARAM + | (EXECUTE|EXEC) + ; + +constant_LOCAL_ID + : constant + | LOCAL_ID + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/expressions-transact-sql +// Operator precendence: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/operator-precedence-transact-sql +expression + : local_id (DOT calls+=method_call)* + | subquery (DOT calls+=method_call)* + | bracket_expression (DOT calls+=method_call)* + | function_call (DOT calls+=method_call)* + | expression COLLATE id + | expression time_zone + | unary_operator_expression + | expression op=(STAR | DIVIDE | PERCENT_SIGN ) expression + | expression op=(PLUS | MINUS | BIT_AND | BIT_XOR | BIT_OR ) expression + | full_column_name +// | primitive_expression + | constant + | DEFAULT + | case_expression + | hierarchyid_coloncolon_methods + | over_clause + | odbc_literal + | DOLLAR_ACTION + ; + +method_call + : xml_methods + | hierarchyid_methods + | spatial_methods + ; + +time_zone + : AT_KEYWORD TIME ZONE expression + ; + +//primitive_expression +// : DEFAULT | constant +// ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/case-transact-sql +case_expression + : CASE caseExpr=expression switch_section+ (ELSE elseExpr=expression)? END + | CASE switch_search_condition_section+ (ELSE elseExpr=expression)? END + ; + +unary_operator_expression + : BIT_NOT expression + | op=(PLUS | MINUS) expression + ; + +bracket_expression + : LR_BRACKET expression RR_BRACKET + ; + +constant_expression + : constant + | LOCAL_ID + | LR_BRACKET constant_expression RR_BRACKET + ; + +subquery + : LR_BRACKET select_statement RR_BRACKET + ; + +// https://msdn.microsoft.com/en-us/library/ms175972.aspx +with_expression + : WITH ctes+=common_table_expression (COMMA ctes+=common_table_expression)* + ; + +common_table_expression + : expression_name=id ( LR_BRACKET columns=column_name_list RR_BRACKET )? AS LR_BRACKET cte_query=select_statement RR_BRACKET + ; + +update_elem + : LOCAL_ID EQUAL full_column_name ( EQUAL | assignment_operator) expression //Combined variable and column update + | (full_column_name | LOCAL_ID) ( EQUAL | assignment_operator) expression + | udt_column_name=id DOT method_name=id LR_BRACKET expression_list RR_BRACKET + //| full_column_name DOT WRITE (expression, ) + ; + +update_elem_merge + : (full_column_name | LOCAL_ID) ( EQUAL | assignment_operator) expression + | udt_column_name=id DOT method_name=id LR_BRACKET expression_list RR_BRACKET + //| full_column_name DOT WRITE (expression, ) + ; + +search_condition + : pred+=predicate_br (log=(OR | AND) pred+=predicate_br)* + ; + +predicate_br + : NOT* predicate + | NOT* LR_BRACKET search_condition RR_BRACKET + ; + +predicate + : EXISTS subquery + | freetext_predicate + | expression comparison_operator expression + | expression comparison_operator (ALL | SOME | ANY) subquery + | expression NOT? IN subquery + | expression NOT? BETWEEN expression AND expression + | expression NOT? IN LR_BRACKET expression_list RR_BRACKET + | expression NOT? LIKE expression (ESCAPE expression)? + | expression IS null_notnull + | trigger_column_updated + ; + +query_expression + : (query_specification order_by_clause? | LR_BRACKET query_expression order_by_clause? RR_BRACKET ) sql_union* + ; + +// this accepts ORDER BY also when it is not in the last part of the UNION +sql_union + : union_keyword (query_specification order_by_clause? | LR_BRACKET query_expression order_by_clause? RR_BRACKET ) + ; + +union_keyword + : (UNION ALL? | EXCEPT | INTERSECT) + ; + +query_specification + : SELECT allOrDistinct=(ALL | DISTINCT)? top=top_clause? + columns=select_list + // https://msdn.microsoft.com/en-us/library/ms188029.aspx + (INTO into=table_name)? + (FROM from=table_sources)? + (WHERE where=search_condition)? + // https://msdn.microsoft.com/en-us/library/ms177673.aspx + (GROUP BY groupByAll=ALL? group_by_item (COMMA group_by_item)* with_rollup_cube? )? + (HAVING having=search_condition)? + ; + +// https://msdn.microsoft.com/en-us/library/ms189463.aspx +top_clause + : TOP (top_percent | top_count) (WITH TIES)? + ; + +top_percent + : percent_constant=(REAL | FLOAT | DECIMAL) PERCENT + | LR_BRACKET topper_expression=expression RR_BRACKET PERCENT + ; + +top_count + : count_constant=DECIMAL + | LR_BRACKET topcount_expression=expression RR_BRACKET + | subquery + ; + +// https://msdn.microsoft.com/en-us/library/ms188385.aspx +order_by_clause + : ORDER BY order_bys+=order_by_expression (COMMA order_bys+=order_by_expression)* + (OFFSET offset_exp=expression offset_rows=(ROW | ROWS) (FETCH fetch_offset=(FIRST | NEXT) fetch_exp=expression fetch_rows=(ROW | ROWS) ONLY)?)? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/queries/select-for-clause-transact-sql +for_clause + : FOR BROWSE + | FOR XML (RAW ( LR_BRACKET STRING RR_BRACKET )? | AUTO) + ( xml_common_directives | (COMMA (XMLDATA | XMLSCHEMA ( LR_BRACKET STRING RR_BRACKET )?)) | (COMMA ELEMENTS (XSINIL | ABSENT)?) )* + | FOR XML EXPLICIT ( xml_common_directives | COMMA XMLDATA)* + | FOR XML PATH ( LR_BRACKET STRING RR_BRACKET )? (xml_common_directives | COMMA ELEMENTS (XSINIL | ABSENT)?)* + + | FOR JSON (AUTO | PATH) + ( (COMMA ROOT ( LR_BRACKET STRING RR_BRACKET )?) + | (COMMA INCLUDE_NULL_VALUES) + | (COMMA WITHOUT_ARRAY_WRAPPER) + )* + ; + +xml_common_directives + : COMMA (BINARY_KEYWORD BASE64 | TYPE | ROOT ( LR_BRACKET STRING RR_BRACKET )?) + ; + +order_by_expression + : order_by=expression (ascending=ASC | descending=DESC)? + ; + +group_by_item + : expression + | full_column_name with_distributed_agg + | group_rollup_spec + | group_cube_spec + | grouping_sets_spec + | group_grand_total_spec + ; + +group_rollup_spec + : ROLLUP LR_BRACKET expression_list RR_BRACKET + ; + +group_cube_spec + : CUBE LR_BRACKET expression_list RR_BRACKET + ; + +grouping_sets_spec + : GROUPING SETS LR_BRACKET grouping_set_expression_list RR_BRACKET + ; + +grouping_set_expression_list + : LR_BRACKET grouping_set_expression_list RR_BRACKET (COMMA grouping_set_expression)* + | grouping_set_expression (COMMA grouping_set_expression)* + ; + +grouping_set_expression + : expression + | group_rollup_spec + | group_cube_spec + | group_grand_total_spec + ; + +group_grand_total_spec + : LR_BRACKET RR_BRACKET + ; + +with_rollup_cube + : WITH (CUBE | ROLLUP) + ; + +with_distributed_agg + : WITH LR_BRACKET DISTRIBUTED_AGG RR_BRACKET + ; + +option_clause + // https://msdn.microsoft.com/en-us/library/ms181714.aspx + : OPTION LR_BRACKET options+=option (COMMA options+=option)* RR_BRACKET + ; + +// these are query hints: +option + : ( HASH | ORDER ) GROUP + | ( MERGE | HASH | CONCAT ) UNION + | ( LOOP | MERGE | HASH ) JOIN + | ( FORCE | DISABLE ) EXTERNALPUSHDOWN + | ( FORCE | DISABLE ) SCALEOUTEXECUTION + | ( KEEP | KEEPFIXED ) PLAN + | EXPAND VIEWS + | FAST number_rows=DECIMAL + | FORCE ORDER + | IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX + | MAX_GRANT_PERCENT EQUAL expression + | MIN_GRANT_PERCENT EQUAL expression + | MAXDOP number_of_processors=DECIMAL + | MAXRECURSION number_recursion=DECIMAL + | NO_PERFORMANCE_SPOOL + | OPTIMIZE FOR LR_BRACKET optimize_for_arg (COMMA optimize_for_arg)* RR_BRACKET + | OPTIMIZE FOR UNKNOWN + | PARAMETERIZATION (SIMPLE | FORCED) + | QUERYTRACEON traceflag=DECIMAL + | RECOMPILE + | ROBUST PLAN + | TABLE HINT LR_BRACKET table_name COMMA table_hint (COMMA table_hint)* RR_BRACKET + | USE PLAN STRING + | USE HINT LR_BRACKET STRING (COMMA STRING)* RR_BRACKET + ; + +optimize_for_arg + : LOCAL_ID (UNKNOWN | EQUAL constant) + ; + +// https://msdn.microsoft.com/en-us/library/ms176104.aspx +select_list + : selectElement+=select_list_elem (COMMA selectElement+=select_list_elem)* + ; + +// https://docs.microsoft.com/ru-ru/sql/t-sql/queries/select-clause-transact-sql +asterisk + : (table_name DOT)? STAR + ; + +column_elem + : (full_column_name | DOLLAR_IDENTITY | DOLLAR_ROWGUID | NULL_P) as_column_alias? + ; + +expression_elem + : leftAlias=column_alias eq= EQUAL leftAssignment=expression + | expressionAs=expression as_column_alias? + ; + +select_list_elem + : asterisk + | column_elem + | LOCAL_ID (assignment_operator | EQUAL ) expression + | expression_elem + ; + +table_sources + : table_source_item (COMMA table_source_item)* + ; + +table_source_item + : table_source_item (INNER join_hint?)? JOIN table_source_item ON search_condition + | table_source_item (LEFT|RIGHT|FULL) OUTER? join_hint? JOIN table_source_item ON search_condition + | table_source_item CROSS JOIN table_source_item + | table_source_item (CROSS|OUTER) APPLY table_source_item + | table_source_item PIVOT pivot_clause as_table_alias? + | table_source_item UNPIVOT unpivot_clause as_table_alias? + | table_source_item for_system_time as_table_alias? + | full_object_name (as_table_alias|with_table_hints)* + | local_id (as_table_alias|with_table_hints)* + | derived_table (as_table_alias column_alias_list?)? + | subquery as_table_alias? + | rowset_function as_table_alias? + | xml_nodes_method (as_table_alias column_alias_list?)? + | (LOCAL_ID DOT)? function_call (as_table_alias column_alias_list?)? + | LR_BRACKET table_source_item RR_BRACKET + | colon_colon function_call as_table_alias? // Built-in function (old syntax) + ; + +join_hint + : LOOP + | HASH + | MERGE + | REMOTE + | REDUCE + | REDISTRIBUTE + | REPLICATE + ; + +pivot_clause + : LR_BRACKET aggregate_windowed_function FOR full_column_name IN column_alias_list RR_BRACKET + ; + +unpivot_clause + : LR_BRACKET unpivot_exp=expression FOR full_column_name IN LR_BRACKET full_column_name_list RR_BRACKET RR_BRACKET + ; + +column_declaration + : id data_type (COLLATE id)? STRING? + ; + +full_column_name_list + : column+=full_column_name (COMMA column+=full_column_name)* + ; + +table_name_with_hint + : table_name with_table_hints? + ; + +// runtime check. +bulk_option + : id EQUAL bulk_option_value=(DECIMAL | STRING) + ; + +derived_table + : select_statement + | subquery + | table_value_constructor + | LR_BRACKET table_value_constructor RR_BRACKET + | LR_BRACKET derived_table RR_BRACKET + ; + +function_call + : ranking_windowed_function #RANKING_WINDOWED_FUNC + | aggregate_windowed_function #AGGREGATE_WINDOWED_FUNC + | analytic_windowed_function #ANALYTIC_WINDOWED_FUNC + | func_proc_name_server_database_schema LR_BRACKET function_arg_list? RR_BRACKET #SCALAR_FUNCTION + | built_in_functions #BUILT_IN_FUNC + | freetext_function #FREE_TEXT + | NEXT VALUE FOR full_object_name #nextvaluefor + | L_CURLY FN odbc_scalar_function R_CURLY #odbcscalar + | partition_function_call #ptn_function + ; + +partition_function_call + : (db_name=id DOT)? DOLLAR_PARTITION DOT func_name=id LR_BRACKET function_arg_list RR_BRACKET + ; + +freetext_function + : (CONTAINSTABLE | FREETEXTTABLE) LR_BRACKET table_name COMMA (full_column_name | LR_BRACKET full_column_name (COMMA full_column_name)* RR_BRACKET | STAR ) COMMA expression (COMMA LANGUAGE expression)? (COMMA expression)? RR_BRACKET + | (SEMANTICSIMILARITYTABLE | SEMANTICKEYPHRASETABLE) LR_BRACKET table_name COMMA (full_column_name | LR_BRACKET full_column_name (COMMA full_column_name)* RR_BRACKET | STAR ) COMMA expression RR_BRACKET + | SEMANTICSIMILARITYDETAILSTABLE LR_BRACKET table_name COMMA full_column_name COMMA expression COMMA full_column_name COMMA expression RR_BRACKET + ; + +freetext_predicate + : CONTAINS LR_BRACKET (full_column_name | LR_BRACKET full_column_name (COMMA full_column_name)* RR_BRACKET | STAR | PROPERTY LR_BRACKET full_column_name COMMA expression RR_BRACKET ) COMMA expression RR_BRACKET + | FREETEXT LR_BRACKET table_name COMMA (full_column_name | LR_BRACKET full_column_name (COMMA full_column_name)* RR_BRACKET | STAR ) COMMA expression (COMMA LANGUAGE expression)? RR_BRACKET + ; + +// these are functions with a different call syntax than regular functions; +built_in_functions + : bif_cast_parse + | bif_convert + | bif_other + | bif_no_brackets=( + CURRENT_TIMESTAMP + // https://msdn.microsoft.com/en-us/library/ms176050.aspx + | CURRENT_USER + // https://msdn.microsoft.com/en-us/library/ms177587.aspx + | SESSION_USER + // https://msdn.microsoft.com/en-us/library/ms179930.aspx + | SYSTEM_USER + | USER + ) + ; + +bif_cast_parse + : bif=(CAST | TRY_CAST | PARSE | TRY_PARSE) LR_BRACKET expression AS data_type RR_BRACKET + ; + +bif_convert + : bif=(CONVERT | TRY_CONVERT) LR_BRACKET convert_data_type=data_type COMMA convert_expression=expression (COMMA style=expression)? RR_BRACKET + ; + +bif_other + // https://docs.microsoft.com/en-us/sql/t-sql/functions/logical-functions-iif-transact-sql + : IIF LR_BRACKET cond=search_condition COMMA left=expression COMMA right=expression RR_BRACKET # IIF + | TRIM LR_BRACKET (expression trim_from)? expression RR_BRACKET #TRIM + | STRING_AGG LR_BRACKET expr=expression COMMA separator=expression RR_BRACKET (WITHIN GROUP LR_BRACKET order_by_clause RR_BRACKET )? #STRING_AGG + ; + +// ODBC scalar functions/literals are called 'escape sequences' in the docs +odbc_scalar_function + : CONVERT LR_BRACKET expression COMMA data_type RR_BRACKET + | EXTRACT LR_BRACKET (YEAR | MONTH | DAY | HOUR | MINUTE | SECOND) FROM expression RR_BRACKET + | INSERT LR_BRACKET expression (COMMA expression)+ RR_BRACKET + | POSITION LR_BRACKET expression IN expression RR_BRACKET + | TRUNCATE LR_BRACKET (COMMA expression)+ RR_BRACKET + | id (LR_BRACKET (COMMA? expression)* RR_BRACKET)? + ; + +odbc_literal + : L_CURLY ( D | T | TS | GUID) STRING R_CURLY + | L_CURLY INTERVAL sign? STRING id (LR_BRACKET expression RR_BRACKET)? (TO id (LR_BRACKET expression RR_BRACKET)?)? R_CURLY // {INTERVAL '163' HOUR(3)}, {INTERVAL '163 12' DAY(3) TO HOUR} + ; + +trigger_column_updated + : UPDATE LR_BRACKET full_column_name RR_BRACKET + ; + +spatial_methods // we could expand the entire list here, but it is very long + : ( id ) LR_BRACKET expression_list? RR_BRACKET + | NULL_P // no bracket + ; + +hierarchyid_methods + : ( GETANCESTOR | GETDESCENDANT | GETLEVEL | ISDESCENDANTOF | PARSE | READ | GETREPARENTEDVALUE | TOSTRING ) LR_BRACKET expression_list? RR_BRACKET + ; + +hierarchyid_coloncolon_methods + : id colon_colon ( GETROOT | PARSE ) LR_BRACKET RR_BRACKET + ; + +xml_data_type_methods + : xml_value_method + | xml_query_method + | xml_exist_method + | xml_modify_method + ; + +xml_methods + : xml_value_call + | xml_query_call + | xml_exist_call + | xml_modify_call + ; + +xml_value_method + : (loc_id=LOCAL_ID | value_id=id | eventdata=EVENTDATA | query=xml_query_method | subquery) DOT call=xml_value_call + ; + +xml_value_call + : VALUE LR_BRACKET xquery=STRING COMMA sqltype=STRING RR_BRACKET + ; + +xml_query_method + : (loc_id=LOCAL_ID | value_id=id | table=full_object_name | subquery) DOT call=xml_query_call + ; + +xml_query_call + : QUERY LR_BRACKET xquery=STRING RR_BRACKET + ; + +xml_exist_method + : (loc_id=LOCAL_ID | value_id=id | subquery) DOT call=xml_exist_call + ; + +xml_exist_call + : EXIST LR_BRACKET xquery=STRING RR_BRACKET + ; + +xml_modify_method + : (loc_id=LOCAL_ID | value_id=id | subquery) DOT call=xml_modify_call + ; + +xml_modify_call + : MODIFY LR_BRACKET xml_dml=STRING RR_BRACKET + ; + +xml_nodes_method + : (loc_id=LOCAL_ID | value_id=id | subquery) DOT NODES LR_BRACKET xquery=STRING RR_BRACKET + ; + +switch_section + : WHEN expression THEN expression + ; + +switch_search_condition_section + : WHEN search_condition THEN expression + ; + +as_column_alias + : AS? column_alias + ; + +as_table_alias + : AS? table_alias + ; + +table_alias + : id with_table_hints? + ; + +// https://msdn.microsoft.com/en-us/library/ms187373.aspx +with_table_hints + : LR_BRACKET hint+=table_hint RR_BRACKET // without WITH, one hint can be specified + | WITH LR_BRACKET hint+=table_hint (COMMA? hint+=table_hint)* RR_BRACKET + | sample_clause + ; + +sample_clause + : TABLESAMPLE SYSTEM? LR_BRACKET expression (PERCENT|ROWS) RR_BRACKET (REPEATABLE LR_BRACKET PLUS? DECIMAL RR_BRACKET)? + ; + +// Id runtime check. Id can be (FORCESCAN, HOLDLOCK, NOLOCK, NOWAIT, PAGLOCK, READCOMMITTED, +// READCOMMITTEDLOCK, READPAST, READUNCOMMITTED, REPEATABLEREAD, ROWLOCK, TABLOCK, TABLOCKX +// UPDLOCK, XLOCK) +table_hint + : NOEXPAND? ( INDEX (LR_BRACKET index_value (COMMA index_value)* RR_BRACKET | index_value (COMMA index_value)*) ) + | INDEX EQUAL index_value + | NOEXPAND + | FORCESEEK ( LR_BRACKET index_value LR_BRACKET ID (COMMA ID)* RR_BRACKET RR_BRACKET )? + | SERIALIZABLE + | SNAPSHOT + | SPATIAL_WINDOW_MAX_CELLS EQUAL DECIMAL + | NOWAIT + | HOLDLOCK + | ID + ; + +index_value + : id | DECIMAL + ; + +column_alias_list + : LR_BRACKET alias+=column_alias (COMMA alias+=column_alias)* RR_BRACKET + ; + +column_alias + : id + | STRING + ; + +table_value_constructor + : VALUES LR_BRACKET exps+=expression_list RR_BRACKET (COMMA LR_BRACKET exps+=expression_list RR_BRACKET )* + ; + +function_arg_list + : ( STAR | expression ) (COMMA exp+=expression)* + ; + +expression_list + : exp+=expression (COMMA exp+=expression)* + ; + +// https://msdn.microsoft.com/en-us/library/ms189798.aspx +ranking_windowed_function + : agg_func=(RANK | DENSE_RANK | ROW_NUMBER) LR_BRACKET RR_BRACKET over_clause + | NTILE LR_BRACKET expression RR_BRACKET over_clause + ; + +// https://msdn.microsoft.com/en-us/library/ms173454.aspx +aggregate_windowed_function + : agg_func=(AVG | MAX | MIN | SUM | STDEV | STDEVP | VAR | VARP) LR_BRACKET all_distinct_expression RR_BRACKET over_clause? + | cnt=(COUNT | COUNT_BIG) LR_BRACKET ( STAR | all_distinct_expression) RR_BRACKET over_clause? + | CHECKSUM_AGG LR_BRACKET all_distinct_expression RR_BRACKET + | GROUPING LR_BRACKET expression RR_BRACKET + | GROUPING_ID LR_BRACKET expression_list RR_BRACKET + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/functions/analytic-functions-transact-sql +analytic_windowed_function + : first_last=(FIRST_VALUE | LAST_VALUE) LR_BRACKET expression RR_BRACKET over_clause + | lag_lead=(LAG | LEAD) LR_BRACKET expression (COMMA expression (COMMA expression)? )? RR_BRACKET over_clause + | rank=(CUME_DIST | PERCENT_RANK) LR_BRACKET RR_BRACKET OVER LR_BRACKET (PARTITION BY expression_list)? order_by_clause RR_BRACKET + | pct=(PERCENTILE_CONT | PERCENTILE_DISC) LR_BRACKET expression RR_BRACKET WITHIN GROUP LR_BRACKET ORDER BY expression (ASC | DESC)? RR_BRACKET OVER LR_BRACKET (PARTITION BY expression_list)? RR_BRACKET + ; + +all_distinct_expression + : (ALL | DISTINCT)? expression + ; + +// https://msdn.microsoft.com/en-us/library/ms189461.aspx +over_clause + : OVER LR_BRACKET (PARTITION BY expression_list)? order_by_clause? row_or_range_clause? RR_BRACKET + ; + +row_or_range_clause + : (ROWS | RANGE) window_frame_extent + ; + +window_frame_extent + : window_frame_preceding + | BETWEEN window_frame_bound AND window_frame_bound + ; + +window_frame_bound + : window_frame_preceding + | window_frame_following + ; + +window_frame_preceding + : UNBOUNDED PRECEDING + | DECIMAL PRECEDING + | CURRENT ROW + ; + +window_frame_following + : UNBOUNDED FOLLOWING + | DECIMAL FOLLOWING + ; + +create_database_option + : FILESTREAM ( database_filestream_option (COMMA database_filestream_option)* ) + | DEFAULT_LANGUAGE EQUAL ( id | STRING | DECIMAL ) + | DEFAULT_FULLTEXT_LANGUAGE EQUAL ( id | STRING | DECIMAL ) + | NESTED_TRIGGERS EQUAL ( OFF | ON ) + | TRANSFORM_NOISE_WORDS EQUAL ( OFF | ON ) + | TWO_DIGIT_YEAR_CUTOFF EQUAL DECIMAL + | DB_CHAINING ( OFF | ON ) + | TRUSTWORTHY ( OFF | ON ) + | CATALOG_COLLATION EQUAL id + | PERSISTENT_LOG_BUFFER EQUAL ON LR_BRACKET DIRECTORY_NAME EQUAL STRING RR_BRACKET + ; + +database_filestream_option + : LR_BRACKET + ( + ( NON_TRANSACTED_ACCESS EQUAL ( OFF | READ_ONLY | FULL ) ) + | + ( DIRECTORY_NAME EQUAL STRING ) + ) + RR_BRACKET + ; + +database_file_spec + : file_group | file_spec + ; + +file_group + : FILEGROUP id + ( CONTAINS FILESTREAM )? + ( DEFAULT )? + ( CONTAINS MEMORY_OPTIMIZED_DATA )? + file_spec ( COMMA file_spec )* + ; +file_spec + : LR_BRACKET + NAME EQUAL ( id | STRING ) COMMA? + ( FILENAME EQUAL file = STRING COMMA? )? + ( SIZE EQUAL file_size COMMA? )? + ( MAXSIZE EQUAL (file_size | UNLIMITED )COMMA? )? + ( FILEGROWTH EQUAL file_size COMMA? )? + RR_BRACKET + ; + +if_exists + : IF EXISTS + ; + +trim_from + : FROM + ; + +on_off + : ON + | OFF + ; + +clustered + : CLUSTERED + | NONCLUSTERED + ; + +null_notnull + : NOT? NULL_P + ; + +begin_conversation_timer + : BEGIN CONVERSATION TIMER LR_BRACKET LOCAL_ID RR_BRACKET TIMEOUT EQUAL time SEMI? + ; + +begin_conversation_dialog + : BEGIN DIALOG (CONVERSATION)? dialog_handle=LOCAL_ID + FROM SERVICE initiator_service_name=service_name + TO SERVICE target_service_name=service_name (COMMA service_broker_guid=STRING)? + ON CONTRACT contract_name + (WITH + ((RELATED_CONVERSATION | RELATED_CONVERSATION_GROUP) EQUAL LOCAL_ID COMMA?)? + (LIFETIME EQUAL (DECIMAL | LOCAL_ID) COMMA?)? + (ENCRYPTION EQUAL (ON | OFF))? )? + SEMI? + ; + +contract_name + : (id | expression) + ; + +service_name + : (id | expression) + ; + +end_conversation + : END CONVERSATION conversation_handle=LOCAL_ID SEMI? + (WITH (ERROR EQUAL faliure_code=(LOCAL_ID | STRING) DESCRIPTION EQUAL failure_text=(LOCAL_ID | STRING))? CLEANUP? )? + ; + +waitfor_conversation + : WAITFOR? LR_BRACKET get_conversation RR_BRACKET (COMMA? TIMEOUT timeout=time)? SEMI? + ; + +get_conversation + :GET CONVERSATION GROUP conversation_group_id=(STRING | LOCAL_ID) FROM queue=queue_id SEMI? + ; + +// https://docs.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql +waitfor_receive_statement + : WAITFOR receive_statement? (COMMA TIMEOUT time)? SEMI? + ; + +receive_statement + : RECEIVE (ALL | DISTINCT | top_clause | STAR)? + ((LOCAL_ID EQUAL)? expression COMMA?)* FROM full_object_name + (INTO table_variable=LOCAL_ID)? (WHERE where=search_condition)? SEMI? + | LR_BRACKET receive_statement RR_BRACKET SEMI? + ; + +queue_id + : database_name=id (DOT schema_name=id DOT name=id)? + ; + +send_conversation + : SEND ON CONVERSATION conversation_handle=(STRING | LOCAL_ID) + MESSAGE TYPE message_type_name=expression + ( LR_BRACKET message_body_expression=(STRING | LOCAL_ID) RR_BRACKET )? + SEMI? + ; + +// https://msdn.microsoft.com/en-us/library/ms187752.aspx +data_type + : ext_type=simple_name LR_BRACKET scale=DECIMAL COMMA prec=DECIMAL RR_BRACKET + | NATIONAL? ext_type=simple_name VARYING? LR_BRACKET scale=(DECIMAL|MAX) RR_BRACKET + | ext_type=simple_name IDENTITY (LR_BRACKET sign? seed=DECIMAL COMMA sign? inc=DECIMAL RR_BRACKET)? + | cursor_type=CURSOR + | double_prec=DOUBLE PRECISION? + | NATIONAL? unscaled_type=simple_name VARYING? + | xml_type_definition + ; + +// https://msdn.microsoft.com/en-us/library/ms179899.aspx +constant + : STRING // string, datetime or uniqueidentifier + | BINARY + | NULL_P + | sign? (REAL | MONEY | DECIMAL | FLOAT) + ; + +sign + : PLUS + | MINUS + ; + +keyword + : ABORT_AFTER_WAIT + | ABSENT + | ABSOLUTE + | ACCENT_SENSITIVITY + | ACCESS + | ACTION + | ACTIVATION + | ACTIVE + | ADDRESS + | ADMINISTER + | AES + | AES_128 + | AES_192 + | AES_256 + | AFFINITY + | AFTER + | AGGREGATE + | ALGORITHM + | ALLOWED + | ALLOW_CONNECTIONS + | ALLOW_ENCRYPTED_VALUE_MODIFICATIONS + | ALLOW_MULTIPLE_EVENT_LOSS + | ALLOW_SINGLE_EVENT_LOSS + | ALLOW_SNAPSHOT_ISOLATION + | ALWAYS + | ANONYMOUS + | ANSI_NULLS + | ANSI_NULL_DEFAULT + | ANSI_PADDING + | ANSI_WARNINGS + | APPEND + | APPLICATION + | APPLICATION_LOG + | APPLY + | ARITHABORT + | ASSEMBLY + | ASYMMETRIC + | ASYNCHRONOUS_COMMIT + | ATOMIC + | AT_KEYWORD + | AUDIT + | AUDIT_GUID + | AUTHENTICATE + | AUTHENTICATION + | AUTO + | AUTOMATED_BACKUP_PREFERENCE + | AUTOMATIC + | AUTO_CLEANUP + | AUTO_CLOSE + | AUTO_CREATE_STATISTICS + | AUTO_SHRINK + | AUTO_UPDATE_STATISTICS + | AUTO_UPDATE_STATISTICS_ASYNC + | AVAILABILITY + | AVAILABILITY_MODE + | AVG + | BACKUP_PRIORITY + | BEFORE + | BEGIN_DIALOG + | BIGINT + | BASE64 + | BINARY_CHECKSUM + | BINDING + | BLOB_STORAGE + | BLOCK + | BLOCKERS + | BLOCKING_HIERARCHY + | BLOCKSIZE + | BOUNDING_BOX + | BROKER + | BROKER_INSTANCE + | BUFFER + | BUFFERCOUNT + | BULK_LOGGED + | CACHE + | CALLED + | CALLER + | CAP_CPU_PERCENT + | CAST + | CATALOG + | CATALOG_COLLATION + | CATCH + | CELLS_PER_OBJECT + | CERTIFICATE + | CHANGE + | CHANGES + | CHANGETABLE + | CHANGE_RETENTION + | CHANGE_TRACKING + | CHECKSUM + | CHECKSUM_AGG + | CHECK_EXPIRATION + | CHECK_POLICY + | CLASSIFIER_FUNCTION + | CLEANUP + | CLEANUP_POLICY + | CLEAR + | CLUSTER + | COALESCE + | COLLECTION + | COLUMNS + | COLUMNSTORE + | COLUMN_MASTER_KEY + | COMMITTED + | COMPATIBILITY_LEVEL + | COMPRESSION + | CONCAT + | CONCAT_NULL_YIELDS_NULL + | CONFIGURATION + | CONNECTION + | CONTAINED + | CONTAINMENT + | CONTENT + | CONTEXT + | CONTINUE_AFTER_ERROR + | CONTRACT + | CONTRACT_NAME + | CONTROL + | CONVERSATION + | COOKIE + | COPY_ONLY + | COUNT + | COUNTER + | COUNT_BIG + | CPU + | CREATE_NEW + | CREATION_DISPOSITION + | CREDENTIAL + | CRYPTOGRAPHIC + | CUBE + | CUME_DIST + | CURSOR_CLOSE_ON_COMMIT + | CURSOR_DEFAULT + | CUSTOM + | CYCLE + | D + | DATA + | DATABASE_MIRRORING + | DATASPACE + | DATA_COMPRESSION + | DATA_CONSISTENCY_CHECK + | DATA_FLUSH_INTERVAL_SECONDS + | DATA_SOURCE + | DATEADD + | DATEDIFF + | DATEFIRST + | DATEFORMAT + | DATENAME + | DATEPART + | DATE_CORRELATION_OPTIMIZATION + | DATE_FORMAT + | DAY + | DAYS + | DB_CHAINING + | DB_FAILOVER + | DDL + | DECRYPTION + | DEFAULT_DATABASE + | DEFAULT_DOUBLE_QUOTE + | DEFAULT_FULLTEXT_LANGUAGE + | DEFAULT_LANGUAGE + | DEFAULT_SCHEMA + | DEFINITION + | DELAY + | DELAYED_DURABILITY + | DELETED + | DENSE_RANK + | DEPENDENTS + | DES + | DESCRIPTION + | DESX + | DHCP + | DIAGNOSTICS + | DIALOG + | DIFFERENTIAL + | DIRECTORY_NAME + | DISABLE + | DISABLED + | DISABLE_BROKER + | DISK + | DISK_DRIVE + | DISTRIBUTED_AGG + | DOCUMENT + | DTC_SUPPORT + | DYNAMIC + | ELEMENTS + | EMERGENCY + | EMPTY + | ENABLE + | ENABLED + | ENABLE_BROKER + | ENCODING + | ENCRYPTED_VALUE + | ENCRYPTION + | ENDPOINT + | ENDPOINT_URL + | ERROR + | ERROR_BROKER_CONVERSATIONS + | EVENT + | EVENTDATA + | EVENT_RETENTION_MODE + | EXCLUSIVE + | EXECUTABLE + | EXECUTABLE_FILE + | EXECUTION_COUNT + | EXIST + | EXPAND + | EXPIREDATE + | EXPIRY_DATE + | EXPLICIT + | EXTENSION + | EXTERNAL_ACCESS + | EXTRACT + | FAILOVER + | FAILOVER_MODE + | FAILURE + | FAILURECONDITIONLEVEL + | FAILURE_CONDITION_LEVEL + | FAIL_OPERATION + | FALSE + | FAN_IN + | FAST + | FAST_FORWARD + | FIELD_TERMINATOR + | FILEGROUP + | FILEGROWTH + | FILENAME + | FILEPATH + | FILESTREAM + | FILETABLE + | FILE_SNAPSHOT + | FILTER + | FIRST + | FIRST_ROW + | FIRST_VALUE + | FN + | FOLLOWING + | FORCE + | FORCED + | FORCESEEK + | FORCE_FAILOVER_ALLOW_DATA_LOSS + | FORCE_SERVICE_ALLOW_DATA_LOSS + | FORMAT + | FORMAT_OPTIONS + | FORMAT_TYPE + | FORWARD_ONLY + | FULLSCAN + | FULLTEXT + | GB + | GENERATED + | GEOGRAPHY_AUTO_GRID + | GEOGRAPHY_GRID + | GEOMETRY_AUTO_GRID + | GEOMETRY_GRID + | GET + | GETANCESTOR + | GETDATE + | GETDESCENDANT + | GETLEVEL + | GETREPARENTEDVALUE + | GETROOT + | GETUTCDATE + | GLOBAL + | GOVERNOR + | GRIDS + | GROUPING + | GROUPING_ID + | GROUP_MAX_REQUESTS + | GUID + | HADR + | HASH + | HASHED + | HEALTHCHECKTIMEOUT + | HEALTH_CHECK_TIMEOUT + | HIDDEN_RENAMED + | HIGH + | HINT + | HISTORY_RETENTION_PERIOD + | HISTORY_TABLE + | HOLDLOCK + | HONOR_BROKER_PRIORITY + | HOUR + | HOURS + | IDENTITY + | IDENTITYCOL + | IDENTITY_VALUE + | IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX + | IIF + | IMMEDIATE + | IMPERSONATE + | IMPORTANCE + | INCLUDE + | INCLUDE_NULL_VALUES + | INCREMENT + | INCREMENTAL + | INFINITE + | INIT + | INITIATOR + | INPUT + | INSENSITIVE + | INSERTED + | INSTEAD + | INT + | INTERVAL + | INTERVAL_LENGTH_MINUTES + | IO + | IP + | ISDESCENDANTOF + | ISNULL + | ISOLATION + | JOB + | JSON + | KB + | KEEP + | KEEPFIXED + | KEEP_CDC + | KEEP_REPLICATION + | KERBEROS + | KEYS + | KEYSET + | KEY_PATH + | KEY_SOURCE + | KEY_STORE_PROVIDER_NAME + | LAG + | LANGUAGE + | LAST + | LAST_VALUE + | LEAD + | LEDGER + | LEFT + | LEVEL + | LIBRARY + | LIFETIME + | LINKED + | LINUX + | LIST + | LISTENER + | LISTENER_IP + | LISTENER_PORT + | LISTENER_URL + | LOB_COMPACTION + | LOCAL + | LOCAL_SERVICE_NAME + | LOCATION + | LOCK + | LOCK_ESCALATION + | LOG + | LOGIN + | LOOP + | LOW + | MANUAL + | MARK + | MASK + | MASKED + | MASTER + | MATCHED + | MATERIALIZED + | MAX + | MAXDOP + | MAXRECURSION + | MAXSIZE + | MAXTRANSFER + | MAXVALUE + | MAX_CPU_PERCENT + | MAX_DISPATCH_LATENCY + | MAX_DOP + | MAX_DURATION + | MAX_EVENT_SIZE + | MAX_FILES + | MAX_GRANT_PERCENT + | MAX_IOPS_PER_VOLUME + | MAX_MEMORY + | MAX_MEMORY_PERCENT + | MAX_OUTSTANDING_IO_PER_VOLUME + | MAX_PLANS_PER_QUERY + | MAX_PROCESSES + | MAX_QUEUE_READERS + | MAX_ROLLOVER_FILES + | MAX_SIZE + | MAX_SIZE_MB + | MAX_STORAGE_SIZE_MB + | MB + | MEDIADESCRIPTION + | MEDIANAME + | MEDIUM + | MEMBER + | MEMORY_OPTIMIZED_DATA + | MEMORY_PARTITION_MODE + | MESSAGE + | MESSAGE_FORWARDING + | MESSAGE_FORWARD_SIZE + | MIN + | MINUTE + | MINUTES + | MINVALUE + | MIN_ACTIVE_ROWVERSION + | MIN_CPU_PERCENT + | MIN_GRANT_PERCENT + | MIN_IOPS_PER_VOLUME + | MIN_MEMORY_PERCENT + | MIRROR + | MIRROR_ADDRESS + | MIXED_PAGE_ALLOCATION + | MODE + | MODEL + | MODIFY + | MONTH + | MONTHS + | MOVE + | MULTI_USER + | MUST_CHANGE + | NAME + | NATIVE_COMPILATION + | NESTED_TRIGGERS + | NEW_ACCOUNT + | NEW_BROKER + | NEW_PASSWORD + | NEXT + | NO + | NOCOMPUTE + | NOCOUNT + | NODE + | NODES + | NOEXPAND + | NOFORMAT + | NOINIT + | NONE + | NON_TRANSACTED_ACCESS + | NORECOMPUTE + | NORECOVERY + | NOREWIND + | NOSKIP + | NOTIFICATION + | NOTIFICATIONS + | NOUNLOAD + | NOWAIT + | NO_CHECKSUM + | NO_COMPRESSION + | NO_EVENT_LOSS + | NO_TRUNCATE + | NO_WAIT + | NTILE + | NTLM + | NULLIF + | NUMANODE + | NUMBER + | NUMERIC_ROUNDABORT + | OBJECT + | OFFLINE + | OFFSET + | OLD_ACCOUNT + | OLD_PASSWORD + | ONLINE + | ONLY + | ON_FAILURE + | OPENJSON + | OPEN_EXISTING + | OPERATIONS + | OPERATION_MODE + | OPTIMISTIC + | OPTIMIZE + | OUT + | OUTPUT + | OVERRIDE + | OWNER + | OWNERSHIP + | PAGE + | PAGECOUNT + | PAGE_VERIFY + | PARAM + | PARAMETERIZATION + | PARAM_NODE + | PARTIAL + | PARTITION + | PARTITIONS + | PARTNER + | PASSWORD + | PATH + | PERCENTILE_CONT + | PERCENTILE_DISC + | PERCENT_RANK + | PERIOD + | PERMISSION_SET + | PERSISTED + | PERSIST_SAMPLE_PERCENT + | PERSISTENT_LOG_BUFFER + | PER_CPU + | PER_DB + | PER_NODE + | PLATFORM + | POISON_MESSAGE_HANDLING + | POLICY + | POOL + | PORT + | POSITION + | PRECEDING + | PREDICATE + | PREDICT + | PRIMARY_ROLE + | PRIOR + | PRIORITY + | PRIORITY_LEVEL + | PRIVATE + | PRIVATE_KEY + | PRIVILEGES + | PROCEDURE_NAME + | PROCESS + | PROFILE + | PROPERTY + | PROVIDER + | PROVIDER_KEY_NAME + | PYTHON + | QUERY + | QUERY_CAPTURE_MODE + | QUERY_CAPTURE_POLICY + | QUERY_STORE + | QUEUE + | QUEUE_DELAY + | QUOTED_IDENTIFIER + | R + | RANGE + | RANK + | RC2 + | RC4 + | RC4_128 + | READONLY + | READ_COMMITTED_SNAPSHOT + | READ_ONLY + | READ_ONLY_ROUTING_LIST + | READ_WRITE + | READ_WRITE_FILEGROUPS + | REBUILD + | RECEIVE + | RECOMPILE + | RECOVERY + | RECURSIVE_TRIGGERS + | REDISTRIBUTE + | REDUCE + | REGENERATE + | RELATED_CONVERSATION + | RELATED_CONVERSATION_GROUP + | RELATIVE + | REMOTE + | REMOTE_SERVICE_NAME + | REMOVE + | REORGANIZE + | REPEATABLE + | REPLACE + | REPLICA + | REPLICATE + | REQUEST_MAX_CPU_TIME_SEC + | REQUEST_MAX_MEMORY_GRANT_PERCENT + | REQUEST_MEMORY_GRANT_TIMEOUT_SEC + | REQUIRED + | REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT + | RESAMPLE + | RESERVE_DISK_SPACE + | RESET + | RESOURCE + | RESOURCES + | RESOURCE_MANAGER_LOCATION + | RESTART + | RESTRICTED_USER + | RESULT + | RESUME + | RETAINDAYS + | RETENTION + | RETURNS + | REWIND + | RIGHT + | ROBUST + | ROLE + | ROLLUP + | ROOT + | ROUTE + | ROW + | ROWGUID + | ROWS + | ROW_NUMBER + | RSA_1024 + | RSA_2048 + | RSA_3072 + | RSA_4096 + | RSA_512 + | RUNTIME + | SAFE + | SAFETY + | SAMPLE + | SCHEDULER + | SCHEMABINDING + | SCHEME + | SCOPED + | SCRIPT + | SCROLL + | SCROLL_LOCKS + | SEARCH + | SECOND + | SECONDARY + | SECONDARY_ONLY + | SECONDARY_ROLE + | SECONDS + | SECRET + | SECURABLES + | SECURITY + | SECURITY_LOG + | SEEDING_MODE + | SELF + | SELECTIVE + | SEMI_SENSITIVE + | SEND + | SENT + | SEQUENCE + | SEQUENCE_NUMBER + | SERIALIZABLE + | SERVER + | SERVICE + | SERVICE_BROKER + | SERVICE_NAME + | SESSION + | SESSION_TIMEOUT + | SETERROR + | SETS + | SETTINGS + | SHARE + | SHOWPLAN + | SID + | SIGNATURE + | SIMPLE + | SINGLETON + | SINGLE_USER + | SIZE + | SIZE_BASED_CLEANUP_MODE + | SKIP_KEYWORD + | SMALLINT + | SNAPSHOT + | SOFTNUMA + | SOURCE + | SPARSE + | SPATIAL + | SPATIAL_WINDOW_MAX_CELLS + | SPECIFICATION + | SPLIT + | SQL + | SQLDUMPERFLAGS + | SQLDUMPERPATH + | SQLDUMPERTIMEOUT + | STALE_CAPTURE_POLICY_THRESHOLD + | STANDBY + | START + | STARTED + | STARTUP_STATE + | START_DATE + | STATE + | STATEMENT + | STATIC + | STATS + | STATS_STREAM + | STATUS + | STATUSONLY + | STDEV + | STDEVP + | STOP + | STOPAT + | STOPATMARK + | STOPBEFOREMARK + | STOPLIST + | STOPPED + | STOP_ON_ERROR + | STRING_AGG + | STRING_DELIMITER + | STUFF + | SUBJECT + | SUBSCRIBE + | SUBSCRIPTION + | SUM + | SUPPORTED + | SUSPEND + | SWITCH + | SYMMETRIC + | SYNCHRONOUS_COMMIT + | SYNONYM + | SYSTEM + | SYSTEM_TIME + | SYSTEM_VERSIONING + | T + | TAKE + | TAPE + | TARGET + | TARGET_RECOVERY_TIME + | TB + | TCP + | TEXTIMAGE_ON + | THROW + | TIES + | TIME + | TIMEOUT + | TIMER + | TINYINT + | TORN_PAGE_DETECTION + | TOSTRING + | TOTAL_COMPILE_CPU_TIME_MS + | TOTAL_EXECUTION_CPU_TIME_MS + | TRACE + | TRACKING + | TRACK_CAUSALITY + | TRACK_COLUMNS_UPDATED + | TRANSACTION_ID + | TRANSFER + | TRANSFORM_NOISE_WORDS + | TRIM + | TRIPLE_DES + | TRIPLE_DES_3KEY + | TRUE + | TRUSTWORTHY + | TRY + | TRY_CAST + | TS + | TSQL + | TWO_DIGIT_YEAR_CUTOFF + | TYPE + | TYPE_WARNING + | UNBOUNDED + | UNCHECKED + | UNCOMMITTED + | UNKNOWN + | UNLIMITED + | UNLOCK + | UNMASK + | UNSAFE + | UOW + | URL + | USED + | USE_TYPE_DEFAULT + | USING + | VALIDATION + | VALID_XML + | VALUE + | VAR + | VARP + | VERBOSELOGGING + | VERSION + | VIEWS + | VIEW_METADATA + | VISIBILITY + | WAIT + | WAIT_AT_LOW_PRIORITY + | WAIT_STATS_CAPTURE_MODE + | WEEK + | WEEKS + | WELL_FORMED_XML + | WINDOWS + | WITHOUT + | WITHOUT_ARRAY_WRAPPER + | WITNESS + | WORK + | WORKLOAD + | XMAX + | XMIN + | XML + | XMLDATA + | XMLNAMESPACES + | XMLSCHEMA + | XQUERY + | XSINIL + | YEAR + | YEARS + | YMAX + | YMIN + | ZONE + //Built-ins: + | VARCHAR + | NVARCHAR + | BINARY_KEYWORD + | VARBINARY_KEYWORD + | PRECISION //For some reason this is possible to use as ID + ; + +entity_name + : (((server=id DOT database=id DOT)? schema=id | database=id DOT schema=id?) DOT)? table=id + ; + +full_object_name + : (((server=id? DOT)? database=id? DOT)? schema=id? DOT)? object_name=id + ; + +table_name + : ((database=id? DOT)? schema=id? DOT)? table=id + ; + +simple_name + : DOT? (schema=id? DOT)? name=id + ; + +func_proc_name_schema + : DOT? ((schema=id)? DOT)? procedure=id + ; + +func_proc_name_database_schema + : DOT? database=id? DOT schema=id? DOT procedure=id + | (schema=id? DOT)? procedure=id + ; + +func_proc_name_server_database_schema + : (server=id? DOT)? database=id? DOT schema=id? DOT procedure=id + | (schema=id? DOT)? procedure=id + ; + +ddl_object + : full_object_name + | local_id + ; + +external_name + : EXTERNAL NAME full_object_name + ; + +full_column_name + : (((server=id? DOT)? schema=id? DOT)? tablename=id? DOT)? column_name=id + ; + +column_name_list_with_order + : id (ASC | DESC)? (COMMA id (ASC | DESC)?)* + ; + +//For some reason, tsql allows any number of prefixes: Here, h is the column: a.b.c.d.e.f.g.h +insert_column_name_list + : col+=insert_column_id (COMMA col+=insert_column_id)* + ; + +insert_column_id + : (ignore+=id? DOT )* id + ; + +column_name_list + : col+=id (COMMA col+=id)* + ; + +cursor_name + : id + | LOCAL_ID + ; + +// https://msdn.microsoft.com/en-us/library/ms175874.aspx +id + : ID + | DOUBLE_QUOTE_ID + | SQUARE_BRACKET_ID + | keyword + | id colon_colon id + ; + +local_id + : LOCAL_ID + ; + +// https://msdn.microsoft.com/en-us/library/ms188074.aspx +// Spaces are allowed for comparison operators. +comparison_operator + : EQUAL | GREATER | LESS | LESS EQUAL | GREATER EQUAL | LESS GREATER | EXCLAMATION EQUAL | EXCLAMATION GREATER | EXCLAMATION LESS + ; + +assignment_operator + : PLUS_ASSIGN | MINUS_ASSIGN | MULT_ASSIGN | DIV_ASSIGN | MOD_ASSIGN | AND_ASSIGN | XOR_ASSIGN| OR_ASSIGN + ; + +file_size + : DECIMAL( KB | MB | GB | TB | PERCENT_SIGN )? + ; + +// +// end of file +// diff --git a/contrib/babelfishpg_tsql/antlr/cmake-dir/FindANTLR.cmake b/contrib/babelfishpg_tsql/antlr/cmake-dir/FindANTLR.cmake new file mode 100644 index 00000000000..968feded7a8 --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/cmake-dir/FindANTLR.cmake @@ -0,0 +1,128 @@ +#find_package(Java QUIET COMPONENTS Runtime) + +if(NOT ANTLR_EXECUTABLE) + find_program(ANTLR_EXECUTABLE + NAMES antlr.jar antlr4.jar antlr-4.jar antlr-4.9.3-complete.jar) +endif() + +set(Java_JAVA_EXECUTABLE $ENV{ANTLR4_JAVA_BIN}) + +message(STATUS "java executable=${Java_JAVA_EXECUTABLE}") + +if(ANTLR_EXECUTABLE AND Java_JAVA_EXECUTABLE) + execute_process( + COMMAND ${Java_JAVA_EXECUTABLE} -jar ${ANTLR_EXECUTABLE} + OUTPUT_VARIABLE ANTLR_COMMAND_OUTPUT + ERROR_VARIABLE ANTLR_COMMAND_ERROR + RESULT_VARIABLE ANTLR_COMMAND_RESULT + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(ANTLR_COMMAND_RESULT EQUAL 0) + string(REGEX MATCH "Version [0-9]+(\\.[0-9])*" ANTLR_VERSION ${ANTLR_COMMAND_OUTPUT}) + string(REPLACE "Version " "" ANTLR_VERSION ${ANTLR_VERSION}) + else() + message( + SEND_ERROR + "Command '${Java_JAVA_EXECUTABLE} -jar ${ANTLR_EXECUTABLE}' " + "failed with the output '${ANTLR_COMMAND_ERROR}'") + endif() + + macro(ANTLR_TARGET Name InputFile) + set(ANTLR_OPTIONS LEXER PARSER LISTENER VISITOR) + set(ANTLR_ONE_VALUE_ARGS PACKAGE OUTPUT_DIRECTORY DEPENDS_ANTLR) + set(ANTLR_MULTI_VALUE_ARGS COMPILE_FLAGS DEPENDS) + cmake_parse_arguments(ANTLR_TARGET + "${ANTLR_OPTIONS}" + "${ANTLR_ONE_VALUE_ARGS}" + "${ANTLR_MULTI_VALUE_ARGS}" + ${ARGN}) + + set(ANTLR_${Name}_INPUT ${InputFile}) + + get_filename_component(ANTLR_INPUT ${InputFile} NAME_WE) + + if(ANTLR_TARGET_OUTPUT_DIRECTORY) + set(ANTLR_${Name}_OUTPUT_DIR ${ANTLR_TARGET_OUTPUT_DIRECTORY}) + else() + set(ANTLR_${Name}_OUTPUT_DIR + ${CMAKE_CURRENT_BINARY_DIR}/antlr4cpp_generated_src/${ANTLR_INPUT}) + endif() + + unset(ANTLR_${Name}_CXX_OUTPUTS) + + if((ANTLR_TARGET_LEXER AND NOT ANTLR_TARGET_PARSER) OR + (ANTLR_TARGET_PARSER AND NOT ANTLR_TARGET_LEXER)) + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.cpp) + set(ANTLR_${Name}_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.interp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}.tokens) + else() + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.cpp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Parser.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Parser.cpp) + list(APPEND ANTLR_${Name}_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.interp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Lexer.tokens) + endif() + + if(ANTLR_TARGET_LISTENER) + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseListener.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseListener.cpp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Listener.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Listener.cpp) + list(APPEND ANTLR_TARGET_COMPILE_FLAGS -listener) + endif() + + if(ANTLR_TARGET_VISITOR) + list(APPEND ANTLR_${Name}_CXX_OUTPUTS + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseVisitor.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}BaseVisitor.cpp + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Visitor.h + ${ANTLR_${Name}_OUTPUT_DIR}/${ANTLR_INPUT}Visitor.cpp) + list(APPEND ANTLR_TARGET_COMPILE_FLAGS -visitor) + endif() + + if(ANTLR_TARGET_PACKAGE) + list(APPEND ANTLR_TARGET_COMPILE_FLAGS -package ${ANTLR_TARGET_PACKAGE}) + endif() + + list(APPEND ANTLR_${Name}_OUTPUTS ${ANTLR_${Name}_CXX_OUTPUTS}) + + if(ANTLR_TARGET_DEPENDS_ANTLR) + if(ANTLR_${ANTLR_TARGET_DEPENDS_ANTLR}_INPUT) + list(APPEND ANTLR_TARGET_DEPENDS + ${ANTLR_${ANTLR_TARGET_DEPENDS_ANTLR}_INPUT}) + list(APPEND ANTLR_TARGET_DEPENDS + ${ANTLR_${ANTLR_TARGET_DEPENDS_ANTLR}_OUTPUTS}) + else() + message(SEND_ERROR + "ANTLR target '${ANTLR_TARGET_DEPENDS_ANTLR}' not found") + endif() + endif() + + add_custom_command( + OUTPUT ${ANTLR_${Name}_OUTPUTS} + COMMAND ${Java_JAVA_EXECUTABLE} -jar ${ANTLR_EXECUTABLE} + ${InputFile} + -o ${ANTLR_${Name}_OUTPUT_DIR} + -no-listener + -Dlanguage=Cpp + ${ANTLR_TARGET_COMPILE_FLAGS} + DEPENDS ${InputFile} + ${ANTLR_TARGET_DEPENDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Building ${Name} with ANTLR ${ANTLR_VERSION}") + endmacro(ANTLR_TARGET) + +endif(ANTLR_EXECUTABLE AND Java_JAVA_EXECUTABLE) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + ANTLR + REQUIRED_VARS ANTLR_EXECUTABLE Java_JAVA_EXECUTABLE + VERSION_VAR ANTLR_VERSION) diff --git a/contrib/babelfishpg_tsql/antlr/cmake-dir/README.md b/contrib/babelfishpg_tsql/antlr/cmake-dir/README.md new file mode 100644 index 00000000000..0ebe1dd51e5 --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/cmake-dir/README.md @@ -0,0 +1,157 @@ +## Getting started with Antlr4Cpp + +Here is how you can use this external project to create the antlr4cpp demo to start your project off. + +1. Create your project source folder somewhere. e.g. ~/srcfolder/ + 1. Make a subfolder cmake + 2. Copy the files in this folder to srcfolder/cmake + 3. Cut below and use it to create srcfolder/CMakeLists.txt + 4. Copy main.cpp, TLexer.g4 and TParser.g4 to ./srcfolder/ from [here](https://github.com/antlr/antlr4/tree/master/runtime/Cpp/demo) +2. Make a build folder e.g. ~/buildfolder/ +3. From the buildfolder, run `cmake ~/srcfolder; make` + +```cmake +# minimum required CMAKE version +CMAKE_MINIMUM_REQUIRED(VERSION 3.7 FATAL_ERROR) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +# compiler must be 11 or 14 +set(CMAKE_CXX_STANDARD 11) + +# required if linking to static library +add_definitions(-DANTLR4CPP_STATIC) + +# using /MD flag for antlr4_runtime (for Visual C++ compilers only) +set(ANTLR4_WITH_STATIC_CRT OFF) +# add external build for antlrcpp +include(ExternalAntlr4Cpp) +# add antrl4cpp artifacts to project environment +include_directories(${ANTLR4_INCLUDE_DIRS}) + +# set variable pointing to the antlr tool that supports C++ +# this is not required if the jar file can be found under PATH environment +set(ANTLR_EXECUTABLE /home/user/antlr-4.9.3-complete.jar) +# add macros to generate ANTLR Cpp code from grammar +find_package(ANTLR REQUIRED) + +# Call macro to add lexer and grammar to your build dependencies. +antlr_target(SampleGrammarLexer TLexer.g4 LEXER + PACKAGE antlrcpptest) +antlr_target(SampleGrammarParser TParser.g4 PARSER + PACKAGE antlrcpptest + DEPENDS_ANTLR SampleGrammarLexer + COMPILE_FLAGS -lib ${ANTLR_SampleGrammarLexer_OUTPUT_DIR}) + +# include generated files in project environment +include_directories(${ANTLR_SampleGrammarLexer_OUTPUT_DIR}) +include_directories(${ANTLR_SampleGrammarParser_OUTPUT_DIR}) + +# add generated grammar to demo binary target +add_executable(demo main.cpp + ${ANTLR_SampleGrammarLexer_CXX_OUTPUTS} + ${ANTLR_SampleGrammarParser_CXX_OUTPUTS}) +target_link_libraries(demo antlr4_static) +``` + +## Documentation for FindANTLR + +The module defines the following variables: + +``` +ANTLR_FOUND - true is ANTLR jar executable is found +ANTLR_EXECUTABLE - the path to the ANTLR jar executable +ANTLR_VERSION - the version of ANTLR +``` + +If ANTLR is found, the module will provide the macros: + +``` +ANTLR_TARGET( + [PACKAGE namespace] + [OUTPUT_DIRECTORY dir] + [DEPENDS_ANTLR ] + [COMPILE_FLAGS [args...]] + [DEPENDS [depends...]] + [LEXER] + [PARSER] + [LISTENER] + [VISITOR]) +``` + +which creates a custom command to generate C++ files from ``. Running the macro defines the following variables: + +``` +ANTLR_${name}_INPUT - the ANTLR input used for the macro +ANTLR_${name}_OUTPUTS - the outputs generated by ANTLR +ANTLR_${name}_CXX_OUTPUTS - the C++ outputs generated by ANTLR +ANTLR_${name}_OUTPUT_DIR - the output directory for ANTLR +``` + +The options are: + +* `PACKAGE` - defines a namespace for the generated C++ files +* `OUTPUT_DIRECTORY` - the output directory for the generated files. By default it uses `${CMAKE_CURRENT_BINARY_DIR}` +* `DEPENDS_ANTLR` - the dependent target generated from antlr_target for the current call +* `COMPILE_FLAGS` - additional compile flags for ANTLR tool +* `DEPENDS` - specify the files on which the command depends. It works the same way `DEPENDS` in [`add_custom_command()`](https://cmake.org/cmake/help/v3.11/command/add_custom_command.html) +* `LEXER` - specify that the input file is a lexer grammar +* `PARSER` - specify that the input file is a parser grammar +* `LISTENER` - tell ANTLR tool to generate a parse tree listener +* `VISITOR` - tell ANTLR tool to generate a parse tree visitor + +### Examples + +To generate C++ files from an ANTLR input file T.g4, which defines both lexer and parser grammar one may call: + +```cmake +find_package(ANTLR REQUIRED) +antlr_target(Sample T.g4) +``` + +Note that this command will do nothing unless the outputs of `Sample`, i.e. `ANTLR_Sample_CXX_OUTPUTS` gets used by some target. + +## Documentation for ExternalAntlr4Cpp + +Including ExternalAntlr4Cpp will add `antlr4_static` and `antlr4_shared` as an optional target. It will also define the following variables: + +``` +ANTLR4_INCLUDE_DIRS - the include directory that should be included when compiling C++ source file +ANTLR4_STATIC_LIBRARIES - path to antlr4 static library +ANTLR4_SHARED_LIBRARIES - path to antlr4 shared library +ANTLR4_RUNTIME_LIBRARIES - path to antlr4 shared runtime library (such as DLL, DYLIB and SO file) +ANTLR4_TAG - branch/tag used for building antlr4 library +``` + +`ANTLR4_TAG` is set to master branch by default to keep antlr4 updated. However, it will be required to rebuild after every `clean` is called. Set `ANTLR4_TAG` to a desired commit hash value to avoid rebuilding after every `clean` and keep the build stable, at the cost of not automatically update to latest commit. + +The ANTLR C++ runtime source is downloaded from GitHub by default. However, users may specify `ANTLR4_ZIP_REPOSITORY` to list the zip file from [ANTLR downloads](http://www.antlr.org/download.html) (under *C++ Target*). This variable can list a zip file included in the project directory; this is useful for maintaining a canonical source for each new build. + +Visual C++ compiler users may want to additionally define `ANTLR4_WITH_STATIC_CRT` before including the file. Set `ANTLR4_WITH_STATIC_CRT` to true if ANTLR4 C++ runtime library should be compiled with `/MT` flag, otherwise will be compiled with `/MD` flag. This variable has a default value of `OFF`. Changing `ANTLR4_WITH_STATIC_CRT` after building the library may require reinitialization of CMake or `clean` for the library to get rebuilt. + +You may need to modify your local copy of ExternalAntlr4Cpp.cpp to modify some build settings. For example, to specify the C++ standard to use when building the runtime, add `-DCMAKE_CXX_STANDARD:STRING=17` to `CMAKE_CACHE_ARGS`. + +### Examples + +To build and link ANTLR4 static library to a target one may call: + +```cmake +include(ExternalAntlr4Cpp) +include_directories(${ANTLR4_INCLUDE_DIRS}) +add_executable(output main.cpp) +target_link_libraries(output antlr4_static) +``` + +It may also be a good idea to copy the runtime libraries (DLL, DYLIB or SO file) to the executable for it to run properly after build. i.e. To build and link antlr4 shared library to a target one may call: + +```cmake +include(ExternalAntlr4Cpp) +include_directories(${ANTLR4_INCLUDE_DIRS}) +add_executable(output main.cpp) +target_link_libraries(output antlr4_shared) +add_custom_command(TARGET output + POST_BUILD + COMMAND ${CMAKE_COMMAND} + -E copy ${ANTLR4_RUNTIME_LIBRARIES} . + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +``` diff --git a/contrib/babelfishpg_tsql/antlr/thirdparty/antlr/antlr-4.9.3-complete.jar b/contrib/babelfishpg_tsql/antlr/thirdparty/antlr/antlr-4.9.3-complete.jar new file mode 100644 index 00000000000..749296fe7b9 Binary files /dev/null and b/contrib/babelfishpg_tsql/antlr/thirdparty/antlr/antlr-4.9.3-complete.jar differ diff --git a/contrib/babelfishpg_tsql/babelfishpg_tsql.control.in b/contrib/babelfishpg_tsql/babelfishpg_tsql.control.in new file mode 100644 index 00000000000..2cc456730cd --- /dev/null +++ b/contrib/babelfishpg_tsql/babelfishpg_tsql.control.in @@ -0,0 +1,7 @@ +# babelfishpg_tsql extension +comment = 'Transact SQL compatibility' +default_version = '@EXTVERSION@' +module_pathname = '@MODULEPATH@' +relocatable = true +superuser = true +requires = 'uuid-ossp, babelfishpg_common' diff --git a/contrib/babelfishpg_tsql/runtime/basic.sql b/contrib/babelfishpg_tsql/runtime/basic.sql new file mode 100644 index 00000000000..37cf9748a9a --- /dev/null +++ b/contrib/babelfishpg_tsql/runtime/basic.sql @@ -0,0 +1,16 @@ +CREATE EXTENSION "babelfishpg_tsql"; + +CREATE SCHEMA IF NOT EXISTS dbo; +ALTER SYSTEM SET search_path = dbo, "$user", public; +SELECT pg_reload_conf(); + +CREATE OR REPLACE FUNCTION dbo.stuff(src text, start int, len int, replacement text) + RETURNS text AS + $$ SELECT overlay($1 PLACING $4 FROM $2 FOR $3) $$ + LANGUAGE 'sql'; + + +CREATE FUNCTION dbo.len(arg text) + RETURNS integer AS + $$ SELECT pg_catalog.length($1); $$ + LANGUAGE 'sql'; diff --git a/contrib/babelfishpg_tsql/runtime/functions.c b/contrib/babelfishpg_tsql/runtime/functions.c new file mode 100644 index 00000000000..734994ffc20 --- /dev/null +++ b/contrib/babelfishpg_tsql/runtime/functions.c @@ -0,0 +1,524 @@ +#include "postgres.h" +#include "port.h" + +#include "access/detoast.h" +#include "access/htup_details.h" +#include "access/table.h" +#include "access/xact.h" +#include "catalog/namespace.h" +#include "catalog/pg_database.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_type.h" +#include "commands/dbcommands.h" +#include "miscadmin.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/elog.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/varlena.h" +#include "utils/queryenvironment.h" +#include + +#include "../src/babelfish_version.h" +#include "../src/datatypes.h" +#include "../src/pltsql.h" +#include "../src/pltsql_instr.h" +#include "../src/multidb.h" +#include "../src/session.h" +#include "../src/catalog.h" + +PG_FUNCTION_INFO_V1(trancount); +PG_FUNCTION_INFO_V1(version); +PG_FUNCTION_INFO_V1(error); +PG_FUNCTION_INFO_V1(pgerror); +PG_FUNCTION_INFO_V1(datalength); +PG_FUNCTION_INFO_V1(int_floor); +PG_FUNCTION_INFO_V1(int_ceiling); +PG_FUNCTION_INFO_V1(bit_floor); +PG_FUNCTION_INFO_V1(bit_ceiling); +PG_FUNCTION_INFO_V1(servername); +PG_FUNCTION_INFO_V1(xact_state); +PG_FUNCTION_INFO_V1(get_enr_list); +PG_FUNCTION_INFO_V1(tsql_random); +PG_FUNCTION_INFO_V1(is_member); +PG_FUNCTION_INFO_V1(schema_id); +PG_FUNCTION_INFO_V1(schema_name); +PG_FUNCTION_INFO_V1(datefirst); +PG_FUNCTION_INFO_V1(options); +PG_FUNCTION_INFO_V1(default_domain); + +/* Not supported -- only syntax support */ +PG_FUNCTION_INFO_V1(procid); + +void* get_servername_internal(void); +extern bool canCommitTransaction(void); + +extern int pltsql_datefirst; +extern bool pltsql_implicit_transactions; +extern bool pltsql_cursor_close_on_commit; +extern bool pltsql_ansi_warnings; +extern bool pltsql_ansi_padding; +extern bool pltsql_ansi_nulls; +extern bool pltsql_arithabort; +extern bool pltsql_arithignore; +extern bool pltsql_quoted_identifier; +extern bool pltsql_nocount; +extern bool pltsql_ansi_null_dflt_on; +extern bool pltsql_ansi_null_dflt_off; +extern bool pltsql_concat_null_yields_null; +extern bool pltsql_numeric_roundabort; +extern bool pltsql_xact_abort; + +char *bbf_servername = "BABELFISH"; + +Datum +trancount(PG_FUNCTION_ARGS) +{ + PG_RETURN_UINT32(NestedTranCount); +} + +Datum +procid(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("syntax not supported with babel yet"))); + PG_RETURN_UINT32(1); +} + +/* + * This function will return following version string + * Babelfish for PostgreSQL with SQL Server Compatibility - 12.0.2000.8 + * + * Copyright (c) Amazon Web Services + * PostgreSQL xx.xx on + */ +Datum +version(PG_FUNCTION_ARGS) +{ + StringInfoData temp; + void *info; + + initStringInfo(&temp); + + if (pg_strcasecmp(pltsql_version, "default") == 0) + { + char *pg_version = pstrdup(PG_VERSION_STR); + char *temp_str = pg_version; + + temp_str = strstr(temp_str, ", compiled by"); + *temp_str = '\0'; + + appendStringInfo(&temp, + "Babelfish for PostgreSQL with SQL Server Compatibility - %s" + "\n%s %s\nCopyright (c) Amazon Web Services\n%s", + BABEL_COMPATIBILITY_VERSION, + __DATE__, __TIME__, pg_version); + } + else + appendStringInfoString(&temp, pltsql_version); + + /* + * TODO: Return Build number with version string as well. + */ + + info = tsql_varchar_input(temp.data, temp.len, -1); + pfree(temp.data); + PG_RETURN_VARCHAR_P(info); +} + +void* get_servername_internal() +{ + StringInfoData temp; + void* info; + + initStringInfo(&temp); + appendStringInfoString(&temp, bbf_servername); + + info = tsql_varchar_input(temp.data, temp.len, -1); + pfree(temp.data); + return info; +} + +/* + * This function will return the servername. + */ +Datum +servername(PG_FUNCTION_ARGS) +{ + PG_RETURN_VARCHAR_P(get_servername_internal()); +} + +Datum +error(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT32(latest_error_code); +} + +Datum +pgerror(PG_FUNCTION_ARGS) +{ + char *error_sqlstate = unpack_sql_state(latest_pg_error_code); + PG_RETURN_VARCHAR_P(tsql_varchar_input((error_sqlstate), strlen(error_sqlstate), -1)); +} + + +/* returns data length of one Datum + * this function is very similar to pg_column_size, but returns untoasted data without header sizes for bytea objects +*/ +Datum +datalength(PG_FUNCTION_ARGS) +{ + Datum value = PG_GETARG_DATUM(0); + int32 result; + int typlen; + + /* On first call, get the input type's typlen, and save at *fn_extra */ + if (fcinfo->flinfo->fn_extra == NULL) + { + /* Lookup the datatype of the supplied argument */ + Oid argtypeid = get_fn_expr_argtype(fcinfo->flinfo, 0); + + typlen = get_typlen(argtypeid); + if (typlen == 0) /* should not happen */ + elog(ERROR, "cache lookup failed for type %u", argtypeid); + + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(int)); + *((int *) fcinfo->flinfo->fn_extra) = typlen; + } + else + typlen = *((int *) fcinfo->flinfo->fn_extra); + + if (typlen == -1) + { + /* varlena type, untoasted and without header*/ + result = toast_raw_datum_size(value) - VARHDRSZ; + } + else if (typlen == -2) + { + /* cstring */ + result = strlen(DatumGetCString(value)) + 1; + } + else + { + /* ordinary fixed-width type */ + result = typlen; + } + + PG_RETURN_INT32(result); +} + +/* +* The int_floor() and int_ceiling() functions are made to just return the +* original argument because floor(int) and ceiling(int) are always equal to int +* itself. This can only be done for int types and we are sure that these +* functions only have int arguments because these functions are ONLY invoked +* from wrapper functions that accept bigint, int, smallint and tinyint arguments. +*/ +Datum +int_floor(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + /* Floor of an integer is the integer itself */ + PG_RETURN_INT64(arg1); +} + +Datum +int_ceiling(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + /* Ceiling of an integer is the integer itself */ + PG_RETURN_INT64(arg1); +} + +/* +* Floor/ceiling of bit type returns FLOATNTYPE in tsql. By default, we +* return numeric for floor/ceiling of bit. This function is to return a double +* precision output for a bit input. +*/ +Datum +bit_floor(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + /* Floor of a bit is the bit itself */ + PG_RETURN_FLOAT8((float8) arg1); +} + +Datum +bit_ceiling(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + /* Ceiling of a bit is the bit itself */ + PG_RETURN_FLOAT8((float8) arg1); +} + +Datum xact_state(PG_FUNCTION_ARGS) +{ + if (NestedTranCount == 0) + { + PG_RETURN_INT16(0); + } + else if (canCommitTransaction()) + { + PG_RETURN_INT16(1); + } + else + { + PG_RETURN_INT16(-1); + } +} + +Datum +get_enr_list(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + List *enr_list = get_namedRelList(); + ListCell *lc; + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* need to build tuplestore in query context */ + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + /* build tupdesc for result tuples. */ + tupdesc = CreateTemplateTupleDesc(2); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "reloid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "relname", + TEXTOID, -1, 0); + + tupstore = + tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, + false, 1024); + /* generate junk in short-term context */ + MemoryContextSwitchTo(oldcontext); + + /* scan all the variables in top estate */ + foreach(lc, enr_list) + { + Datum values[2]; + bool nulls[2]; + + MemSet(nulls, 0, sizeof(nulls)); + + values[0] = ((EphemeralNamedRelationMetadata)lfirst(lc))->reliddesc; + values[1] = CStringGetTextDatum(((EphemeralNamedRelationMetadata)lfirst(lc))->name); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + PG_RETURN_NULL(); +} + +Datum +tsql_random(PG_FUNCTION_ARGS) +{ + LOCAL_FCINFO(fcinfo1, 0); + int seed = PG_GETARG_INT32(0); + Datum result; + + /* set the seed first */ + DirectFunctionCall1(setseed, Float8GetDatum((double) seed / 2147483649)); + + /* call PG's random function */ + InitFunctionCallInfoData(*fcinfo1, NULL, 0, InvalidOid, NULL, NULL); + result = drandom(fcinfo1); + + return result; +} + +Datum +is_member(PG_FUNCTION_ARGS) +{ + const char *role = text_to_cstring(PG_GETARG_TEXT_P(0)); + Oid role_oid = get_role_oid(role, true); + + if (!OidIsValid(role_oid)) + { + PG_RETURN_NULL(); + } + + if (is_member_of_role(GetUserId(), role_oid)) + { + PG_RETURN_INT32(1); + } + else + { + PG_RETURN_INT32(0); + } +} + +Datum +schema_name(PG_FUNCTION_ARGS) +{ + Oid oid = PG_GETARG_OID(0); + HeapTuple tup; + Form_pg_namespace nspform; + NameData name; + const char *logical_name; + + VarChar *result; + + if (!OidIsValid(oid)) + { + PG_RETURN_NULL(); + } + + tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(oid)); + + if (!HeapTupleIsValid(tup)) + { + PG_RETURN_NULL(); + } + + nspform = (Form_pg_namespace) GETSTRUCT(tup); + name = nspform->nspname; + + logical_name = get_logical_schema_name(name.data, true); + if (logical_name) + result = tsql_varchar_input(logical_name, strlen(logical_name), -1); + else + result = tsql_varchar_input(name.data, strlen(name.data), -1); + + ReleaseSysCache(tup); + PG_RETURN_VARCHAR_P(result); +} + +Datum +schema_id(PG_FUNCTION_ARGS) +{ + const char *name = text_to_cstring(PG_GETARG_TEXT_P(0)); + int id; + HeapTuple tup; + Oid nspOid; + Form_pg_namespace nspform; + const char *physical_name = get_physical_schema_name(get_cur_db_name(), name); + + tup = SearchSysCache1(NAMESPACENAME, CStringGetDatum(physical_name)); + + if (!HeapTupleIsValid(tup)) + { + PG_RETURN_NULL(); + } + + nspform = (Form_pg_namespace) GETSTRUCT(tup); + nspOid = nspform->oid; + id = (int) nspOid; + + ReleaseSysCache(tup); + PG_RETURN_INT32(id); +} + +Datum +datefirst(PG_FUNCTION_ARGS) +{ + PG_RETURN_UINT32(pltsql_datefirst); +} + +/* @@OPTIONS returns a bitmap of the current boolean SET options */ +Datum +options(PG_FUNCTION_ARGS) +{ + int options = 0; + + /* 1st bit is for DISABLE_DEF_CNST_CHK, which is an obsolete setting and should always be 0 */ + + /* 2nd bit: IMPLICIT_TRANSACTIONS */ + if (pltsql_implicit_transactions) + options += 2; + + /* 3rd bit: CURSOR_CLOSE_ON_COMMIT */ + if (pltsql_cursor_close_on_commit) + options += 4; + + /* 4th bit: ANSI_WARNINGS */ + if (pltsql_ansi_warnings) + options += 8; + + /* 5th bit: ANSI_PADDING, this setting is WIP. We only support the default ON setting atm */ + if (pltsql_ansi_padding) + options += 16; + + /* 6th bit: ANSI_NULLS */ + if (pltsql_ansi_nulls) + options += 32; + + /* 7th bit: ARITHABORT */ + if (pltsql_arithabort) + options += 64; + + /* 8th bit: ARITHIGNORE */ + if (pltsql_arithignore) + options += 128; + + /* 9th bit: QUOTED_IDENTIFIER */ + if (pltsql_quoted_identifier) + options += 256; + + /* 10th bit: NOCOUNT */ + if (pltsql_nocount) + options += 512; + + /* 11th bit: ANSI_NULL_DFLT_ON */ + if (pltsql_ansi_null_dflt_on) + options += 1024; + + /* 12th bit: ANSI_NULL_DFLT_OFF */ + if (pltsql_ansi_null_dflt_off) + options += 2048; + + /* 13th bit: CONCAT_NULL_YIELDS_NULL */ + if (pltsql_concat_null_yields_null) + options += 4096; + + /* 14th bit: NUMERIC_ROUNDABORT */ + if (pltsql_numeric_roundabort) + options += 8192; + + /* 15th bit: XACT_ABORT */ + if (pltsql_xact_abort) + options += 16384; + + PG_RETURN_UINT32(options); +} + +/* This function will return the default AD domain name */ +Datum +default_domain(PG_FUNCTION_ARGS) +{ + char* login_domainname = NULL; + + if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->get_login_domainname) + login_domainname = (*pltsql_protocol_plugin_ptr)->get_login_domainname(); + + if (login_domainname) + PG_RETURN_VARCHAR_P(tsql_varchar_input(login_domainname, strlen(login_domainname), -1)); + else + PG_RETURN_NULL(); +} diff --git a/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.in b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.in new file mode 100644 index 00000000000..8ae289bf27e --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.in @@ -0,0 +1,28 @@ +/* + * All objects created by the included files will be created in sys + */ + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +#include "datatype.sql" +#include "datatype_string_operators.sql" +#include "sys.sql" +#include "sys_languages.sql" +#include "sys_function_helpers.sql" +#include "sys_functions.sql" +#include "sys_cast.sql" +#include "coerce.sql" +#include "sys_views.sql" +#include "collation.sql" +#include "sys_procedures.sql" +#include "ownership.sql" +#include "import_export_compatibility.sql" +#include "babelfishpg_tsql.sql" + +/* + * Remove schema sys from search_path otherwise it causes BABEL-257 for some reason + * Notice schema sys will be automatically added to implicitly-searched namespaces by + * recomputeNamespacePath() in tsql dialect + */ +SELECT set_config('search_path', trim(leading 'sys, ' from current_setting('search_path')), false); +RESET client_min_messages; diff --git a/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql new file mode 100644 index 00000000000..13f06a0fc9e --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql @@ -0,0 +1,875 @@ +CREATE FUNCTION pltsql_call_handler () + RETURNS language_handler AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE FUNCTION pltsql_validator (oid) + RETURNS void AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE FUNCTION pltsql_inline_handler(internal) + RETURNS void AS 'babelfishpg_tsql' LANGUAGE C; + +-- language +CREATE TRUSTED LANGUAGE pltsql + HANDLER pltsql_call_handler + INLINE pltsql_inline_handler + VALIDATOR pltsql_validator; +GRANT USAGE ON LANGUAGE pltsql TO public; + +COMMENT ON LANGUAGE pltsql IS 'PL/TSQL procedural language'; + +CREATE FUNCTION serverproperty (TEXT) + RETURNS sys.SQL_VARIANT AS 'babelfishpg_tsql', 'serverproperty' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION databasepropertyex (TEXT, TEXT) + RETURNS sys.SQL_VARIANT AS 'babelfishpg_tsql', 'databasepropertyex' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION connectionproperty (TEXT) + RETURNS sys.SQL_VARIANT AS 'babelfishpg_tsql', 'connectionproperty' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION collationproperty (TEXT, TEXT) + RETURNS sys.SQL_VARIANT AS 'babelfishpg_tsql', 'collationproperty' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sessionproperty (TEXT) + RETURNS sys.SQL_VARIANT AS 'babelfishpg_tsql', 'sessionproperty' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- The procedures below requires return code as a RETURN statement which is +-- only possible in pltsql. Therefore, we create them here and call into the +-- corresponding internal functions. +CREATE OR REPLACE PROCEDURE sys.sp_getapplock(IN "@resource" varchar(255), + IN "@lockmode" varchar(32), + IN "@lockowner" varchar(32) DEFAULT 'TRANSACTION', + IN "@locktimeout" INTEGER DEFAULT -99, + IN "@dbprincipal" varchar(32) DEFAULT 'dbo') +LANGUAGE 'pltsql' +AS $$ +begin + declare @ret int; + select @ret = sp_getapplock_function(@resource, @lockmode, @lockowner, @locktimeout, @dbprincipal); + return @ret; +end; +$$; + +CREATE OR REPLACE PROCEDURE sys.sp_releaseapplock(IN "@resource" varchar(255), + IN "@lockowner" varchar(32) DEFAULT 'TRANSACTION', + IN "@dbprincipal" varchar(32) DEFAULT 'dbo') +LANGUAGE 'pltsql' +AS $$ +begin + declare @ret int; + select @ret = sp_releaseapplock_function(@resource, @lockowner, @dbprincipal); + return @ret; +end; +$$; + +-- sys.sp_oledb_ro_usrname is needed for TDS v7.2 +-- In tsql, sp_oledb_ro_usrname stored procedure returns the database read only status. +-- Return values: +-- 1. RO status (VARCHAR(1)) - "N" or "Y", "N" for not read only, "Y" for read only +-- 2. user_name (sysname or NVARCHAR(128)) - The current database user +CREATE OR REPLACE PROCEDURE sys.sp_oledb_ro_usrname() +LANGUAGE 'pltsql' +AS $$ +BEGIN + SELECT CAST((SELECT CASE WHEN pg_is_in_recovery() = 'f' THEN 'N' ELSE 'Y' END) AS VARCHAR(1)) RO, CAST(current_user as NVARCHAR(128)); +END ; +$$; + +CREATE OR REPLACE PROCEDURE sys.sp_helpdb() +LANGUAGE 'pltsql' +AS $$ +BEGIN + SELECT + CAST(name AS sys.nvarchar(128)), + CAST(db_size AS sys.nvarchar(13)), + CAST(owner AS sys.nvarchar(128)), + CAST(dbid AS sys.int), + CAST(created AS sys.nvarchar(11)), + CAST(status AS sys.nvarchar(600)), + CAST(compatibility_level AS sys.tinyint) + FROM sys.babelfish_helpdb(); + + RETURN 0; +END; +$$; + +CREATE OR REPLACE PROCEDURE sys.sp_helpdb(IN "@dbname" VARCHAR(32)) +LANGUAGE 'pltsql' +AS $$ +BEGIN + SELECT + CAST(name AS sys.nvarchar(128)), + CAST(db_size AS sys.nvarchar(13)), + CAST(owner AS sys.nvarchar(128)), + CAST(dbid AS sys.int), + CAST(created AS sys.nvarchar(11)), + CAST(status AS sys.nvarchar(600)), + CAST(compatibility_level AS sys.tinyint) + FROM sys.babelfish_helpdb("@dbname"); + + SELECT + CAST(NULL AS sys.nchar(128)) AS name, + CAST(NULL AS smallint) AS fileid, + CAST(NULL AS sys.nchar(260)) AS filename, + CAST(NULL AS sys.nvarchar(128)) AS filegroup, + CAST(NULL AS sys.nvarchar(18)) AS size, + CAST(NULL AS sys.nvarchar(18)) AS maxsize, + CAST(NULL AS sys.nvarchar(18)) AS growth, + CAST(NULL AS sys.varchar(9)) AS usage; + + RETURN 0; +END; +$$; + +-- BABEL-1643 +CREATE TABLE sys.spt_datatype_info_table +(TYPE_NAME VARCHAR(20), DATA_TYPE INT, PRECISION BIGINT, +LITERAL_PREFIX VARCHAR(20), LITERAL_SUFFIX VARCHAR(20), +CREATE_PARAMS CHAR(20), NULLABLE INT, CASE_SENSITIVE INT, +SEARCHABLE INT, UNSIGNED_ATTRIBUTE INT, MONEY INT, +AUTO_INCREMENT INT, LOCAL_TYPE_NAME VARCHAR(20), +MINIMUM_SCALE INT, MAXIMUM_SCALE INT, SQL_DATA_TYPE INT, +SQL_DATETIME_SUB INT, NUM_PREC_RADIX INT, INTERVAL_PRECISION INT, +USERTYPE INT, LENGTH INT, SS_DATA_TYPE SYS.TINYINT, +-- below column is added in order to join PG's information_schema.columns for sys.sp_columns_100_view +PG_TYPE_NAME VARCHAR(20) +); +GRANT SELECT ON sys.spt_datatype_info_table TO PUBLIC; + +INSERT INTO sys.spt_datatype_info_table VALUES (N'datetimeoffset', -155, 34, N'''', N'''', N'scale ', 1, 0, 3, NULL, 0, NULL, N'datetimeoffset', 0, 7, -155, 0, NULL, NULL, 0, 68, 0, 'datetimeoffset'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'time', -154, 16, N'''', N'''', N'scale ', 1, 0, 3, NULL, 0, NULL, N'time', 0, 7, -154, 0, NULL, NULL, 0, 32, 0, 'time'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'xml', -152, 0, N'N''', N'''', NULL, 1, 1, 0, NULL, 0, NULL, N'xml', NULL, NULL, -152, NULL, NULL, NULL, 0, 2147483646, 0, N'xml'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'sql_variant', -150, 8000, NULL, NULL, NULL, 1, 0, 2, NULL, 0, NULL, N'sql_variant', 0, 0, -150, NULL, 10, NULL, 0, 8000, 39, 'sql_variant'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'uniqueidentifier', -11, 36, N'''', N'''', NULL, 1, 0, 2, NULL, 0, NULL, N'uniqueidentifier', NULL, NULL, -11, NULL, NULL, NULL, 0, 16, 37, 'uniqueidentifier'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'ntext', -10, 1073741823, N'N''', N'''', NULL, 1, 1, 1, NULL, 0, NULL, N'ntext', NULL, NULL, -10, NULL, NULL, NULL, 0, 2147483646, 35, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'nvarchar', -9, 4000, N'N''', N'''', N'max length ', 1, 1, 3, NULL, 0, NULL, N'nvarchar', NULL, NULL, -9, NULL, NULL, NULL, 0, 2, 39, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'sysname', -9, 128, N'N''', N'''', NULL, 0, 1, 3, NULL, 0, NULL, N'sysname', NULL, NULL, -9, NULL, NULL, NULL, 18, 256, 39, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'nchar', -8, 4000, N'N''', N'''', N'length ', 1, 1, 3, NULL, 0, NULL, N'nchar', NULL, NULL, -8, NULL, NULL, NULL, 0, 2, 39, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'bit', -7, 1, NULL, NULL, NULL, 1, 0, 2, NULL, 0, NULL, N'bit', 0, 0, -7, NULL, NULL, NULL, 16, 1, 50, 'bit'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'tinyint', -6, 3, NULL, NULL, NULL, 1, 0, 2, 1, 0, 0, N'tinyint', 0, 0, -6, NULL, 10, NULL, 5, 1, 38, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'tinyint identity', -6, 3, NULL, NULL, NULL, 0, 0, 2, 1, 0, 1, N'tinyint identity', 0, 0, -6, NULL, 10, NULL, 5, 1, 38, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'bigint', -5, 19, NULL, NULL, NULL, 1, 0, 2, 0, 0, 0, N'bigint', 0, 0, -5, NULL, 10, NULL, 0, 8, 108, 'int8'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'bigint identity', -5, 19, NULL, NULL, NULL, 0, 0, 2, 0, 0, 1, N'bigint identity', 0, 0, -5, NULL, 10, NULL, 0, 8, 108, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'image', -4, 2147483647, N'0x', NULL, NULL, 1, 0, 0, NULL, 0, NULL, N'image', NULL, NULL, -4, NULL, NULL, NULL, 20, 2147483647, 34, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'varbinary', -3, 8000, N'0x', NULL, N'max length ', 1, 0, 2, NULL, 0, NULL, N'varbinary', NULL, NULL, -3, NULL, NULL, NULL, 4, 1, 37, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'binary', -2, 8000, N'0x', NULL, N'length ', 1, 0, 2, NULL, 0, NULL, N'binary', NULL, NULL, -2, NULL, NULL, NULL, 3, 1, 37, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'timestamp', -2, 8, N'0x', NULL, NULL, 0, 0, 2, NULL, 0, NULL, N'timestamp', NULL, NULL, -2, NULL, NULL, NULL, 80, 8, 45, 'timestamp'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'text', -1, 2147483647, N'''', N'''', NULL, 1, 1, 1, NULL, 0, NULL, N'text', NULL, NULL, -1, NULL, NULL, NULL, 19, 2147483647, 35, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'char', 1, 8000, N'''', N'''', N'length ', 1, 1, 3, NULL, 0, NULL, N'char', NULL, NULL, 1, NULL, NULL, NULL, 1, 1, 39, N'bpchar'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'numeric', 2, 38, NULL, NULL, N'precision,scale ', 1, 0, 2, 0, 0, 0, N'numeric', 0, 38, 2, NULL, 10, NULL, 10, 20, 108, 'numeric'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'numeric() identity', 2, 38, NULL, NULL, N'precision ', 0, 0, 2, 0, 0, 1, N'numeric() identity', 0, 0, 2, NULL, 10, NULL, 10, 20, 108, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'decimal', 3, 38, NULL, NULL, N'precision,scale ', 1, 0, 2, 0, 0, 0, N'decimal', 0, 38, 3, NULL, 10, NULL, 24, 20, 106, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'money', 3, 19, N'$', NULL, NULL, 1, 0, 2, 0, 1, 0, N'money', 4, 4, 3, NULL, 10, NULL, 11, 21, 110, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'smallmoney', 3, 10, N'$', NULL, NULL, 1, 0, 2, 0, 1, 0, N'smallmoney', 4, 4, 3, NULL, 10, NULL, 21, 12, 110, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'decimal() identity', 3, 38, NULL, NULL, N'precision ', 0, 0, 2, 0, 0, 1, N'decimal() identity', 0, 0, 3, NULL, 10, NULL, 24, 20, 106, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'int', 4, 10, NULL, NULL, NULL, 1, 0, 2, 0, 0, 0, N'int', 0, 0, 4, NULL, 10, NULL, 7, 4, 38, N'int4'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'int identity', 4, 10, NULL, NULL, NULL, 0, 0, 2, 0, 0, 1, N'int identity', 0, 0, 4, NULL, 10, NULL, 7, 4, 38, N''); +INSERT INTO sys.spt_datatype_info_table VALUES (N'smallint', 5, 5, NULL, NULL, NULL, 1, 0, 2, 0, 0, 0, N'smallint', 0, 0, 5, NULL, 10, NULL, 6, 2, 38, 'int2'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'smallint identity', 5, 5, NULL, NULL, NULL, 0, 0, 2, 0, 0, 1, N'smallint identity', 0, 0, 5, NULL, 10, NULL, 6, 2, 38, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'float', 6, 53, NULL, NULL, NULL, 1, 0, 2, 0, 0, 0, N'float', NULL, NULL, 6, NULL, 2, NULL, 8, 8, 109, 'float8'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'real', 7, 24, NULL, NULL, NULL, 1, 0, 2, 0, 0, 0, N'real', NULL, NULL, 7, NULL, 2, NULL, 23, 4, 109, 'float4'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'varchar', 12, 8000, N'''', N'''', N'max length ', 1, 1, 3, NULL, 0, NULL, N'varchar', NULL, NULL, 12, NULL, NULL, NULL, 2, 1, 39, NULL); +INSERT INTO sys.spt_datatype_info_table VALUES (N'date', 91, 10, N'''', N'''', NULL, 1, 0, 3, NULL, 0, NULL, N'date', NULL, 0, 9, 1, NULL, NULL, 0, 20, 0, 'date'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'datetime2', 93, 27, N'''', N'''', N'scale ', 1, 0, 3, NULL, 0, NULL, N'datetime2', 0, 7, 9, 3, NULL, NULL, 0, 54, 0, 'datetime2'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'datetime', 93, 23, N'''', N'''', NULL, 1, 0, 3, NULL, 0, NULL, N'datetime', 3, 3, 9, 3, NULL, NULL, 12, 16, 111, 'datetime'); +INSERT INTO sys.spt_datatype_info_table VALUES (N'smalldatetime', 93, 16, N'''', N'''', NULL, 1, 0, 3, NULL, 0, NULL, N'smalldatetime', 0, 0, 9, 3, NULL, NULL, 22, 16, 111, 'smalldatetime'); + +-- ODBCVer ignored for now +CREATE OR REPLACE PROCEDURE sys.sp_datatype_info ( + "@data_type" int = 0, + "@odbcver" smallint = 2) +AS $$ +BEGIN + select TYPE_NAME, DATA_TYPE, PRECISION, LITERAL_PREFIX, LITERAL_SUFFIX, + CREATE_PARAMS, NULLABLE, CASE_SENSITIVE, SEARCHABLE, + UNSIGNED_ATTRIBUTE, MONEY, AUTO_INCREMENT, LOCAL_TYPE_NAME, + MINIMUM_SCALE, MAXIMUM_SCALE, SQL_DATA_TYPE, SQL_DATETIME_SUB, + NUM_PREC_RADIX, INTERVAL_PRECISION, USERTYPE + from sys.spt_datatype_info_table where @data_type = 0 or data_type = @data_type; +END; +$$ +LANGUAGE 'pltsql'; +-- same as sp_datatype_info +CREATE OR REPLACE PROCEDURE sys.sp_datatype_info_100 ( + "@data_type" int = 0, + "@odbcver" smallint = 2) +AS $$ +BEGIN + select TYPE_NAME, DATA_TYPE, PRECISION, LITERAL_PREFIX, LITERAL_SUFFIX, + CREATE_PARAMS, NULLABLE, CASE_SENSITIVE, SEARCHABLE, + UNSIGNED_ATTRIBUTE, MONEY, AUTO_INCREMENT, LOCAL_TYPE_NAME, + MINIMUM_SCALE, MAXIMUM_SCALE, SQL_DATA_TYPE, SQL_DATETIME_SUB, + NUM_PREC_RADIX, INTERVAL_PRECISION, USERTYPE + from sys.spt_datatype_info_table where @data_type = 0 or data_type = @data_type; +END; +$$ +LANGUAGE 'pltsql'; + + +-- BABEL-1784: support for sp_columns/sp_columns_100 +CREATE VIEW sys.sp_columns_100_view AS +SELECT +CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER, +CAST(t3.rolname AS sys.sysname) AS TABLE_OWNER, +CAST(t1.relname AS sys.sysname) AS TABLE_NAME, +CAST(t4.column_name AS sys.sysname) AS COLUMN_NAME, +CAST(t5.data_type AS smallint) AS DATA_TYPE, +CAST(t5.type_name AS sys.sysname) AS TYPE_NAME, +CAST(t4.numeric_precision AS INT) AS PRECISION, +CAST(t5.length AS int) AS LENGTH, +CAST(t4.numeric_scale AS smallint) AS SCALE, +CAST(t4.numeric_precision_radix AS smallint) AS RADIX, +case + when t4.is_nullable = 'YES' then CAST(1 AS smallint) + else CAST(0 AS smallint) +end AS NULLABLE, +CAST(NULL AS varchar(254)) AS remarks, +CAST(t4.column_default AS sys.nvarchar(4000)) AS COLUMN_DEF, +CAST(t5.sql_data_type AS smallint) AS SQL_DATA_TYPE, +CAST(t5.SQL_DATETIME_SUB AS smallint) AS SQL_DATETIME_SUB, +CAST(t4.character_octet_length AS int) AS CHAR_OCTET_LENGTH, +CAST(t4.dtd_identifier AS int) AS ORDINAL_POSITION, +CAST(t4.is_nullable AS varchar(254)) AS IS_NULLABLE, +CAST(t5.ss_data_type AS sys.tinyint) AS SS_DATA_TYPE, +CAST(0 AS smallint) AS SS_IS_SPARSE, +CAST(0 AS smallint) AS SS_IS_COLUMN_SET, +case + when t4.is_generated = 'NEVER' then CAST(0 AS smallint) + else CAST(1 AS smallint) +end AS SS_IS_COMPUTED, +case + when t4.is_identity = 'YES' then CAST(1 AS smallint) + else CAST(0 AS smallint) +end AS SS_IS_IDENTITY, +CAST(NULL AS varchar(254)) SS_UDT_CATALOG_NAME, +CAST(NULL AS varchar(254)) SS_UDT_SCHEMA_NAME, +CAST(NULL AS varchar(254)) SS_UDT_ASSEMBLY_TYPE_NAME, +CAST(NULL AS varchar(254)) SS_XML_SCHEMACOLLECTION_CATALOG_NAME, +CAST(NULL AS varchar(254)) SS_XML_SCHEMACOLLECTION_SCHEMA_NAME, +CAST(NULL AS varchar(254)) SS_XML_SCHEMACOLLECTION_NAME +FROM pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN pg_catalog.pg_roles t3 ON t1.relowner = t3.oid + JOIN information_schema.columns t4 ON t1.relname = t4.table_name, + sys.spt_datatype_info_table AS t5 +WHERE (t4.data_type = t5.pg_type_name + OR ((SELECT coalesce(t4.domain_name, '') != 'tinyint') AND (SELECT coalesce(t4.domain_name, '') != 'nchar') AND t5.pg_type_name = t4.udt_name) + OR (t4.domain_schema = 'sys' AND t5.type_name = t4.domain_name)); +GRANT SELECT on sys.sp_columns_100_view TO PUBLIC; + +-- internal function in order to workaround BABEL-1597 for BABEL-1784 +drop function if exists sys.sp_columns_100_internal( + in_table_name sys.nvarchar(384), + in_table_owner sys.nvarchar(384), + in_table_qualifier sys.nvarchar(384), + in_column_name sys.nvarchar(384), + in_NameScope int, + in_ODBCVer int, + in_fusepattern smallint); +create function sys.sp_columns_100_internal( + in_table_name sys.nvarchar(384), + in_table_owner sys.nvarchar(384) = '', + in_table_qualifier sys.nvarchar(384) = '', + in_column_name sys.nvarchar(384) = '', + in_NameScope int = 0, + in_ODBCVer int = 2, + in_fusepattern smallint = 1) +returns table ( + out_table_qualifier sys.sysname, + out_table_owner sys.sysname, + out_table_name sys.sysname, + out_column_name sys.sysname, + out_data_type smallint, + out_type_name sys.sysname, + out_precision int, + out_length int, + out_scale smallint, + out_radix smallint, + out_nullable smallint, + out_remarks varchar(254), + out_column_def sys.nvarchar(4000), + out_sql_data_type smallint, + out_sql_datetime_sub smallint, + out_char_octet_length int, + out_ordinal_position int, + out_is_nullable varchar(254), + out_ss_is_sparse smallint, + out_ss_is_column_set smallint, + out_ss_is_computed smallint, + out_ss_is_identity smallint, + out_ss_udt_catalog_name varchar(254), + out_ss_udt_schema_name varchar(254), + out_ss_udt_assembly_type_name varchar(254), + out_ss_xml_schemacollection_catalog_name varchar(254), + out_ss_xml_schemacollection_schema_name varchar(254), + out_ss_xml_schemacollection_name varchar(254), + out_ss_data_type sys.tinyint +) +as $$ +begin + IF in_fusepattern = 1 THEN + return query + select table_qualifier, + table_owner, + table_name, + column_name, + data_type, + type_name, + precision, + length, + scale, + radix, + nullable, + remarks, + column_def, + sql_data_type, + sql_datetime_sub, + char_octet_length, + ordinal_position, + is_nullable, + ss_is_sparse, + ss_is_column_set, + ss_is_computed, + ss_is_identity, + ss_udt_catalog_name, + ss_udt_schema_name, + ss_udt_assembly_type_name, + ss_xml_schemacollection_catalog_name, + ss_xml_schemacollection_schema_name, + ss_xml_schemacollection_name, + ss_data_type + from sys.sp_columns_100_view + where table_name like in_table_name + and ((SELECT coalesce(in_table_owner,'')) = '' or table_owner like in_table_owner) + and ((SELECT coalesce(in_table_qualifier,'')) = '' or table_qualifier like in_table_qualifier) + and ((SELECT coalesce(in_column_name,'')) = '' or column_name like in_column_name) + order by table_qualifier, table_owner, table_name; + ELSE + return query + select table_qualifier, precision from sys.sp_columns_100_view + where in_table_name = table_name + and ((SELECT coalesce(in_table_owner,'')) = '' or table_owner = in_table_owner) + and ((SELECT coalesce(in_table_qualifier,'')) = '' or table_qualifier = in_table_qualifier) + and ((SELECT coalesce(in_column_name,'')) = '' or column_name = in_column_name) + order by table_qualifier, table_owner, table_name; + END IF; +end; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE PROCEDURE sys.sp_columns ( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = '', + "@table_qualifier" sys.nvarchar(384) = '', + "@column_name" sys.nvarchar(384) = '', + "@namescope" int = 0, + "@odbcver" int = 2, + "@fusepattern" smallint = 1) +AS $$ +BEGIN + select out_table_qualifier as table_qualifier, + out_table_owner as table_owner, + out_table_name as table_name, + out_column_name as column_name, + out_data_type as data_type, + out_type_name as type_name, + out_precision as precision, + out_length as length, + out_scale as scale, + out_radix as radix, + out_nullable as nullable, + out_remarks as remarks, + out_column_def as column_def, + out_sql_data_type as sql_data_type, + out_sql_datetime_sub as sql_datetime_sub, + out_char_octet_length as char_octet_length, + out_ordinal_position as ordinal_position, + out_is_nullable as is_nullable, + out_ss_data_type as ss_data_type + from sys.sp_columns_100_internal(@table_name, @table_owner,@table_qualifier, @column_name, @NameScope,@ODBCVer, @fusepattern); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_columns TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_columns_100 ( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = '', + "@table_qualifier" sys.nvarchar(384) = '', + "@column_name" sys.nvarchar(384) = '', + "@namescope" int = 0, + "@odbcver" int = 2, + "@fusepattern" smallint = 1) +AS $$ +BEGIN + select out_table_qualifier as table_qualifier, + out_table_owner as table_owner, + out_table_name as table_name, + out_column_name as column_name, + out_data_type as data_type, + out_type_name as type_name, + out_precision as precision, + out_length as length, + out_scale as scale, + out_radix as radix, + out_nullable as nullable, + out_remarks as remarks, + out_column_def as column_def, + out_sql_data_type as sql_data_type, + out_sql_datetime_sub as sql_datetime_sub, + out_char_octet_length as char_octet_length, + out_ordinal_position as ordinal_position, + out_is_nullable as is_nullable, + out_ss_is_sparse as ss_is_sparse, + out_ss_is_column_set as ss_is_column_set, + out_ss_is_computed as ss_is_computed, + out_ss_is_identity as ss_is_identity, + out_ss_udt_catalog_name as ss_udt_catalog_name, + out_ss_udt_schema_name as ss_udt_schema_name, + out_ss_udt_assembly_type_name as ss_udt_assembly_type_name, + out_ss_xml_schemacollection_catalog_name as ss_xml_schemacollection_catalog_name, + out_ss_xml_schemacollection_schema_name as ss_xml_schemacollection_schema_name, + out_ss_xml_schemacollection_name as ss_xml_schemacollection_name, + out_ss_data_type as ss_data_type + from sys.sp_columns_100_internal(@table_name, @table_owner,@table_qualifier, @column_name, @NameScope,@ODBCVer, @fusepattern); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_columns_100 TO PUBLIC; + +-- BABEL-1785: initial support of sp_describe_first_result_set +-- sys.sp_describe_first_result_set_internal: internal function +-- used to workaround BABEL-1597 +create function sys.sp_describe_first_result_set_internal( + tsqlquery varchar(384), + params varchar(384) = NULL, + browseMode sys.tinyint = 0 +) +returns table ( + is_hidden sys.bit, + column_ordinal int, + name sys.sysname, + is_nullable sys.bit, + system_type_id int, + system_type_name sys.nvarchar(256), + max_length smallint, + "precision" sys.tinyint, + scale sys.tinyint, + collation_name sys.sysname, + user_type_id int, + user_type_database sys.sysname, + user_type_schema sys.sysname, + user_type_name sys.sysname, + assembly_qualified_type_name sys.nvarchar(4000), + xml_collection_id int, + xml_collection_database sys.sysname, + xml_collection_schema sys.sysname, + xml_collection_name sys.sysname, + is_xml_document sys.bit, + is_case_sensitive sys.bit, + is_fixed_length_clr_type sys.bit, + source_server sys.sysname, + source_database sys.sysname, + source_schema sys.sysname, + source_table sys.sysname, + source_column sys.sysname, + is_identity_column sys.bit, + is_part_of_unique_key sys.bit, + is_updateable sys.bit, + is_computed_column sys.bit, + is_sparse_column_set sys.bit, + ordinal_in_order_by_list smallint, + order_by_list_length smallint, + order_by_is_descending smallint, + tds_type_id int, + tds_length int, + tds_collation_id int, + ss_data_type sys.tinyint +) +as $$ + declare _args text[]; -- placeholder: parse @params and feed the tsqlquery +begin + IF tsqlquery ILIKE 'select %' THEN + DROP VIEW IF EXISTS sp_describe_first_result_set_view; + EXECUTE 'create temp view sp_describe_first_result_set_view as ' || tsqlquery USING _args; + RETURN query + SELECT + CAST(0 AS sys.bit) AS is_hidden, + CAST(t1.dtd_identifier AS int) AS column_ordinal, + CAST(t1.column_name AS sys.sysname) AS name, + case + when t1.is_nullable = 'Y' then CAST(1 AS sys.bit) + else CAST(0 AS sys.bit) + end as is_nullable, + 0 as system_type_id, + CAST('' as sys.nvarchar(256)) as system_type_name, + CAST(t2.length AS smallint) AS max_length, + CAST(t1.numeric_precision AS sys.tinyint) AS precision, + CAST(t1.numeric_scale AS sys.tinyint) AS scale, + CAST((SELECT coalesce(t1.collation_name, '')) AS sys.sysname) as collation_name, + CAST(NULL as int) as user_type_id, + CAST('' as sys.sysname) as user_type_database, + CAST('' as sys.sysname) as user_type_schema, + CAST('' as sys.sysname) as user_type_name, + CAST('' as sys.nvarchar(4000)) as assembly_qualified_type_name, + CAST(NULL as int) as xml_collection_id, + CAST('' as sys.sysname) as xml_collection_database, + CAST('' as sys.sysname) as xml_collection_schema, + CAST('' as sys.sysname) as xml_collection_name, + case + when t1.data_type = 'xml' then CAST(1 AS sys.bit) + else CAST(0 AS sys.bit) + end as is_xml_document, + case + when t1.udt_name = 'citext' then CAST(0 AS sys.bit) + else CAST(1 AS sys.bit) + end as is_case_sensitive, + CAST(0 as sys.bit) as is_fixed_length_clr_type, + CAST('' as sys.sysname) as source_server, + CAST('' as sys.sysname) as source_database, + CAST('' as sys.sysname) as source_schema, + CAST('' as sys.sysname) as source_table, + CAST('' as sys.sysname) as source_column, + case + when t1.is_identity = 'YES' then CAST(1 AS sys.bit) + else CAST(0 AS sys.bit) + end as is_identity_column, + CAST(NULL as sys.bit) as is_part_of_unique_key,-- pg_constraint + case + when t1.is_updatable = 'YES' then CAST(1 AS sys.bit) + else CAST(0 AS sys.bit) + end as is_updateable, + case + when t1.is_generated = 'NEVER' then CAST(0 AS sys.bit) + else CAST(1 AS sys.bit) + end as is_computed_column, + CAST(0 as sys.bit) as is_sparse_column_set, + CAST(NULL as smallint) ordinal_in_order_by_list, + CAST(NULL as smallint) order_by_list_length, + CAST(NULL as smallint) order_by_is_descending, + -- below are for internal usage + CAST(NULL as int) as tds_type_id, + CAST(NULL as int) as tds_length, + CAST(NULL as int) as tds_collation_id, + CAST(1 AS sys.tinyint) AS tds_collation_sort_id + FROM information_schema.columns t1, sys.spt_datatype_info_table t2 + WHERE table_name = 'sp_describe_first_result_set_view' + AND (t1.data_type = t2.pg_type_name + OR ((SELECT coalesce(t1.domain_name, '') != 'tinyint') + AND (SELECT coalesce(t1.domain_name, '') != 'nchar') + AND t2.pg_type_name = t1.udt_name) + OR (t1.domain_schema = 'sys' AND t2.type_name = t1.domain_name)); + DROP VIEW sp_describe_first_result_set_view; + END IF; +end; +$$ +LANGUAGE plpgsql; +GRANT ALL on FUNCTION sys.sp_describe_first_result_set_internal TO PUBLIC; + +CREATE PROCEDURE sys.sp_describe_first_result_set ( + "@tsql" varchar(384), + "@params" varchar(384) = NULL, + "@browse_information_mode" sys.tinyint = 0) +AS $$ +BEGIN + select * from sys.sp_describe_first_result_set_internal(@tsql, @params, @browse_information_mode); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_describe_first_result_set TO PUBLIC; + +CREATE OR REPLACE VIEW sys.spt_tablecollations_view AS + SELECT + o.object_id AS object_id, + o.schema_id AS schema_id, + c.column_id AS colid, + c.name AS name, + CAST(CollationProperty(c.collation_name,'tdscollation') AS binary(5)) AS tds_collation_28, + CAST(CollationProperty(c.collation_name,'tdscollation') AS binary(5)) AS tds_collation_90, + CAST(CollationProperty(c.collation_name,'tdscollation') AS binary(5)) AS tds_collation_100, + CAST(c.collation_name AS nvarchar(128)) AS collation_28, + CAST(c.collation_name AS nvarchar(128)) AS collation_90, + CAST(c.collation_name AS nvarchar(128)) AS collation_100 + FROM + sys.all_columns c INNER JOIN + sys.all_objects o ON (c.object_id = o.object_id) + WHERE + c.is_sparse = 0; +GRANT SELECT ON sys.spt_tablecollations_view TO PUBLIC; + +-- We are limited by what postgres procedures can return here, but IEW may not +-- need it for initial compatibility +CREATE OR REPLACE PROCEDURE sys.sp_tablecollations_100 +( + IN "@object" nvarchar(4000) +) +AS $$ +BEGIN + select + s_tcv.colid AS colid, + s_tcv.name AS name, + s_tcv.tds_collation_100 AS tds_collation_100, + s_tcv.collation_100 AS collation + from + sys.spt_tablecollations_view s_tcv + where + s_tcv.object_id = sys.object_id("@object") AND + s_tcv.name NOT IN ('cmin', 'cmax', 'xmin', 'xmax', 'ctid', 'tableoid') + order by colid; +END; +$$ +LANGUAGE 'pltsql'; + +CREATE VIEW sys.spt_columns_view_managed AS +SELECT + o.object_id AS OBJECT_ID, + isc.table_catalog AS TABLE_CATALOG, + isc.table_schema AS TABLE_SCHEMA, + o.name AS TABLE_NAME, + c.name AS COLUMN_NAME, + isc.ordinal_position AS ORDINAL_POSITION, + isc.column_default AS COLUMN_DEFAULT, + isc.is_nullable AS IS_NULLABLE, + isc.data_type AS DATA_TYPE, + isc.character_maximum_length AS CHARACTER_MAXIMUM_LENGTH, + isc.character_octet_length AS CHARACTER_OCTET_LENGTH, + isc.numeric_precision AS NUMERIC_PRECISION, + isc.numeric_precision_radix AS NUMERIC_PRECISION_RADIX, + isc.numeric_scale AS NUMERIC_SCALE, + isc.datetime_precision AS DATETIME_PRECISION, + isc.character_set_catalog AS CHARACTER_SET_CATALOG, + isc.character_set_schema AS CHARACTER_SET_SCHEMA, + isc.character_set_name AS CHARACTER_SET_NAME, + isc.collation_catalog AS COLLATION_CATALOG, + isc.collation_schema AS COLLATION_SCHEMA, + c.collation_name AS COLLATION_NAME, + isc.domain_catalog AS DOMAIN_CATALOG, + isc.domain_schema AS DOMAIN_SCHEMA, + isc.domain_name AS DOMAIN_NAME, + c.is_sparse AS IS_SPARSE, + c.is_column_set AS IS_COLUMN_SET, + c.is_filestream AS IS_FILESTREAM +FROM + sys.objects o JOIN sys.columns c ON + ( + c.object_id = o.object_id and + o.type in ('U', 'V') -- limit columns to tables and views + ) + LEFT JOIN information_schema.columns isc ON + ( + sys.schema_name(o.schema_id) = isc.table_schema and + o.name = isc.table_name and + c.name = isc.column_name + ) + WHERE CAST(column_name AS sys.nvarchar(128)) NOT IN ('cmin', 'cmax', 'xmin', 'xmax', 'ctid', 'tableoid'); + +CREATE FUNCTION sys.sp_columns_managed_internal( + in_catalog sys.nvarchar(128), + in_owner sys.nvarchar(128), + in_table sys.nvarchar(128), + in_column sys.nvarchar(128), + in_schematype int) +RETURNS TABLE ( + out_table_catalog sys.nvarchar(128), + out_table_schema sys.nvarchar(128), + out_table_name sys.nvarchar(128), + out_column_name sys.nvarchar(128), + out_ordinal_position int, + out_column_default sys.nvarchar(4000), + out_is_nullable sys.nvarchar(3), + out_data_type sys.nvarchar, + out_character_maximum_length int, + out_character_octet_length int, + out_numeric_precision int, + out_numeric_precision_radix int, + out_numeric_scale int, + out_datetime_precision int, + out_character_set_catalog sys.nvarchar(128), + out_character_set_schema sys.nvarchar(128), + out_character_set_name sys.nvarchar(128), + out_collation_catalog sys.nvarchar(128), + out_is_sparse int, + out_is_column_set int, + out_is_filestream int + ) +AS +$$ +BEGIN + RETURN QUERY + SELECT CAST(table_catalog AS sys.nvarchar(128)), + CAST(table_schema AS sys.nvarchar(128)), + CAST(table_name AS sys.nvarchar(128)), + CAST(column_name AS sys.nvarchar(128)), + CAST(ordinal_position AS int), + CAST(column_default AS sys.nvarchar(4000)), + CAST(is_nullable AS sys.nvarchar(3)), + CAST(data_type AS sys.nvarchar), + CAST(character_maximum_length AS int), + CAST(character_octet_length AS int), + CAST(numeric_precision AS int), + CAST(numeric_precision_radix AS int), + CAST(numeric_scale AS int), + CAST(datetime_precision AS int), + CAST(character_set_catalog AS sys.nvarchar(128)), + CAST(character_set_schema AS sys.nvarchar(128)), + CAST(character_set_name AS sys.nvarchar(128)), + CAST(collation_catalog AS sys.nvarchar(128)), + CAST(is_sparse AS int), + CAST(is_column_set AS int), + CAST(is_filestream AS int) + FROM sys.spt_columns_view_managed s_cv + WHERE + (in_catalog IS NULL OR s_cv.TABLE_CATALOG LIKE in_catalog) AND + (in_owner IS NULL OR s_cv.TABLE_SCHEMA LIKE in_owner) AND + (in_table IS NULL OR s_cv.TABLE_NAME LIKE in_table) AND + (in_column IS NULL OR s_cv.COLUMN_NAME LIKE in_column) AND + (in_schematype = 0 AND (s_cv.IS_SPARSE = 0) OR in_schematype = 1 OR in_schematype = 2 AND (s_cv.IS_SPARSE = 1)); +END; +$$ +language plpgsql; + +CREATE PROCEDURE sys.sp_columns_managed +( + "@Catalog" nvarchar(128) = NULL, + "@Owner" nvarchar(128) = NULL, + "@Table" nvarchar(128) = NULL, + "@Column" nvarchar(128) = NULL, + "@SchemaType" nvarchar(128) = 0) -- 0 = 'select *' behavior (default), 1 = all columns, 2 = columnset columns +AS +$$ +BEGIN + SELECT + out_TABLE_CATALOG AS TABLE_CATALOG, + out_TABLE_SCHEMA AS TABLE_SCHEMA, + out_TABLE_NAME AS TABLE_NAME, + out_COLUMN_NAME AS COLUMN_NAME, + out_ORDINAL_POSITION AS ORDINAL_POSITION, + out_COLUMN_DEFAULT AS COLUMN_DEFAULT, + out_IS_NULLABLE AS IS_NULLABLE, + out_DATA_TYPE AS DATA_TYPE, + out_CHARACTER_MAXIMUM_LENGTH AS CHARACTER_MAXIMUM_LENGTH, + out_CHARACTER_OCTET_LENGTH AS CHARACTER_OCTET_LENGTH, + out_NUMERIC_PRECISION AS NUMERIC_PRECISION, + out_NUMERIC_PRECISION_RADIX AS NUMERIC_PRECISION_RADIX, + out_NUMERIC_SCALE AS NUMERIC_SCALE, + out_DATETIME_PRECISION AS DATETIME_PRECISION, + out_CHARACTER_SET_CATALOG AS CHARACTER_SET_CATALOG, + out_CHARACTER_SET_SCHEMA AS CHARACTER_SET_SCHEMA, + out_CHARACTER_SET_NAME AS CHARACTER_SET_NAME, + out_COLLATION_CATALOG AS COLLATION_CATALOG, + out_IS_SPARSE AS IS_SPARSE, + out_IS_COLUMN_SET AS IS_COLUMN_SET, + out_IS_FILESTREAM AS IS_FILESTREAM + FROM + sys.sp_columns_managed_internal(@Catalog, @Owner, "@Table", "@Column", @SchemaType) s_cv + ORDER BY TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, IS_NULLABLE; +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_columns_managed TO PUBLIC; + +-- BABEL-1797: initial support of sp_describe_undeclared_parameters +-- sys.sp_describe_undeclared_parameters_internal: internal function +-- For the result rows, can we create a template table for it? +create function sys.sp_describe_undeclared_parameters_internal( + tsqlquery varchar(384), + params varchar(384) = NULL +) +returns table ( + parameter_ordinal int, -- NOT NULL + name sys.sysname, -- NOT NULL + suggested_system_type_id int, -- NOT NULL + suggested_system_type_name sys.nvarchar(256), + suggested_max_length smallint, -- NOT NULL + suggested_precision sys.tinyint, -- NOT NULL + suggested_scale sys.tinyint, -- NOT NULL + suggested_user_type_id int, -- NOT NULL + suggested_user_type_database sys.sysname, + suggested_user_type_schema sys.sysname, + suggested_user_type_name sys.sysname, + suggested_assembly_qualified_type_name sys.nvarchar(4000), + suggested_xml_collection_id int, + suggested_xml_collection_database sys.sysname, + suggested_xml_collection_schema sys.sysname, + suggested_xml_collection_name sys.sysname, + suggested_is_xml_document sys.bit, -- NOT NULL + suggested_is_case_sensitive sys.bit, -- NOT NULL + suggested_is_fixed_length_clr_type sys.bit, -- NOT NULL + suggested_is_input sys.bit, -- NOT NULL + suggested_is_output sys.bit, -- NOT NULL + formal_parameter_name sys.sysname, + suggested_tds_type_id int, -- NOT NULL + suggested_tds_length int -- NOT NULL +) +AS 'babelfishpg_tsql', 'sp_describe_undeclared_parameters_internal' +LANGUAGE C; +GRANT ALL on FUNCTION sys.sp_describe_undeclared_parameters_internal TO PUBLIC; + +CREATE PROCEDURE sys.sp_describe_undeclared_parameters ( + "@tsql" varchar(384), + "@params" varchar(384) = NULL) +AS $$ +BEGIN + select * from sys.sp_describe_undeclared_parameters_internal(@tsql, @params); + return 1; +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_describe_undeclared_parameters TO PUBLIC; + +-- BABEL-1782 +CREATE VIEW sys.sp_tables_view AS +SELECT +t2.dbname AS TABLE_QUALIFIER, +t3.rolname AS TABLE_OWNER, +t1.relname AS TABLE_NAME, +case + when t1.relkind = 'v' then 'VIEW' + else 'TABLE' +end AS TABLE_TYPE, +CAST(NULL AS varchar(254)) AS remarks +FROM pg_catalog.pg_class AS t1, sys.pg_namespace_ext AS t2, pg_catalog.pg_roles AS t3 +WHERE t1.relowner = t3.oid AND t1.relnamespace = t2.oid; +GRANT SELECT on sys.sp_tables_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_tables ( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = '', + "@table_qualifier" sys.sysname = '', + "@table_type" sys.nvarchar(100) = '', + "@fusepattern" sys.bit = '1') +AS $$ +BEGIN + DECLARE @_opt_view varchar(16) = '' + DECLARE @_opt_table varchar(16) = '' + IF (select count(*) from STRING_SPLIT(@table_type, ',') where trim(value) = 'VIEW') = 1 + BEGIN + SET @_opt_view = 'VIEW' + END + IF (select count(*) from STRING_SPLIT(@table_type, ',') where trim(value) = 'TABLE') = 1 + BEGIN + SET @_opt_table = 'TABLE' + END + IF @fUsePattern = '1' + BEGIN + select * from sys.sp_tables_view where + (@table_name = '' or table_name like @table_name) + and (@table_owner = '' or table_owner like @table_owner) + and (@table_qualifier = '' or table_qualifier like @table_qualifier) + and (@table_type = '' or table_type = @_opt_table or table_type = @_opt_view); + END + ELSE + BEGIN + select * from sys.sp_tables_view where + (@table_name = '' or table_name = @table_name) + and (@table_owner = '' or table_owner = @table_owner) + and (@table_qualifier = '' or table_qualifier = @table_qualifier) + and (@table_type = '' or table_type = @_opt_table or table_type = @_opt_view); + END +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_tables TO PUBLIC; + +CREATE FUNCTION sys.fn_mapped_system_error_list () +returns table (sql_error_code int) +AS 'babelfishpg_tsql', 'babel_list_mapped_error' +LANGUAGE C IMMUTABLE STRICT; +GRANT ALL on FUNCTION sys.fn_mapped_system_error_list TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/coerce.sql b/contrib/babelfishpg_tsql/sql/coerce.sql new file mode 100644 index 00000000000..430a23fa60d --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/coerce.sql @@ -0,0 +1,11 @@ +-- Manually initialize translation table +CREATE OR REPLACE PROCEDURE babel_coercion_initializer() +LANGUAGE C +AS 'babelfishpg_tsql', 'init_tsql_coerce_hash_tab'; +CALL babel_coercion_initializer(); + +-- Manually initialize translation table +CREATE OR REPLACE PROCEDURE babel_datatype_precedence_initializer() +LANGUAGE C +AS 'babelfishpg_tsql', 'init_tsql_datatype_precedence_hash_tab'; +CALL babel_datatype_precedence_initializer(); diff --git a/contrib/babelfishpg_tsql/sql/collation.sql b/contrib/babelfishpg_tsql/sql/collation.sql new file mode 100644 index 00000000000..ecd2070a565 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/collation.sql @@ -0,0 +1,347 @@ +-- create babelfish collations + +CREATE COLLATION IF NOT EXISTS sys.Arabic_CS_AS (provider = icu, locale = 'ar_SA'); +CREATE COLLATION sys.Arabic_CI_AS (provider = icu, locale = 'ar_SA@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.Arabic_CI_AI (provider = icu, locale = 'ar_SA@colStrength=primary', deterministic = false); + +CREATE COLLATION sys.BBF_Unicode_BIN2 FROM "C"; + +CREATE COLLATION sys.BBF_Unicode_General_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_General_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_General_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_General_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_General_Pref_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1250_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1250_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1250_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1250_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1250_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1251_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1251_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1251_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1251_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1251_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1253_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1253_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1253_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1253_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1253_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1254_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1254_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1254_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1254_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1254_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1255_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1255_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1255_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1255_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1255_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1256_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1256_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1256_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1256_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1256_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1257_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1257_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1257_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1257_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1257_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP1258_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1258_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1258_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP1258_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP1258_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION sys.BBF_Unicode_CP874_CI_AS (provider = icu, locale = '@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP874_CI_AI (provider = icu, locale = '@colStrength=primary', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP874_CS_AI (provider = icu, locale = '@colStrength=primary;colCaseLevel=yes;colCaseFirst=upper', deterministic = false); +CREATE COLLATION sys.BBF_Unicode_CP874_CS_AS (provider = icu, locale = '@colStrength=tertiary', deterministic = true); +CREATE COLLATION sys.BBF_Unicode_Pref_CP874_CS_AS (provider = icu, locale = '@colStrength=tertiary;colCaseFirst=upper', deterministic = true); + +CREATE COLLATION IF NOT EXISTS sys.Chinese_PRC_CS_AS (provider = icu, locale = 'zh_CN'); +CREATE COLLATION sys.Chinese_PRC_CI_AS (provider = icu, locale = 'zh_CN@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.Chinese_PRC_CI_AI (provider = icu, locale = 'zh_CN@colStrength=primary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Cyrillic_General_CS_AS (provider = icu, locale='ru_RU'); +CREATE COLLATION sys.Cyrillic_General_CI_AS (provider = icu, locale='ru_RU@colStrength=secondary', deterministic = false); +CREATE COLLATION sys.Cyrillic_General_CI_AI (provider = icu, locale='ru_RU@colStrength=primary', deterministic = false); + +CREATE COLLATION sys.Estonian_CS_AS (provider = icu, locale='et_EE'); +CREATE COLLATION sys.Estonian_CI_AI (provider = icu, locale='et_EE@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Estonian_CI_AS (provider = icu, locale='et_EE@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Finnish_Swedish_CS_AS (provider = icu, locale = 'sv_SE'); +CREATE COLLATION sys.Finnish_Swedish_CI_AI (provider = icu, locale = 'sv_SE@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Finnish_Swedish_CI_AS (provider = icu, locale = 'sv_SE@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.French_CS_AS (provider = icu, locale = 'fr_FR'); +CREATE COLLATION sys.French_CI_AI (provider = icu, locale = 'fr_FR@colStrength=primary', deterministic = false); +CREATE COLLATION sys.French_CI_AS (provider = icu, locale = 'fr_FR@colStrength=secondary', deterministic = false); + +CREATE COLLATION sys.Greek_CS_AS (provider = icu, locale = 'el_GR'); +CREATE COLLATION sys.Greek_CI_AI (provider = icu, locale = 'el_GR@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Greek_CI_AS (provider = icu, locale = 'el_GR@colStrength=secondary', deterministic = false); + +CREATE COLLATION sys.Hebrew_CS_AS (provider = icu, locale = 'he_IL'); +CREATE COLLATION sys.Hebrew_CI_AI (provider = icu, locale = 'he_IL@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Hebrew_CI_AS (provider = icu, locale = 'he_IL@colStrength=secondary', deterministic = false); + +CREATE COLLATION sys.Korean_Wansung_CS_AS (provider = icu, locale = 'ko_KR'); +CREATE COLLATION sys.Korean_Wansung_CI_AI (provider = icu, locale = 'ko_KR@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Korean_Wansung_CI_AS (provider = icu, locale = 'ko_KR@colStrength=secondary', deterministic = false); + +CREATE COLLATION sys.Modern_Spanish_CS_AS (provider = icu, locale = 'en_ES'); +CREATE COLLATION sys.Modern_Spanish_CI_AI (provider = icu, locale='es_ES@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Modern_Spanish_CI_AS (provider = icu, locale='es_ES@colStrength=secondary', deterministic = false); + +CREATE COLLATION sys.Mongolian_CS_AS (provider = icu, locale = 'mn_MN'); +CREATE COLLATION sys.Mongolian_CI_AI (provider = icu, locale = 'mn_MN@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Mongolian_CI_AS (provider = icu, locale = 'mn_MN@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Polish_CS_AS (provider = icu, locale = 'pl_PL'); +CREATE COLLATION sys.Polish_CI_AI (provider = icu, locale = 'pl_PL@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Polish_CI_AS (provider = icu, locale = 'pl_PL@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Thai_CS_AS (provider = icu, locale = 'th_TH'); +CREATE COLLATION sys.Thai_CI_AI (provider = icu, locale = 'th_TH@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Thai_CI_AS (provider = icu, locale = 'th_TH@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Traditional_Spanish_CS_AS (provider = icu, locale = 'es_TRADITIONAL'); +CREATE COLLATION sys.Traditional_Spanish_CI_AI (provider = icu, locale = 'es_TRADITIONAL@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Traditional_Spanish_CI_AS (provider = icu, locale = 'es_TRADITIONAL@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Turkish_CS_AS (provider = icu, locale = 'tr_TR'); +CREATE COLLATION sys.Turkish_CI_AI (provider = icu, locale = 'tr_TR@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Turkish_CI_AS (provider = icu, locale = 'tr_TR@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Ukrainian_CS_AS (provider = icu, locale = 'uk_UA'); +CREATE COLLATION sys.Ukrainian_CI_AI (provider = icu, locale = 'uk_UA@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Ukrainian_CI_AS (provider = icu, locale = 'uk_UA@colStrength=secondary', deterministic = false); + +CREATE COLLATION IF NOT EXISTS sys.Vietnamese_CS_AS (provider = icu, locale = 'vi_VN'); +CREATE COLLATION sys.Vietnamese_CI_AI (provider = icu, locale = 'vi_VN@colStrength=primary', deterministic = false); +CREATE COLLATION sys.Vietnamese_CI_AS (provider = icu, locale = 'vi_VN@colStrength=secondary', deterministic = false); + +-- collation catalog +create table sys.babelfish_helpcollation( + Name VARCHAR(128) NOT NULL, + Description VARCHAR(1000) NOT NULL +); +GRANT SELECT ON sys.babelfish_helpcollation TO PUBLIC; + +create or replace function sys.fn_helpcollations() +returns table (Name VARCHAR(128), Description VARCHAR(1000)) +AS +$$ +BEGIN + return query select * from sys.babelfish_helpcollation; +END +$$ +LANGUAGE 'plpgsql'; +INSERT INTO sys.babelfish_helpcollation VALUES (N'arabic_cs_as', N'Arabic, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'arabic_ci_ai', N'Arabic, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'arabic_ci_as', N'Arabic, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_bin2', N'Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1250_ci_ai', N'Default locale, code page 1250, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1250_ci_as', N'Default locale, code page 1250, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1250_cs_ai', N'Default locale, code page 1250, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1250_cs_as', N'Default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1250_cs_as', N'Default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1251_ci_ai', N'Default locale, code page 1251, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1251_ci_as', N'Default locale, code page 1251, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1251_cs_ai', N'Default locale, code page 1251, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1251_cs_as', N'Default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1251_cs_as', N'Default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1253_ci_ai', N'Default locale, code page 1253, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1253_ci_as', N'Default locale, code page 1253, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1253_cs_ai', N'Default locale, code page 1253, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1253_cs_as', N'Default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1253_cs_as', N'Default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1254_ci_ai', N'Default locale, code page 1254, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1254_ci_as', N'Default locale, code page 1254, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1254_cs_ai', N'Default locale, code page 1254, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1254_cs_as', N'Default locale, code page 1254, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1254_cs_as', N'Default locale, code page 1254, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1255_ci_ai', N'Default locale, code page 1255, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1255_ci_as', N'Default locale, code page 1255, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1255_cs_ai', N'Default locale, code page 1255, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1255_cs_as', N'Default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1255_cs_as', N'Default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1256_ci_ai', N'Default locale, code page 1256, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1256_ci_as', N'Default locale, code page 1256, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1256_cs_ai', N'Default locale, code page 1256, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1256_cs_as', N'Default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1256_cs_as', N'Default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1257_ci_ai', N'Default locale, code page 1257, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1257_ci_as', N'Default locale, code page 1257, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1257_cs_ai', N'Default locale, code page 1257, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1257_cs_as', N'Default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1257_cs_as', N'Default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1258_ci_ai', N'Default locale, code page 1258, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1258_ci_as', N'Default locale, code page 1258, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1258_cs_ai', N'Default locale, code page 1258, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1258_cs_as', N'Default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1258_cs_as', N'Default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1_ci_ai', N'Default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1_ci_as', N'Default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1_cs_ai', N'Default locale, code page 1252, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp1_cs_as', N'Default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp1_cs_as', N'Default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp847_ci_ai', N'Default locale, code page 847, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp847_ci_as', N'Default locale, code page 847, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp847_cs_ai', N'Default locale, code page 847, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_cp847_cs_as', N'Default locale, code page 847, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_pref_cp847_cs_as', N'Default locale, code page 847, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_general_ci_ai', N'Default locale, default code page, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_general_ci_as', N'Default locale, default code page, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_general_cs_ai', N'Default locale, default code page, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_general_cs_as', N'Default locale, default code page, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'bbf_unicode_general_pref_cs_as', N'Default locale, default code page, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'chinese_prc_cs_as', N'Chinese-PRC, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'chinese_prc_ci_ai', N'Chinese-PRC, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'chinese_prc_ci_as', N'Chinese-PRC, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'cyrillic_general_cs_as', N'Cyrillic-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'cyrillic_general_ci_ai', N'Cyrillic-General, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'cyrillic_general_ci_as', N'Cyrillic-General, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'finnish_swedish_cs_as', N'Finnish-Swedish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'finnish_swedish_ci_as', N'Finnish-Swedish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'finnish_swedish_ci_ai', N'Finnish-Swedish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'french_cs_as', N'French, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'french_ci_as', N'French, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'french_ci_ai', N'French, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'korean_wansung_cs_as', N'Korean-Wansung, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'korean_wansung_ci_as', N'Korean-Wansung, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'korean_wansung_ci_ai', N'Korean-Wansung, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_bin2', N'Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_90_bin2', N'Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_100_bin2', N'Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_140_bin2', N'Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_ci_ai', N'Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_ci_as', N'Virtual, default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_cs_ai', N'Virtual, default locale, code page 1252, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'latin1_general_cs_as', N'Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'modern_spanish_cs_as', N'Traditional-Spanish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'modern_spanish_ci_as', N'Traditional-Spanish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'modern_spanish_ci_ai', N'Traditional-Spanish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'polish_cs_as', N'Polish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'polish_ci_as', N'Polish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'polish_ci_ai', N'Polish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1250_ci_as', N'Virtual, default locale, code page 1250, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1250_cs_as', N'Virtual, default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1251_ci_as', N'Virtual, default locale, code page 1251, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1251_cs_as', N'Virtual, default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1_ci_ai', N'Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1_ci_as', N'Virtual, default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1_ci_ai', N'Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1_cs_as', N'Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_pref_cp1_cs_as', N'Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1253_ci_as', N'Virtual, default locale, code page 1253, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1253_cs_as', N'Virtual, default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1254_ci_as', N'Virtual, default locale, code page 1254, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1254_cs_as', N'Virtual, default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1255_ci_as', N'Virtual, default locale, code page 1255, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1255_cs_as', N'Virtual, default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1256_ci_as', N'Virtual, default locale, code page 1256, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1256_cs_as', N'Virtual, default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1257_ci_as', N'Virtual, default locale, code page 1257, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1257_cs_as', N'Virtual, default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1258_ci_as', N'Virtual, default locale, code page 1258, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'sql_latin1_general_cp1258_cs_as', N'Virtual, default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'thai_cs_as', N'Thai, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'thai_ci_as', N'Thai, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'thai_ci_ai', N'Thai, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'traditional_spanish_cs_as', N'Traditional-Spanish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'traditional_spanish_ci_as', N'Traditional-Spanish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'traditional_spanish_ci_ai', N'Traditional-Spanish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'turkish_cs_as', N'Turkish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'turkish_ci_as', N'Turkish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'turkish_ci_ai', N'Turkish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'ukrainian_cs_as', N'Ukrainian, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'ukrainian_ci_as', N'Ukrainian, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'ukrainian_ci_ai', N'Ukrainian, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +INSERT INTO sys.babelfish_helpcollation VALUES (N'vietnamese_cs_as', N'Vietnamese, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'vietnamese_ci_as', N'Vietnamese, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive'); +INSERT INTO sys.babelfish_helpcollation VALUES (N'vietnamese_ci_ai', N'Vietnamese, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive'); + +DROP FUNCTION IF EXISTS sys.get_babel_server_collation_oid; +CREATE OR REPLACE FUNCTION sys.get_babel_server_collation_oid() RETURNS OID +LANGUAGE C +AS 'babelfishpg_tsql', 'get_server_collation_oid'; + +DROP PROCEDURE IF EXISTS sys.init_database_collation_oid; +CREATE OR REPLACE PROCEDURE sys.init_server_collation_oid() +AS $$ +DECLARE + server_colloid OID; +BEGIN + server_colloid = sys.get_babel_server_collation_oid(); + perform pg_catalog.set_config('babelfishpg_tsql.server_collation_oid', server_colloid::text, false); + execute format('ALTER DATABASE %I SET babelfishpg_tsql.server_collation_oid FROM CURRENT', current_database()); +END; +$$ +LANGUAGE plpgsql; + +CALL sys.init_server_collation_oid(); + +-- Fill in the oids in coll_infos +CREATE OR REPLACE PROCEDURE sys.babel_collation_initializer() +LANGUAGE C +AS 'babelfishpg_tsql', 'init_collid_trans_tab'; +CALL sys.babel_collation_initializer(); +DROP PROCEDURE sys.babel_collation_initializer; + +-- Manually initialize like mapping table +CREATE OR REPLACE PROCEDURE sys.babel_like_ilike_info_initializer() +LANGUAGE C +AS 'babelfishpg_tsql', 'init_like_ilike_table'; +CALL sys.babel_like_ilike_info_initializer(); +DROP PROCEDURE sys.babel_like_ilike_info_initializer; diff --git a/contrib/babelfishpg_tsql/sql/datatype.sql b/contrib/babelfishpg_tsql/sql/datatype.sql new file mode 100644 index 00000000000..925b3c42bab --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/datatype.sql @@ -0,0 +1,6 @@ +-- Types with different default typmod behavior +SET enable_domain_typmod = TRUE; + +CREATE DOMAIN sys.CURSOR AS REFCURSOR; + +RESET enable_domain_typmod; diff --git a/contrib/babelfishpg_tsql/sql/datatype_string_operators.sql b/contrib/babelfishpg_tsql/sql/datatype_string_operators.sql new file mode 100644 index 00000000000..77c0b599b3b --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/datatype_string_operators.sql @@ -0,0 +1,53 @@ +CREATE OR REPLACE FUNCTION sys.hashbytes(IN alg VARCHAR, IN data VARCHAR) RETURNS sys.bbf_varbinary +AS 'babelfishpg_tsql', 'hashbytes' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.hashbytes(IN VARCHAR, IN VARCHAR) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.hashbytes(IN alg VARCHAR, IN data sys.bbf_varbinary) RETURNS sys.bbf_varbinary +AS 'babelfishpg_tsql', 'hashbytes' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.hashbytes(IN VARCHAR, IN sys.bbf_varbinary) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.quotename(IN input_string VARCHAR, IN delimiter char default '[') RETURNS +sys.nvarchar AS 'babelfishpg_tsql', 'quotename' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.quotename(IN VARCHAR, IN char) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.unicode(IN str VARCHAR) returns INTEGER +as +$BODY$ + select ascii(str); +$BODY$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.unicode(IN VARCHAR) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.string_split(IN string VARCHAR, IN separator VARCHAR, OUT value VARCHAR) RETURNS SETOF VARCHAR AS +$body$ +BEGIN + if length(separator) != 1 then + RAISE EXCEPTION 'Invalid separator: %', separator USING HINT = + 'Separator must be length 1'; +else + RETURN QUERY(SELECT cast(unnest(string_to_array(string, separator)) as varchar)); +end if; +END +$body$ +LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.string_split(IN VARCHAR, IN VARCHAR, OUT VARCHAR) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.string_escape(IN str sys.NVARCHAR, IN type TEXT) RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'string_escape' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.string_escape(IN sys.NVARCHAR, IN TEXT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.formatmessage(IN message_str TEXT) RETURNS sys.nvarchar +AS 'babelfishpg_tsql', 'formatmessage' LANGUAGE C IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.formatmessage(IN TEXT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.formatmessage(IN message_str VARCHAR) RETURNS sys.nvarchar +AS 'babelfishpg_tsql', 'formatmessage' LANGUAGE C IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.formatmessage(IN VARCHAR) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.formatmessage(IN message_str TEXT, VARIADIC "any") RETURNS sys.nvarchar +AS 'babelfishpg_tsql', 'formatmessage' LANGUAGE C IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.formatmessage(IN TEXT, VARIADIC "any") TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.formatmessage(IN message_str VARCHAR, VARIADIC "any") RETURNS sys.nvarchar +AS 'babelfishpg_tsql', 'formatmessage' LANGUAGE C IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.formatmessage(IN VARCHAR, VARIADIC "any") TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/import_export_compatibility.sql b/contrib/babelfishpg_tsql/sql/import_export_compatibility.sql new file mode 100644 index 00000000000..2b99499e7cd --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/import_export_compatibility.sql @@ -0,0 +1,37 @@ +CREATE TABLE sys.assemblies( + name VARCHAR(255), + principal_id int, + assembly_id int, + is_nullable int, + is_fixed_length int, + max_length int +); +GRANT SELECT ON sys.assemblies TO PUBLIC; + +CREATE TABLE sys.assembly_types ( + assembly_id int, + assembly_class VARCHAR(255) +); +GRANT SELECT ON sys.assembly_types TO PUBLIC; + +-- Cannot be implemented without a full implementation of assemblies. +-- However, a full implementation isn't needed for import-export support yet +CREATE OR REPLACE FUNCTION assemblyproperty(IN a VARCHAR, IN b VARCHAR) RETURNS sys.sql_variant +AS +$body$ + SELECT CAST('' AS sys.sql_variant); +$body$ +LANGUAGE SQL IMMUTABLE STRICT; +GRANT EXECUTE ON FUNCTION assemblyproperty(IN VARCHAR, IN VARCHAR) TO PUBLIC; + +CREATE OR REPLACE FUNCTION is_member(IN a VARCHAR) RETURNS INT +AS 'babelfishpg_tsql', 'is_member' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION is_member(IN VARCHAR) TO PUBLIC; + +CREATE OR REPLACE FUNCTION schema_id(IN schema_name VARCHAR) RETURNS INT +AS 'babelfishpg_tsql', 'schema_id' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION schema_id(IN VARCHAR) TO PUBLIC; + +CREATE OR REPLACE FUNCTION schema_name(IN id oid) RETURNS VARCHAR +AS 'babelfishpg_tsql', 'schema_name' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION schema_name(IN oid) TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/ownership.sql b/contrib/babelfishpg_tsql/sql/ownership.sql new file mode 100644 index 00000000000..ef52fc77670 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/ownership.sql @@ -0,0 +1,314 @@ +-- BBF_SYSDATABASES +-- Note: change here requires change in FormData_sysdatabases too +CREATE TABLE sys.babelfish_sysdatabases ( + dbid SMALLINT NOT NULL UNIQUE, + status INT NOT NULL, + status2 INT NOT NULL, + owner NAME NOT NULL, + default_collation NAME NOT NULL, + name TEXT NOT NULL COLLATE "C", + crdate timestamptz NOT NULL, + properties TEXT NOT NULL COLLATE "C", + PRIMARY KEY (name) +); + +GRANT SELECT on sys.babelfish_sysdatabases TO PUBLIC; + +-- BABELFISH_NAMESPACE_EXT +CREATE TABLE sys.babelfish_namespace_ext ( + nspname NAME NOT NULL, + dbid SMALLINT NOT NULL, + orig_name sys.NVARCHAR(128) NOT NULL, + properties TEXT NOT NULL COLLATE "C", + PRIMARY KEY (nspname) +); +GRANT SELECT ON sys.babelfish_namespace_ext TO PUBLIC; + +-- SYSDATABASES +CREATE OR REPLACE VIEW sys.sysdatabases AS +SELECT +t.name, +sys.db_id(t.name) AS dbid, +CAST(CAST(r.oid AS int) AS SYS.VARBINARY(85)) AS sid, +CAST(0 AS SMALLINT) AS mode, +t.status, +t.status2, +CAST(t.crdate AS SYS.DATETIME) AS crdate, +CAST('1900-01-01 00:00:00.000' AS SYS.DATETIME) AS reserved, +CAST(0 AS INT) AS category, +CAST(NULL AS SYS.TINYINT) AS cmptlevel, +CAST(NULL AS SYS.NVARCHAR(260)) AS filename, +CAST(NULL AS SMALLINT) AS version +FROM sys.babelfish_sysdatabases AS t +LEFT OUTER JOIN pg_catalog.pg_roles r on r.rolname = t.owner; + +GRANT SELECT ON sys.sysdatabases TO PUBLIC; + +-- PG_NAMESPACE_EXT +CREATE VIEW sys.pg_namespace_ext AS +SELECT BASE.* , DB.name as dbname FROM +pg_catalog.pg_namespace AS base +LEFT OUTER JOIN sys.babelfish_namespace_ext AS EXT on BASE.nspname = EXT.nspname +INNER JOIN sys.babelfish_sysdatabases AS DB ON EXT.dbid = DB.dbid; + +GRANT SELECT ON sys.pg_namespace_ext TO PUBLIC; + +-- Logical Schema Views +create or replace view sys.schemas as +select + CAST(ext.orig_name as sys.SYSNAME) as name + , base.oid as schema_id + , base.nspowner as principal_id +from pg_catalog.pg_namespace base INNER JOIN sys.babelfish_namespace_ext ext on base.nspname = ext.nspname +where base.nspname not in ('information_schema', 'pg_catalog', 'pg_toast', 'sys', 'public') +and ext.dbid = cast(sys.db_id() as oid); +GRANT SELECT ON sys.schemas TO PUBLIC; +CREATE SEQUENCE sys.babelfish_db_seq MAXVALUE 32767 CYCLE; + +-- CATALOG INITIALIZER +CREATE OR REPLACE PROCEDURE babel_catalog_initializer() +LANGUAGE C +AS 'babelfishpg_tsql', 'init_catalog'; + +CALL babel_catalog_initializer(); + +CREATE OR REPLACE PROCEDURE babel_create_builtin_dbs(IN login TEXT) +LANGUAGE C +AS 'babelfishpg_tsql', 'create_builtin_dbs'; + +CREATE OR REPLACE PROCEDURE sys.babel_drop_all_dbs() +LANGUAGE C +AS 'babelfishpg_tsql', 'drop_all_dbs'; + +CREATE OR REPLACE PROCEDURE sys.babel_initialize_logins(IN login TEXT) +LANGUAGE C +AS 'babelfishpg_tsql', 'initialize_logins'; + +CREATE OR REPLACE PROCEDURE sys.babel_drop_all_logins() +LANGUAGE C +AS 'babelfishpg_tsql', 'drop_all_logins'; + +CREATE OR REPLACE PROCEDURE initialize_babelfish ( sa_name VARCHAR(128) ) +LANGUAGE plpgsql +AS $$ +DECLARE + reserved_roles varchar[] := ARRAY['sysadmin', 'master_dbo', 'master_guest', 'master_db_owner', 'tempdb_dbo', 'tempdb_guest', 'tempdb_db_owner']; + user_id oid := -1; + db_name name := NULL; + role_name varchar; + dba_name varchar; +BEGIN + -- check reserved roles + FOREACH role_name IN ARRAY reserved_roles LOOP + BEGIN + SELECT oid INTO user_id FROM pg_roles WHERE rolname = role_name; + IF user_id > 0 THEN + SELECT datname INTO db_name FROM pg_shdepend AS s INNER JOIN pg_database AS d ON s.dbid = d.oid WHERE s.refobjid = user_id; + IF db_name IS NOT NULL THEN + RAISE E'Could not initialize babelfish in current database: Reserved role % used in database %.\nIf babelfish was initialized in %, please remove babelfish and try again.', role_name, db_name, db_name; + ELSE + RAISE E'Could not initialize babelfish in current database: Reserved role % exists. \nPlease rename or drop existing role and try again ', role_name; + END IF; + END IF; + END; + END LOOP; + + SELECT pg_get_userbyid(datdba) INTO dba_name FROM pg_database WHERE datname = CURRENT_DATABASE(); + IF sa_name <> dba_name THEN + RAISE E'Could not initialize babelfish with given role name: % is not the DB owner of current database.', sa_name; + END IF; + + EXECUTE format('CREATE ROLE sysadmin CREATEDB CREATEROLE INHERIT ROLE %I', sa_name); + EXECUTE format('GRANT USAGE, SELECT ON SEQUENCE sys.babelfish_db_seq TO sysadmin WITH GRANT OPTION'); + EXECUTE format('GRANT CREATE, CONNECT, TEMPORARY ON DATABASE %s TO sysadmin WITH GRANT OPTION', CURRENT_DATABASE()); + EXECUTE format('ALTER DATABASE %s SET babelfishpg_tsql.enable_ownership_structure = true', CURRENT_DATABASE()); + EXECUTE 'SET babelfishpg_tsql.enable_ownership_structure = true'; + CALL sys.babel_initialize_logins(sa_name); + CALL sys.babel_create_builtin_dbs(sa_name); +END +$$; + +CREATE OR REPLACE PROCEDURE remove_babelfish () +LANGUAGE plpgsql +AS $$ +BEGIN + CALL sys.babel_drop_all_dbs(); + CALL sys.babel_drop_all_logins(); + EXECUTE format('ALTER DATABASE %s SET babelfishpg_tsql.enable_ownership_structure = false', CURRENT_DATABASE()); + EXECUTE 'ALTER SEQUENCE sys.babelfish_db_seq RESTART'; + DROP OWNED BY sysadmin; + DROP ROLE sysadmin; +END +$$; + +-- LOGIN EXT +-- Note: change here requires change in FormData_authid_login_ext too +CREATE TABLE sys.babelfish_authid_login_ext ( +rolname NAME NOT NULL, -- pg_authid.rolname +is_disabled INT NOT NULL DEFAULT 0, -- to support enable/disable login +type CHAR(1) NOT NULL DEFAULT 'S', +credential_id INT NOT NULL, +owning_principal_id INT NOT NULL, +is_fixed_role INT NOT NULL DEFAULT 0, +create_date timestamptz NOT NULL, +modify_date timestamptz NOT NULL, +default_database_name SYS.NVARCHAR(128) NOT NULL, +default_language_name SYS.NVARCHAR(128) NOT NULL, +properties JSONB, +PRIMARY KEY (rolname)); +GRANT SELECT ON sys.babelfish_authid_login_ext TO PUBLIC; + +SELECT pg_catalog.pg_extension_config_dump('sys.babelfish_sysdatabases', ''); +SELECT pg_catalog.pg_extension_config_dump('sys.babelfish_db_seq', ''); +SELECT pg_catalog.pg_extension_config_dump('sys.babelfish_namespace_ext', ''); +SELECT pg_catalog.pg_extension_config_dump('sys.babelfish_authid_login_ext', ''); + +-- SERVER_PRINCIPALS +CREATE VIEW sys.server_principals +AS SELECT +CAST(Base.rolname AS sys.SYSNAME) AS name, +CAST(Base.oid As INT) AS principal_id, +CAST(CAST(Base.oid as INT) as sys.varbinary(85)) AS sid, +Ext.type, +CAST(CASE WHEN Ext.type = 'S' THEN 'SQL_LOGIN' ELSE NULL END AS NVARCHAR(60)) AS type_desc, +Ext.is_disabled, +Ext.create_date, +Ext.modify_date, +Ext.default_database_name, +Ext.default_language_name, +Ext.credential_id, +Ext.owning_principal_id, +CAST(Ext.is_fixed_role AS sys.BIT) AS is_fixed_role +FROM pg_catalog.pg_authid AS Base INNER JOIN sys.babelfish_authid_login_ext AS Ext ON Base.rolname = Ext.rolname; + +GRANT SELECT ON sys.server_principals TO PUBLIC; + +-- internal table function for sp_helpdb with no arguments +CREATE OR REPLACE FUNCTION sys.babelfish_helpdb() +RETURNS table ( + name varchar(128), + db_size varchar(13), + owner varchar(128), + dbid int, + created varchar(11), + status varchar(600), + compatibility_level smallint +) AS 'babelfishpg_tsql', 'babelfish_helpdb' LANGUAGE C; + +-- internal table function for helpdb with dbname as input +CREATE OR REPLACE FUNCTION sys.babelfish_helpdb(varchar) +RETURNS table ( + name varchar(128), + db_size varchar(13), + owner varchar(128), + dbid int, + created varchar(11), + status varchar(600), + compatibility_level smallint +) AS 'babelfishpg_tsql', 'babelfish_helpdb' LANGUAGE C; + +create or replace view sys.databases as +select + d.name as name + , sys.db_id(d.name) as database_id + , null::integer as source_database_id + , cast(cast(r.oid as int) as varbinary(85)) as owner_sid + , CAST(d.crdate AS SYS.DATETIME) as create_date + , CAST(NULL AS SYS.TINYINT) as compatibility_level + , c.collname::sys.nvarchar(128) as collation_name + , 0 as user_access + , 'MULTI_USER'::varchar(60) as user_access_desc + , 0 as is_read_only + , 0 as is_auto_close_on + , 0 as is_auto_shrink_on + , 0 as state + , 'ONLINE'::varchar(60) as state_desc + , CASE + WHEN pg_is_in_recovery() is false THEN 0 + WHEN pg_is_in_recovery() is true THEN 1 + END as is_in_standby + , 0 as is_cleanly_shutdown + , 0 as is_supplemental_logging_enabled + , 1 as snapshot_isolation_state + , 'ON'::varchar(60) as snapshot_isolation_state_desc + , 1 as is_read_committed_snapshot_on + , 1 as recovery_model + , 'FULL'::varchar(60) as recovery_model_desc + , 0 as page_verify_option + , null::varchar(60) as page_verify_option_desc + , 1 as is_auto_create_stats_on + , 0 as is_auto_create_stats_incremental_on + , 0 as is_auto_update_stats_on + , 0 as is_auto_update_stats_async_on + , 0 as is_ansi_null_default_on + , 0 as is_ansi_nulls_on + , 0 as is_ansi_padding_on + , 0 as is_ansi_warnings_on + , 0 as is_arithabort_on + , 0 as is_concat_null_yields_null_on + , 0 as is_numeric_roundabort_on + , 0 as is_quoted_identifier_on + , 0 as is_recursive_triggers_on + , 0 as is_cursor_close_on_commit_on + , 0 as is_local_cursor_default + , 0 as is_fulltext_enabled + , 0 as is_trustworthy_on + , 0 as is_db_chaining_on + , 0 as is_parameterization_forced + , 0 as is_master_key_encrypted_by_server + , 0 as is_query_store_on + , 0 as is_published + , 0 as is_subscribed + , 0 as is_merge_published + , 0 as is_distributor + , 0 as is_sync_with_backup + , null::sys.UNIQUEIDENTIFIER as service_broker_guid + , 0 as is_broker_enabled + , 0 as log_reuse_wait + , 'NOTHING'::varchar(60) as log_reuse_wait_desc + , 0 as is_date_correlation_on + , 0 as is_cdc_enabled + , 0 as is_encrypted + , 0 as is_honor_broker_priority_on + , null::sys.UNIQUEIDENTIFIER as replica_id + , null::sys.UNIQUEIDENTIFIER as group_database_id + , null::int as resource_pool_id + , null::smallint as default_language_lcid + , null::sys.nvarchar(128) as default_language_name + , null::int as default_fulltext_language_lcid + , null::sys.nvarchar(128) as default_fulltext_language_name + , null::sys.bit as is_nested_triggers_on + , null::sys.bit as is_transform_noise_words_on + , null::smallint as two_digit_year_cutoff + , 0 as containment + , 'NONE'::varchar(60) as containment_desc + , 0 as target_recovery_time_in_seconds + , 0 as delayed_durability + , null::sys.nvarchar(60) as delayed_durability_desc + , 0 as is_memory_optimized_elevate_to_snapshot_on + , 0 as is_federation_member + , 0 as is_remote_data_archive_enabled + , 0 as is_mixed_page_allocation_on + , 0 as is_temporal_history_retention_enabled + , 0 as catalog_collation_type + , 'Not Applicable'::sys.nvarchar(60) as catalog_collation_type_desc + , null::sys.nvarchar(128) as physical_database_name + , 0 as is_result_set_caching_on + , 0 as is_accelerated_database_recovery_on + , 0 as is_tempdb_spill_to_remote_store + , 0 as is_stale_page_detection_on + , 0 as is_memory_optimized_enabled + , 0 as is_ledger_on + from sys.babelfish_sysdatabases d LEFT OUTER JOIN pg_catalog.pg_collation c ON d.default_collation = c.collname + LEFT OUTER JOIN pg_catalog.pg_roles r on r.rolname = d.owner; + +GRANT SELECT ON sys.databases TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.babelfish_inconsistent_metadata() +RETURNS table ( + object_type varchar(32), + schema_name varchar(128), + object_name varchar(128), + detail text +) AS 'babelfishpg_tsql', 'babelfish_inconsistent_metadata' LANGUAGE C; + diff --git a/contrib/babelfishpg_tsql/sql/sys.sql b/contrib/babelfishpg_tsql/sql/sys.sql new file mode 100644 index 00000000000..e670e557492 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys.sql @@ -0,0 +1,129 @@ +/* Built in functions */ +CREATE FUNCTION sys.sysdatetime() RETURNS datetime2 + AS $$select clock_timestamp()::datetime2;$$ + LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.sysdatetime() TO PUBLIC; + +CREATE FUNCTION sys.sysdatetimeoffset() RETURNS sys.datetimeoffset + -- Casting to text as there are not type cast function from timestamptz to datetimeoffset + AS $$select cast(cast(clock_timestamp() as text) as sys.datetimeoffset);$$ + LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.sysdatetimeoffset() TO PUBLIC; + + +CREATE FUNCTION sys.sysutcdatetime() RETURNS sys.datetime2 + AS $$select (clock_timestamp() AT TIME ZONE 'UTC')::sys.datetime2;$$ + LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.sysutcdatetime() TO PUBLIC; + + +CREATE FUNCTION sys.getdate() RETURNS sys.datetime + AS $$select date_trunc('millisecond', clock_timestamp()::timestamp)::sys.datetime;$$ + LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.getdate() TO PUBLIC; + + +CREATE FUNCTION sys.getutcdate() RETURNS sys.datetime + AS $$select date_trunc('millisecond', clock_timestamp() AT TIME ZONE 'UTC')::sys.datetime;$$ + LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.getutcdate() TO PUBLIC; + + +CREATE FUNCTION sys.isnull(text,text) RETURNS text AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(text,text) TO PUBLIC; + +CREATE FUNCTION sys.isnull(boolean,boolean) RETURNS boolean AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(boolean,boolean) TO PUBLIC; + +CREATE FUNCTION sys.isnull(smallint,smallint) RETURNS smallint AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(smallint,smallint) TO PUBLIC; + +CREATE FUNCTION sys.isnull(integer,integer) RETURNS integer AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(integer,integer) TO PUBLIC; + +CREATE FUNCTION sys.isnull(bigint,bigint) RETURNS bigint AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(bigint,bigint) TO PUBLIC; + +CREATE FUNCTION sys.isnull(real,real) RETURNS real AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(real,real) TO PUBLIC; + +CREATE FUNCTION sys.isnull(double precision, double precision) RETURNS double precision AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(double precision, double precision) TO PUBLIC; + +CREATE FUNCTION sys.isnull(numeric,numeric) RETURNS numeric AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(numeric,numeric) TO PUBLIC; + +CREATE FUNCTION sys.isnull(date, date) RETURNS date AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(date,date) TO PUBLIC; + +CREATE FUNCTION sys.isnull(timestamp,timestamp) RETURNS timestamp AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(timestamp,timestamp) TO PUBLIC; + +CREATE FUNCTION sys.isnull(timestamp with time zone,timestamp with time zone) RETURNS timestamp with time zone AS $$ + SELECT COALESCE($1,$2); +$$ +LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.isnull(timestamp with time zone,timestamp with time zone) TO PUBLIC; + +/* Tsql tables */ +CREATE TABLE IF NOT EXISTS sys.service_settings +( + service character varying(50) NOT NULL + ,setting character varying(100) NOT NULL + ,value character varying +); +GRANT SELECT ON sys.service_settings TO PUBLIC; + +comment on table sys.service_settings is 'Settings for Extension Pack services'; +comment on column sys.service_settings.service is 'Service name'; +comment on column sys.service_settings.setting is 'Setting name'; +comment on column sys.service_settings.value is 'Setting value'; + +CREATE TABLE sys.versions +( + extpackcomponentname VARCHAR(256) NOT NULL, + componentversion VARCHAR(256) +); +GRANT SELECT ON sys.versions TO PUBLIC; + +CREATE TABLE sys.syslanguages ( + lang_id SMALLINT, + lang_name_pg VARCHAR(30), + lang_alias_pg VARCHAR(30), + lang_name_mssql VARCHAR(30), + lang_alias_mssql VARCHAR(30), + territory VARCHAR(50), + spec_culture VARCHAR(10), + lang_data_jsonb JSONB +) WITH (OIDS = FALSE); +GRANT SELECT ON sys.syslanguages TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/sys_cast.sql b/contrib/babelfishpg_tsql/sql/sys_cast.sql new file mode 100644 index 00000000000..1f5998841c6 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_cast.sql @@ -0,0 +1,131 @@ +-- CAST and related functions. +-- Duplicate functions with arg TEXT since ANYELEMNT cannot handle type unknown. + + +CREATE OR REPLACE FUNCTION sys.babelfish_cast_floor_smallint(IN arg TEXT) +RETURNS SMALLINT +AS $BODY$ BEGIN + RETURN CAST(arg AS SMALLINT); +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_cast_floor_smallint(IN arg ANYELEMENT) +RETURNS SMALLINT +AS $BODY$ BEGIN + CASE pg_typeof(arg) + WHEN 'numeric'::regtype, 'double precision'::regtype, 'real'::regtype THEN + RETURN CAST(TRUNC(arg) AS SMALLINT); + WHEN 'sys.money'::regtype, 'sys.smallmoney'::regtype THEN + RETURN CAST(ROUND(arg) AS BIGINT); + ELSE + RETURN CAST(arg AS SMALLINT); + END CASE; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_cast_floor_int(IN arg TEXT) +RETURNS INT +AS $BODY$ BEGIN + RETURN CAST(arg AS INT); +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_cast_floor_int(IN arg ANYELEMENT) +RETURNS INT +AS $BODY$ BEGIN + CASE pg_typeof(arg) + WHEN 'numeric'::regtype, 'double precision'::regtype, 'real'::regtype THEN + RETURN CAST(TRUNC(arg) AS INT); + WHEN 'sys.money'::regtype, 'sys.smallmoney'::regtype THEN + RETURN CAST(ROUND(arg) AS BIGINT); + ELSE + RETURN CAST(arg AS INT); + END CASE; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_cast_floor_bigint(IN arg TEXT) +RETURNS BIGINT +AS $BODY$ BEGIN + RETURN CAST(arg AS BIGINT); +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_cast_floor_bigint(IN arg ANYELEMENT) +RETURNS BIGINT +AS $BODY$ BEGIN + CASE pg_typeof(arg) + WHEN 'numeric'::regtype, 'double precision'::regtype, 'real'::regtype THEN + RETURN CAST(TRUNC(arg) AS BIGINT); + WHEN 'sys.money'::regtype, 'sys.smallmoney'::regtype THEN + RETURN CAST(ROUND(arg) AS BIGINT); + ELSE + RETURN CAST(arg AS BIGINT); + END CASE; +END; $BODY$ +LANGUAGE plpgsql; + + +-- TRY_CAST helper functions +CREATE OR REPLACE FUNCTION sys.babelfish_try_cast_floor_smallint(IN arg TEXT) RETURNS SMALLINT +AS $BODY$ BEGIN + RETURN sys.babelfish_cast_floor_smallint(arg); + EXCEPTION WHEN OTHERS THEN RETURN NULL; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_try_cast_floor_smallint(IN arg ANYELEMENT) RETURNS SMALLINT +AS $BODY$ BEGIN + RETURN sys.babelfish_cast_floor_smallint(arg); + EXCEPTION WHEN OTHERS THEN RETURN NULL; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_try_cast_floor_int(IN arg TEXT) RETURNS INT +AS $BODY$ BEGIN + RETURN sys.babelfish_cast_floor_int(arg); + EXCEPTION WHEN OTHERS THEN RETURN NULL; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_try_cast_floor_int(IN arg ANYELEMENT) RETURNS INT +AS $BODY$ BEGIN + RETURN sys.babelfish_cast_floor_int(arg); + EXCEPTION WHEN OTHERS THEN RETURN NULL; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_try_cast_floor_bigint(IN arg TEXT) RETURNS BIGINT +AS $BODY$ BEGIN + RETURN sys.babelfish_cast_floor_bigint(arg); + EXCEPTION WHEN OTHERS THEN RETURN NULL; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_try_cast_floor_bigint(IN arg ANYELEMENT) RETURNS BIGINT +AS $BODY$ BEGIN + RETURN sys.babelfish_cast_floor_bigint(arg); + EXCEPTION WHEN OTHERS THEN RETURN NULL; +END; $BODY$ +LANGUAGE plpgsql; + + +CREATE OR REPLACE FUNCTION sys.babelfish_try_cast_to_any(IN arg TEXT, INOUT output ANYELEMENT, IN typmod INT) +RETURNS ANYELEMENT +AS $BODY$ BEGIN + EXECUTE format('SELECT CAST(%L AS %s)', arg, format_type(pg_typeof(output), typmod)) INTO output; + EXCEPTION + WHEN OTHERS THEN + -- Do nothing. Output carries NULL. +END; $BODY$ +LANGUAGE plpgsql; diff --git a/contrib/babelfishpg_tsql/sql/sys_function_helpers.sql b/contrib/babelfishpg_tsql/sql/sys_function_helpers.sql new file mode 100644 index 00000000000..c61ddd2d233 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_function_helpers.sql @@ -0,0 +1,10128 @@ +/* Tsql functions + */ + +CREATE OR REPLACE FUNCTION sys.babelfish_get_last_identity() +RETURNS INT8 +AS 'babelfishpg_tsql', 'get_last_identity' +LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.bbf_get_current_physical_schema_name(IN schemaname TEXT) +RETURNS TEXT +AS 'babelfishpg_tsql', 'get_current_physical_schema_name' +LANGUAGE C STABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.babelfish_set_role(IN role_name TEXT) +RETURNS INT4 +AS 'babelfishpg_tsql', 'babelfish_set_role' +LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_last_identity_numeric() +RETURNS numeric(38,0) AS +$BODY$ + SELECT sys.babelfish_get_last_identity()::numeric(38,0); +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.get_min_id_from_table(IN id_colname TEXT, + IN schemaname TEXT, + IN tablename TEXT, + OUT result INT8) +AS +$BODY$ +BEGIN + EXECUTE FORMAT('SELECT MIN(%I) FROM %I.%I', id_colname, schemaname, tablename) INTO result; +END +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.get_max_id_from_table(IN id_colname TEXT, + IN schemaname TEXT, + IN tablename TEXT, + OUT result INT8) +AS +$BODY$ +BEGIN + EXECUTE FORMAT('SELECT MAX(%I) FROM %I.%I', id_colname, schemaname, tablename) INTO result; +END +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.user_name_sysname() +RETURNS sys.SYSNAME AS +$BODY$ + SELECT COALESCE(sys.user_name(), ''); +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_identity_param(IN tablename TEXT, IN optionname TEXT) +RETURNS INT8 +AS 'babelfishpg_tsql', 'get_identity_param' +LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_identity_current(IN tablename TEXT) +RETURNS INT8 +AS 'babelfishpg_tsql', 'get_identity_current' +LANGUAGE C STRICT; + +create or replace function sys.babelfish_get_id_by_name(object_name text) +returns bigint as +$BODY$ +declare res bigint; +begin + execute 'select x''' || substring(encode(digest(object_name, 'sha1'), 'hex'), 1, 8) || '''::bigint' into res; + return res; +end; +$BODY$ +language plpgsql returns null on null input; + +create or replace function sys.babelfish_get_sequence_value(in sequence_name character varying) +returns bigint as +$BODY$ +declare + v_res bigint; +begin + execute 'select last_value from '|| sequence_name into v_res; + return v_res; +end; +$BODY$ +language plpgsql returns null on null input; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_login_default_db(IN login_name TEXT) +RETURNS TEXT +AS 'babelfishpg_tsql', 'bbf_get_login_default_db' +LANGUAGE C STRICT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_date_to_string(IN p_datatype TEXT, + IN p_dateval DATE, + IN p_style NUMERIC DEFAULT 20) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_day VARCHAR; + v_dateval DATE; + v_style SMALLINT; + v_month SMALLINT; + v_resmask VARCHAR; + v_datatype VARCHAR; + v_language VARCHAR; + v_monthname VARCHAR; + v_resstring VARCHAR; + v_lengthexpr VARCHAR; + v_maxlength SMALLINT; + v_res_length SMALLINT; + v_err_message VARCHAR; + v_res_datatype VARCHAR; + v_lang_metadata_json JSONB; + VARCHAR_MAX CONSTANT SMALLINT := 8000; + NVARCHAR_MAX CONSTANT SMALLINT := 4000; + CONVERSION_LANG CONSTANT VARCHAR := ''; + DATATYPE_REGEXP CONSTANT VARCHAR := '^\s*(CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*$'; + DATATYPE_MASK_REGEXP CONSTANT VARCHAR := '^\s*(?:CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*\(\s*(\d+|MAX)\s*\)\s*$'; +BEGIN + v_datatype := upper(trim(p_datatype)); + v_style := floor(p_style)::SMALLINT; + + IF (scale(p_style) > 0) THEN + RAISE most_specific_type_mismatch; + ELSIF (NOT ((v_style BETWEEN 0 AND 13) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 113) OR + v_style IN (120, 121, 126, 127, 130, 131))) + THEN + RAISE invalid_parameter_value; + ELSIF (v_style IN (8, 24, 108)) THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_datatype ~* DATATYPE_MASK_REGEXP) THEN + v_res_datatype := rtrim(split_part(v_datatype, '(', 1)); + + v_maxlength := CASE + WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX + ELSE NVARCHAR_MAX + END; + + v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP); + + IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4) THEN + RAISE interval_field_overflow; + END IF; + + v_res_length := CASE v_lengthexpr + WHEN 'MAX' THEN v_maxlength + ELSE v_lengthexpr::SMALLINT + END; + ELSIF (v_datatype ~* DATATYPE_REGEXP) THEN + v_res_datatype := v_datatype; + ELSE + RAISE datatype_mismatch; + END IF; + + v_dateval := CASE + WHEN (v_style NOT IN (130, 131)) THEN p_dateval + ELSE sys.babelfish_conv_greg_to_hijri(p_dateval) + 1 + END; + + v_day := ltrim(to_char(v_dateval, 'DD'), '0'); + v_month := to_char(v_dateval, 'MM')::SMALLINT; + + v_language := CASE + WHEN (v_style IN (130, 131)) THEN 'HIJRI' + ELSE CONVERSION_LANG + END; + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(v_language); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_character_value_for_cast; + END; + + v_monthname := (v_lang_metadata_json -> 'months_shortnames') ->> v_month - 1; + + v_resmask := CASE + WHEN (v_style IN (1, 22)) THEN 'MM/DD/YY' + WHEN (v_style = 101) THEN 'MM/DD/YYYY' + WHEN (v_style = 2) THEN 'YY.MM.DD' + WHEN (v_style = 102) THEN 'YYYY.MM.DD' + WHEN (v_style = 3) THEN 'DD/MM/YY' + WHEN (v_style = 103) THEN 'DD/MM/YYYY' + WHEN (v_style = 4) THEN 'DD.MM.YY' + WHEN (v_style = 104) THEN 'DD.MM.YYYY' + WHEN (v_style = 5) THEN 'DD-MM-YY' + WHEN (v_style = 105) THEN 'DD-MM-YYYY' + WHEN (v_style = 6) THEN 'DD $mnme$ YY' + WHEN (v_style IN (13, 106, 113)) THEN 'DD $mnme$ YYYY' + WHEN (v_style = 7) THEN '$mnme$ DD, YY' + WHEN (v_style = 107) THEN '$mnme$ DD, YYYY' + WHEN (v_style = 10) THEN 'MM-DD-YY' + WHEN (v_style = 110) THEN 'MM-DD-YYYY' + WHEN (v_style = 11) THEN 'YY/MM/DD' + WHEN (v_style = 111) THEN 'YYYY/MM/DD' + WHEN (v_style = 12) THEN 'YYMMDD' + WHEN (v_style = 112) THEN 'YYYYMMDD' + WHEN (v_style IN (20, 21, 23, 25, 120, 121, 126, 127)) THEN 'YYYY-MM-DD' + WHEN (v_style = 130) THEN 'DD $mnme$ YYYY' + WHEN (v_style = 131) THEN format('%s/MM/YYYY', lpad(v_day, 2, ' ')) + WHEN (v_style IN (0, 9, 100, 109)) THEN format('$mnme$ %s YYYY', lpad(v_day, 2, ' ')) + END; + + v_resstring := to_char(v_dateval, v_resmask); + v_resstring := replace(v_resstring, '$mnme$', v_monthname); + + v_resstring := substring(v_resstring, 1, coalesce(v_res_length, char_length(v_resstring))); + v_res_length := coalesce(v_res_length, + CASE v_res_datatype + WHEN 'CHAR' THEN 30 + ELSE 60 + END); + RETURN CASE + WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring + ELSE rpad(v_resstring, v_res_length, ' ') + END; +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 3 of convert function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('%s is not a valid style number when converting from DATE to a character string.', v_style), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := format('Error converting data type DATE to %s.', trim(p_datatype)), + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := format('The size (%s) given to the convert specification ''%s'' exceeds the maximum allowed for any data type (%s).', + v_lengthexpr, + lower(v_res_datatype), + v_maxlength), + DETAIL := 'Use of incorrect size value of data type parameter during conversion process.', + HINT := 'Change size component of data type parameter to the allowable value and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Data type should be one of these values: ''CHAR(n|MAX)'', ''NCHAR(n|MAX)'', ''VARCHAR(n|MAX)'', ''NVARCHAR(n|MAX)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG), + DETAIL := 'Compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Correct CONVERSION_LANG constant value in function''s body, recompile it and try again.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT (or INTEGER) data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_datetime_to_string(IN p_datatype TEXT, + IN p_src_datatype TEXT, + IN p_datetimeval TIMESTAMP(6) WITHOUT TIME ZONE, + IN p_style NUMERIC DEFAULT -1) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_day VARCHAR; + v_hour VARCHAR; + v_month SMALLINT; + v_style SMALLINT; + v_scale SMALLINT; + v_resmask VARCHAR; + v_language VARCHAR; + v_datatype VARCHAR; + v_fseconds VARCHAR; + v_fractsep VARCHAR; + v_monthname VARCHAR; + v_resstring VARCHAR; + v_lengthexpr VARCHAR; + v_maxlength SMALLINT; + v_res_length SMALLINT; + v_err_message VARCHAR; + v_src_datatype VARCHAR; + v_res_datatype VARCHAR; + v_lang_metadata_json JSONB; + VARCHAR_MAX CONSTANT SMALLINT := 8000; + NVARCHAR_MAX CONSTANT SMALLINT := 4000; + CONVERSION_LANG CONSTANT VARCHAR := ''; + DATATYPE_REGEXP CONSTANT VARCHAR := '^\s*(CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*$'; + SRCDATATYPE_MASK_REGEXP VARCHAR := '^(?:DATETIME|SMALLDATETIME|DATETIME2)\s*(?:\s*\(\s*(\d+)\s*\)\s*)?$'; + DATATYPE_MASK_REGEXP CONSTANT VARCHAR := '^\s*(?:CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*\(\s*(\d+|MAX)\s*\)\s*$'; + v_datetimeval TIMESTAMP(6) WITHOUT TIME ZONE; +BEGIN + v_datatype := upper(trim(p_datatype)); + v_src_datatype := upper(trim(p_src_datatype)); + v_style := floor(p_style)::SMALLINT; + + IF (v_src_datatype ~* SRCDATATYPE_MASK_REGEXP) + THEN + v_scale := substring(v_src_datatype, SRCDATATYPE_MASK_REGEXP)::SMALLINT; + + v_src_datatype := rtrim(split_part(v_src_datatype, '(', 1)); + + IF (v_src_datatype <> 'DATETIME2' AND v_scale IS NOT NULL) THEN + RAISE invalid_indicator_parameter_value; + ELSIF (v_scale NOT BETWEEN 0 AND 7) THEN + RAISE invalid_regular_expression; + END IF; + + v_scale := coalesce(v_scale, 7); + ELSE + RAISE most_specific_type_mismatch; + END IF; + + IF (scale(p_style) > 0) THEN + RAISE escape_character_conflict; + ELSIF (NOT ((v_style BETWEEN 0 AND 14) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 114) OR + v_style IN (-1, 120, 121, 126, 127, 130, 131))) + THEN + RAISE invalid_parameter_value; + END IF; + + IF (v_datatype ~* DATATYPE_MASK_REGEXP) THEN + v_res_datatype := rtrim(split_part(v_datatype, '(', 1)); + + v_maxlength := CASE + WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX + ELSE NVARCHAR_MAX + END; + + v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP); + + IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4) + THEN + RAISE interval_field_overflow; + END IF; + + v_res_length := CASE v_lengthexpr + WHEN 'MAX' THEN v_maxlength + ELSE v_lengthexpr::SMALLINT + END; + ELSIF (v_datatype ~* DATATYPE_REGEXP) THEN + v_res_datatype := v_datatype; + ELSE + RAISE datatype_mismatch; + END IF; + + v_datetimeval := CASE + WHEN (v_style NOT IN (130, 131)) THEN p_datetimeval + ELSE sys.babelfish_conv_greg_to_hijri(p_datetimeval) + INTERVAL '1 day' + END; + + v_day := ltrim(to_char(v_datetimeval, 'DD'), '0'); + v_hour := ltrim(to_char(v_datetimeval, 'HH12'), '0'); + v_month := to_char(v_datetimeval, 'MM')::SMALLINT; + + v_language := CASE + WHEN (v_style IN (130, 131)) THEN 'HIJRI' + ELSE CONVERSION_LANG + END; + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(v_language); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_character_value_for_cast; + END; + + v_monthname := (v_lang_metadata_json -> 'months_shortnames') ->> v_month - 1; + + IF (v_src_datatype IN ('DATETIME', 'SMALLDATETIME')) THEN + v_fseconds := sys.babelfish_round_fractseconds(to_char(v_datetimeval, 'MS')); + + IF (v_fseconds::INTEGER = 1000) THEN + v_fseconds := '000'; + v_datetimeval := v_datetimeval + INTERVAL '1 second'; + ELSE + v_fseconds := lpad(v_fseconds, 3, '0'); + END IF; + ELSE + v_fseconds := sys.babelfish_get_microsecs_from_fractsecs(to_char(v_datetimeval, 'US'), v_scale); + + IF (v_scale = 7) THEN + v_fseconds := concat(v_fseconds, '0'); + END IF; + END IF; + + v_fractsep := CASE v_src_datatype + WHEN 'DATETIME2' THEN '.' + ELSE ':' + END; + + IF ((v_style = -1 AND v_src_datatype <> 'DATETIME2') OR + v_style IN (0, 9, 100, 109)) + THEN + v_resmask := format('$mnme$ %s YYYY %s:MI%s', + lpad(v_day, 2, ' '), + lpad(v_hour, 2, ' '), + CASE + WHEN (v_style IN (-1, 0, 100)) THEN 'AM' + ELSE format(':SS:%sAM', v_fseconds) + END); + ELSIF (v_style = 1) THEN + v_resmask := 'MM/DD/YY'; + ELSIF (v_style = 101) THEN + v_resmask := 'MM/DD/YYYY'; + ELSIF (v_style = 2) THEN + v_resmask := 'YY.MM.DD'; + ELSIF (v_style = 102) THEN + v_resmask := 'YYYY.MM.DD'; + ELSIF (v_style = 3) THEN + v_resmask := 'DD/MM/YY'; + ELSIF (v_style = 103) THEN + v_resmask := 'DD/MM/YYYY'; + ELSIF (v_style = 4) THEN + v_resmask := 'DD.MM.YY'; + ELSIF (v_style = 104) THEN + v_resmask := 'DD.MM.YYYY'; + ELSIF (v_style = 5) THEN + v_resmask := 'DD-MM-YY'; + ELSIF (v_style = 105) THEN + v_resmask := 'DD-MM-YYYY'; + ELSIF (v_style = 6) THEN + v_resmask := 'DD $mnme$ YY'; + ELSIF (v_style = 106) THEN + v_resmask := 'DD $mnme$ YYYY'; + ELSIF (v_style = 7) THEN + v_resmask := '$mnme$ DD, YY'; + ELSIF (v_style = 107) THEN + v_resmask := '$mnme$ DD, YYYY'; + ELSIF (v_style IN (8, 24, 108)) THEN + v_resmask := 'HH24:MI:SS'; + ELSIF (v_style = 10) THEN + v_resmask := 'MM-DD-YY'; + ELSIF (v_style = 110) THEN + v_resmask := 'MM-DD-YYYY'; + ELSIF (v_style = 11) THEN + v_resmask := 'YY/MM/DD'; + ELSIF (v_style = 111) THEN + v_resmask := 'YYYY/MM/DD'; + ELSIF (v_style = 12) THEN + v_resmask := 'YYMMDD'; + ELSIF (v_style = 112) THEN + v_resmask := 'YYYYMMDD'; + ELSIF (v_style IN (13, 113)) THEN + v_resmask := format('DD $mnme$ YYYY HH24:MI:SS%s%s', v_fractsep, v_fseconds); + ELSIF (v_style IN (14, 114)) THEN + v_resmask := format('HH24:MI:SS%s%s', v_fractsep, v_fseconds); + ELSIF (v_style IN (20, 120)) THEN + v_resmask := 'YYYY-MM-DD HH24:MI:SS'; + ELSIF ((v_style = -1 AND v_src_datatype = 'DATETIME2') OR + v_style IN (21, 25, 121)) + THEN + v_resmask := format('YYYY-MM-DD HH24:MI:SS.%s', v_fseconds); + ELSIF (v_style = 22) THEN + v_resmask := format('MM/DD/YY %s:MI:SS AM', lpad(v_hour, 2, ' ')); + ELSIF (v_style = 23) THEN + v_resmask := 'YYYY-MM-DD'; + ELSIF (v_style IN (126, 127)) THEN + v_resmask := CASE v_src_datatype + WHEN 'SMALLDATETIME' THEN 'YYYY-MM-DDT$rem$HH24:MI:SS' + ELSE format('YYYY-MM-DDT$rem$HH24:MI:SS.%s', v_fseconds) + END; + ELSIF (v_style IN (130, 131)) THEN + v_resmask := concat(CASE p_style + WHEN 131 THEN format('%s/MM/YYYY ', lpad(v_day, 2, ' ')) + ELSE format('%s $mnme$ YYYY ', lpad(v_day, 2, ' ')) + END, + format('%s:MI:SS%s%sAM', lpad(v_hour, 2, ' '), v_fractsep, v_fseconds)); + END IF; + + v_resstring := to_char(v_datetimeval, v_resmask); + v_resstring := replace(v_resstring, '$mnme$', v_monthname); + v_resstring := replace(v_resstring, '$rem$', ''); + + v_resstring := substring(v_resstring, 1, coalesce(v_res_length, char_length(v_resstring))); + v_res_length := coalesce(v_res_length, + CASE v_res_datatype + WHEN 'CHAR' THEN 30 + ELSE 60 + END); + RETURN CASE + WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring + ELSE rpad(v_resstring, v_res_length, ' ') + END; +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Source data type should be one of these values: ''DATETIME'', ''SMALLDATETIME'', ''DATETIME2'' or ''DATETIME2(n)''.', + DETAIL := 'Use of incorrect "src_datatype" parameter value during conversion process.', + HINT := 'Change "srcdatatype" parameter to the proper value and try again.'; + + WHEN invalid_regular_expression THEN + RAISE USING MESSAGE := format('The source data type scale (%s) given to the convert specification exceeds the maximum allowable value (7).', + v_scale), + DETAIL := 'Use of incorrect scale value of source data type parameter during conversion process.', + HINT := 'Change scale component of source data type parameter to the allowable value and try again.'; + + WHEN invalid_indicator_parameter_value THEN + RAISE USING MESSAGE := format('Invalid attributes specified for data type %s.', v_src_datatype), + DETAIL := 'Use of incorrect scale value, which is not corresponding to specified data type.', + HINT := 'Change data type scale component or select different data type and try again.'; + + WHEN escape_character_conflict THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 4 of convert function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('%s is not a valid style number when converting from %s to a character string.', + v_style, v_src_datatype), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := format('The size (%s) given to the convert specification ''%s'' exceeds the maximum allowed for any data type (%s).', + v_lengthexpr, lower(v_res_datatype), v_maxlength), + DETAIL := 'Use of incorrect size value of data type parameter during conversion process.', + HINT := 'Change size component of data type parameter to the allowable value and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Data type should be one of these values: ''CHAR(n|MAX)'', ''NCHAR(n|MAX)'', ''VARCHAR(n|MAX)'', ''NVARCHAR(n|MAX)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG), + DETAIL := 'Compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Correct CONVERSION_LANG constant value in function''s body, recompile it and try again.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_greg_to_hijri(IN p_dateval DATE) +RETURNS DATE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_greg_to_hijri(extract(day from p_dateval)::NUMERIC, + extract(month from p_dateval)::NUMERIC, + extract(year from p_dateval)::NUMERIC); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_greg_to_hijri(IN p_day NUMERIC, + IN p_month NUMERIC, + IN p_year NUMERIC) +RETURNS DATE +AS +$BODY$ +DECLARE + v_day SMALLINT; + v_month SMALLINT; + v_year INTEGER; + v_jdnum DOUBLE PRECISION; + v_lnum DOUBLE PRECISION; + v_inum DOUBLE PRECISION; + v_nnum DOUBLE PRECISION; + v_jnum DOUBLE PRECISION; +BEGIN + v_day := floor(p_day)::SMALLINT; + v_month := floor(p_month)::SMALLINT; + v_year := floor(p_year)::INTEGER; + + IF ((sign(v_day) = -1) OR (sign(v_month) = -1) OR (sign(v_year) = -1)) + THEN + RAISE invalid_character_value_for_cast; + ELSIF (v_year = 0) THEN + RAISE null_value_not_allowed; + END IF; + + IF ((p_year > 1582) OR ((p_year = 1582) AND (p_month > 10)) OR ((p_year = 1582) AND (p_month = 10) AND (p_day > 14))) + THEN + v_jdnum := sys.babelfish_get_int_part((1461 * (p_year + 4800 + sys.babelfish_get_int_part((p_month - 14) / 12))) / 4) + + sys.babelfish_get_int_part((367 * (p_month - 2 - 12 * (sys.babelfish_get_int_part((p_month - 14) / 12)))) / 12) - + sys.babelfish_get_int_part((3 * (sys.babelfish_get_int_part((p_year + 4900 + + sys.babelfish_get_int_part((p_month - 14) / 12)) / 100))) / 4) + p_day - 32075; + ELSE + v_jdnum := 367 * p_year - sys.babelfish_get_int_part((7 * (p_year + 5001 + + sys.babelfish_get_int_part((p_month - 9) / 7))) / 4) + + sys.babelfish_get_int_part((275 * p_month) / 9) + p_day + 1729777; + END IF; + + v_lnum := v_jdnum - 1948440 + 10632; + v_nnum := sys.babelfish_get_int_part((v_lnum - 1) / 10631); + v_lnum := v_lnum - 10631 * v_nnum + 354; + v_jnum := (sys.babelfish_get_int_part((10985 - v_lnum) / 5316)) * (sys.babelfish_get_int_part((50 * v_lnum) / 17719)) + + (sys.babelfish_get_int_part(v_lnum / 5670)) * (sys.babelfish_get_int_part((43 * v_lnum) / 15238)); + v_lnum := v_lnum - (sys.babelfish_get_int_part((30 - v_jnum) / 15)) * (sys.babelfish_get_int_part((17719 * v_jnum) / 50)) - + (sys.babelfish_get_int_part(v_jnum / 16)) * (sys.babelfish_get_int_part((15238 * v_jnum) / 43)) + 29; + + v_month := sys.babelfish_get_int_part((24 * v_lnum) / 709); + v_day := v_lnum - sys.babelfish_get_int_part((709 * v_month) / 24); + v_year := 30 * v_nnum + v_jnum - 30; + + RETURN to_date(concat_ws('.', v_day, v_month, v_year), 'DD.MM.YYYY'); +EXCEPTION + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := 'Could not convert Gregorian to Hijri date if any part of the date is negative.', + DETAIL := 'Some of the supplied date parts (day, month, year) is negative.', + HINT := 'Change the value of the date part (day, month, year) wich was found to be negative.'; + + WHEN null_value_not_allowed THEN + RAISE USING MESSAGE := 'Could not convert Gregorian to Hijri date if year value is equal to zero.', + DETAIL := 'Supplied year value is equal to zero.', + HINT := 'Change the value of the year so that it is greater than zero.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_greg_to_hijri(IN p_day TEXT, + IN p_month TEXT, + IN p_year TEXT) +RETURNS DATE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_greg_to_hijri(p_day::NUMERIC, + p_month::NUMERIC, + p_year::NUMERIC); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_greg_to_hijri(IN p_datetimeval TIMESTAMP WITHOUT TIME ZONE) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_hijri_date DATE; +BEGIN + v_hijri_date := sys.babelfish_conv_greg_to_hijri(extract(day from p_datetimeval)::SMALLINT, + extract(month from p_datetimeval)::SMALLINT, + extract(year from p_datetimeval)::INTEGER); + + RETURN to_timestamp(format('%s %s', to_char(v_hijri_date, 'DD.MM.YYYY'), + to_char(p_datetimeval, ' HH24:MI:SS.US')), + 'DD.MM.YYYY HH24:MI:SS.US'); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_hijri_to_greg(IN p_dateval DATE) +RETURNS DATE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_hijri_to_greg(extract(day from p_dateval)::NUMERIC, + extract(month from p_dateval)::NUMERIC, + extract(year from p_dateval)::NUMERIC); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_hijri_to_greg(IN p_day NUMERIC, + IN p_month NUMERIC, + IN p_year NUMERIC) +RETURNS DATE +AS +$BODY$ +DECLARE + v_day SMALLINT; + v_month SMALLINT; + v_year INTEGER; + v_err_message VARCHAR; + v_jdnum DOUBLE PRECISION; + v_lnum DOUBLE PRECISION; + v_inum DOUBLE PRECISION; + v_nnum DOUBLE PRECISION; + v_jnum DOUBLE PRECISION; + v_knum DOUBLE PRECISION; +BEGIN + v_day := floor(p_day)::SMALLINT; + v_month := floor(p_month)::SMALLINT; + v_year := floor(p_year)::INTEGER; + + IF ((sign(v_day) = -1) OR (sign(v_month) = -1) OR (sign(v_year) = -1)) + THEN + RAISE invalid_character_value_for_cast; + ELSIF (v_year = 0) THEN + RAISE null_value_not_allowed; + END IF; + + v_jdnum = sys.babelfish_get_int_part((11 * v_year + 3) / 30) + 354 * v_year + 30 * v_month - + sys.babelfish_get_int_part((v_month - 1) / 2) + v_day + 1948440 - 385; + + IF (v_jdnum > 2299160) + THEN + v_lnum := v_jdnum + 68569; + v_nnum := sys.babelfish_get_int_part((4 * v_lnum) / 146097); + v_lnum := v_lnum - sys.babelfish_get_int_part((146097 * v_nnum + 3) / 4); + v_inum := sys.babelfish_get_int_part((4000 * (v_lnum + 1)) / 1461001); + v_lnum := v_lnum - sys.babelfish_get_int_part((1461 * v_inum) / 4) + 31; + v_jnum := sys.babelfish_get_int_part((80 * v_lnum) / 2447); + v_day := v_lnum - sys.babelfish_get_int_part((2447 * v_jnum) / 80); + v_lnum := sys.babelfish_get_int_part(v_jnum / 11); + v_month := v_jnum + 2 - 12 * v_lnum; + v_year := 100 * (v_nnum - 49) + v_inum + v_lnum; + ELSE + v_jnum := v_jdnum + 1402; + v_knum := sys.babelfish_get_int_part((v_jnum - 1) / 1461); + v_lnum := v_jnum - 1461 * v_knum; + v_nnum := sys.babelfish_get_int_part((v_lnum - 1) / 365) - sys.babelfish_get_int_part(v_lnum / 1461); + v_inum := v_lnum - 365 * v_nnum + 30; + v_jnum := sys.babelfish_get_int_part((80 * v_inum) / 2447); + v_day := v_inum-sys.babelfish_get_int_part((2447 * v_jnum) / 80); + v_inum := sys.babelfish_get_int_part(v_jnum / 11); + v_month := v_jnum + 2 - 12 * v_inum; + v_year := 4 * v_knum + v_nnum + v_inum - 4716; + END IF; + + RETURN to_date(concat_ws('.', v_day, v_month, v_year), 'DD.MM.YYYY'); +EXCEPTION + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := 'Could not convert Hijri to Gregorian date if any part of the date is negative.', + DETAIL := 'Some of the supplied date parts (day, month, year) is negative.', + HINT := 'Change the value of the date part (day, month, year) wich was found to be negative.'; + + WHEN null_value_not_allowed THEN + RAISE USING MESSAGE := 'Could not convert Hijri to Gregorian date if year value is equal to zero.', + DETAIL := 'Supplied year value is equal to zero.', + HINT := 'Change the value of the year so that it is greater than zero.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_hijri_to_greg(IN p_day TEXT, + IN p_month TEXT, + IN p_year TEXT) +RETURNS DATE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_hijri_to_greg(p_day::NUMERIC, + p_month::NUMERIC, + p_year::NUMERIC); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_hijri_to_greg(IN p_datetimeval TIMESTAMP WITHOUT TIME ZONE) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_hijri_date DATE; +BEGIN + v_hijri_date := sys.babelfish_conv_hijri_to_greg(extract(day from p_dateval)::NUMERIC, + extract(month from p_dateval)::NUMERIC, + extract(year from p_dateval)::NUMERIC); + + RETURN to_timestamp(format('%s %s', to_char(v_hijri_date, 'DD.MM.YYYY'), + to_char(p_datetimeval, ' HH24:MI:SS.US')), + 'DD.MM.YYYY HH24:MI:SS.US'); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_string_to_date(IN p_datestring TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS DATE +AS +$BODY$ +DECLARE + v_day VARCHAR; + v_year VARCHAR; + v_month VARCHAR; + v_hijridate DATE; + v_style SMALLINT; + v_leftpart VARCHAR; + v_middlepart VARCHAR; + v_rightpart VARCHAR; + v_fractsecs VARCHAR; + v_datestring VARCHAR; + v_err_message VARCHAR; + v_date_format VARCHAR; + v_regmatch_groups TEXT[]; + v_lang_metadata_json JSONB; + v_compmonth_regexp VARCHAR; + CONVERSION_LANG CONSTANT VARCHAR := ''; + DATE_FORMAT CONSTANT VARCHAR := ''; + DAYMM_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + FULLYEAR_REGEXP CONSTANT VARCHAR := '(\d{4})'; + SHORTYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + COMPYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,2}|\d{4})'; + AMPM_REGEXP CONSTANT VARCHAR := '(?:[AP]M)'; + TIMEUNIT_REGEXP CONSTANT VARCHAR := '\s*\d{1,2}\s*'; + FRACTSECS_REGEXP CONSTANT VARCHAR := '\s*\d{1,9}'; + HHMMSSFS_PART_REGEXP CONSTANT VARCHAR := concat('(', TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '(?:\.|\:)', FRACTSECS_REGEXP, + ')\s*', AMPM_REGEXP, '?'); + HHMMSSFS_DOTPART_REGEXP CONSTANT VARCHAR := concat('(', TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\.', FRACTSECS_REGEXP, + ')\s*', AMPM_REGEXP, '?'); + HHMMSSFS_REGEXP CONSTANT VARCHAR := concat('^', HHMMSSFS_PART_REGEXP, '$'); + HHMMSSFS_DOT_REGEXP CONSTANT VARCHAR := concat('^', HHMMSSFS_DOTPART_REGEXP, '$'); + v_defmask1_regexp VARCHAR := concat('^($comp_month$)\s*', DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, '$'); + v_defmask2_regexp VARCHAR := concat('^', DAYMM_REGEXP, '\s*($comp_month$)\s*', COMPYEAR_REGEXP, '$'); + v_defmask3_regexp VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*($comp_month$)\s*', DAYMM_REGEXP, '$'); + v_defmask4_regexp VARCHAR := concat('^', FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, '\s*($comp_month$)$'); + v_defmask5_regexp VARCHAR := concat('^', DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, '\s*($comp_month$)$'); + v_defmask6_regexp VARCHAR := concat('^($comp_month$)\s*', FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, '$'); + v_defmask7_regexp VARCHAR := concat('^($comp_month$)\s*', DAYMM_REGEXP, '\s*\,\s*', COMPYEAR_REGEXP, '$'); + v_defmask8_regexp VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*($comp_month$)$'); + v_defmask9_regexp VARCHAR := concat('^($comp_month$)\s*', FULLYEAR_REGEXP, '$'); + v_defmask10_regexp VARCHAR := concat('^', DAYMM_REGEXP, '\s*(?:\.|/|-)\s*($comp_month$)\s*(?:\.|/|-)\s*', COMPYEAR_REGEXP, '$'); + DOT_SHORTYEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*\.\s*', DAYMM_REGEXP, '\s*\.\s*', SHORTYEAR_REGEXP, '$'); + DOT_FULLYEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*\.\s*', DAYMM_REGEXP, '\s*\.\s*', FULLYEAR_REGEXP, '$'); + SLASH_SHORTYEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*/\s*', DAYMM_REGEXP, '\s*/\s*', SHORTYEAR_REGEXP, '$'); + SLASH_FULLYEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*/\s*', DAYMM_REGEXP, '\s*/\s*', FULLYEAR_REGEXP, '$'); + DASH_SHORTYEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*-\s*', DAYMM_REGEXP, '\s*-\s*', SHORTYEAR_REGEXP, '$'); + DASH_FULLYEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*-\s*', DAYMM_REGEXP, '\s*-\s*', FULLYEAR_REGEXP, '$'); + DOT_SLASH_DASH_YEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*(?:\.|/|-)\s*', DAYMM_REGEXP, '\s*(?:\.|/|-)\s*', COMPYEAR_REGEXP, '$'); + YEAR_DOTMASK_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*\.\s*', DAYMM_REGEXP, '\s*\.\s*', DAYMM_REGEXP, '$'); + YEAR_SLASHMASK_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*/\s*', DAYMM_REGEXP, '\s*/\s*', DAYMM_REGEXP, '$'); + YEAR_DASHMASK_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*-\s*', DAYMM_REGEXP, '\s*-\s*', DAYMM_REGEXP, '$'); + YEAR_DOT_SLASH_DASH_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*(?:\.|/|-)\s*', DAYMM_REGEXP, '\s*(?:\.|/|-)\s*', DAYMM_REGEXP, '$'); + DIGITMASK1_REGEXP CONSTANT VARCHAR := '^\d{6}$'; + DIGITMASK2_REGEXP CONSTANT VARCHAR := '^\d{8}$'; +BEGIN + v_style := floor(p_style)::SMALLINT; + v_datestring := trim(p_datestring); + + IF (scale(p_style) > 0) THEN + RAISE most_specific_type_mismatch; + ELSIF (NOT ((v_style BETWEEN 0 AND 14) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 114) OR + v_style IN (120, 121, 126, 127, 130, 131))) + THEN + RAISE invalid_parameter_value; + END IF; + + IF (v_datestring ~* HHMMSSFS_PART_REGEXP AND v_datestring !~* HHMMSSFS_REGEXP) + THEN + v_datestring := trim(regexp_replace(v_datestring, HHMMSSFS_PART_REGEXP, '', 'gi')); + END IF; + + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(CONVERSION_LANG); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_character_value_for_cast; + END; + + v_date_format := coalesce(nullif(DATE_FORMAT, ''), v_lang_metadata_json ->> 'date_format'); + + v_compmonth_regexp := array_to_string(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_shortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_names'))), '|'); + + v_defmask1_regexp := replace(v_defmask1_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask2_regexp := replace(v_defmask2_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask3_regexp := replace(v_defmask3_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask4_regexp := replace(v_defmask4_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask5_regexp := replace(v_defmask5_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask6_regexp := replace(v_defmask6_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask7_regexp := replace(v_defmask7_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask8_regexp := replace(v_defmask8_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask9_regexp := replace(v_defmask9_regexp, '$comp_month$', v_compmonth_regexp); + v_defmask10_regexp := replace(v_defmask10_regexp, '$comp_month$', v_compmonth_regexp); + + IF (v_datestring ~* v_defmask1_regexp OR + v_datestring ~* v_defmask2_regexp OR + v_datestring ~* v_defmask3_regexp OR + v_datestring ~* v_defmask4_regexp OR + v_datestring ~* v_defmask5_regexp OR + v_datestring ~* v_defmask6_regexp OR + v_datestring ~* v_defmask7_regexp OR + v_datestring ~* v_defmask8_regexp OR + v_datestring ~* v_defmask9_regexp OR + v_datestring ~* v_defmask10_regexp) + THEN + IF (v_style IN (130, 131)) THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_datestring ~* v_defmask1_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask1_regexp, 'gi'); + v_day := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + + ELSIF (v_datestring ~* v_defmask2_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask2_regexp, 'gi'); + v_day := v_regmatch_groups[1]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + + ELSIF (v_datestring ~* v_defmask3_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask3_regexp, 'gi'); + v_day := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := v_regmatch_groups[1]; + + ELSIF (v_datestring ~* v_defmask4_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask4_regexp, 'gi'); + v_day := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_year := v_regmatch_groups[1]; + + ELSIF (v_datestring ~* v_defmask5_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask5_regexp, 'gi'); + v_day := v_regmatch_groups[1]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[2]); + + ELSIF (v_datestring ~* v_defmask6_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask6_regexp, 'gi'); + v_day := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := v_regmatch_groups[2]; + + ELSIF (v_datestring ~* v_defmask7_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask7_regexp, 'gi'); + v_day := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + + ELSIF (v_datestring ~* v_defmask8_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask8_regexp, 'gi'); + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := v_regmatch_groups[1]; + + ELSIF (v_datestring ~* v_defmask9_regexp) + THEN + v_regmatch_groups := regexp_matches(v_datestring, v_defmask9_regexp, 'gi'); + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := v_regmatch_groups[2]; + ELSE + v_regmatch_groups := regexp_matches(v_datestring, v_defmask10_regexp, 'gi'); + v_day := v_regmatch_groups[1]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + END IF; + ELSEIF (v_datestring ~* DOT_SHORTYEAR_REGEXP OR + v_datestring ~* DOT_FULLYEAR_REGEXP OR + v_datestring ~* SLASH_SHORTYEAR_REGEXP OR + v_datestring ~* SLASH_FULLYEAR_REGEXP OR + v_datestring ~* DASH_SHORTYEAR_REGEXP OR + v_datestring ~* DASH_FULLYEAR_REGEXP) + THEN + IF (v_style IN (6, 7, 8, 9, 12, 13, 14, 24, 100, 106, 107, 108, 109, 112, 113, 114, 130)) THEN + RAISE invalid_regular_expression; + ELSIF (v_style IN (20, 21, 23, 25, 102, 111, 120, 121, 126, 127)) THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, DOT_SLASH_DASH_YEAR_REGEXP, 'gi'); + v_leftpart := v_regmatch_groups[1]; + v_middlepart := v_regmatch_groups[2]; + v_rightpart := v_regmatch_groups[3]; + + IF (v_datestring ~* DOT_SHORTYEAR_REGEXP OR + v_datestring ~* SLASH_SHORTYEAR_REGEXP OR + v_datestring ~* DASH_SHORTYEAR_REGEXP) + THEN + IF ((v_style IN (1, 10, 22) AND v_date_format <> 'MDY') OR + ((v_style IS NULL OR v_style IN (0, 1, 10, 22)) AND v_date_format NOT IN ('YDM', 'YMD', 'DMY', 'DYM', 'MYD'))) + THEN + v_day := v_middlepart; + v_month := v_leftpart; + v_year := sys.babelfish_get_full_year(v_rightpart); + + ELSIF ((v_style IN (2, 11) AND v_date_format <> 'YMD') OR + ((v_style IS NULL OR v_style IN (0, 2, 11)) AND v_date_format = 'YMD')) + THEN + v_day := v_rightpart; + v_month := v_middlepart; + v_year := sys.babelfish_get_full_year(v_leftpart); + + ELSIF ((v_style IN (3, 4, 5) AND v_date_format <> 'DMY') OR + ((v_style IS NULL OR v_style IN (0, 3, 4, 5)) AND v_date_format = 'DMY')) + THEN + v_day := v_leftpart; + v_month := v_middlepart; + v_year := sys.babelfish_get_full_year(v_rightpart); + + ELSIF ((v_style IS NULL OR v_style = 0) AND v_date_format = 'DYM') + THEN + v_day := v_leftpart; + v_month := v_rightpart; + v_year := sys.babelfish_get_full_year(v_middlepart); + + ELSIF ((v_style IS NULL OR v_style = 0) AND v_date_format = 'MYD') + THEN + v_day := v_rightpart; + v_month := v_leftpart; + v_year := sys.babelfish_get_full_year(v_middlepart); + + ELSIF ((v_style IS NULL OR v_style = 0) AND v_date_format = 'YDM') THEN + RAISE character_not_in_repertoire; + ELSIF (v_style IN (101, 103, 104, 105, 110, 131)) THEN + RAISE invalid_datetime_format; + END IF; + ELSE + v_year := v_rightpart; + + IF (v_leftpart::SMALLINT <= 12) + THEN + IF ((v_style IN (103, 104, 105, 131) AND v_date_format <> 'DMY') OR + ((v_style IS NULL OR v_style IN (0, 103, 104, 105, 131)) AND v_date_format = 'DMY')) + THEN + v_day := v_leftpart; + v_month := v_middlepart; + ELSIF ((v_style IN (101, 110) AND v_date_format IN ('YDM', 'DMY', 'DYM')) OR + ((v_style IS NULL OR v_style IN (0, 101, 110)) AND v_date_format NOT IN ('YDM', 'DMY', 'DYM'))) + THEN + v_day := v_middlepart; + v_month := v_leftpart; + ELSIF ((v_style IN (1, 2, 3, 4, 5, 10, 11, 22) AND v_date_format <> 'YDM') OR + ((v_style IS NULL OR v_style IN (0, 1, 2, 3, 4, 5, 10, 11, 22)) AND v_date_format = 'YDM')) + THEN + RAISE invalid_datetime_format; + END IF; + ELSE + IF ((v_style IN (103, 104, 105, 131) AND v_date_format <> 'DMY') OR + ((v_style IS NULL OR v_style IN (0, 103, 104, 105, 131)) AND v_date_format = 'DMY')) + THEN + v_day := v_leftpart; + v_month := v_middlepart; + ELSIF ((v_style IN (1, 2, 3, 4, 5, 10, 11, 22, 101, 110) AND v_date_format = 'DMY') OR + ((v_style IS NULL OR v_style IN (0, 1, 2, 3, 4, 5, 10, 11, 22, 101, 110)) AND v_date_format <> 'DMY')) + THEN + RAISE invalid_datetime_format; + END IF; + END IF; + END IF; + ELSIF (v_datestring ~* YEAR_DOTMASK_REGEXP OR + v_datestring ~* YEAR_SLASHMASK_REGEXP OR + v_datestring ~* YEAR_DASHMASK_REGEXP) + THEN + IF (v_style IN (6, 7, 8, 9, 12, 13, 14, 24, 100, 106, 107, 108, 109, 112, 113, 114, 130)) THEN + RAISE invalid_regular_expression; + ELSIF (v_style IN (1, 2, 3, 4, 5, 10, 11, 22, 101, 103, 104, 105, 110, 131)) THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, YEAR_DOT_SLASH_DASH_REGEXP, 'gi'); + v_day := v_regmatch_groups[3]; + v_month := v_regmatch_groups[2]; + v_year := v_regmatch_groups[1]; + + ELSIF (v_datestring ~* DIGITMASK1_REGEXP OR + v_datestring ~* DIGITMASK2_REGEXP) + THEN + IF (v_datestring ~* DIGITMASK1_REGEXP) + THEN + v_day := substring(v_datestring, 5, 2); + v_month := substring(v_datestring, 3, 2); + v_year := sys.babelfish_get_full_year(substring(v_datestring, 1, 2)); + ELSE + v_day := substring(v_datestring, 7, 2); + v_month := substring(v_datestring, 5, 2); + v_year := substring(v_datestring, 1, 4); + END IF; + ELSIF (v_datestring ~* HHMMSSFS_REGEXP) + THEN + v_fractsecs := coalesce(sys.babelfish_get_timeunit_from_string(v_datestring, 'FRACTSECONDS'), ''); + IF (v_datestring !~* HHMMSSFS_DOT_REGEXP AND char_length(v_fractsecs) > 3) THEN + RAISE invalid_datetime_format; + END IF; + + v_day := '01'; + v_month := '01'; + v_year := '1900'; + ELSE + RAISE invalid_datetime_format; + END IF; + + IF (((v_datestring ~* HHMMSSFS_REGEXP OR v_datestring ~* DIGITMASK1_REGEXP OR v_datestring ~* DIGITMASK2_REGEXP) AND v_style IN (130, 131)) OR + ((v_datestring ~* DOT_FULLYEAR_REGEXP OR v_datestring ~* SLASH_FULLYEAR_REGEXP OR v_datestring ~* DASH_FULLYEAR_REGEXP) AND v_style = 131)) + THEN + IF ((v_day::SMALLINT NOT BETWEEN 1 AND 29) OR + (v_month::SMALLINT NOT BETWEEN 1 AND 12)) + THEN + RAISE invalid_datetime_format; + END IF; + + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_year) - 1; + v_datestring := to_char(v_hijridate, 'DD.MM.YYYY'); + + v_day := split_part(v_datestring, '.', 1); + v_month := split_part(v_datestring, '.', 2); + v_year := split_part(v_datestring, '.', 3); + END IF; + + RETURN to_date(concat_ws('.', v_day, v_month, v_year), 'DD.MM.YYYY'); +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 2 of conv_string_to_date function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('The style %s is not supported for conversions from VARCHAR to DATE.', v_style), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_regular_expression THEN + RAISE USING MESSAGE := format('The input character string doesn''t follow style %s.', v_style), + DETAIL := 'Selected "style" param value isn''t valid for conversion of passed character string.', + HINT := 'Either change the input character string or use a different style.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := 'Conversion failed when converting date from character string.', + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN character_not_in_repertoire THEN + RAISE USING MESSAGE := 'The YDM date format isn''t supported when converting from this string format to date.', + DETAIL := 'Use of incorrect DATE_FORMAT constant value regarding string format parameter during conversion process.', + HINT := 'Change DATE_FORMAT constant to one of these values: MDY|DMY|DYM, recompile function and try again.'; + + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG), + DETAIL := 'Compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Correct CONVERSION_LANG constant value in function''s body, recompile it and try again.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Passed argument value contains illegal characters.', + HINT := 'Correct passed argument value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_string_to_datetime(IN p_datatype TEXT, + IN p_datetimestring TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_day VARCHAR; + v_year VARCHAR; + v_month VARCHAR; + v_style SMALLINT; + v_scale SMALLINT; + v_hours VARCHAR; + v_hijridate DATE; + v_minutes VARCHAR; + v_seconds VARCHAR; + v_fseconds VARCHAR; + v_datatype VARCHAR; + v_timepart VARCHAR; + v_leftpart VARCHAR; + v_middlepart VARCHAR; + v_rightpart VARCHAR; + v_datestring VARCHAR; + v_err_message VARCHAR; + v_date_format VARCHAR; + v_res_datatype VARCHAR; + v_datetimestring VARCHAR; + v_datatype_groups TEXT[]; + v_regmatch_groups TEXT[]; + v_lang_metadata_json JSONB; + v_compmonth_regexp VARCHAR; + v_resdatetime TIMESTAMP(6) WITHOUT TIME ZONE; + CONVERSION_LANG CONSTANT VARCHAR := ''; + DATE_FORMAT CONSTANT VARCHAR := ''; + DAYMM_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + FULLYEAR_REGEXP CONSTANT VARCHAR := '(\d{4})'; + SHORTYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + COMPYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,2}|\d{4})'; + AMPM_REGEXP CONSTANT VARCHAR := '(?:[AP]M)'; + MASKSEP_REGEXP CONSTANT VARCHAR := '(?:\.|-|/)'; + TIMEUNIT_REGEXP CONSTANT VARCHAR := '\s*\d{1,2}\s*'; + FRACTSECS_REGEXP CONSTANT VARCHAR := '\s*\d{1,9}\s*'; + DATATYPE_REGEXP CONSTANT VARCHAR := '^(DATETIME|SMALLDATETIME|DATETIME2)\s*(?:\()?\s*((?:-)?\d+)?\s*(?:\))?$'; + HHMMSSFS_PART_REGEXP CONSTANT VARCHAR := concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, AMPM_REGEXP, '?|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\.', FRACTSECS_REGEXP, AMPM_REGEXP, '?|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, AMPM_REGEXP, '?|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '(?:\.|\:)', FRACTSECS_REGEXP, AMPM_REGEXP, '?'); + HHMMSSFS_DOT_PART_REGEXP CONSTANT VARCHAR := concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, AMPM_REGEXP, '?|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\.', FRACTSECS_REGEXP, AMPM_REGEXP, '?|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, AMPM_REGEXP, '?|', + TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '(?:\.)', FRACTSECS_REGEXP, AMPM_REGEXP, '?'); + HHMMSSFS_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')$'); + DEFMASK1_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + MASKSEP_REGEXP, '*\s*($comp_month$)\s*', DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK1_1_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '?\s*($comp_month$)\s*', DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, '$'); + DEFMASK1_2_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '\s*($comp_month$)\s*', DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, '$'); + DEFMASK2_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '*\s*($comp_month$)\s*', COMPYEAR_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK2_1_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '?\s*($comp_month$)\s*', COMPYEAR_REGEXP, '$'); + DEFMASK2_2_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*($comp_month$)\s*', COMPYEAR_REGEXP, '$'); + DEFMASK3_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '*\s*($comp_month$)\s*', DAYMM_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK3_1_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '?\s*($comp_month$)\s*', DAYMM_REGEXP, '$'); + DEFMASK3_2_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '\s*($comp_month$)\s*', DAYMM_REGEXP, '$'); + DEFMASK4_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '*\s*($comp_month$)', + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK4_1_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '?\s*($comp_month$)$'); + DEFMASK4_2_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*($comp_month$)$'); + DEFMASK5_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '*\s*($comp_month$)', + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK5_1_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '?\s*($comp_month$)$'); + DEFMASK5_2_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s+', COMPYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '\s*($comp_month$)$'); + DEFMASK6_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + MASKSEP_REGEXP, '*\s*($comp_month$)\s*', FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK6_1_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '?\s*($comp_month$)\s*', FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, '$'); + DEFMASK6_2_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '\s*($comp_month$)\s*', FULLYEAR_REGEXP, '\s+', DAYMM_REGEXP, '$'); + DEFMASK7_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + MASKSEP_REGEXP, '*\s*($comp_month$)\s*', DAYMM_REGEXP, '\s*,\s*', COMPYEAR_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK7_1_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '?\s*($comp_month$)\s*', DAYMM_REGEXP, '\s*,\s*', COMPYEAR_REGEXP, '$'); + DEFMASK7_2_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '\s*($comp_month$)\s*', DAYMM_REGEXP, '\s*,\s*', COMPYEAR_REGEXP, '$'); + DEFMASK8_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '*\s*($comp_month$)', + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK8_1_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '?\s*($comp_month$)$'); + DEFMASK8_2_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '\s*($comp_month$)$'); + DEFMASK9_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + MASKSEP_REGEXP, '*\s*($comp_month$)\s*', FULLYEAR_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK9_1_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '?\s*($comp_month$)\s*', FULLYEAR_REGEXP, '$'); + DEFMASK9_2_REGEXP CONSTANT VARCHAR := concat('^', MASKSEP_REGEXP, '\s*($comp_month$)\s*', FULLYEAR_REGEXP, '$'); + DEFMASK10_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*($comp_month$)\s*', MASKSEP_REGEXP, '\s*', COMPYEAR_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DEFMASK10_1_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*($comp_month$)\s*', MASKSEP_REGEXP, '\s*', COMPYEAR_REGEXP, '$'); + DOT_SLASH_DASH_COMPYEAR1_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + DAYMM_REGEXP, '\s*(?:\.|/|-)\s*', DAYMM_REGEXP, '\s*(?:\.|/|-)\s*', COMPYEAR_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DOT_SLASH_DASH_COMPYEAR1_1_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', COMPYEAR_REGEXP, '$'); + DOT_SLASH_DASH_SHORTYEAR_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', SHORTYEAR_REGEXP, '$'); + DOT_SLASH_DASH_FULLYEAR1_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + DAYMM_REGEXP, '\s*(?:\.|/|-)\s*', DAYMM_REGEXP, '\s*(?:\.|/|-)\s*', FULLYEAR_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + DOT_SLASH_DASH_FULLYEAR1_1_REGEXP CONSTANT VARCHAR := concat('^', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', FULLYEAR_REGEXP, '$'); + FULLYEAR_DOT_SLASH_DASH1_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*', + FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', DAYMM_REGEXP, + '\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + FULLYEAR_DOT_SLASH_DASH1_1_REGEXP CONSTANT VARCHAR := concat('^', FULLYEAR_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', DAYMM_REGEXP, '\s*', MASKSEP_REGEXP, '\s*', DAYMM_REGEXP, '$'); + SHORT_DIGITMASK1_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*\d{6}\s*(', HHMMSSFS_PART_REGEXP, ')?$'); + FULL_DIGITMASK1_0_REGEXP CONSTANT VARCHAR := concat('^(', HHMMSSFS_PART_REGEXP, ')?\s*\d{8}\s*(', HHMMSSFS_PART_REGEXP, ')?$'); +BEGIN + v_datatype := trim(p_datatype); + v_datetimestring := upper(trim(p_datetimestring)); + v_style := floor(p_style)::SMALLINT; + + v_datatype_groups := regexp_matches(v_datatype, DATATYPE_REGEXP, 'gi'); + + v_res_datatype := upper(v_datatype_groups[1]); + v_scale := v_datatype_groups[2]::SMALLINT; + + IF (v_res_datatype IS NULL) THEN + RAISE datatype_mismatch; + ELSIF (v_res_datatype <> 'DATETIME2' AND v_scale IS NOT NULL) + THEN + RAISE invalid_indicator_parameter_value; + ELSIF (coalesce(v_scale, 0) NOT BETWEEN 0 AND 7) + THEN + RAISE interval_field_overflow; + ELSIF (v_scale IS NULL) THEN + v_scale := 7; + END IF; + + IF (scale(p_style) > 0) THEN + RAISE most_specific_type_mismatch; + ELSIF (NOT ((v_style BETWEEN 0 AND 14) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 114) OR + (v_style IN (120, 121, 126, 127, 130, 131))) AND + v_res_datatype = 'DATETIME2') + THEN + RAISE invalid_parameter_value; + END IF; + + v_timepart := trim(substring(v_datetimestring, HHMMSSFS_PART_REGEXP)); + v_datestring := trim(regexp_replace(v_datetimestring, HHMMSSFS_PART_REGEXP, '', 'gi')); + + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(CONVERSION_LANG); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_escape_sequence; + END; + + v_date_format := coalesce(nullif(DATE_FORMAT, ''), v_lang_metadata_json ->> 'date_format'); + + v_compmonth_regexp := array_to_string(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_shortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_names'))), '|'); + + IF (v_datetimestring ~* replace(DEFMASK1_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK2_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK3_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK4_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK5_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK6_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK7_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK8_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK9_0_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datetimestring ~* replace(DEFMASK10_0_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + IF ((v_style IN (127, 130, 131) AND v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) OR + (v_style IN (130, 131) AND v_res_datatype = 'DATETIME2')) + THEN + RAISE invalid_datetime_format; + END IF; + + IF ((v_datestring ~* replace(DEFMASK1_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK2_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK3_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK4_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK5_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK6_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK7_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK8_2_REGEXP, '$comp_month$', v_compmonth_regexp) OR + v_datestring ~* replace(DEFMASK9_2_REGEXP, '$comp_month$', v_compmonth_regexp)) AND + v_res_datatype = 'DATETIME2') + THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_datestring ~* replace(DEFMASK1_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK1_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + + ELSIF (v_datestring ~* replace(DEFMASK2_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK2_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[1]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + + ELSIF (v_datestring ~* replace(DEFMASK3_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK3_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := v_regmatch_groups[1]; + + ELSIF (v_datestring ~* replace(DEFMASK4_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK4_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_year := v_regmatch_groups[1]; + + ELSIF (v_datestring ~* replace(DEFMASK5_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK5_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[1]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[2]); + + ELSIF (v_datestring ~* replace(DEFMASK6_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK6_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := v_regmatch_groups[2]; + + ELSIF (v_datestring ~* replace(DEFMASK7_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK7_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + + ELSIF (v_datestring ~* replace(DEFMASK8_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK8_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := v_regmatch_groups[1]; + + ELSIF (v_datestring ~* replace(DEFMASK9_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK9_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[1], v_lang_metadata_json); + v_year := v_regmatch_groups[2]; + + ELSIF (v_datestring ~* replace(DEFMASK10_1_REGEXP, '$comp_month$', v_compmonth_regexp)) + THEN + v_regmatch_groups := regexp_matches(v_datestring, replace(DEFMASK10_1_REGEXP, '$comp_month$', v_compmonth_regexp), 'gi'); + v_day := v_regmatch_groups[1]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_year := sys.babelfish_get_full_year(v_regmatch_groups[3]); + ELSE + RAISE invalid_character_value_for_cast; + END IF; + ELSIF (v_datetimestring ~* DOT_SLASH_DASH_COMPYEAR1_0_REGEXP) + THEN + IF (v_style IN (6, 7, 8, 9, 12, 13, 14, 24, 100, 106, 107, 108, 109, 112, 113, 114, 130) AND + v_res_datatype = 'DATETIME2') + THEN + RAISE invalid_regular_expression; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, DOT_SLASH_DASH_COMPYEAR1_1_REGEXP, 'gi'); + v_leftpart := v_regmatch_groups[1]; + v_middlepart := v_regmatch_groups[2]; + v_rightpart := v_regmatch_groups[3]; + + IF (v_datestring ~* DOT_SLASH_DASH_SHORTYEAR_REGEXP) + THEN + IF ((v_style NOT IN (0, 1, 2, 3, 4, 5, 10, 11) AND v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) OR + (v_style NOT IN (0, 1, 2, 3, 4, 5, 10, 11, 12) AND v_res_datatype = 'DATETIME2')) + THEN + RAISE invalid_datetime_format; + END IF; + + IF ((v_style IN (1, 10) AND v_date_format <> 'MDY' AND v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) OR + (v_style IN (0, 1, 10) AND v_date_format NOT IN ('DMY', 'DYM', 'MYD', 'YMD', 'YDM') AND v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) OR + (v_style IN (0, 1, 10, 22) AND v_date_format NOT IN ('DMY', 'DYM', 'MYD', 'YMD', 'YDM') AND v_res_datatype = 'DATETIME2') OR + (v_style IN (1, 10, 22) AND v_date_format IN ('DMY', 'DYM', 'MYD', 'YMD', 'YDM') AND v_res_datatype = 'DATETIME2')) + THEN + v_day := v_middlepart; + v_month := v_leftpart; + v_year := sys.babelfish_get_full_year(v_rightpart); + + ELSIF ((v_style IN (2, 11) AND v_date_format <> 'YMD') OR + (v_style IN (0, 2, 11) AND v_date_format = 'YMD')) + THEN + v_day := v_rightpart; + v_month := v_middlepart; + v_year := sys.babelfish_get_full_year(v_leftpart); + + ELSIF ((v_style IN (3, 4, 5) AND v_date_format <> 'DMY') OR + (v_style IN (0, 3, 4, 5) AND v_date_format = 'DMY')) + THEN + v_day := v_leftpart; + v_month := v_middlepart; + v_year := sys.babelfish_get_full_year(v_rightpart); + + ELSIF (v_style = 0 AND v_date_format = 'DYM') + THEN + v_day = v_leftpart; + v_month = v_rightpart; + v_year = sys.babelfish_get_full_year(v_middlepart); + + ELSIF (v_style = 0 AND v_date_format = 'MYD') + THEN + v_day := v_rightpart; + v_month := v_leftpart; + v_year = sys.babelfish_get_full_year(v_middlepart); + + ELSIF (v_style = 0 AND v_date_format = 'YDM') + THEN + IF (v_res_datatype = 'DATETIME2') THEN + RAISE character_not_in_repertoire; + END IF; + + v_day := v_middlepart; + v_month := v_rightpart; + v_year := sys.babelfish_get_full_year(v_leftpart); + ELSE + RAISE invalid_character_value_for_cast; + END IF; + ELSIF (v_datestring ~* DOT_SLASH_DASH_FULLYEAR1_1_REGEXP) + THEN + IF (v_style NOT IN (0, 20, 21, 101, 102, 103, 104, 105, 110, 111, 120, 121, 130, 131) AND + v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) + THEN + RAISE invalid_datetime_format; + ELSIF (v_style IN (130, 131) AND v_res_datatype = 'SMALLDATETIME') THEN + RAISE invalid_character_value_for_cast; + END IF; + + v_year := v_rightpart; + IF (v_leftpart::SMALLINT <= 12) + THEN + IF ((v_style IN (103, 104, 105, 130, 131) AND v_date_format NOT IN ('DMY', 'DYM', 'YDM')) OR + (v_style IN (0, 103, 104, 105, 130, 131) AND ((v_date_format = 'DMY' AND v_res_datatype = 'DATETIME2') OR + (v_date_format IN ('DMY', 'DYM', 'YDM') AND v_res_datatype <> 'DATETIME2'))) OR + (v_style IN (103, 104, 105, 130, 131) AND v_date_format IN ('DMY', 'DYM', 'YDM') AND v_res_datatype = 'DATETIME2')) + THEN + v_day := v_leftpart; + v_month := v_middlepart; + + ELSIF ((v_style IN (20, 21, 101, 102, 110, 111, 120, 121) AND v_date_format IN ('DMY', 'DYM', 'YDM') AND v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) OR + (v_style IN (0, 20, 21, 101, 102, 110, 111, 120, 121) AND v_date_format NOT IN ('DMY', 'DYM', 'YDM') AND v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) OR + (v_style IN (101, 110) AND v_date_format IN ('DMY', 'DYM', 'MYD', 'YDM') AND v_res_datatype = 'DATETIME2') OR + (v_style IN (0, 101, 110) AND v_date_format NOT IN ('DMY', 'DYM', 'MYD', 'YDM') AND v_res_datatype = 'DATETIME2')) + THEN + v_day := v_middlepart; + v_month := v_leftpart; + END IF; + ELSE + IF ((v_style IN (103, 104, 105, 130, 131) AND v_date_format NOT IN ('DMY', 'DYM', 'YDM')) OR + (v_style IN (0, 103, 104, 105, 130, 131) AND ((v_date_format = 'DMY' AND v_res_datatype = 'DATETIME2') OR + (v_date_format IN ('DMY', 'DYM', 'YDM') AND v_res_datatype <> 'DATETIME2'))) OR + (v_style IN (103, 104, 105, 130, 131) AND v_date_format IN ('DMY', 'DYM', 'YDM') AND v_res_datatype = 'DATETIME2')) + THEN + v_day := v_leftpart; + v_month := v_middlepart; + ELSE + IF (v_res_datatype = 'DATETIME2') THEN + RAISE invalid_datetime_format; + END IF; + + RAISE invalid_character_value_for_cast; + END IF; + END IF; + END IF; + ELSIF (v_datetimestring ~* FULLYEAR_DOT_SLASH_DASH1_0_REGEXP) + THEN + IF (v_style NOT IN (0, 20, 21, 101, 102, 103, 104, 105, 110, 111, 120, 121, 130, 131) AND + v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) + THEN + RAISE invalid_datetime_format; + ELSIF (v_style IN (6, 7, 8, 9, 12, 13, 14, 24, 100, 106, 107, 108, 109, 112, 113, 114, 130) AND + v_res_datatype = 'DATETIME2') + THEN + RAISE invalid_regular_expression; + ELSIF (v_style IN (130, 131) AND v_res_datatype = 'SMALLDATETIME') + THEN + RAISE invalid_character_value_for_cast; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, FULLYEAR_DOT_SLASH_DASH1_1_REGEXP, 'gi'); + v_year := v_regmatch_groups[1]; + v_middlepart := v_regmatch_groups[2]; + v_rightpart := v_regmatch_groups[3]; + + IF ((v_res_datatype IN ('DATETIME', 'SMALLDATETIME') AND v_rightpart::SMALLINT <= 12) OR v_res_datatype = 'DATETIME2') + THEN + IF ((v_style IN (20, 21, 101, 102, 110, 111, 120, 121) AND v_date_format IN ('DMY', 'DYM', 'YDM') AND v_res_datatype <> 'DATETIME2') OR + (v_style IN (0, 20, 21, 101, 102, 110, 111, 120, 121) AND v_date_format NOT IN ('DMY', 'DYM', 'YDM') AND v_res_datatype <> 'DATETIME2') OR + (v_style IN (0, 20, 21, 23, 25, 101, 102, 110, 111, 120, 121, 126, 127) AND v_res_datatype = 'DATETIME2')) + THEN + v_day := v_rightpart; + v_month := v_middlepart; + + ELSIF ((v_style IN (103, 104, 105, 130, 131) AND v_date_format NOT IN ('DMY', 'DYM', 'YDM')) OR + v_style IN (0, 103, 104, 105, 130, 131) AND v_date_format IN ('DMY', 'DYM', 'YDM')) + THEN + v_day := v_middlepart; + v_month := v_rightpart; + END IF; + ELSIF (v_res_datatype IN ('DATETIME', 'SMALLDATETIME') AND v_rightpart::SMALLINT > 12) + THEN + IF ((v_style IN (20, 21, 101, 102, 110, 111, 120, 121) AND v_date_format IN ('DMY', 'DYM', 'YDM')) OR + (v_style IN (0, 20, 21, 101, 102, 110, 111, 120, 121) AND v_date_format NOT IN ('DMY', 'DYM', 'YDM'))) + THEN + v_day := v_rightpart; + v_month := v_middlepart; + + ELSIF ((v_style IN (103, 104, 105, 130, 131) AND v_date_format NOT IN ('DMY', 'DYM', 'YDM')) OR + (v_style IN (0, 103, 104, 105, 130, 131) AND v_date_format IN ('DMY', 'DYM', 'YDM'))) + THEN + RAISE invalid_character_value_for_cast; + END IF; + END IF; + ELSIF (v_datetimestring ~* SHORT_DIGITMASK1_0_REGEXP OR + v_datetimestring ~* FULL_DIGITMASK1_0_REGEXP) + THEN + IF (v_style = 127 AND v_res_datatype <> 'DATETIME2') + THEN + RAISE invalid_datetime_format; + ELSIF (v_style IN (130, 131) AND v_res_datatype = 'SMALLDATETIME') + THEN + RAISE invalid_character_value_for_cast; + END IF; + + IF (v_datestring ~* '^\d{6}$') + THEN + v_day := substr(v_datestring, 5, 2); + v_month := substr(v_datestring, 3, 2); + v_year := sys.babelfish_get_full_year(substr(v_datestring, 1, 2)); + + ELSIF (v_datestring ~* '^\d{8}$') + THEN + v_day := substr(v_datestring, 7, 2); + v_month := substr(v_datestring, 5, 2); + v_year := substr(v_datestring, 1, 4); + END IF; + ELSIF (v_datetimestring ~* HHMMSSFS_REGEXP) + THEN + v_day := '01'; + v_month := '01'; + v_year := '1900'; + ELSE + RAISE invalid_datetime_format; + END IF; + + IF (((v_datetimestring ~* HHMMSSFS_PART_REGEXP AND v_res_datatype = 'DATETIME2') OR + (v_datetimestring ~* SHORT_DIGITMASK1_0_REGEXP OR v_datetimestring ~* FULL_DIGITMASK1_0_REGEXP OR + v_datetimestring ~* FULLYEAR_DOT_SLASH_DASH1_0_REGEXP OR v_datetimestring ~* DOT_SLASH_DASH_FULLYEAR1_0_REGEXP)) AND + v_style IN (130, 131)) + THEN + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_year) - 1; + v_day = to_char(v_hijridate, 'DD'); + v_month = to_char(v_hijridate, 'MM'); + v_year = to_char(v_hijridate, 'YYYY'); + END IF; + + v_hours := coalesce(sys.babelfish_get_timeunit_from_string(v_timepart, 'HOURS'), '0'); + v_minutes := coalesce(sys.babelfish_get_timeunit_from_string(v_timepart, 'MINUTES'), '0'); + v_seconds := coalesce(sys.babelfish_get_timeunit_from_string(v_timepart, 'SECONDS'), '0'); + v_fseconds := coalesce(sys.babelfish_get_timeunit_from_string(v_timepart, 'FRACTSECONDS'), '0'); + + IF ((v_res_datatype IN ('DATETIME', 'SMALLDATETIME') OR + (v_res_datatype = 'DATETIME2' AND v_timepart !~* HHMMSSFS_DOT_PART_REGEXP)) AND + char_length(v_fseconds) > 3) + THEN + RAISE invalid_datetime_format; + END IF; + + BEGIN + IF (v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) + THEN + v_resdatetime := sys.datetimefromparts(v_year, v_month, v_day, + v_hours, v_minutes, v_seconds, + rpad(v_fseconds, 3, '0')); + IF (v_res_datatype = 'SMALLDATETIME' AND + to_char(v_resdatetime, 'SS') <> '00') + THEN + IF (to_char(v_resdatetime, 'SS')::SMALLINT >= 30) THEN + v_resdatetime := v_resdatetime + INTERVAL '1 minute'; + END IF; + + v_resdatetime := to_timestamp(to_char(v_resdatetime, 'DD.MM.YYYY.HH24.MI'), 'DD.MM.YYYY.HH24.MI'); + END IF; + ELSIF (v_res_datatype = 'DATETIME2') + THEN + v_fseconds := sys.babelfish_get_microsecs_from_fractsecs(v_fseconds, v_scale); + v_seconds := concat_ws('.', v_seconds, v_fseconds); + + v_resdatetime := make_timestamp(v_year::SMALLINT, v_month::SMALLINT, v_day::SMALLINT, + v_hours::SMALLINT, v_minutes::SMALLINT, v_seconds::NUMERIC); + END IF; + EXCEPTION + WHEN datetime_field_overflow THEN + RAISE invalid_datetime_format; + WHEN OTHERS THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + + IF (v_err_message ~* 'Cannot construct data type') THEN + RAISE invalid_character_value_for_cast; + END IF; + END; + + RETURN v_resdatetime; +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 3 of conv_string_to_datetime function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('The style %s is not supported for conversions from VARCHAR to %s.', v_style, v_res_datatype), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_regular_expression THEN + RAISE USING MESSAGE := format('The input character string doesn''t follow style %s.', v_style), + DETAIL := 'Selected "style" param value isn''t valid for conversion of passed character string.', + HINT := 'Either change the input character string or use a different style.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Data type should be one of these values: ''DATETIME'', ''SMALLDATETIME'', ''DATETIME2''/''DATETIME2(n)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_indicator_parameter_value THEN + RAISE USING MESSAGE := format('Invalid attributes specified for data type %s.', v_res_datatype), + DETAIL := 'Use of incorrect scale value, which is not corresponding to specified data type.', + HINT := 'Change data type scale component or select different data type and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := format('Specified scale %s is invalid.', v_scale), + DETAIL := 'Use of incorrect data type scale value during conversion process.', + HINT := 'Change scale component of data type parameter to be in range [0..7] and try again.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := CASE v_res_datatype + WHEN 'SMALLDATETIME' THEN 'Conversion failed when converting character string to SMALLDATETIME data type.' + ELSE 'Conversion failed when converting date and time from character string.' + END, + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := 'The conversion of a VARCHAR data type to a DATETIME data type resulted in an out-of-range value.', + DETAIL := 'Use of incorrect pair of input parameter values during conversion process.', + HINT := 'Check input parameter values, correct them if needed, and try again.'; + + WHEN character_not_in_repertoire THEN + RAISE USING MESSAGE := 'The YDM date format isn''t supported when converting from this string format to date and time.', + DETAIL := 'Use of incorrect DATE_FORMAT constant value regarding string format parameter during conversion process.', + HINT := 'Change DATE_FORMAT constant to one of these values: MDY|DMY|DYM, recompile function and try again.'; + + WHEN invalid_escape_sequence THEN + RAISE USING MESSAGE := format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG), + DETAIL := 'Compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Correct CONVERSION_LANG constant value in function''s body, recompile it and try again.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Passed argument value contains illegal characters.', + HINT := 'Correct passed argument value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_string_to_time(IN p_datatype TEXT, + IN p_timestring TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIME WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_hours SMALLINT; + v_style SMALLINT; + v_scale SMALLINT; + v_daypart VARCHAR; + v_seconds VARCHAR; + v_minutes SMALLINT; + v_fseconds VARCHAR; + v_datatype VARCHAR; + v_timestring VARCHAR; + v_err_message VARCHAR; + v_src_datatype VARCHAR; + v_timeunit_mask VARCHAR; + v_datatype_groups TEXT[]; + v_regmatch_groups TEXT[]; + AMPM_REGEXP CONSTANT VARCHAR := '\s*([AP]M)'; + TIMEUNIT_REGEXP CONSTANT VARCHAR := '\s*(\d{1,2})\s*'; + FRACTSECS_REGEXP CONSTANT VARCHAR := '\s*(\d{1,9})'; + HHMMSSFS_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, + '\:', TIMEUNIT_REGEXP, + '\:', TIMEUNIT_REGEXP, + '(?:\.|\:)', FRACTSECS_REGEXP, '$'); + HHMMSS_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '$'); + HHMMFS_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\.', FRACTSECS_REGEXP, '$'); + HHMM_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '$'); + HH_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '$'); + DATATYPE_REGEXP CONSTANT VARCHAR := '^(TIME)\s*(?:\()?\s*((?:-)?\d+)?\s*(?:\))?$'; +BEGIN + v_datatype := trim(regexp_replace(p_datatype, 'DATETIME', 'TIME', 'gi')); + v_timestring := upper(trim(p_timestring)); + v_style := floor(p_style)::SMALLINT; + + v_datatype_groups := regexp_matches(v_datatype, DATATYPE_REGEXP, 'gi'); + + v_src_datatype := upper(v_datatype_groups[1]); + v_scale := v_datatype_groups[2]::SMALLINT; + + IF (v_src_datatype IS NULL) THEN + RAISE datatype_mismatch; + ELSIF (coalesce(v_scale, 0) NOT BETWEEN 0 AND 7) + THEN + RAISE interval_field_overflow; + ELSIF (v_scale IS NULL) THEN + v_scale := 7; + END IF; + + IF (scale(p_style) > 0) THEN + RAISE most_specific_type_mismatch; + ELSIF (NOT ((v_style BETWEEN 0 AND 14) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 114) OR + v_style IN (120, 121, 126, 127, 130, 131))) + THEN + RAISE invalid_parameter_value; + END IF; + + v_daypart := substring(v_timestring, 'AM|PM'); + v_timestring := trim(regexp_replace(v_timestring, coalesce(v_daypart, ''), '')); + + v_timeunit_mask := + CASE + WHEN (v_timestring ~* HHMMSSFS_REGEXP) THEN HHMMSSFS_REGEXP + WHEN (v_timestring ~* HHMMSS_REGEXP) THEN HHMMSS_REGEXP + WHEN (v_timestring ~* HHMMFS_REGEXP) THEN HHMMFS_REGEXP + WHEN (v_timestring ~* HHMM_REGEXP) THEN HHMM_REGEXP + WHEN (v_timestring ~* HH_REGEXP) THEN HH_REGEXP + END; + + IF (v_timeunit_mask IS NULL) THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_timestring, v_timeunit_mask, 'gi'); + + v_hours := v_regmatch_groups[1]::SMALLINT; + v_minutes := v_regmatch_groups[2]::SMALLINT; + + IF (v_timestring ~* HHMMFS_REGEXP) THEN + v_fseconds := v_regmatch_groups[3]; + ELSE + v_seconds := v_regmatch_groups[3]; + v_fseconds := v_regmatch_groups[4]; + END IF; + + IF (v_daypart IS NOT NULL) THEN + IF ((v_daypart = 'AM' AND v_hours NOT BETWEEN 0 AND 12) OR + (v_daypart = 'PM' AND v_hours NOT BETWEEN 1 AND 23)) + THEN + RAISE numeric_value_out_of_range; + ELSIF (v_daypart = 'PM' AND v_hours < 12) THEN + v_hours := v_hours + 12; + ELSIF (v_daypart = 'AM' AND v_hours = 12) THEN + v_hours := v_hours - 12; + END IF; + END IF; + + v_fseconds := sys.babelfish_get_microsecs_from_fractsecs(v_fseconds, v_scale); + v_seconds := concat_ws('.', v_seconds, v_fseconds); + + RETURN make_time(v_hours, v_minutes, v_seconds::NUMERIC); +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 3 of conv_string_to_time function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('The style %s is not supported for conversions from VARCHAR to TIME.', v_style), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Source data type should be ''TIME'' or ''TIME(n)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := format('Specified scale %s is invalid.', v_scale), + DETAIL := 'Use of incorrect data type scale value during conversion process.', + HINT := 'Change scale component of data type parameter to be in range [0..7] and try again.'; + + WHEN numeric_value_out_of_range THEN + RAISE USING MESSAGE := 'Could not extract correct hour value due to it''s inconsistency with AM|PM day part mark.', + DETAIL := 'Extracted hour value doesn''t fall in correct day part mark range: 0..12 for "AM" or 1..23 for "PM".', + HINT := 'Correct a hour value in the source string or remove AM|PM day part mark out of it.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := 'Conversion failed when converting time from character string.', + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_time_to_string(IN p_datatype TEXT, + IN p_src_datatype TEXT, + IN p_timeval TIME(6) WITHOUT TIME ZONE, + IN p_style NUMERIC DEFAULT 25) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_hours VARCHAR; + v_style SMALLINT; + v_scale SMALLINT; + v_resmask VARCHAR; + v_fseconds VARCHAR; + v_datatype VARCHAR; + v_resstring VARCHAR; + v_lengthexpr VARCHAR; + v_res_length SMALLINT; + v_res_datatype VARCHAR; + v_src_datatype VARCHAR; + v_res_maxlength SMALLINT; + VARCHAR_MAX CONSTANT SMALLINT := 8000; + NVARCHAR_MAX CONSTANT SMALLINT := 4000; + -- We use the regex below to make sure input p_datatype is one of them + DATATYPE_REGEXP CONSTANT VARCHAR := '^\s*(CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*$'; + -- We use the regex below to get the length of the datatype, if specified + -- For example, to get the '10' out of 'varchar(10)' + DATATYPE_MASK_REGEXP CONSTANT VARCHAR := '^\s*(?:CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*\(\s*(\d+|MAX)\s*\)\s*$'; + SRCDATATYPE_MASK_REGEXP VARCHAR := '^\s*(?:TIME)\s*(?:\s*\(\s*(\d+)\s*\)\s*)?\s*$'; +BEGIN + v_datatype := upper(trim(p_datatype)); + v_src_datatype := upper(trim(p_src_datatype)); + v_style := floor(p_style)::SMALLINT; + + IF (v_src_datatype ~* SRCDATATYPE_MASK_REGEXP) + THEN + v_scale := coalesce(substring(v_src_datatype, SRCDATATYPE_MASK_REGEXP)::SMALLINT, 7); + + IF (v_scale NOT BETWEEN 0 AND 7) THEN + RAISE invalid_regular_expression; + END IF; + ELSE + RAISE most_specific_type_mismatch; + END IF; + + IF (v_datatype ~* DATATYPE_MASK_REGEXP) + THEN + v_res_datatype := rtrim(split_part(v_datatype, '(', 1)); + + v_res_maxlength := CASE + WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX + ELSE NVARCHAR_MAX + END; + + v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP); + + IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4) THEN + RAISE interval_field_overflow; + END IF; + + v_res_length := CASE v_lengthexpr + WHEN 'MAX' THEN v_res_maxlength + ELSE v_lengthexpr::SMALLINT + END; + ELSIF (v_datatype ~* DATATYPE_REGEXP) THEN + v_res_datatype := v_datatype; + ELSE + RAISE datatype_mismatch; + END IF; + + IF (scale(p_style) > 0) THEN + RAISE escape_character_conflict; + ELSIF (NOT ((v_style BETWEEN 0 AND 14) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 114) OR + v_style IN (120, 121, 126, 127, 130, 131))) + THEN + RAISE invalid_parameter_value; + ELSIF ((v_style BETWEEN 1 AND 7) OR + (v_style BETWEEN 10 AND 12) OR + (v_style BETWEEN 101 AND 107) OR + (v_style BETWEEN 110 AND 112) OR + v_style = 23) + THEN + RAISE invalid_datetime_format; + END IF; + + v_hours := ltrim(to_char(p_timeval, 'HH12'), '0'); + v_fseconds := sys.babelfish_get_microsecs_from_fractsecs(to_char(p_timeval, 'US'), v_scale); + + IF (v_scale = 7) THEN + v_fseconds := concat(v_fseconds, '0'); + END IF; + + IF (v_style IN (0, 100)) + THEN + v_resmask := concat(v_hours, ':MIAM'); + ELSIF (v_style IN (8, 20, 24, 108, 120)) + THEN + v_resmask := 'HH24:MI:SS'; + ELSIF (v_style IN (9, 109)) + THEN + v_resmask := CASE + WHEN (char_length(v_fseconds) = 0) THEN concat(v_hours, ':MI:SSAM') + ELSE format('%s:MI:SS.%sAM', v_hours, v_fseconds) + END; + ELSIF (v_style IN (13, 14, 21, 25, 113, 114, 121, 126, 127)) + THEN + v_resmask := CASE + WHEN (char_length(v_fseconds) = 0) THEN 'HH24:MI:SS' + ELSE concat('HH24:MI:SS.', v_fseconds) + END; + ELSIF (v_style = 22) + THEN + v_resmask := format('%s:MI:SS AM', lpad(v_hours, 2, ' ')); + ELSIF (v_style IN (130, 131)) + THEN + v_resmask := CASE + WHEN (char_length(v_fseconds) = 0) THEN concat(lpad(v_hours, 2, ' '), ':MI:SSAM') + ELSE format('%s:MI:SS.%sAM', lpad(v_hours, 2, ' '), v_fseconds) + END; + END IF; + + v_resstring := to_char(p_timeval, v_resmask); + + v_resstring := substring(v_resstring, 1, coalesce(v_res_length, char_length(v_resstring))); + v_res_length := coalesce(v_res_length, + CASE v_res_datatype + WHEN 'CHAR' THEN 30 + ELSE 60 + END); + RETURN CASE + WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring + ELSE rpad(v_resstring, v_res_length, ' ') + END; +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Source data type should be ''TIME'' or ''TIME(n)''.', + DETAIL := 'Use of incorrect "src_datatype" parameter value during conversion process.', + HINT := 'Change "src_datatype" parameter to the proper value and try again.'; + + WHEN invalid_regular_expression THEN + RAISE USING MESSAGE := format('The source data type scale (%s) given to the convert specification exceeds the maximum allowable value (7).', + v_scale), + DETAIL := 'Use of incorrect scale value of source data type parameter during conversion process.', + HINT := 'Change scale component of source data type parameter to the allowable value and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := format('The size (%s) given to the convert specification ''%s'' exceeds the maximum allowed for any data type (%s).', + v_lengthexpr, lower(v_res_datatype), v_res_maxlength), + DETAIL := 'Use of incorrect size value of target data type parameter during conversion process.', + HINT := 'Change size component of data type parameter to the allowable value and try again.'; + + WHEN escape_character_conflict THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 4 of convert function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('%s is not a valid style number when converting from TIME to a character string.', v_style), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Data type should be one of these values: ''CHAR(n|MAX)'', ''NCHAR(n|MAX)'', ''VARCHAR(n|MAX)'', ''NVARCHAR(n|MAX)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := format('Error converting data type TIME to %s.', + rtrim(split_part(trim(p_datatype), '(', 1))), + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +create or replace function sys.babelfish_dbts() +returns bigint as +$BODY$ +declare + v_res bigint; +begin + SELECT last_value INTO v_res FROM sys_data.inc_seq_rowversion; + return v_res; +end; +$BODY$ +language plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_full_year(IN p_short_year TEXT, + IN p_base_century TEXT DEFAULT '', + IN p_year_cutoff NUMERIC DEFAULT 49) +RETURNS VARCHAR +AS +$BODY$ +DECLARE + v_err_message VARCHAR; + v_full_year SMALLINT; + v_short_year SMALLINT; + v_base_century SMALLINT; + v_result_param_set JSONB; + v_full_year_res_jsonb JSONB; +BEGIN + v_short_year := p_short_year::SMALLINT; + + BEGIN + v_full_year_res_jsonb := nullif(current_setting('sys.full_year_res_json'), '')::JSONB; + EXCEPTION + WHEN undefined_object THEN + v_full_year_res_jsonb := NULL; + END; + + SELECT result + INTO v_full_year + FROM jsonb_to_recordset(v_full_year_res_jsonb) AS result_set (param1 SMALLINT, + param2 TEXT, + param3 NUMERIC, + result VARCHAR) + WHERE param1 = v_short_year + AND param2 = p_base_century + AND param3 = p_year_cutoff; + + IF (v_full_year IS NULL) + THEN + IF (v_short_year <= 99) + THEN + v_base_century := CASE + WHEN (p_base_century ~ '^\s*([1-9]{1,2})\s*$') THEN concat(trim(p_base_century), '00')::SMALLINT + ELSE trunc(extract(year from current_date)::NUMERIC, -2) + END; + + v_full_year = v_base_century + v_short_year; + v_full_year = CASE + WHEN (v_short_year > p_year_cutoff) THEN v_full_year - 100 + ELSE v_full_year + END; + ELSE v_full_year := v_short_year; + END IF; + + v_result_param_set := jsonb_build_object('param1', v_short_year, + 'param2', p_base_century, + 'param3', p_year_cutoff, + 'result', v_full_year); + v_full_year_res_jsonb := CASE + WHEN (v_full_year_res_jsonb IS NULL) THEN jsonb_build_array(v_result_param_set) + ELSE v_full_year_res_jsonb || v_result_param_set + END; + + PERFORM set_config('sys.full_year_res_json', + v_full_year_res_jsonb::TEXT, + FALSE); + END IF; + + RETURN v_full_year; +EXCEPTION + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +STABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_int_part(IN p_srcnumber DOUBLE PRECISION) +RETURNS DOUBLE PRECISION +AS +$BODY$ +BEGIN + RETURN CASE + WHEN (p_srcnumber < -0.0000001) THEN ceil(p_srcnumber - 0.0000001) + ELSE floor(p_srcnumber + 0.0000001) + END; +END; +$BODY$ +LANGUAGE plpgsql +STABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_jobs () +RETURNS table( job integer, what text, search_path varchar ) +AS +$body$ +DECLARE + var_job integer; + var_what text; + var_search_path varchar; +BEGIN + + SELECT js.job_step_id, js.command, '' + FROM sys.sysjobschedules s + INNER JOIN sys.sysjobs j on j.job_id = s.job_id + INNER JOIN sys.sysjobsteps js ON js.job_id = j.job_id + INTO var_job, var_what, var_search_path + WHERE (s.next_run_date + s.next_run_time) <= now()::timestamp + AND j.enabled = 1 + ORDER BY (s.next_run_date + s.next_run_time) ASC + LIMIT 1; + + IF var_job > 0 + THEN + return query select var_job, var_what, var_search_path; + END IF; + +END; +$body$ +LANGUAGE 'plpgsql'; +CREATE OR REPLACE FUNCTION sys.babelfish_get_lang_metadata_json(IN p_lang_spec_culture TEXT) +RETURNS JSONB +AS +$BODY$ +DECLARE + v_locale_parts TEXT[]; + v_lang_data_jsonb JSONB; + v_lang_spec_culture VARCHAR; + v_is_cached BOOLEAN := FALSE; +BEGIN + v_lang_spec_culture := upper(trim(p_lang_spec_culture)); + + IF (char_length(v_lang_spec_culture) > 0) + THEN + BEGIN + v_lang_data_jsonb := nullif(current_setting(format('sys.lang_metadata_json.%s', + v_lang_spec_culture)), '')::JSONB; + EXCEPTION + WHEN undefined_object THEN + v_lang_data_jsonb := NULL; + END; + + IF (v_lang_data_jsonb IS NULL) + THEN + IF (v_lang_spec_culture IN ('AR', 'FI') OR + v_lang_spec_culture ~ '-') + THEN + SELECT lang_data_jsonb + INTO STRICT v_lang_data_jsonb + FROM sys.syslanguages + WHERE spec_culture = v_lang_spec_culture; + ELSE + SELECT lang_data_jsonb + INTO STRICT v_lang_data_jsonb + FROM sys.syslanguages + WHERE lang_name_mssql = v_lang_spec_culture + OR lang_alias_mssql = v_lang_spec_culture; + END IF; + ELSE + v_is_cached := TRUE; + END IF; + ELSE + v_lang_spec_culture := current_setting('LC_TIME'); + + v_lang_spec_culture := CASE + WHEN (v_lang_spec_culture !~ '\.') THEN v_lang_spec_culture + ELSE substring(v_lang_spec_culture, '(.*)(?:\.)') + END; + + v_lang_spec_culture := upper(regexp_replace(v_lang_spec_culture, '_|,\s*', '-', 'gi')); + + BEGIN + v_lang_data_jsonb := nullif(current_setting(format('sys.lang_metadata_json.%s', + v_lang_spec_culture)), '')::JSONB; + EXCEPTION + WHEN undefined_object THEN + v_lang_data_jsonb := NULL; + END; + + IF (v_lang_data_jsonb IS NULL) + THEN + BEGIN + IF (char_length(v_lang_spec_culture) = 5) + THEN + SELECT lang_data_jsonb + INTO STRICT v_lang_data_jsonb + FROM sys.syslanguages + WHERE spec_culture = v_lang_spec_culture; + ELSE + v_locale_parts := string_to_array(v_lang_spec_culture, '-'); + + SELECT lang_data_jsonb + INTO STRICT v_lang_data_jsonb + FROM sys.syslanguages + WHERE lang_name_pg = v_locale_parts[1] + AND territory = v_locale_parts[2]; + END IF; + EXCEPTION + WHEN OTHERS THEN + v_lang_spec_culture := 'EN-US'; + + SELECT lang_data_jsonb + INTO v_lang_data_jsonb + FROM sys.syslanguages + WHERE spec_culture = v_lang_spec_culture; + END; + ELSE + v_is_cached := TRUE; + END IF; + END IF; + + IF (NOT v_is_cached) THEN + PERFORM set_config(format('sys.lang_metadata_json.%s', + v_lang_spec_culture), + v_lang_data_jsonb::TEXT, + FALSE); + END IF; + + RETURN v_lang_data_jsonb; +EXCEPTION + WHEN invalid_text_representation THEN + RAISE USING MESSAGE := format('The language metadata JSON value extracted from chache is not a valid JSON object.', + p_lang_spec_culture), + HINT := 'Drop the current session, fix the appropriate record in "sys.syslanguages" table, and try again after reconnection.'; + + WHEN OTHERS THEN + RAISE USING MESSAGE := format('"%s" is not a valid special culture or language name parameter.', + p_lang_spec_culture), + DETAIL := 'Use of incorrect "lang_spec_culture" parameter value during conversion process.', + HINT := 'Change "lang_spec_culture" parameter to the proper value and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_microsecs_from_fractsecs(IN p_fractsecs TEXT, + IN p_scale NUMERIC DEFAULT 7) +RETURNS VARCHAR +AS +$BODY$ +DECLARE + v_scale SMALLINT; + v_decplaces INTEGER; + v_fractsecs VARCHAR; + v_pureplaces VARCHAR; + v_rnd_fractsecs INTEGER; + v_fractsecs_len INTEGER; + v_pureplaces_len INTEGER; + v_err_message VARCHAR; +BEGIN + v_fractsecs := trim(p_fractsecs); + v_fractsecs_len := char_length(v_fractsecs); + v_scale := floor(p_scale)::SMALLINT; + + IF (v_fractsecs_len < 7) THEN + v_fractsecs := rpad(v_fractsecs, 7, '0'); + v_fractsecs_len := char_length(v_fractsecs); + END IF; + + v_pureplaces := trim(leading '0' from v_fractsecs); + v_pureplaces_len := char_length(v_pureplaces); + + v_decplaces := v_fractsecs_len - v_pureplaces_len; + + v_rnd_fractsecs := round(v_fractsecs::INTEGER, (v_pureplaces_len - (v_scale - (v_fractsecs_len - v_pureplaces_len))) * (-1)); + + v_fractsecs := concat(replace(rpad('', v_decplaces), ' ', '0'), v_rnd_fractsecs); + + RETURN substring(v_fractsecs, 1, CASE + WHEN (v_scale >= 7) THEN 6 + ELSE v_scale + END); +EXCEPTION + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +STABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_monthnum_by_name(IN p_monthname TEXT, + IN p_lang_metadata_json JSONB) +RETURNS VARCHAR +AS +$BODY$ +DECLARE + v_monthname TEXT; + v_monthnum SMALLINT; +BEGIN + v_monthname := lower(trim(p_monthname)); + + v_monthnum := array_position(ARRAY(SELECT lower(jsonb_array_elements_text(p_lang_metadata_json -> 'months_shortnames'))), v_monthname); + + v_monthnum := coalesce(v_monthnum, + array_position(ARRAY(SELECT lower(jsonb_array_elements_text(p_lang_metadata_json -> 'months_names'))), v_monthname)); + + v_monthnum := coalesce(v_monthnum, + array_position(ARRAY(SELECT lower(jsonb_array_elements_text(p_lang_metadata_json -> 'months_extrashortnames'))), v_monthname)); + + v_monthnum := coalesce(v_monthnum, + array_position(ARRAY(SELECT lower(jsonb_array_elements_text(p_lang_metadata_json -> 'months_extranames'))), v_monthname)); + + IF (v_monthnum IS NULL) THEN + RAISE datetime_field_overflow; + END IF; + + RETURN v_monthnum; +EXCEPTION + WHEN datetime_field_overflow THEN + RAISE USING MESSAGE := format('Can not convert value "%s" to a correct month number.', + trim(p_monthname)), + DETAIL := 'Supplied month name is not valid.', + HINT := 'Correct supplied month name value and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_service_setting ( + IN p_service sys.service_settings.service%TYPE + , IN p_setting sys.service_settings.setting%TYPE +) +RETURNS sys.service_settings.value%TYPE +AS +$BODY$ +DECLARE + settingValue sys.service_settings.value%TYPE; +BEGIN + SELECT value + INTO settingValue + FROM sys.service_settings + WHERE service = p_service + AND setting = p_setting; + + RETURN settingValue; +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_timeunit_from_string(IN p_timepart TEXT, + IN p_timeunit TEXT) +RETURNS VARCHAR +AS +$BODY$ +DECLARE + v_hours VARCHAR; + v_minutes VARCHAR; + v_seconds VARCHAR; + v_fractsecs VARCHAR; + v_daypart VARCHAR; + v_timepart VARCHAR; + v_timeunit VARCHAR; + v_err_message VARCHAR; + v_timeunit_mask VARCHAR; + v_regmatch_groups TEXT[]; + AMPM_REGEXP CONSTANT VARCHAR := '\s*([AP]M)'; + TIMEUNIT_REGEXP CONSTANT VARCHAR := '\s*(\d{1,2})\s*'; + FRACTSECS_REGEXP CONSTANT VARCHAR := '\s*(\d{1,9})'; + HHMMSSFS_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, + '\:', TIMEUNIT_REGEXP, + '\:', TIMEUNIT_REGEXP, + '(?:\.|\:)', FRACTSECS_REGEXP, '$'); + HHMMSS_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '$'); + HHMMFS_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '\.', FRACTSECS_REGEXP, '$'); + HHMM_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '\:', TIMEUNIT_REGEXP, '$'); + HH_REGEXP CONSTANT VARCHAR := concat('^', TIMEUNIT_REGEXP, '$'); +BEGIN + v_timepart := upper(trim(p_timepart)); + v_timeunit := upper(trim(p_timeunit)); + + v_daypart := substring(v_timepart, 'AM|PM'); + v_timepart := trim(regexp_replace(v_timepart, coalesce(v_daypart, ''), '')); + + v_timeunit_mask := + CASE + WHEN (v_timepart ~* HHMMSSFS_REGEXP) THEN HHMMSSFS_REGEXP + WHEN (v_timepart ~* HHMMSS_REGEXP) THEN HHMMSS_REGEXP + WHEN (v_timepart ~* HHMMFS_REGEXP) THEN HHMMFS_REGEXP + WHEN (v_timepart ~* HHMM_REGEXP) THEN HHMM_REGEXP + WHEN (v_timepart ~* HH_REGEXP) THEN HH_REGEXP + END; + + v_regmatch_groups := regexp_matches(v_timepart, v_timeunit_mask, 'gi'); + + v_hours := v_regmatch_groups[1]; + v_minutes := v_regmatch_groups[2]; + + IF (v_timepart ~* HHMMFS_REGEXP) THEN + v_fractsecs := v_regmatch_groups[3]; + ELSE + v_seconds := v_regmatch_groups[3]; + v_fractsecs := v_regmatch_groups[4]; + END IF; + + IF (v_timeunit = 'HOURS' AND v_daypart IS NOT NULL) + THEN + IF ((v_daypart = 'AM' AND v_hours::SMALLINT NOT BETWEEN 0 AND 12) OR + (v_daypart = 'PM' AND v_hours::SMALLINT NOT BETWEEN 1 AND 23)) + THEN + RAISE numeric_value_out_of_range; + ELSIF (v_daypart = 'PM' AND v_hours::SMALLINT < 12) THEN + v_hours := (v_hours::SMALLINT + 12)::VARCHAR; + ELSIF (v_daypart = 'AM' AND v_hours::SMALLINT = 12) THEN + v_hours := (v_hours::SMALLINT - 12)::VARCHAR; + END IF; + END IF; + + RETURN CASE v_timeunit + WHEN 'HOURS' THEN v_hours + WHEN 'MINUTES' THEN v_minutes + WHEN 'SECONDS' THEN v_seconds + WHEN 'FRACTSECONDS' THEN v_fractsecs + END; +EXCEPTION + WHEN numeric_value_out_of_range THEN + RAISE USING MESSAGE := 'Could not extract correct hour value due to it''s inconsistency with AM|PM day part mark.', + DETAIL := 'Extracted hour value doesn''t fall in correct day part mark range: 0..12 for "AM" or 1..23 for "PM".', + HINT := 'Correct a hour value in the source string or remove AM|PM day part mark out of it.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_version(pComponentName VARCHAR(256)) + RETURNS VARCHAR(256) AS +$BODY$ +DECLARE + lComponentVersion VARCHAR(256); +BEGIN + SELECT componentversion + INTO lComponentVersion + FROM sys.versions + WHERE extpackcomponentname = pComponentName; + + RETURN lComponentVersion; +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_weekdaynum_by_name(IN p_weekdayname TEXT, + IN p_lang_metadata_json JSONB) +RETURNS SMALLINT +AS +$BODY$ +DECLARE + v_weekdayname TEXT; + v_weekdaynum SMALLINT; +BEGIN + v_weekdayname := lower(trim(p_weekdayname)); + + v_weekdaynum := array_position(ARRAY(SELECT lower(jsonb_array_elements_text(p_lang_metadata_json -> 'days_names'))), v_weekdayname); + + v_weekdaynum := coalesce(v_weekdaynum, + array_position(ARRAY(SELECT lower(jsonb_array_elements_text(p_lang_metadata_json -> 'days_shortnames'))), v_weekdayname)); + + v_weekdaynum := coalesce(v_weekdaynum, + array_position(ARRAY(SELECT lower(jsonb_array_elements_text(p_lang_metadata_json -> 'days_extrashortnames'))), v_weekdayname)); + + IF (v_weekdaynum IS NULL) THEN + RAISE datetime_field_overflow; + END IF; + + RETURN v_weekdaynum; +EXCEPTION + WHEN datetime_field_overflow THEN + RAISE USING MESSAGE := format('Can not convert value "%s" to a correct weekday number.', + trim(p_weekdayname)), + DETAIL := 'Supplied weekday name is not valid.', + HINT := 'Correct supplied weekday name value and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_is_ossp_present() +RETURNS BOOLEAN AS +$BODY$ +DECLARE + result SMALLINT; +BEGIN + select + case when exists + (select 1 from pg_extension where extname = 'uuid-ossp') + then 1 + else 0 end + INTO result; + + RETURN (result = 1); +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_is_spatial_present() +RETURNS BOOLEAN AS +$BODY$ +DECLARE + result SMALLINT; +BEGIN + select + case when exists + (select 1 from pg_extension where extname = 'postgis') + then 1 + else 0 end + INTO result; + + RETURN (result = 1); +END; +$BODY$ +LANGUAGE plpgsql; + +create or replace function sys.babelfish_istime(v text) +returns boolean +as +$body$ +begin + perform v::time; + return true; +exception + when others then + return false; +end +$body$ +language 'plpgsql'; + +-- Remove closing square brackets at both ends, otherwise do nothing. +CREATE OR REPLACE FUNCTION sys.babelfish_single_unbracket_name(IN name TEXT) +RETURNS TEXT AS +$BODY$ +BEGIN + IF length(name) >= 2 AND left(name, 1) = '[' AND right(name, 1) = ']' THEN + IF length(name) = 2 THEN + RETURN ''; + ELSE + RETURN substring(name from 2 for length(name)-2); + END IF; + END IF; + RETURN name; +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_openxml(IN DocHandle BIGINT) + RETURNS TABLE (XmlData XML) +AS +$BODY$ +DECLARE + XmlDocument$data XML; +BEGIN + + SELECT t.XmlData + INTO STRICT XmlDocument$data + FROM sys$openxml t + WHERE t.DocID = DocHandle; + + RETURN QUERY SELECT XmlDocument$data; + + EXCEPTION + WHEN SQLSTATE '42P01' OR SQLSTATE 'P0002' THEN + RAISE EXCEPTION '%','Could not find prepared statement with handle '||CASE + WHEN DocHandle IS NULL THEN 'null' + ELSE DocHandle::TEXT + END; +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_parse_to_date(IN p_datestring TEXT, + IN p_culture TEXT DEFAULT '') +RETURNS DATE +AS +$BODY$ +DECLARE + v_day VARCHAR; + v_year SMALLINT; + v_month VARCHAR; + v_res_date DATE; + v_hijridate DATE; + v_culture VARCHAR; + v_dayparts TEXT[]; + v_resmask VARCHAR; + v_raw_year VARCHAR; + v_left_part VARCHAR; + v_right_part VARCHAR; + v_resmask_fi VARCHAR; + v_datestring VARCHAR; + v_timestring VARCHAR; + v_correctnum VARCHAR; + v_weekdaynum SMALLINT; + v_err_message VARCHAR; + v_date_format VARCHAR; + v_weekdaynames TEXT[]; + v_hours SMALLINT := 0; + v_minutes SMALLINT := 0; + v_seconds NUMERIC := 0; + v_found BOOLEAN := TRUE; + v_compday_regexp VARCHAR; + v_regmatch_groups TEXT[]; + v_compmonth_regexp VARCHAR; + v_lang_metadata_json JSONB; + v_resmask_cnt SMALLINT := 10; + DAYMM_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + FULLYEAR_REGEXP CONSTANT VARCHAR := '(\d{3,4})'; + SHORTYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + COMPYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,4})'; + AMPM_REGEXP CONSTANT VARCHAR := '(?:[AP]M|ص|م)'; + TIMEUNIT_REGEXP CONSTANT VARCHAR := '\s*\d{1,2}\s*'; + MASKSEPONE_REGEXP CONSTANT VARCHAR := '\s*(?:/|-)?'; + MASKSEPTWO_REGEXP CONSTANT VARCHAR := '\s*(?:\s|/|-|\.|,)'; + MASKSEPTWO_FI_REGEXP CONSTANT VARCHAR := '\s*(?:\s|/|-|,)'; + MASKSEPTHREE_REGEXP CONSTANT VARCHAR := '\s*(?:/|-|\.|,)'; + TIME_MASKSEP_REGEXP CONSTANT VARCHAR := '(?:\s|\.|,)*'; + TIME_MASKSEP_FI_REGEXP CONSTANT VARCHAR := '(?:\s|,)*'; + WEEKDAYAMPM_START_REGEXP CONSTANT VARCHAR := '(^|[[:digit:][:space:]\.,])'; + WEEKDAYAMPM_END_REGEXP CONSTANT VARCHAR := '([[:digit:][:space:]\.,]|$)(?=[^/-]|$)'; + CORRECTNUM_REGEXP CONSTANT VARCHAR := '(?:([+-]\d{1,4})(?:[[:space:]\.,]|[AP]M|ص|م|$))'; + ANNO_DOMINI_REGEXP VARCHAR := '(AD|A\.D\.)'; + ANNO_DOMINI_COMPREGEXP VARCHAR := concat(WEEKDAYAMPM_START_REGEXP, ANNO_DOMINI_REGEXP, WEEKDAYAMPM_END_REGEXP); + HHMMSSFS_PART_REGEXP CONSTANT VARCHAR := + concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\s*\d{1,2}\.\d+(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?'); + HHMMSSFS_PART_FI_REGEXP CONSTANT VARCHAR := + concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?\.?|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '\s*\d{1,2}\.\d+(?!\d)\.?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?'); + v_defmask1_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(?:[\.|,]+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, '$'); + v_defmask1_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(?:[\.|,]+', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask2_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(?:(?:[\.|,]+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?)|', + CORRECTNUM_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, '$'); + v_defmask2_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(?:(?:[\.|,]+', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?)|', + CORRECTNUM_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask3_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, ')|', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', TIME_MASKSEP_REGEXP, AMPM_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask3_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + TIME_MASKSEP_FI_REGEXP, '[\./]?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)', + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask4_0_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask4_1_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:\s|,)+', + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask4_2_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '\s*[\.]+', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask5_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask5_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask6_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask6_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:\s*[\.])?', + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask7_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask7_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask8_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:[\.|,]+', AMPM_REGEXP, ')?', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask8_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:[\,]+|\s*/\s*)', AMPM_REGEXP, ')?', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask9_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', + HHMMSSFS_PART_REGEXP, + ')', TIME_MASKSEP_REGEXP, '$'); + v_defmask9_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '(', + HHMMSSFS_PART_FI_REGEXP, + ')', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask10_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask10_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask11_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask11_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask12_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask12_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask13_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$'); + v_defmask13_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask14_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)' + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask14_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)' + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_FI_REGEXP, + COMPYEAR_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask15_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask15_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask16_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask16_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask17_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask17_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask18_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask18_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask19_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask19_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + CONVERSION_LANG CONSTANT VARCHAR := ''; + DATE_FORMAT CONSTANT VARCHAR := ''; +BEGIN + v_datestring := upper(trim(p_datestring)); + v_culture := coalesce(nullif(upper(trim(p_culture)), ''), 'EN-US'); + + v_dayparts := ARRAY(SELECT upper(array_to_string(regexp_matches(v_datestring, '[AP]M|ص|م', 'gi'), ''))); + + IF (array_length(v_dayparts, 1) > 1) THEN + RAISE invalid_datetime_format; + END IF; + + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(coalesce(nullif(CONVERSION_LANG, ''), p_culture)); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_parameter_value; + END; + + v_compday_regexp := array_to_string(array_cat(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_names')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_shortnames'))), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_extrashortnames'))), '|'); + + v_weekdaynames := ARRAY(SELECT array_to_string(regexp_matches(v_datestring, v_compday_regexp, 'gi'), '')); + + IF (array_length(v_weekdaynames, 1) > 1) THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_weekdaynames[1] IS NOT NULL AND + v_datestring ~* concat(WEEKDAYAMPM_START_REGEXP, '(', v_compday_regexp, ')', WEEKDAYAMPM_END_REGEXP)) + THEN + v_datestring := replace(v_datestring, v_weekdaynames[1], ' '); + END IF; + + IF (v_datestring ~* ANNO_DOMINI_COMPREGEXP) + THEN + IF (v_culture !~ 'EN[-_]US|DA[-_]DK|SV[-_]SE|EN[-_]GB|HI[-_]IS') THEN + RAISE invalid_datetime_format; + END IF; + + v_datestring := regexp_replace(v_datestring, + ANNO_DOMINI_COMPREGEXP, + regexp_replace(array_to_string(regexp_matches(v_datestring, ANNO_DOMINI_COMPREGEXP, 'gi'), ''), + ANNO_DOMINI_REGEXP, ' ', 'gi'), + 'gi'); + END IF; + + v_date_format := coalesce(nullif(upper(trim(DATE_FORMAT)), ''), v_lang_metadata_json ->> 'date_format'); + + v_compmonth_regexp := + array_to_string(array_cat(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_shortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_names'))), + array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_extrashortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_extranames'))) + ), '|'); + + IF ((v_datestring ~* v_defmask1_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask1_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_datestring ~ concat(CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, CASE v_culture + WHEN 'FI' THEN v_defmask1_fi_regexp + ELSE v_defmask1_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[2]; + v_correctnum := coalesce(v_regmatch_groups[1], v_regmatch_groups[3], + v_regmatch_groups[5], v_regmatch_groups[6]); + + IF (v_date_format = 'DMY' OR + v_culture IN ('SV-SE', 'SV_SE', 'LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[7]; + ELSE + v_day := v_regmatch_groups[7]; + v_month := v_regmatch_groups[4]; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + v_month::SMALLINT > 12) THEN + RAISE invalid_datetime_format; + END IF; + + v_raw_year := to_char(sys.babelfish_conv_greg_to_hijri(current_date + 1), 'YYYY'); + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + ELSE + v_year := to_char(current_date, 'YYYY')::SMALLINT; + END IF; + + ELSIF ((v_datestring ~* v_defmask6_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask6_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d{3,4}', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, CASE v_culture + WHEN 'FI' THEN v_defmask6_fi_regexp + ELSE v_defmask6_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[3]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[2]::SMALLINT - 543 + ELSE v_regmatch_groups[2]::SMALLINT + END; + + ELSIF ((v_datestring ~* v_defmask2_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask2_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, CASE v_culture + WHEN 'FI' THEN v_defmask2_fi_regexp + ELSE v_defmask2_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[2]; + v_correctnum := coalesce(v_regmatch_groups[1], v_regmatch_groups[3], v_regmatch_groups[5], + v_regmatch_groups[6], v_regmatch_groups[8], v_regmatch_groups[9]); + v_day := '01'; + v_month := v_regmatch_groups[7]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[4]::SMALLINT - 543 + ELSE v_regmatch_groups[4]::SMALLINT + END; + + ELSIF (v_datestring ~* v_defmask4_1_regexp OR + (v_datestring ~* v_defmask4_2_regexp AND v_culture !~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') OR + (v_datestring ~* v_defmask9_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask9_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_datestring ~ concat('\d+\s*\.?(?:,+|,*', AMPM_REGEXP, ')', TIME_MASKSEP_FI_REGEXP, '\.+', TIME_MASKSEP_REGEXP, '$|', + '\d+\s*\.', TIME_MASKSEP_FI_REGEXP, '\.', TIME_MASKSEP_FI_REGEXP, '$') AND + v_culture = 'FI') + THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_datestring ~* v_defmask4_0_regexp) THEN + v_timestring := (regexp_matches(v_datestring, v_defmask4_0_regexp, 'gi'))[1]; + ELSE + v_timestring := v_datestring; + END IF; + + v_res_date := current_date; + v_day := to_char(v_res_date, 'DD'); + v_month := to_char(v_res_date, 'MM'); + v_year := to_char(v_res_date, 'YYYY')::SMALLINT; + + ELSIF ((v_datestring ~* v_defmask3_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask3_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', + TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, '|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, CASE v_culture + WHEN 'FI' THEN v_defmask3_fi_regexp + ELSE v_defmask3_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[1]; + v_day := '01'; + v_month := v_regmatch_groups[2]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[3]::SMALLINT - 543 + ELSE v_regmatch_groups[3]::SMALLINT + END; + + ELSIF ((v_datestring ~* v_defmask5_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask5_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$|', + '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, v_defmask5_regexp, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[4]::SMALLINT - 543 + ELSE v_regmatch_groups[4]::SMALLINT + END; + + IF (v_date_format = 'DMY' OR + v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[2]; + v_month := v_regmatch_groups[3]; + ELSE + v_day := v_regmatch_groups[3]; + v_month := v_regmatch_groups[2]; + END IF; + + ELSIF ((v_datestring ~* v_defmask7_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask7_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', + MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}|', + '\d{3,4}', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, CASE v_culture + WHEN 'FI' THEN v_defmask7_fi_regexp + ELSE v_defmask7_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[2]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[3]::SMALLINT - 543 + ELSE v_regmatch_groups[3]::SMALLINT + END; + + ELSIF ((v_datestring ~* v_defmask8_regexp AND v_culture <> 'FI') OR + (v_datestring ~* v_defmask8_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_datestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'FI|DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datestring, CASE v_culture + WHEN 'FI' THEN v_defmask8_fi_regexp + ELSE v_defmask8_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + + IF (v_date_format = 'DMY' OR + v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[2]; + v_month := v_regmatch_groups[3]; + v_raw_year := v_regmatch_groups[4]; + ELSIF (v_date_format = 'YMD') + THEN + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[3]; + v_raw_year := v_regmatch_groups[2]; + ELSE + v_day := v_regmatch_groups[3]; + v_month := v_regmatch_groups[2]; + v_raw_year := v_regmatch_groups[4]; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + v_month::SMALLINT > 12) THEN + RAISE invalid_datetime_format; + END IF; + + v_raw_year := sys.babelfish_get_full_year(v_raw_year, '14'); + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + + ELSIF (v_culture IN ('TH-TH', 'TH_TH')) THEN + v_year := sys.babelfish_get_full_year(v_raw_year)::SMALLINT - 43; + ELSE + v_year := sys.babelfish_get_full_year(v_raw_year, '', 29)::SMALLINT; + END IF; + ELSE + v_found := FALSE; + END IF; + + WHILE (NOT v_found AND v_resmask_cnt < 20) + LOOP + v_resmask := replace(CASE v_resmask_cnt + WHEN 10 THEN v_defmask10_regexp + WHEN 11 THEN v_defmask11_regexp + WHEN 12 THEN v_defmask12_regexp + WHEN 13 THEN v_defmask13_regexp + WHEN 14 THEN v_defmask14_regexp + WHEN 15 THEN v_defmask15_regexp + WHEN 16 THEN v_defmask16_regexp + WHEN 17 THEN v_defmask17_regexp + WHEN 18 THEN v_defmask18_regexp + WHEN 19 THEN v_defmask19_regexp + END, + '$comp_month$', v_compmonth_regexp); + + v_resmask_fi := replace(CASE v_resmask_cnt + WHEN 10 THEN v_defmask10_fi_regexp + WHEN 11 THEN v_defmask11_fi_regexp + WHEN 12 THEN v_defmask12_fi_regexp + WHEN 13 THEN v_defmask13_fi_regexp + WHEN 14 THEN v_defmask14_fi_regexp + WHEN 15 THEN v_defmask15_fi_regexp + WHEN 16 THEN v_defmask16_fi_regexp + WHEN 17 THEN v_defmask17_fi_regexp + WHEN 18 THEN v_defmask18_fi_regexp + WHEN 19 THEN v_defmask19_fi_regexp + END, + '$comp_month$', v_compmonth_regexp); + + IF ((v_datestring ~* v_resmask AND v_culture <> 'FI') OR + (v_datestring ~* v_resmask_fi AND v_culture = 'FI')) + THEN + v_found := TRUE; + v_regmatch_groups := regexp_matches(v_datestring, CASE v_culture + WHEN 'FI' THEN v_resmask_fi + ELSE v_resmask + END, 'gi'); + v_timestring := CASE + WHEN v_resmask_cnt IN (10, 11, 12, 13) THEN concat(v_regmatch_groups[1], v_regmatch_groups[4]) + ELSE concat(v_regmatch_groups[1], v_regmatch_groups[5]) + END; + + IF (v_resmask_cnt = 10) + THEN + IF (v_regmatch_groups[3] = 'MAR' AND + v_culture IN ('IT-IT', 'IT_IT')) + THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_date_format = 'YMD' AND v_culture NOT IN ('SV-SE', 'SV_SE', 'LV-LV', 'LV_LV')) + THEN + v_day := '01'; + v_year := sys.babelfish_get_full_year(v_regmatch_groups[2], '', 29)::SMALLINT; + ELSE + v_day := v_regmatch_groups[2]; + v_year := to_char(current_date, 'YYYY')::SMALLINT; + END IF; + + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := to_char(sys.babelfish_conv_greg_to_hijri(current_date + 1), 'YYYY'); + + ELSIF (v_resmask_cnt = 11) + THEN + IF (v_date_format IN ('YMD', 'MDY') AND v_culture NOT IN ('SV-SE', 'SV_SE')) + THEN + v_day := v_regmatch_groups[3]; + v_year := to_char(current_date, 'YYYY')::SMALLINT; + ELSE + v_day := '01'; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_regmatch_groups[3])::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_regmatch_groups[3], '', 29)::SMALLINT + END; + END IF; + + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := sys.babelfish_get_full_year(substring(v_year::TEXT, 3, 2), '14'); + + ELSIF (v_resmask_cnt = 12) + THEN + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 13) + THEN + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[3]; + + ELSIF (v_resmask_cnt IN (14, 15, 16)) + THEN + IF (v_resmask_cnt = 14) + THEN + v_left_part := v_regmatch_groups[4]; + v_right_part := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + ELSIF (v_resmask_cnt = 15) + THEN + v_left_part := v_regmatch_groups[4]; + v_right_part := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + ELSE + v_left_part := v_regmatch_groups[3]; + v_right_part := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[4], v_lang_metadata_json); + END IF; + + IF (char_length(v_left_part) <= 2) + THEN + IF (v_date_format = 'YMD' AND v_culture NOT IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_left_part; + v_raw_year := sys.babelfish_get_full_year(v_right_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_right_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_right_part, '', 29)::SMALLINT + END; + BEGIN + v_res_date := make_date(v_year, v_month::SMALLINT, v_day::SMALLINT); + EXCEPTION + WHEN OTHERS THEN + v_day := v_right_part; + v_raw_year := sys.babelfish_get_full_year(v_left_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_left_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_left_part, '', 29)::SMALLINT + END; + END; + END IF; + + IF (v_date_format IN ('MDY', 'DMY') OR v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_right_part; + v_raw_year := sys.babelfish_get_full_year(v_left_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_left_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_left_part, '', 29)::SMALLINT + END; + BEGIN + v_res_date := make_date(v_year, v_month::SMALLINT, v_day::SMALLINT); + EXCEPTION + WHEN OTHERS THEN + v_day := v_left_part; + v_raw_year := sys.babelfish_get_full_year(v_right_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_right_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_right_part, '', 29)::SMALLINT + END; + END; + END IF; + ELSE + v_day := v_right_part; + v_raw_year := v_left_part; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_left_part::SMALLINT - 543 + ELSE v_left_part::SMALLINT + END; + END IF; + + ELSIF (v_resmask_cnt = 17) + THEN + v_day := v_regmatch_groups[4]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 18) + THEN + v_day := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[4], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 19) + THEN + v_day := v_regmatch_groups[4]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[3]; + END IF; + + IF (v_resmask_cnt NOT IN (10, 11, 14, 15, 16)) + THEN + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_raw_year::SMALLINT - 543 + ELSE v_raw_year::SMALLINT + END; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + (v_resmask_cnt NOT IN (10, 11, 14, 15, 16) AND v_year NOT BETWEEN 1318 AND 1501) OR + (v_resmask_cnt IN (14, 15, 16) AND v_raw_year::SMALLINT NOT BETWEEN 1318 AND 1501)) + THEN + RAISE invalid_datetime_format; + END IF; + + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + END IF; + END IF; + + v_resmask_cnt := v_resmask_cnt + 1; + END LOOP; + + IF (NOT v_found) THEN + RAISE invalid_datetime_format; + END IF; + + IF (char_length(v_timestring) > 0 AND v_timestring NOT IN ('AM', 'ص', 'PM', 'م')) + THEN + IF (v_culture = 'FI') THEN + v_timestring := translate(v_timestring, '.,', ': '); + + IF (char_length(split_part(v_timestring, ':', 4)) > 0) THEN + v_timestring := regexp_replace(v_timestring, ':(?=\s*\d+\s*:?\s*(?:[AP]M|ص|م)?\s*$)', '.'); + END IF; + END IF; + + v_timestring := replace(regexp_replace(v_timestring, '\.?[AP]M|ص|م|\s|\,|\.\D|[\.|:]$', '', 'gi'), ':.', ':'); + BEGIN + v_hours := coalesce(split_part(v_timestring, ':', 1)::SMALLINT, 0); + + IF ((v_dayparts[1] IN ('AM', 'ص') AND v_hours NOT BETWEEN 0 AND 12) OR + (v_dayparts[1] IN ('PM', 'م') AND v_hours NOT BETWEEN 1 AND 23)) + THEN + RAISE invalid_datetime_format; + END IF; + + v_minutes := coalesce(nullif(split_part(v_timestring, ':', 2), '')::SMALLINT, 0); + v_seconds := coalesce(nullif(split_part(v_timestring, ':', 3), '')::NUMERIC, 0); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_datetime_format; + END; + ELSIF (v_dayparts[1] IN ('PM', 'م')) + THEN + v_hours := 12; + END IF; + + v_res_date := make_timestamp(v_year, v_month::SMALLINT, v_day::SMALLINT, + v_hours, v_minutes, v_seconds); + + IF (v_weekdaynames[1] IS NOT NULL) THEN + v_weekdaynum := sys.babelfish_get_weekdaynum_by_name(v_weekdaynames[1], v_lang_metadata_json); + + IF (CASE date_part('dow', v_res_date)::SMALLINT + WHEN 0 THEN 7 + ELSE date_part('dow', v_res_date)::SMALLINT + END <> v_weekdaynum) + THEN + RAISE invalid_datetime_format; + END IF; + END IF; + + RETURN v_res_date; +EXCEPTION + WHEN invalid_datetime_format OR datetime_field_overflow THEN + RAISE USING MESSAGE := format('Error converting string value ''%s'' into data type DATE using culture ''%s''.', + p_datestring, p_culture), + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := CASE char_length(coalesce(CONVERSION_LANG, '')) + WHEN 0 THEN format('The culture parameter ''%s'' provided in the function call is not supported.', + p_culture) + ELSE format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG) + END, + DETAIL := 'Passed incorrect value for "p_culture" parameter or compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Check "p_culture" input parameter value, correct it if needed, and try again. Also check CONVERSION_LANG constant value.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_parse_to_datetime(IN p_datatype TEXT, + IN p_datetimestring TEXT, + IN p_culture TEXT DEFAULT '') +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_day VARCHAR; + v_year SMALLINT; + v_month VARCHAR; + v_res_date DATE; + v_scale SMALLINT; + v_hijridate DATE; + v_culture VARCHAR; + v_dayparts TEXT[]; + v_resmask VARCHAR; + v_datatype VARCHAR; + v_raw_year VARCHAR; + v_left_part VARCHAR; + v_right_part VARCHAR; + v_resmask_fi VARCHAR; + v_timestring VARCHAR; + v_correctnum VARCHAR; + v_weekdaynum SMALLINT; + v_err_message VARCHAR; + v_date_format VARCHAR; + v_weekdaynames TEXT[]; + v_hours SMALLINT := 0; + v_minutes SMALLINT := 0; + v_res_datatype VARCHAR; + v_error_message VARCHAR; + v_found BOOLEAN := TRUE; + v_compday_regexp VARCHAR; + v_regmatch_groups TEXT[]; + v_datatype_groups TEXT[]; + v_datetimestring VARCHAR; + v_seconds VARCHAR := '0'; + v_fseconds VARCHAR := '0'; + v_compmonth_regexp VARCHAR; + v_lang_metadata_json JSONB; + v_resmask_cnt SMALLINT := 10; + v_res_datetime TIMESTAMP(6) WITHOUT TIME ZONE; + DAYMM_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + FULLYEAR_REGEXP CONSTANT VARCHAR := '(\d{3,4})'; + SHORTYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + COMPYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,4})'; + AMPM_REGEXP CONSTANT VARCHAR := '(?:[AP]M|ص|م)'; + TIMEUNIT_REGEXP CONSTANT VARCHAR := '\s*\d{1,2}\s*'; + MASKSEPONE_REGEXP CONSTANT VARCHAR := '\s*(?:/|-)?'; + MASKSEPTWO_REGEXP CONSTANT VARCHAR := '\s*(?:\s|/|-|\.|,)'; + MASKSEPTWO_FI_REGEXP CONSTANT VARCHAR := '\s*(?:\s|/|-|,)'; + MASKSEPTHREE_REGEXP CONSTANT VARCHAR := '\s*(?:/|-|\.|,)'; + TIME_MASKSEP_REGEXP CONSTANT VARCHAR := '(?:\s|\.|,)*'; + TIME_MASKSEP_FI_REGEXP CONSTANT VARCHAR := '(?:\s|,)*'; + WEEKDAYAMPM_START_REGEXP CONSTANT VARCHAR := '(^|[[:digit:][:space:]\.,])'; + WEEKDAYAMPM_END_REGEXP CONSTANT VARCHAR := '([[:digit:][:space:]\.,]|$)(?=[^/-]|$)'; + CORRECTNUM_REGEXP CONSTANT VARCHAR := '(?:([+-]\d{1,4})(?:[[:space:]\.,]|[AP]M|ص|م|$))'; + DATATYPE_REGEXP CONSTANT VARCHAR := '^(DATETIME|SMALLDATETIME|DATETIME2)\s*(?:\()?\s*((?:-)?\d+)?\s*(?:\))?$'; + ANNO_DOMINI_REGEXP VARCHAR := '(AD|A\.D\.)'; + ANNO_DOMINI_COMPREGEXP VARCHAR := concat(WEEKDAYAMPM_START_REGEXP, ANNO_DOMINI_REGEXP, WEEKDAYAMPM_END_REGEXP); + HHMMSSFS_PART_REGEXP CONSTANT VARCHAR := + concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\s*\d{1,2}\.\d+(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?'); + HHMMSSFS_PART_FI_REGEXP CONSTANT VARCHAR := + concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?\.?|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '\s*\d{1,2}\.\d+(?!\d)\.?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?'); + v_defmask1_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(?:[\.|,]+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, '$'); + v_defmask1_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(?:[\.|,]+', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask2_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(?:(?:[\.|,]+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?)|', + CORRECTNUM_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, '$'); + v_defmask2_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(?:(?:[\.|,]+', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?)|', + CORRECTNUM_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask3_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, ')|', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', TIME_MASKSEP_REGEXP, AMPM_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask3_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + TIME_MASKSEP_FI_REGEXP, '[\./]?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)', + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask4_0_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask4_1_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:\s|,)+', + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask4_2_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '\s*[\.]+', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask5_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask5_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask6_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask6_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:\s*[\.])?', + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask7_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask7_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask8_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:[\.|,]+', AMPM_REGEXP, ')?', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask8_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:[\,]+|\s*/\s*)', AMPM_REGEXP, ')?', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask9_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', + HHMMSSFS_PART_REGEXP, + ')', TIME_MASKSEP_REGEXP, '$'); + v_defmask9_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '(', + HHMMSSFS_PART_FI_REGEXP, + ')', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask10_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask10_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask11_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask11_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask12_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask12_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask13_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$'); + v_defmask13_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask14_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)' + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask14_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)' + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_FI_REGEXP, + COMPYEAR_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask15_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask15_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask16_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask16_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask17_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask17_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask18_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask18_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask19_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask19_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + CONVERSION_LANG CONSTANT VARCHAR := ''; + DATE_FORMAT CONSTANT VARCHAR := ''; +BEGIN + v_datatype := trim(p_datatype); + v_datetimestring := upper(trim(p_datetimestring)); + v_culture := coalesce(nullif(upper(trim(p_culture)), ''), 'EN-US'); + + v_datatype_groups := regexp_matches(v_datatype, DATATYPE_REGEXP, 'gi'); + + v_res_datatype := upper(v_datatype_groups[1]); + v_scale := v_datatype_groups[2]::SMALLINT; + + IF (v_res_datatype IS NULL) THEN + RAISE datatype_mismatch; + ELSIF (v_res_datatype <> 'DATETIME2' AND v_scale IS NOT NULL) + THEN + RAISE invalid_indicator_parameter_value; + ELSIF (coalesce(v_scale, 0) NOT BETWEEN 0 AND 7) + THEN + RAISE interval_field_overflow; + ELSIF (v_scale IS NULL) THEN + v_scale := 7; + END IF; + + v_dayparts := ARRAY(SELECT upper(array_to_string(regexp_matches(v_datetimestring, '[AP]M|ص|م', 'gi'), ''))); + + IF (array_length(v_dayparts, 1) > 1) THEN + RAISE invalid_datetime_format; + END IF; + + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(coalesce(nullif(CONVERSION_LANG, ''), p_culture)); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_parameter_value; + END; + + v_compday_regexp := array_to_string(array_cat(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_names')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_shortnames'))), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_extrashortnames'))), '|'); + + v_weekdaynames := ARRAY(SELECT array_to_string(regexp_matches(v_datetimestring, v_compday_regexp, 'gi'), '')); + + IF (array_length(v_weekdaynames, 1) > 1) THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_weekdaynames[1] IS NOT NULL AND + v_datetimestring ~* concat(WEEKDAYAMPM_START_REGEXP, '(', v_compday_regexp, ')', WEEKDAYAMPM_END_REGEXP)) + THEN + v_datetimestring := replace(v_datetimestring, v_weekdaynames[1], ' '); + END IF; + + IF (v_datetimestring ~* ANNO_DOMINI_COMPREGEXP) + THEN + IF (v_culture !~ 'EN[-_]US|DA[-_]DK|SV[-_]SE|EN[-_]GB|HI[-_]IS') THEN + RAISE invalid_datetime_format; + END IF; + + v_datetimestring := regexp_replace(v_datetimestring, + ANNO_DOMINI_COMPREGEXP, + regexp_replace(array_to_string(regexp_matches(v_datetimestring, ANNO_DOMINI_COMPREGEXP, 'gi'), ''), + ANNO_DOMINI_REGEXP, ' ', 'gi'), + 'gi'); + END IF; + + v_date_format := coalesce(nullif(upper(trim(DATE_FORMAT)), ''), v_lang_metadata_json ->> 'date_format'); + + v_compmonth_regexp := + array_to_string(array_cat(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_shortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_names'))), + array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_extrashortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_extranames'))) + ), '|'); + + IF ((v_datetimestring ~* v_defmask1_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask1_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_datetimestring ~ concat(CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datetimestring, CASE v_culture + WHEN 'FI' THEN v_defmask1_fi_regexp + ELSE v_defmask1_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[2]; + v_correctnum := coalesce(v_regmatch_groups[1], v_regmatch_groups[3], + v_regmatch_groups[5], v_regmatch_groups[6]); + + IF (v_date_format = 'DMY' OR + v_culture IN ('SV-SE', 'SV_SE', 'LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[7]; + ELSE + v_day := v_regmatch_groups[7]; + v_month := v_regmatch_groups[4]; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + v_month::SMALLINT > 12) THEN + RAISE invalid_datetime_format; + END IF; + + v_raw_year := to_char(sys.babelfish_conv_greg_to_hijri(current_date + 1), 'YYYY'); + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + ELSE + v_year := to_char(current_date, 'YYYY')::SMALLINT; + END IF; + + ELSIF ((v_datetimestring ~* v_defmask6_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask6_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datetimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d{3,4}', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datetimestring, CASE v_culture + WHEN 'FI' THEN v_defmask6_fi_regexp + ELSE v_defmask6_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[3]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[2]::SMALLINT - 543 + ELSE v_regmatch_groups[2]::SMALLINT + END; + + ELSIF ((v_datetimestring ~* v_defmask2_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask2_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datetimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datetimestring, CASE v_culture + WHEN 'FI' THEN v_defmask2_fi_regexp + ELSE v_defmask2_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[2]; + v_correctnum := coalesce(v_regmatch_groups[1], v_regmatch_groups[3], v_regmatch_groups[5], + v_regmatch_groups[6], v_regmatch_groups[8], v_regmatch_groups[9]); + v_day := '01'; + v_month := v_regmatch_groups[7]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[4]::SMALLINT - 543 + ELSE v_regmatch_groups[4]::SMALLINT + END; + + ELSIF (v_datetimestring ~* v_defmask4_1_regexp OR + (v_datetimestring ~* v_defmask4_2_regexp AND v_culture !~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') OR + (v_datetimestring ~* v_defmask9_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask9_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_datetimestring ~ concat('\d+\s*\.?(?:,+|,*', AMPM_REGEXP, ')', TIME_MASKSEP_FI_REGEXP, '\.+', TIME_MASKSEP_REGEXP, '$|', + '\d+\s*\.', TIME_MASKSEP_FI_REGEXP, '\.', TIME_MASKSEP_FI_REGEXP, '$') AND + v_culture = 'FI') + THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_datetimestring ~* v_defmask4_0_regexp) THEN + v_timestring := (regexp_matches(v_datetimestring, v_defmask4_0_regexp, 'gi'))[1]; + ELSE + v_timestring := v_datetimestring; + END IF; + + v_res_date := current_date; + v_day := to_char(v_res_date, 'DD'); + v_month := to_char(v_res_date, 'MM'); + v_year := to_char(v_res_date, 'YYYY')::SMALLINT; + + ELSIF ((v_datetimestring ~* v_defmask3_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask3_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datetimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', + TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, '|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datetimestring, CASE v_culture + WHEN 'FI' THEN v_defmask3_fi_regexp + ELSE v_defmask3_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[1]; + v_day := '01'; + v_month := v_regmatch_groups[2]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[3]::SMALLINT - 543 + ELSE v_regmatch_groups[3]::SMALLINT + END; + + ELSIF ((v_datetimestring ~* v_defmask5_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask5_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datetimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$|', + '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datetimestring, v_defmask5_regexp, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[4]::SMALLINT - 543 + ELSE v_regmatch_groups[4]::SMALLINT + END; + + IF (v_date_format = 'DMY' OR + v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[2]; + v_month := v_regmatch_groups[3]; + ELSE + v_day := v_regmatch_groups[3]; + v_month := v_regmatch_groups[2]; + END IF; + + ELSIF ((v_datetimestring ~* v_defmask7_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask7_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_datetimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', + MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}|', + '\d{3,4}', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datetimestring, CASE v_culture + WHEN 'FI' THEN v_defmask7_fi_regexp + ELSE v_defmask7_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[2]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[3]::SMALLINT - 543 + ELSE v_regmatch_groups[3]::SMALLINT + END; + + ELSIF ((v_datetimestring ~* v_defmask8_regexp AND v_culture <> 'FI') OR + (v_datetimestring ~* v_defmask8_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_datetimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'FI|DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_datetimestring, CASE v_culture + WHEN 'FI' THEN v_defmask8_fi_regexp + ELSE v_defmask8_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + + IF (v_date_format = 'DMY' OR + v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[2]; + v_month := v_regmatch_groups[3]; + v_raw_year := v_regmatch_groups[4]; + ELSIF (v_date_format = 'YMD') + THEN + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[3]; + v_raw_year := v_regmatch_groups[2]; + ELSE + v_day := v_regmatch_groups[3]; + v_month := v_regmatch_groups[2]; + v_raw_year := v_regmatch_groups[4]; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + v_month::SMALLINT > 12) THEN + RAISE invalid_datetime_format; + END IF; + + v_raw_year := sys.babelfish_get_full_year(v_raw_year, '14'); + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + + ELSIF (v_culture IN ('TH-TH', 'TH_TH')) THEN + v_year := sys.babelfish_get_full_year(v_raw_year)::SMALLINT - 43; + ELSE + v_year := sys.babelfish_get_full_year(v_raw_year, '', 29)::SMALLINT; + END IF; + ELSE + v_found := FALSE; + END IF; + + WHILE (NOT v_found AND v_resmask_cnt < 20) + LOOP + v_resmask := replace(CASE v_resmask_cnt + WHEN 10 THEN v_defmask10_regexp + WHEN 11 THEN v_defmask11_regexp + WHEN 12 THEN v_defmask12_regexp + WHEN 13 THEN v_defmask13_regexp + WHEN 14 THEN v_defmask14_regexp + WHEN 15 THEN v_defmask15_regexp + WHEN 16 THEN v_defmask16_regexp + WHEN 17 THEN v_defmask17_regexp + WHEN 18 THEN v_defmask18_regexp + WHEN 19 THEN v_defmask19_regexp + END, + '$comp_month$', v_compmonth_regexp); + + v_resmask_fi := replace(CASE v_resmask_cnt + WHEN 10 THEN v_defmask10_fi_regexp + WHEN 11 THEN v_defmask11_fi_regexp + WHEN 12 THEN v_defmask12_fi_regexp + WHEN 13 THEN v_defmask13_fi_regexp + WHEN 14 THEN v_defmask14_fi_regexp + WHEN 15 THEN v_defmask15_fi_regexp + WHEN 16 THEN v_defmask16_fi_regexp + WHEN 17 THEN v_defmask17_fi_regexp + WHEN 18 THEN v_defmask18_fi_regexp + WHEN 19 THEN v_defmask19_fi_regexp + END, + '$comp_month$', v_compmonth_regexp); + + IF ((v_datetimestring ~* v_resmask AND v_culture <> 'FI') OR + (v_datetimestring ~* v_resmask_fi AND v_culture = 'FI')) + THEN + v_found := TRUE; + v_regmatch_groups := regexp_matches(v_datetimestring, CASE v_culture + WHEN 'FI' THEN v_resmask_fi + ELSE v_resmask + END, 'gi'); + v_timestring := CASE + WHEN v_resmask_cnt IN (10, 11, 12, 13) THEN concat(v_regmatch_groups[1], v_regmatch_groups[4]) + ELSE concat(v_regmatch_groups[1], v_regmatch_groups[5]) + END; + + IF (v_resmask_cnt = 10) + THEN + IF (v_regmatch_groups[3] = 'MAR' AND + v_culture IN ('IT-IT', 'IT_IT')) + THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_date_format = 'YMD' AND v_culture NOT IN ('SV-SE', 'SV_SE', 'LV-LV', 'LV_LV')) + THEN + v_day := '01'; + v_year := sys.babelfish_get_full_year(v_regmatch_groups[2], '', 29)::SMALLINT; + ELSE + v_day := v_regmatch_groups[2]; + v_year := to_char(current_date, 'YYYY')::SMALLINT; + END IF; + + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := to_char(sys.babelfish_conv_greg_to_hijri(current_date + 1), 'YYYY'); + + ELSIF (v_resmask_cnt = 11) + THEN + IF (v_date_format IN ('YMD', 'MDY') AND v_culture NOT IN ('SV-SE', 'SV_SE')) + THEN + v_day := v_regmatch_groups[3]; + v_year := to_char(current_date, 'YYYY')::SMALLINT; + ELSE + v_day := '01'; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_regmatch_groups[3])::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_regmatch_groups[3], '', 29)::SMALLINT + END; + END IF; + + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := sys.babelfish_get_full_year(substring(v_year::TEXT, 3, 2), '14'); + + ELSIF (v_resmask_cnt = 12) + THEN + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 13) + THEN + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[3]; + + ELSIF (v_resmask_cnt IN (14, 15, 16)) + THEN + IF (v_resmask_cnt = 14) + THEN + v_left_part := v_regmatch_groups[4]; + v_right_part := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + ELSIF (v_resmask_cnt = 15) + THEN + v_left_part := v_regmatch_groups[4]; + v_right_part := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + ELSE + v_left_part := v_regmatch_groups[3]; + v_right_part := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[4], v_lang_metadata_json); + END IF; + + IF (char_length(v_left_part) <= 2) + THEN + IF (v_date_format = 'YMD' AND v_culture NOT IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_left_part; + v_raw_year := sys.babelfish_get_full_year(v_right_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_right_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_right_part, '', 29)::SMALLINT + END; + BEGIN + v_res_date := make_date(v_year, v_month::SMALLINT, v_day::SMALLINT); + EXCEPTION + WHEN OTHERS THEN + v_day := v_right_part; + v_raw_year := sys.babelfish_get_full_year(v_left_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_left_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_left_part, '', 29)::SMALLINT + END; + END; + END IF; + + IF (v_date_format IN ('MDY', 'DMY') OR v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_right_part; + v_raw_year := sys.babelfish_get_full_year(v_left_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_left_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_left_part, '', 29)::SMALLINT + END; + BEGIN + v_res_date := make_date(v_year, v_month::SMALLINT, v_day::SMALLINT); + EXCEPTION + WHEN OTHERS THEN + v_day := v_left_part; + v_raw_year := sys.babelfish_get_full_year(v_right_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_right_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_right_part, '', 29)::SMALLINT + END; + END; + END IF; + ELSE + v_day := v_right_part; + v_raw_year := v_left_part; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_left_part::SMALLINT - 543 + ELSE v_left_part::SMALLINT + END; + END IF; + + ELSIF (v_resmask_cnt = 17) + THEN + v_day := v_regmatch_groups[4]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 18) + THEN + v_day := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[4], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 19) + THEN + v_day := v_regmatch_groups[4]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[3]; + END IF; + + IF (v_resmask_cnt NOT IN (10, 11, 14, 15, 16)) + THEN + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_raw_year::SMALLINT - 543 + ELSE v_raw_year::SMALLINT + END; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + (v_resmask_cnt NOT IN (10, 11, 14, 15, 16) AND v_year NOT BETWEEN 1318 AND 1501) OR + (v_resmask_cnt IN (14, 15, 16) AND v_raw_year::SMALLINT NOT BETWEEN 1318 AND 1501)) + THEN + RAISE invalid_datetime_format; + END IF; + + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + END IF; + END IF; + + v_resmask_cnt := v_resmask_cnt + 1; + END LOOP; + + IF (NOT v_found) THEN + RAISE invalid_datetime_format; + END IF; + + IF (char_length(v_timestring) > 0 AND v_timestring NOT IN ('AM', 'ص', 'PM', 'م')) + THEN + IF (v_culture = 'FI') THEN + v_timestring := translate(v_timestring, '.,', ': '); + + IF (char_length(split_part(v_timestring, ':', 4)) > 0) THEN + v_timestring := regexp_replace(v_timestring, ':(?=\s*\d+\s*:?\s*(?:[AP]M|ص|م)?\s*$)', '.'); + END IF; + END IF; + + v_timestring := replace(regexp_replace(v_timestring, '\.?[AP]M|ص|م|\s|\,|\.\D|[\.|:]$', '', 'gi'), ':.', ':'); + BEGIN + v_hours := coalesce(split_part(v_timestring, ':', 1)::SMALLINT, 0); + + IF ((v_dayparts[1] IN ('AM', 'ص') AND v_hours NOT BETWEEN 0 AND 12) OR + (v_dayparts[1] IN ('PM', 'م') AND v_hours NOT BETWEEN 1 AND 23)) + THEN + RAISE invalid_datetime_format; + ELSIF (v_dayparts[1] = 'PM' AND v_hours < 12) THEN + v_hours := v_hours + 12; + ELSIF (v_dayparts[1] = 'AM' AND v_hours = 12) THEN + v_hours := v_hours - 12; + END IF; + + v_minutes := coalesce(nullif(split_part(v_timestring, ':', 2), '')::SMALLINT, 0); + v_seconds := coalesce(nullif(split_part(v_timestring, ':', 3), ''), '0'); + + IF (v_seconds ~ '\.') THEN + v_fseconds := split_part(v_seconds, '.', 2); + v_seconds := split_part(v_seconds, '.', 1); + END IF; + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_datetime_format; + END; + ELSIF (v_dayparts[1] IN ('PM', 'م')) + THEN + v_hours := 12; + END IF; + + BEGIN + IF (v_res_datatype IN ('DATETIME', 'SMALLDATETIME')) + THEN + v_res_datetime := sys.datetimefromparts(v_year, v_month::SMALLINT, v_day::SMALLINT, + v_hours, v_minutes, v_seconds::SMALLINT, + rpad(v_fseconds, 3, '0')::NUMERIC); + IF (v_res_datatype = 'SMALLDATETIME' AND + to_char(v_res_datetime, 'SS') <> '00') + THEN + IF (to_char(v_res_datetime, 'SS')::SMALLINT >= 30) THEN + v_res_datetime := v_res_datetime + INTERVAL '1 minute'; + END IF; + + v_res_datetime := to_timestamp(to_char(v_res_datetime, 'DD.MM.YYYY.HH24.MI'), 'DD.MM.YYYY.HH24.MI'); + END IF; + ELSE + v_fseconds := sys.babelfish_get_microsecs_from_fractsecs(rpad(v_fseconds, 9, '0'), v_scale); + v_seconds := concat_ws('.', v_seconds, v_fseconds); + + v_res_datetime := make_timestamp(v_year, v_month::SMALLINT, v_day::SMALLINT, + v_hours, v_minutes, v_seconds::NUMERIC); + END IF; + EXCEPTION + WHEN OTHERS THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + + IF (v_err_message ~* 'Cannot construct data type') THEN + RAISE invalid_datetime_format; + END IF; + END; + + IF (v_weekdaynames[1] IS NOT NULL) THEN + v_weekdaynum := sys.babelfish_get_weekdaynum_by_name(v_weekdaynames[1], v_lang_metadata_json); + + IF (CASE date_part('dow', v_res_date)::SMALLINT + WHEN 0 THEN 7 + ELSE date_part('dow', v_res_date)::SMALLINT + END <> v_weekdaynum) + THEN + RAISE invalid_datetime_format; + END IF; + END IF; + + RETURN v_res_datetime; +EXCEPTION + WHEN invalid_datetime_format OR datetime_field_overflow THEN + RAISE USING MESSAGE := format('Error converting string value ''%s'' into data type %s using culture ''%s''.', + p_datetimestring, v_res_datatype, p_culture), + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Data type should be one of these values: ''DATETIME'', ''SMALLDATETIME'', ''DATETIME2''/''DATETIME2(n)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_indicator_parameter_value THEN + RAISE USING MESSAGE := format('Invalid attributes specified for data type %s.', v_res_datatype), + DETAIL := 'Use of incorrect scale value, which is not corresponding to specified data type.', + HINT := 'Change data type scale component or select different data type and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := format('Specified scale %s is invalid.', v_scale), + DETAIL := 'Use of incorrect data type scale value during conversion process.', + HINT := 'Change scale component of data type parameter to be in range [0..7] and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := CASE char_length(coalesce(CONVERSION_LANG, '')) + WHEN 0 THEN format('The culture parameter ''%s'' provided in the function call is not supported.', + p_culture) + ELSE format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG) + END, + DETAIL := 'Passed incorrect value for "p_culture" parameter or compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Check "p_culture" input parameter value, correct it if needed, and try again. Also check CONVERSION_LANG constant value.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_parse_to_time(IN p_datatype TEXT, + IN p_srctimestring TEXT, + IN p_culture TEXT DEFAULT '') +RETURNS TIME WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_day VARCHAR; + v_year SMALLINT; + v_month VARCHAR; + v_res_date DATE; + v_scale SMALLINT; + v_hijridate DATE; + v_culture VARCHAR; + v_dayparts TEXT[]; + v_resmask VARCHAR; + v_datatype VARCHAR; + v_raw_year VARCHAR; + v_left_part VARCHAR; + v_right_part VARCHAR; + v_resmask_fi VARCHAR; + v_timestring VARCHAR; + v_correctnum VARCHAR; + v_weekdaynum SMALLINT; + v_err_message VARCHAR; + v_date_format VARCHAR; + v_weekdaynames TEXT[]; + v_hours SMALLINT := 0; + v_srctimestring VARCHAR; + v_minutes SMALLINT := 0; + v_res_datatype VARCHAR; + v_error_message VARCHAR; + v_found BOOLEAN := TRUE; + v_compday_regexp VARCHAR; + v_regmatch_groups TEXT[]; + v_datatype_groups TEXT[]; + v_seconds VARCHAR := '0'; + v_fseconds VARCHAR := '0'; + v_compmonth_regexp VARCHAR; + v_lang_metadata_json JSONB; + v_resmask_cnt SMALLINT := 10; + v_res_time TIME WITHOUT TIME ZONE; + DAYMM_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + FULLYEAR_REGEXP CONSTANT VARCHAR := '(\d{3,4})'; + SHORTYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,2})'; + COMPYEAR_REGEXP CONSTANT VARCHAR := '(\d{1,4})'; + AMPM_REGEXP CONSTANT VARCHAR := '(?:[AP]M|ص|م)'; + TIMEUNIT_REGEXP CONSTANT VARCHAR := '\s*\d{1,2}\s*'; + MASKSEPONE_REGEXP CONSTANT VARCHAR := '\s*(?:/|-)?'; + MASKSEPTWO_REGEXP CONSTANT VARCHAR := '\s*(?:\s|/|-|\.|,)'; + MASKSEPTWO_FI_REGEXP CONSTANT VARCHAR := '\s*(?:\s|/|-|,)'; + MASKSEPTHREE_REGEXP CONSTANT VARCHAR := '\s*(?:/|-|\.|,)'; + TIME_MASKSEP_REGEXP CONSTANT VARCHAR := '(?:\s|\.|,)*'; + TIME_MASKSEP_FI_REGEXP CONSTANT VARCHAR := '(?:\s|,)*'; + WEEKDAYAMPM_START_REGEXP CONSTANT VARCHAR := '(^|[[:digit:][:space:]\.,])'; + WEEKDAYAMPM_END_REGEXP CONSTANT VARCHAR := '([[:digit:][:space:]\.,]|$)(?=[^/-]|$)'; + CORRECTNUM_REGEXP CONSTANT VARCHAR := '(?:([+-]\d{1,4})(?:[[:space:]\.,]|[AP]M|ص|م|$))'; + DATATYPE_REGEXP CONSTANT VARCHAR := '^(TIME)\s*(?:\()?\s*((?:-)?\d+)?\s*(?:\))?$'; + ANNO_DOMINI_REGEXP VARCHAR := '(AD|A\.D\.)'; + ANNO_DOMINI_COMPREGEXP VARCHAR := concat(WEEKDAYAMPM_START_REGEXP, ANNO_DOMINI_REGEXP, WEEKDAYAMPM_END_REGEXP); + HHMMSSFS_PART_REGEXP CONSTANT VARCHAR := + concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, TIMEUNIT_REGEXP, '\:', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\s*\d{1,2}\.\d+(?!\d)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?'); + HHMMSSFS_PART_FI_REGEXP CONSTANT VARCHAR := + concat(TIMEUNIT_REGEXP, AMPM_REGEXP, '|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?\.?|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '(?!\d)', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, TIMEUNIT_REGEXP, '[\:\.]', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '\s*\d{1,2}\.\d+(?!\d)\.?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?|', + AMPM_REGEXP, '?'); + v_defmask1_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(?:[\.|,]+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, '$'); + v_defmask1_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(?:[\.|,]+', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask2_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(?:(?:[\.|,]+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?)|', + CORRECTNUM_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, '$'); + v_defmask2_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(?:(?:[\.|,]+', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, TIME_MASKSEP_FI_REGEXP, CORRECTNUM_REGEXP, '?)|', + CORRECTNUM_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask3_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, ')|', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', TIME_MASKSEP_REGEXP, AMPM_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask3_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + TIME_MASKSEP_FI_REGEXP, '[\./]?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)', + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask4_0_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask4_1_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:\s|,)+', + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask4_2_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '\s*[\.]+', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, '\s*(', AMPM_REGEXP, ')', + TIME_MASKSEP_REGEXP, '$'); + v_defmask5_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask5_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask6_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask6_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:\s*[\.])?', + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask7_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask7_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask8_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:[\.|,]+', AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:[\.|,]+', AMPM_REGEXP, ')?', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask8_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_FI_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)|', + '(?:[,]+', AMPM_REGEXP, '))', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:[\,]+|\s*/\s*)', AMPM_REGEXP, ')?', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask9_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', + HHMMSSFS_PART_REGEXP, + ')', TIME_MASKSEP_REGEXP, '$'); + v_defmask9_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '(', + HHMMSSFS_PART_FI_REGEXP, + ')', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask10_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask10_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask11_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask11_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask12_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask12_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask13_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$'); + v_defmask13_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask14_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)' + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask14_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)' + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_FI_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_FI_REGEXP, + COMPYEAR_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask15_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask15_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask16_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask16_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + DAYMM_REGEXP, + '(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)', TIME_MASKSEP_REGEXP, + COMPYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask17_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask17_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask18_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, '$'); + v_defmask18_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, '(?:', AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))?)|', + '(?:(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '(?=(?:[[:space:]\.,])+))))', TIME_MASKSEP_REGEXP, + '($comp_month$)', + TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, '$'); + v_defmask19_regexp VARCHAR := concat('^', TIME_MASKSEP_REGEXP, '(', HHMMSSFS_PART_REGEXP, ')?', TIME_MASKSEP_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|\.|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_REGEXP, '))?', TIME_MASKSEP_REGEXP, '$'); + v_defmask19_fi_regexp VARCHAR := concat('^', TIME_MASKSEP_FI_REGEXP, '(', HHMMSSFS_PART_FI_REGEXP, ')?', TIME_MASKSEP_FI_REGEXP, + '($comp_month$)', + '(?:', MASKSEPTHREE_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)?', TIME_MASKSEP_REGEXP, + FULLYEAR_REGEXP, + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, + DAYMM_REGEXP, + '((?:(?:\s|,)+|', AMPM_REGEXP, ')(?:', HHMMSSFS_PART_FI_REGEXP, '))?', TIME_MASKSEP_FI_REGEXP, '$'); + CONVERSION_LANG CONSTANT VARCHAR := ''; + DATE_FORMAT CONSTANT VARCHAR := ''; +BEGIN + v_datatype := trim(p_datatype); + v_srctimestring := upper(trim(p_srctimestring)); + v_culture := coalesce(nullif(upper(trim(p_culture)), ''), 'EN-US'); + + v_datatype_groups := regexp_matches(v_datatype, DATATYPE_REGEXP, 'gi'); + + v_res_datatype := upper(v_datatype_groups[1]); + v_scale := v_datatype_groups[2]::SMALLINT; + + IF (v_res_datatype IS NULL) THEN + RAISE datatype_mismatch; + ELSIF (coalesce(v_scale, 0) NOT BETWEEN 0 AND 7) + THEN + RAISE interval_field_overflow; + ELSIF (v_scale IS NULL) THEN + v_scale := 7; + END IF; + + v_dayparts := ARRAY(SELECT upper(array_to_string(regexp_matches(v_srctimestring, '[AP]M|ص|م', 'gi'), ''))); + + IF (array_length(v_dayparts, 1) > 1) THEN + RAISE invalid_datetime_format; + END IF; + + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(coalesce(nullif(CONVERSION_LANG, ''), p_culture)); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_parameter_value; + END; + + v_compday_regexp := array_to_string(array_cat(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_names')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_shortnames'))), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'days_extrashortnames'))), '|'); + + v_weekdaynames := ARRAY(SELECT array_to_string(regexp_matches(v_srctimestring, v_compday_regexp, 'gi'), '')); + + IF (array_length(v_weekdaynames, 1) > 1) THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_weekdaynames[1] IS NOT NULL AND + v_srctimestring ~* concat(WEEKDAYAMPM_START_REGEXP, '(', v_compday_regexp, ')', WEEKDAYAMPM_END_REGEXP)) + THEN + v_srctimestring := replace(v_srctimestring, v_weekdaynames[1], ' '); + END IF; + + IF (v_srctimestring ~* ANNO_DOMINI_COMPREGEXP) + THEN + IF (v_culture !~ 'EN[-_]US|DA[-_]DK|SV[-_]SE|EN[-_]GB|HI[-_]IS') THEN + RAISE invalid_datetime_format; + END IF; + + v_srctimestring := regexp_replace(v_srctimestring, + ANNO_DOMINI_COMPREGEXP, + regexp_replace(array_to_string(regexp_matches(v_srctimestring, ANNO_DOMINI_COMPREGEXP, 'gi'), ''), + ANNO_DOMINI_REGEXP, ' ', 'gi'), + 'gi'); + END IF; + + v_date_format := coalesce(nullif(upper(trim(DATE_FORMAT)), ''), v_lang_metadata_json ->> 'date_format'); + + v_compmonth_regexp := + array_to_string(array_cat(array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_shortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_names'))), + array_cat(ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_extrashortnames')), + ARRAY(SELECT jsonb_array_elements_text(v_lang_metadata_json -> 'months_extranames'))) + ), '|'); + + IF ((v_srctimestring ~* v_defmask1_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask1_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_srctimestring ~ concat(CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, + AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, + CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_srctimestring, CASE v_culture + WHEN 'FI' THEN v_defmask1_fi_regexp + ELSE v_defmask1_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[2]; + v_correctnum := coalesce(v_regmatch_groups[1], v_regmatch_groups[3], + v_regmatch_groups[5], v_regmatch_groups[6]); + + IF (v_date_format = 'DMY' OR + v_culture IN ('SV-SE', 'SV_SE', 'LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[7]; + ELSE + v_day := v_regmatch_groups[7]; + v_month := v_regmatch_groups[4]; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + v_month::SMALLINT > 12) THEN + RAISE invalid_datetime_format; + END IF; + + v_raw_year := to_char(sys.babelfish_conv_greg_to_hijri(current_date + 1), 'YYYY'); + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + ELSE + v_year := to_char(current_date, 'YYYY')::SMALLINT; + END IF; + + ELSIF ((v_srctimestring ~* v_defmask6_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask6_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_srctimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '))', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d{3,4}', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_srctimestring, CASE v_culture + WHEN 'FI' THEN v_defmask6_fi_regexp + ELSE v_defmask6_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[3]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[2]::SMALLINT - 543 + ELSE v_regmatch_groups[2]::SMALLINT + END; + + ELSIF ((v_srctimestring ~* v_defmask2_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask2_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_srctimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', + '(?:(?:', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?)|', + '(?:', TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?', TIME_MASKSEP_REGEXP, + AMPM_REGEXP, TIME_MASKSEP_REGEXP, CORRECTNUM_REGEXP, '?))', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_srctimestring, CASE v_culture + WHEN 'FI' THEN v_defmask2_fi_regexp + ELSE v_defmask2_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[2]; + v_correctnum := coalesce(v_regmatch_groups[1], v_regmatch_groups[3], v_regmatch_groups[5], + v_regmatch_groups[6], v_regmatch_groups[8], v_regmatch_groups[9]); + v_day := '01'; + v_month := v_regmatch_groups[7]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[4]::SMALLINT - 543 + ELSE v_regmatch_groups[4]::SMALLINT + END; + + ELSIF (v_srctimestring ~* v_defmask4_1_regexp OR + (v_srctimestring ~* v_defmask4_2_regexp AND v_culture !~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') OR + (v_srctimestring ~* v_defmask9_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask9_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_srctimestring ~ concat('\d+\s*\.?(?:,+|,*', AMPM_REGEXP, ')', TIME_MASKSEP_FI_REGEXP, '\.+', TIME_MASKSEP_REGEXP, '$|', + '\d+\s*\.', TIME_MASKSEP_FI_REGEXP, '\.', TIME_MASKSEP_FI_REGEXP, '$') AND + v_culture = 'FI') + THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_srctimestring ~* v_defmask4_0_regexp) THEN + v_timestring := (regexp_matches(v_srctimestring, v_defmask4_0_regexp, 'gi'))[1]; + ELSE + v_timestring := v_srctimestring; + END IF; + + v_res_date := current_date; + v_day := to_char(v_res_date, 'DD'); + v_month := to_char(v_res_date, 'MM'); + v_year := to_char(v_res_date, 'YYYY')::SMALLINT; + + ELSIF ((v_srctimestring ~* v_defmask3_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask3_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_srctimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', + TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, '|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_srctimestring, CASE v_culture + WHEN 'FI' THEN v_defmask3_fi_regexp + ELSE v_defmask3_regexp + END, 'gi'); + v_timestring := v_regmatch_groups[1]; + v_day := '01'; + v_month := v_regmatch_groups[2]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[3]::SMALLINT - 543 + ELSE v_regmatch_groups[3]::SMALLINT + END; + + ELSIF ((v_srctimestring ~* v_defmask5_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask5_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_srctimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$|', + '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_srctimestring, v_defmask5_regexp, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[4]::SMALLINT - 543 + ELSE v_regmatch_groups[4]::SMALLINT + END; + + IF (v_date_format = 'DMY' OR + v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[2]; + v_month := v_regmatch_groups[3]; + ELSE + v_day := v_regmatch_groups[3]; + v_month := v_regmatch_groups[2]; + END IF; + + ELSIF ((v_srctimestring ~* v_defmask7_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask7_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA') OR + (v_srctimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', + MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{3,4}|', + '\d{3,4}', MASKSEPTWO_REGEXP, '?', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV')) + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_srctimestring, CASE v_culture + WHEN 'FI' THEN v_defmask7_fi_regexp + ELSE v_defmask7_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[2]; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_regmatch_groups[3]::SMALLINT - 543 + ELSE v_regmatch_groups[3]::SMALLINT + END; + + ELSIF ((v_srctimestring ~* v_defmask8_regexp AND v_culture <> 'FI') OR + (v_srctimestring ~* v_defmask8_fi_regexp AND v_culture = 'FI')) + THEN + IF (v_srctimestring ~ concat('\s*\d{1,2}\.\s*(?:\.|\d+(?!\d)\s*\.)', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', + MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}|', + '\d{1,2}', MASKSEPTWO_REGEXP, TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}', MASKSEPTWO_REGEXP, + TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '\d{1,2}\s*(?:\.)+|', + '\d+\s*(?:\.)+', TIME_MASKSEP_REGEXP, AMPM_REGEXP, '?', TIME_MASKSEP_REGEXP, '$') AND + v_culture ~ 'FI|DE[-_]DE|NN[-_]NO|CS[-_]CZ|PL[-_]PL|RO[-_]RO|SK[-_]SK|SL[-_]SI|BG[-_]BG|RU[-_]RU|TR[-_]TR|ET[-_]EE|LV[-_]LV') + THEN + RAISE invalid_datetime_format; + END IF; + + v_regmatch_groups := regexp_matches(v_srctimestring, CASE v_culture + WHEN 'FI' THEN v_defmask8_fi_regexp + ELSE v_defmask8_regexp + END, 'gi'); + v_timestring := concat(v_regmatch_groups[1], v_regmatch_groups[5]); + + IF (v_date_format = 'DMY' OR + v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_regmatch_groups[2]; + v_month := v_regmatch_groups[3]; + v_raw_year := v_regmatch_groups[4]; + ELSIF (v_date_format = 'YMD') + THEN + v_day := v_regmatch_groups[4]; + v_month := v_regmatch_groups[3]; + v_raw_year := v_regmatch_groups[2]; + ELSE + v_day := v_regmatch_groups[3]; + v_month := v_regmatch_groups[2]; + v_raw_year := v_regmatch_groups[4]; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + v_month::SMALLINT > 12) THEN + RAISE invalid_datetime_format; + END IF; + + v_raw_year := sys.babelfish_get_full_year(v_raw_year, '14'); + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + + ELSIF (v_culture IN ('TH-TH', 'TH_TH')) THEN + v_year := sys.babelfish_get_full_year(v_raw_year)::SMALLINT - 43; + ELSE + v_year := sys.babelfish_get_full_year(v_raw_year, '', 29)::SMALLINT; + END IF; + ELSE + v_found := FALSE; + END IF; + + WHILE (NOT v_found AND v_resmask_cnt < 20) + LOOP + v_resmask := replace(CASE v_resmask_cnt + WHEN 10 THEN v_defmask10_regexp + WHEN 11 THEN v_defmask11_regexp + WHEN 12 THEN v_defmask12_regexp + WHEN 13 THEN v_defmask13_regexp + WHEN 14 THEN v_defmask14_regexp + WHEN 15 THEN v_defmask15_regexp + WHEN 16 THEN v_defmask16_regexp + WHEN 17 THEN v_defmask17_regexp + WHEN 18 THEN v_defmask18_regexp + WHEN 19 THEN v_defmask19_regexp + END, + '$comp_month$', v_compmonth_regexp); + + v_resmask_fi := replace(CASE v_resmask_cnt + WHEN 10 THEN v_defmask10_fi_regexp + WHEN 11 THEN v_defmask11_fi_regexp + WHEN 12 THEN v_defmask12_fi_regexp + WHEN 13 THEN v_defmask13_fi_regexp + WHEN 14 THEN v_defmask14_fi_regexp + WHEN 15 THEN v_defmask15_fi_regexp + WHEN 16 THEN v_defmask16_fi_regexp + WHEN 17 THEN v_defmask17_fi_regexp + WHEN 18 THEN v_defmask18_fi_regexp + WHEN 19 THEN v_defmask19_fi_regexp + END, + '$comp_month$', v_compmonth_regexp); + + IF ((v_srctimestring ~* v_resmask AND v_culture <> 'FI') OR + (v_srctimestring ~* v_resmask_fi AND v_culture = 'FI')) + THEN + v_found := TRUE; + v_regmatch_groups := regexp_matches(v_srctimestring, CASE v_culture + WHEN 'FI' THEN v_resmask_fi + ELSE v_resmask + END, 'gi'); + v_timestring := CASE + WHEN v_resmask_cnt IN (10, 11, 12, 13) THEN concat(v_regmatch_groups[1], v_regmatch_groups[4]) + ELSE concat(v_regmatch_groups[1], v_regmatch_groups[5]) + END; + + IF (v_resmask_cnt = 10) + THEN + IF (v_regmatch_groups[3] = 'MAR' AND + v_culture IN ('IT-IT', 'IT_IT')) + THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_date_format = 'YMD' AND v_culture NOT IN ('SV-SE', 'SV_SE', 'LV-LV', 'LV_LV')) + THEN + v_day := '01'; + v_year := sys.babelfish_get_full_year(v_regmatch_groups[2], '', 29)::SMALLINT; + ELSE + v_day := v_regmatch_groups[2]; + v_year := to_char(current_date, 'YYYY')::SMALLINT; + END IF; + + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := to_char(sys.babelfish_conv_greg_to_hijri(current_date + 1), 'YYYY'); + + ELSIF (v_resmask_cnt = 11) + THEN + IF (v_date_format IN ('YMD', 'MDY') AND v_culture NOT IN ('SV-SE', 'SV_SE')) + THEN + v_day := v_regmatch_groups[3]; + v_year := to_char(current_date, 'YYYY')::SMALLINT; + ELSE + v_day := '01'; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_regmatch_groups[3])::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_regmatch_groups[3], '', 29)::SMALLINT + END; + END IF; + + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := sys.babelfish_get_full_year(substring(v_year::TEXT, 3, 2), '14'); + + ELSIF (v_resmask_cnt = 12) + THEN + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 13) + THEN + v_day := '01'; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[3]; + + ELSIF (v_resmask_cnt IN (14, 15, 16)) + THEN + IF (v_resmask_cnt = 14) + THEN + v_left_part := v_regmatch_groups[4]; + v_right_part := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + ELSIF (v_resmask_cnt = 15) + THEN + v_left_part := v_regmatch_groups[4]; + v_right_part := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + ELSE + v_left_part := v_regmatch_groups[3]; + v_right_part := v_regmatch_groups[2]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[4], v_lang_metadata_json); + END IF; + + IF (char_length(v_left_part) <= 2) + THEN + IF (v_date_format = 'YMD' AND v_culture NOT IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_left_part; + v_raw_year := sys.babelfish_get_full_year(v_right_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_right_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_right_part, '', 29)::SMALLINT + END; + BEGIN + v_res_date := make_date(v_year, v_month::SMALLINT, v_day::SMALLINT); + EXCEPTION + WHEN OTHERS THEN + v_day := v_right_part; + v_raw_year := sys.babelfish_get_full_year(v_left_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_left_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_left_part, '', 29)::SMALLINT + END; + END; + END IF; + + IF (v_date_format IN ('MDY', 'DMY') OR v_culture IN ('LV-LV', 'LV_LV')) + THEN + v_day := v_right_part; + v_raw_year := sys.babelfish_get_full_year(v_left_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_left_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_left_part, '', 29)::SMALLINT + END; + BEGIN + v_res_date := make_date(v_year, v_month::SMALLINT, v_day::SMALLINT); + EXCEPTION + WHEN OTHERS THEN + v_day := v_left_part; + v_raw_year := sys.babelfish_get_full_year(v_right_part, '14'); + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN sys.babelfish_get_full_year(v_right_part)::SMALLINT - 43 + ELSE sys.babelfish_get_full_year(v_right_part, '', 29)::SMALLINT + END; + END; + END IF; + ELSE + v_day := v_right_part; + v_raw_year := v_left_part; + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_left_part::SMALLINT - 543 + ELSE v_left_part::SMALLINT + END; + END IF; + + ELSIF (v_resmask_cnt = 17) + THEN + v_day := v_regmatch_groups[4]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[3], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 18) + THEN + v_day := v_regmatch_groups[3]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[4], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[2]; + + ELSIF (v_resmask_cnt = 19) + THEN + v_day := v_regmatch_groups[4]; + v_month := sys.babelfish_get_monthnum_by_name(v_regmatch_groups[2], v_lang_metadata_json); + v_raw_year := v_regmatch_groups[3]; + END IF; + + IF (v_resmask_cnt NOT IN (10, 11, 14, 15, 16)) + THEN + v_year := CASE + WHEN v_culture IN ('TH-TH', 'TH_TH') THEN v_raw_year::SMALLINT - 543 + ELSE v_raw_year::SMALLINT + END; + END IF; + + IF (v_culture IN ('AR', 'AR-SA', 'AR_SA')) + THEN + IF (v_day::SMALLINT > 30 OR + (v_resmask_cnt NOT IN (10, 11, 14, 15, 16) AND v_year NOT BETWEEN 1318 AND 1501) OR + (v_resmask_cnt IN (14, 15, 16) AND v_raw_year::SMALLINT NOT BETWEEN 1318 AND 1501)) + THEN + RAISE invalid_datetime_format; + END IF; + + v_hijridate := sys.babelfish_conv_hijri_to_greg(v_day, v_month, v_raw_year) - 1; + + v_day := to_char(v_hijridate, 'DD'); + v_month := to_char(v_hijridate, 'MM'); + v_year := to_char(v_hijridate, 'YYYY')::SMALLINT; + END IF; + END IF; + + v_resmask_cnt := v_resmask_cnt + 1; + END LOOP; + + IF (NOT v_found) THEN + RAISE invalid_datetime_format; + END IF; + + v_res_date := make_date(v_year, v_month::SMALLINT, v_day::SMALLINT); + + IF (v_weekdaynames[1] IS NOT NULL) THEN + v_weekdaynum := sys.babelfish_get_weekdaynum_by_name(v_weekdaynames[1], v_lang_metadata_json); + + IF (date_part('dow', v_res_date)::SMALLINT <> v_weekdaynum) THEN + RAISE invalid_datetime_format; + END IF; + END IF; + + IF (char_length(v_timestring) > 0 AND v_timestring NOT IN ('AM', 'ص', 'PM', 'م')) + THEN + IF (v_culture = 'FI') THEN + v_timestring := translate(v_timestring, '.,', ': '); + + IF (char_length(split_part(v_timestring, ':', 4)) > 0) THEN + v_timestring := regexp_replace(v_timestring, ':(?=\s*\d+\s*:?\s*(?:[AP]M|ص|م)?\s*$)', '.'); + END IF; + END IF; + + v_timestring := replace(regexp_replace(v_timestring, '\.?[AP]M|ص|م|\s|\,|\.\D|[\.|:]$', '', 'gi'), ':.', ':'); + + BEGIN + v_hours := coalesce(split_part(v_timestring, ':', 1)::SMALLINT, 0); + + IF ((v_dayparts[1] IN ('AM', 'ص') AND v_hours NOT BETWEEN 0 AND 12) OR + (v_dayparts[1] IN ('PM', 'م') AND v_hours NOT BETWEEN 1 AND 23)) + THEN + RAISE invalid_datetime_format; + ELSIF (v_dayparts[1] = 'PM' AND v_hours < 12) THEN + v_hours := v_hours + 12; + ELSIF (v_dayparts[1] = 'AM' AND v_hours = 12) THEN + v_hours := v_hours - 12; + END IF; + + v_minutes := coalesce(nullif(split_part(v_timestring, ':', 2), '')::SMALLINT, 0); + v_seconds := coalesce(nullif(split_part(v_timestring, ':', 3), ''), '0'); + + IF (v_seconds ~ '\.') THEN + v_fseconds := split_part(v_seconds, '.', 2); + v_seconds := split_part(v_seconds, '.', 1); + END IF; + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_datetime_format; + END; + ELSIF (v_dayparts[1] IN ('PM', 'م')) + THEN + v_hours := 12; + END IF; + + v_fseconds := sys.babelfish_get_microsecs_from_fractsecs(rpad(v_fseconds, 9, '0'), v_scale); + v_seconds := concat_ws('.', v_seconds, v_fseconds); + + v_res_time := make_time(v_hours, v_minutes, v_seconds::NUMERIC); + + RETURN v_res_time; +EXCEPTION + WHEN invalid_datetime_format OR datetime_field_overflow THEN + RAISE USING MESSAGE := format('Error converting string value ''%s'' into data type %s using culture ''%s''.', + p_srctimestring, v_res_datatype, p_culture), + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Source data type should be ''TIME'' or ''TIME(n)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_indicator_parameter_value THEN + RAISE USING MESSAGE := format('Invalid attributes specified for data type %s.', v_res_datatype), + DETAIL := 'Use of incorrect scale value, which is not corresponding to specified data type.', + HINT := 'Change data type scale component or select different data type and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := format('Specified scale %s is invalid.', v_scale), + DETAIL := 'Use of incorrect data type scale value during conversion process.', + HINT := 'Change scale component of data type parameter to be in range [0..7] and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := CASE char_length(coalesce(CONVERSION_LANG, '')) + WHEN 0 THEN format('The culture parameter ''%s'' provided in the function call is not supported.', + p_culture) + ELSE format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG) + END, + DETAIL := 'Passed incorrect value for "p_culture" parameter or compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Check "p_culture" input parameter value, correct it if needed, and try again. Also check CONVERSION_LANG constant value.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'integer\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +/* *********************************************** +EXTENSION PACK function ROUND3(arg1, arg2, arg3) +schema sys +**************************************************/ +create or replace function sys.babelfish_ROUND3(x in numeric, y in int, z in int)returns numeric +AS +$body$ +BEGIN +/*************************************************************** +EXTENSION PACK function ROUND3(arg1, arg2, arg3) +***************************************************************/ + if z = 0 or z is null then + return round(x,y); + else + return trunc(x,y); + end if; +END; +$body$ +language plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_round_fractseconds(IN p_fractseconds NUMERIC) +RETURNS INTEGER +AS +$BODY$ +DECLARE + v_modpart INTEGER; + v_decpart INTEGER; + v_fractseconds INTEGER; +BEGIN + v_fractseconds := floor(p_fractseconds)::INTEGER; + v_modpart := v_fractseconds % 10; + v_decpart := v_fractseconds - v_modpart; + + RETURN CASE + WHEN (v_modpart BETWEEN 0 AND 1) THEN v_decpart + WHEN (v_modpart BETWEEN 2 AND 4) THEN v_decpart + 3 + WHEN (v_modpart BETWEEN 5 AND 8) THEN v_decpart + 7 + ELSE v_decpart + 10 -- 9 + END; +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_round_fractseconds(IN p_fractseconds TEXT) +RETURNS INTEGER +AS +$BODY$ +BEGIN + RETURN sys.babelfish_round_fractseconds(p_fractseconds::NUMERIC); +EXCEPTION + WHEN invalid_text_representation THEN + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to NUMERIC data type.', trim(p_fractseconds)), + DETAIL := 'Passed argument value contains illegal characters.', + HINT := 'Correct passed argument value, remove all illegal characters.'; + + +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_set_version(pComponentVersion VARCHAR(256),pComponentName VARCHAR(256)) + RETURNS void AS +$BODY$ +DECLARE + rowcount smallint; +BEGIN + UPDATE sys.versions SET componentversion = pComponentVersion + WHERE extpackcomponentname = pComponentName; + GET DIAGNOSTICS rowcount = ROW_COUNT; + + IF rowcount < 1 THEN + INSERT INTO sys.versions(extpackcomponentname,componentversion) + VALUES (pComponentName,pComponentVersion); + END IF; +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_add_job ( + par_job_name varchar, + par_enabled smallint = 1, + par_description varchar = NULL::character varying, + par_start_step_id integer = 1, + par_category_name varchar = NULL::character varying, + par_category_id integer = NULL::integer, + par_owner_login_name varchar = NULL::character varying, + par_notify_level_eventlog integer = 2, + par_notify_level_email integer = 0, + par_notify_level_netsend integer = 0, + par_notify_level_page integer = 0, + par_notify_email_operator_name varchar = NULL::character varying, + par_notify_netsend_operator_name varchar = NULL::character varying, + par_notify_page_operator_name varchar = NULL::character varying, + par_delete_level integer = 0, + inout par_job_id integer = NULL::integer, + par_originating_server varchar = NULL::character varying, + out returncode integer +) +RETURNS record AS +$body$ +DECLARE + var_retval INT DEFAULT 0; + var_notify_email_operator_id INT DEFAULT 0; + var_notify_email_operator_name VARCHAR(128); + var_notify_netsend_operator_id INT DEFAULT 0; + var_notify_page_operator_id INT DEFAULT 0; + var_owner_sid CHAR(85) ; + var_originating_server_id INT DEFAULT 0; +BEGIN + /* Remove any leading/trailing spaces from parameters (except @owner_login_name) */ + SELECT UPPER(LTRIM(RTRIM(par_originating_server))) INTO par_originating_server; + SELECT LTRIM(RTRIM(par_job_name)) INTO par_job_name; + SELECT LTRIM(RTRIM(par_description)) INTO par_description; + SELECT '[Uncategorized (Local)]' INTO par_category_name; + SELECT 0 INTO par_category_id; + SELECT LTRIM(RTRIM(par_notify_email_operator_name)) INTO par_notify_email_operator_name; + SELECT LTRIM(RTRIM(par_notify_netsend_operator_name)) INTO par_notify_netsend_operator_name; + SELECT LTRIM(RTRIM(par_notify_page_operator_name)) INTO par_notify_page_operator_name; + SELECT NULL INTO var_originating_server_id; /* Turn [nullable] empty string parameters into NULLs */ + SELECT NULL INTO par_job_id; + + IF (par_originating_server = '') + THEN + SELECT NULL INTO par_originating_server; + END IF; + + IF (par_description = '') + THEN + SELECT NULL INTO par_description; + END IF; + + IF (par_category_name = '') + THEN + SELECT NULL INTO par_category_name; + END IF; + + IF (par_notify_email_operator_name = '') + THEN + SELECT NULL INTO par_notify_email_operator_name; + END IF; + + IF (par_notify_netsend_operator_name = '') + THEN + SELECT NULL INTO par_notify_netsend_operator_name; + END IF; + + IF (par_notify_page_operator_name = '') + THEN + SELECT NULL INTO par_notify_page_operator_name; + END IF; + + /* Check parameters */ + SELECT t.par_owner_sid + , t.par_notify_level_email + , t.par_notify_level_netsend + , t.par_notify_level_page + , t.par_category_id + , t.par_notify_email_operator_id + , t.par_notify_netsend_operator_id + , t.par_notify_page_operator_id + , t.par_originating_server + , t.returncode + FROM sys.babelfish_sp_verify_job( + par_job_id /* NULL::integer */ + , par_job_name + , par_enabled + , par_start_step_id + , par_category_name + , var_owner_sid /* par_owner_sid */ + , par_notify_level_eventlog + , par_notify_level_email + , par_notify_level_netsend + , par_notify_level_page + , par_notify_email_operator_name + , par_notify_netsend_operator_name + , par_notify_page_operator_name + , par_delete_level + , par_category_id + , var_notify_email_operator_id /* par_notify_email_operator_id */ + , var_notify_netsend_operator_id /* par_notify_netsend_operator_id */ + , var_notify_page_operator_id /* par_notify_page_operator_id */ + , par_originating_server + ) t + INTO var_owner_sid + , par_notify_level_email + , par_notify_level_netsend + , par_notify_level_page + , par_category_id + , var_notify_email_operator_id + , var_notify_netsend_operator_id + , var_notify_page_operator_id + , par_originating_server + , var_retval; + + IF (var_retval <> 0) /* Failure */ + THEN + returncode := 1; + RETURN; + END IF; + + var_notify_email_operator_name := par_notify_email_operator_name; + + /* Default the description (if not supplied) */ + IF (par_description IS NULL) + THEN + SELECT 'No description available.' INTO par_description; + END IF; + + var_originating_server_id := 0; + var_owner_sid := ''; + + INSERT + INTO sys.sysjobs ( + originating_server_id + , name + , enabled + , description + , start_step_id + , category_id + , owner_sid + , notify_level_eventlog + , notify_level_email + , notify_level_netsend + , notify_level_page + , notify_email_operator_id + , notify_email_operator_name + , notify_netsend_operator_id + , notify_page_operator_id + , delete_level + , version_number + ) + VALUES ( + var_originating_server_id + , par_job_name + , par_enabled + , par_description + , par_start_step_id + , par_category_id + , var_owner_sid + , par_notify_level_eventlog + , par_notify_level_email + , par_notify_level_netsend + , par_notify_level_page + , var_notify_email_operator_id + , var_notify_email_operator_name + , var_notify_netsend_operator_id + , var_notify_page_operator_id + , par_delete_level + , 1); + + /* scope_identity() */ + SELECT LASTVAL() INTO par_job_id; + + /* Version number 1 */ + /* SELECT @retval = @@error */ + /* 0 means success */ + returncode := var_retval; + RETURN; + +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_add_jobschedule ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_name varchar = NULL::character varying, + par_enabled smallint = 1, + par_freq_type integer = 1, + par_freq_interval integer = 0, + par_freq_subday_type integer = 0, + par_freq_subday_interval integer = 0, + par_freq_relative_interval integer = 0, + par_freq_recurrence_factor integer = 0, + par_active_start_date integer = 20000101, + par_active_end_date integer = 99991231, + par_active_start_time integer = 0, + par_active_end_time integer = 235959, + inout par_schedule_id integer = NULL::integer, + par_automatic_post smallint = 1, + inout par_schedule_uid char = NULL::bpchar, + out returncode integer +) +AS +$body$ +DECLARE + var_retval INT; + var_owner_login_name VARCHAR(128); +BEGIN + + -- Check that we can uniquely identify the job + SELECT t.par_job_name + , t.par_job_id + , t.returncode + FROM sys.babelfish_sp_verify_job_identifiers ( + '@job_name' + , '@job_id' + , par_job_name + , par_job_id + , 'TEST'::character varying + , NULL::bpchar + ) t + INTO par_job_name + , par_job_id + , var_retval; + + IF (var_retval <> 0) + THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + /* Add the schedule first */ + SELECT t.par_schedule_uid + , t.par_schedule_id + , t.returncode + FROM sys.babelfish_sp_add_schedule( + par_name + , par_enabled + , par_freq_type + , par_freq_interval + , par_freq_subday_type + , par_freq_subday_interval + , par_freq_relative_interval + , par_freq_recurrence_factor + , par_active_start_date + , par_active_end_date + , par_active_start_time + , par_active_end_time + , var_owner_login_name + , par_schedule_uid + , par_schedule_id + , NULL + ) t + INTO par_schedule_uid + , par_schedule_id + , var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_attach_schedule( + par_job_id := par_job_id + , par_job_name := NULL + , par_schedule_id := par_schedule_id + , par_schedule_name := NULL + , par_automatic_post := par_automatic_post + ) t + INTO var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_aws_add_jobschedule(par_job_id, par_schedule_id) t + INTO var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + /* 0 means success */ + returncode := (var_retval); + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_add_jobstep ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_step_id integer = NULL::integer, + par_step_name varchar = NULL::character varying, + par_subsystem varchar = 'TSQL'::bpchar, + par_command text = NULL::text, + par_additional_parameters text = NULL::text, + par_cmdexec_success_code integer = 0, + par_on_success_action smallint = 1, + par_on_success_step_id integer = 0, + par_on_fail_action smallint = 2, + par_on_fail_step_id integer = 0, + par_server varchar = NULL::character varying, + par_database_name varchar = NULL::character varying, + par_database_user_name varchar = NULL::character varying, + par_retry_attempts integer = 0, + par_retry_interval integer = 0, + par_os_run_priority integer = 0, + par_output_file_name varchar = NULL::character varying, + par_flags integer = 0, + par_proxy_id integer = NULL::integer, + par_proxy_name varchar = NULL::character varying, + inout par_step_uid char = NULL::bpchar, + out returncode integer +) +AS +$body$ +DECLARE + var_retval INT; + var_max_step_id INT; + var_step_id INT; +BEGIN + + SELECT t.par_job_name + , t.par_job_id + , t.returncode + FROM sys.babelfish_sp_verify_job_identifiers ( + '@job_name' + , '@job_id' + , par_job_name + , par_job_id + , 'TEST'::character varying + , NULL::bpchar + ) t + INTO par_job_name + , par_job_id + , var_retval; + + IF (var_retval <> 0) THEN + returncode := 1; + RETURN; + END IF; + + -- Default step id (if not supplied) + IF (par_step_id IS NULL) + THEN + SELECT COALESCE(MAX(step_id), 0) + 1 + INTO var_step_id + FROM sys.sysjobsteps + WHERE (job_id = par_job_id); + ELSE + var_step_id := par_step_id; + END IF; + + -- Get current maximum step id + SELECT COALESCE(MAX(step_id), 0) + INTO var_max_step_id + FROM sys.sysjobsteps + WHERE (job_id = par_job_id); + + /* Check parameters */ + SELECT t.returncode + FROM sys.babelfish_sp_verify_jobstep( + par_job_id + , var_step_id --par_step_id + , par_step_name + , par_subsystem + , par_command + , par_server + , par_on_success_action + , par_on_success_step_id + , par_on_fail_action + , par_on_fail_step_id + , par_os_run_priority + , par_flags + , par_output_file_name + , par_proxy_id + ) t + INTO var_retval; + + IF (var_retval <> 0) + THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + /* Modify database. */ + /* Update the job's version/last-modified information */ + UPDATE sys.sysjobs + SET version_number = version_number + 1 + --, date_modified = GETDATE() + WHERE (job_id = par_job_id); + + /* Adjust step id's (unless the new step is being inserted at the 'end') */ + /* NOTE: We MUST do this before inserting the step. */ + IF (var_step_id <= var_max_step_id) + THEN + UPDATE sys.sysjobsteps + SET step_id = step_id + 1 + WHERE (step_id >= var_step_id) AND (job_id = par_job_id); + + /* Clean up OnSuccess/OnFail references */ + UPDATE sys.sysjobsteps + SET on_success_step_id = on_success_step_id + 1 + WHERE (on_success_step_id >= var_step_id) AND (job_id = par_job_id); + + UPDATE sys.sysjobsteps + SET on_fail_step_id = on_fail_step_id + 1 + WHERE (on_fail_step_id >= var_step_id) AND (job_id = par_job_id); + + UPDATE sys.sysjobsteps + SET on_success_step_id = 0 + , on_success_action = 1 /* Quit With Success */ + WHERE (on_success_step_id = var_step_id) + AND (job_id = par_job_id); + + UPDATE sys.sysjobsteps + SET on_fail_step_id = 0 + , on_fail_action = 2 /* Quit With Failure */ + WHERE (on_fail_step_id = var_step_id) + AND (job_id = par_job_id); + END IF; + + /* uuid without extensions uuid-ossp (cheat) */ + SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring) INTO par_step_uid; + + /* Insert the step */ + INSERT + INTO sys.sysjobsteps ( + job_id + , step_id + , step_name + , subsystem + , command + , flags + , additional_parameters + , cmdexec_success_code + , on_success_action + , on_success_step_id + , on_fail_action + , on_fail_step_id + , server + , database_name + , database_user_name + , retry_attempts + , retry_interval + , os_run_priority + , output_file_name + , last_run_outcome + , last_run_duration + , last_run_retries + , last_run_date + , last_run_time + , proxy_id + , step_uid + ) + VALUES ( + par_job_id + , var_step_id + , par_step_name + , par_subsystem + , par_command + , par_flags + , par_additional_parameters + , par_cmdexec_success_code + , par_on_success_action + , par_on_success_step_id + , par_on_fail_action + , par_on_fail_step_id + , par_server + , par_database_name + , par_database_user_name + , par_retry_attempts + , par_retry_interval + , par_os_run_priority + , par_output_file_name + , 0 + , 0 + , 0 + , 0 + , 0 + , par_proxy_id + , par_step_uid + ); + + --PERFORM sys.sp_jobstep_create_proc (par_step_uid); + + returncode := var_retval; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_add_schedule ( + par_schedule_name varchar, + par_enabled smallint = 1, + par_freq_type integer = 0, + par_freq_interval integer = 0, + par_freq_subday_type integer = 0, + par_freq_subday_interval integer = 0, + par_freq_relative_interval integer = 0, + par_freq_recurrence_factor integer = 0, + par_active_start_date integer = NULL::integer, + par_active_end_date integer = 99991231, + par_active_start_time integer = 0, + par_active_end_time integer = 235959, + par_owner_login_name varchar = NULL::character varying, + inout par_schedule_uid char = NULL::bpchar, + inout par_schedule_id integer = NULL::integer, + par_originating_server varchar = NULL::character varying, + out returncode integer +) +AS +$body$ +DECLARE + var_retval INT; + var_owner_sid CHAR(85); + var_orig_server_id INT; +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_schedule_name)) + , LTRIM(RTRIM(par_owner_login_name)) + , UPPER(LTRIM(RTRIM(par_originating_server))) + , 0 + INTO par_schedule_name + , par_owner_login_name + , par_originating_server + , par_schedule_id; + + /* Check schedule (frequency and owner) parameters */ + SELECT t.par_freq_interval + , t.par_freq_subday_type + , t.par_freq_subday_interval + , t.par_freq_relative_interval + , t.par_freq_recurrence_factor + , t.par_active_start_date + , t.par_active_start_time + , t.par_active_end_date + , t.par_active_end_time + , t.returncode + FROM sys.babelfish_sp_verify_schedule( + NULL::integer /* @schedule_id -- schedule_id does not exist for the new schedule */ + , par_schedule_name /* @name */ + , par_enabled /* @enabled */ + , par_freq_type /* @freq_type */ + , par_freq_interval /* @freq_interval */ + , par_freq_subday_type /* @freq_subday_type */ + , par_freq_subday_interval /* @freq_subday_interval */ + , par_freq_relative_interval /* @freq_relative_interval */ + , par_freq_recurrence_factor /* @freq_recurrence_factor */ + , par_active_start_date /* @active_start_date */ + , par_active_start_time /* @active_start_time */ + , par_active_end_date /* @active_end_date */ + , par_active_end_time /* @active_end_time */ + , var_owner_sid + ) t + INTO par_freq_interval + , par_freq_subday_type + , par_freq_subday_interval + , par_freq_relative_interval + , par_freq_recurrence_factor + , par_active_start_date + , par_active_start_time + , par_active_end_date + , par_active_end_time + , var_retval /* @owner_sid */; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + IF (par_schedule_uid IS NULL) + THEN /* Assign the GUID */ + /* uuid without extensions uuid-ossp (cheat) */ + SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring) INTO par_schedule_uid; + END IF; + + var_orig_server_id := 0; + var_owner_sid := uuid_in(md5(random()::text || clock_timestamp()::text)::cstring); + + + INSERT + INTO sys.sysschedules ( + schedule_uid + , originating_server_id + , name + , owner_sid + , enabled + , freq_type + , freq_interval + , freq_subday_type + , freq_subday_interval + , freq_relative_interval + , freq_recurrence_factor + , active_start_date + , active_end_date + , active_start_time + , active_end_time + ) + VALUES ( + par_schedule_uid + , var_orig_server_id + , par_schedule_name + , var_owner_sid + , par_enabled + , par_freq_type + , par_freq_interval + , par_freq_subday_type + , par_freq_subday_interval + , par_freq_relative_interval + , par_freq_recurrence_factor + , par_active_start_date + , par_active_end_date + , par_active_start_time + , par_active_end_time + ); + + /* ZZZ */ + SELECT 0 /* @@ERROR, */, LASTVAL() + INTO var_retval, par_schedule_id; + + /* 0 means success */ + returncode := var_retval; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_attach_schedule ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_schedule_id integer = NULL::integer, + par_schedule_name varchar = NULL::character varying, + par_automatic_post smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_sched_owner_sid CHAR(85); + var_job_owner_sid CHAR(85); +BEGIN + /* Check that we can uniquely identify the job */ + SELECT t.par_job_name + , t.par_job_id + , t.par_owner_sid + , t.returncode + FROM sys.babelfish_sp_verify_job_identifiers( + '@job_name' + , '@job_id' + , par_job_name /* @job_name */ + , par_job_id /* @job_id */ + , 'TEST' /* @sqlagent_starting_test */ + , var_job_owner_sid) t + INTO par_job_name + , par_job_id + , var_job_owner_sid + , var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + /* Check that we can uniquely identify the schedule */ + SELECT t.par_schedule_name + , t.par_schedule_id + , t.par_owner_sid + --, t.par_orig_server_id + , t.returncode + FROM sys.babelfish_sp_verify_schedule_identifiers( + '@schedule_name'::character varying /* @name_of_name_parameter */ + , '@schedule_id'::character varying /* @name_of_id_parameter */ + , par_schedule_name /* @schedule_name */ + , par_schedule_id /* @schedule_id */ + , var_sched_owner_sid /* @owner_sid */ + , NULL::integer /* @orig_server_id */ + , NULL::integer) t + INTO par_schedule_name + , par_schedule_id + , var_sched_owner_sid + , var_retval /* @job_id_filter */; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF + + /* If the record doesn't already exist create it */; + IF ( + NOT EXISTS ( + SELECT 1 + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id) + AND (job_id = par_job_id))) + THEN + INSERT + INTO sys.sysjobschedules (schedule_id, job_id) + VALUES (par_schedule_id, par_job_id); + + SELECT 0 INTO var_retval; /* @@ERROR */ + END IF; + + + PERFORM sys.babelfish_sp_set_next_run (par_job_id, par_schedule_id); + + /* 0 means success */ + returncode := var_retval; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_aws_add_jobschedule ( + par_job_id integer = NULL::integer, + par_schedule_id integer = NULL::integer, + out returncode integer +) +AS +$body$ +DECLARE + var_retval INT; + proc_name_mask VARCHAR(100); + var_owner_login_name VARCHAR(128); + var_xml TEXT DEFAULT ''; + var_cron_expression VARCHAR(50); + var_job_cmd VARCHAR(255); + lambda_arn VARCHAR(255); + return_message text; + var_schedule_name VARCHAR(255); + + var_job_name VARCHAR(128); + var_start_step_id INTEGER; + var_notify_level_email INTEGER; + var_notify_email_operator_id INTEGER; + var_notify_email_operator_name VARCHAR(128); + notify_email_sender VARCHAR(128); + var_delete_level INTEGER; +BEGIN + + IF (EXISTS ( + SELECT 1 + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id) + AND (job_id = par_job_id))) + THEN + SELECT cron_expression + FROM sys.babelfish_sp_schedule_to_cron (par_job_id, par_schedule_id) + INTO var_cron_expression; + + SELECT name + FROM sys.sysschedules + WHERE schedule_id = par_schedule_id + INTO var_schedule_name; + + SELECT name + , start_step_id + , COALESCE(notify_level_email,0) + , COALESCE(notify_email_operator_id,0) + , COALESCE(notify_email_operator_name,'') + , COALESCE(delete_level,0) + FROM sys.sysjobs + WHERE job_id = par_job_id + INTO var_job_name + , var_start_step_id + , var_notify_level_email + , var_notify_email_operator_id + , var_notify_email_operator_name + , var_delete_level; + + proc_name_mask := 'sys_data.sql_agent$job_%s_step_%s'; + var_job_cmd := format(proc_name_mask, par_job_id, '1'); + notify_email_sender := 'aws_test_email_sender@dbbest.com'; + + + var_xml := CONCAT(var_xml, '{'); + var_xml := CONCAT(var_xml, '"mode": "add_job",'); + var_xml := CONCAT(var_xml, '"parameters": {'); + var_xml := CONCAT(var_xml, '"vendor": "postgresql",'); + var_xml := CONCAT(var_xml, '"job_name": "',var_schedule_name,'",'); + var_xml := CONCAT(var_xml, '"job_frequency": "',var_cron_expression,'",'); + var_xml := CONCAT(var_xml, '"job_cmd": "',var_job_cmd,'",'); + var_xml := CONCAT(var_xml, '"notify_level_email": ',var_notify_level_email,','); + var_xml := CONCAT(var_xml, '"delete_level": ',var_delete_level,','); + var_xml := CONCAT(var_xml, '"uid": "',par_job_id,'",'); + var_xml := CONCAT(var_xml, '"callback": "sys.babelfish_sp_job_log",'); + var_xml := CONCAT(var_xml, '"notification": {'); + var_xml := CONCAT(var_xml, '"notify_email_sender": "',notify_email_sender,'",'); + var_xml := CONCAT(var_xml, '"notify_email_recipient": "',var_notify_email_operator_name,'"'); + var_xml := CONCAT(var_xml, '}'); + var_xml := CONCAT(var_xml, '}'); + var_xml := CONCAT(var_xml, '}'); + + -- RAISE NOTICE '%', var_xml; + + + SELECT sys.babelfish_get_service_setting ('JOB', 'LAMBDA_ARN') + INTO lambda_arn; + + SELECT sys.awslambda_fn (lambda_arn, var_xml) INTO return_message; + returncode := 0; + ELSE + returncode := 1; + RAISE 'Job not fount' USING ERRCODE := '50000'; + END IF; + +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_aws_del_jobschedule ( + par_job_id integer = NULL::integer, + par_schedule_id integer = NULL::integer, + out returncode integer +) +AS +$body$ +DECLARE + var_retval INT; + proc_name_mask VARCHAR(100); + var_owner_login_name VARCHAR(128); + var_xml TEXT DEFAULT ''; + var_cron_expression VARCHAR(50); + var_job_cmd VARCHAR(255); + lambda_arn VARCHAR(255); + return_message text; + var_schedule_name VARCHAR(255); +BEGIN + + IF (EXISTS ( + SELECT 1 + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id) + AND (job_id = par_job_id))) + THEN + SELECT name + FROM sys.sysschedules + WHERE schedule_id = par_schedule_id + INTO var_schedule_name; + + var_xml := CONCAT(var_xml, '{'); + var_xml := CONCAT(var_xml, '"mode": "del_schedule",'); + var_xml := CONCAT(var_xml, '"parameters": {'); + var_xml := CONCAT(var_xml, '"schedule_name": "',var_schedule_name,'",'); + var_xml := CONCAT(var_xml, '"force_delete": "TRUE"'); + var_xml := CONCAT(var_xml, '}'); + var_xml := CONCAT(var_xml, '}'); + + SELECT sys.babelfish_get_service_setting ('JOB', 'LAMBDA_ARN') + INTO lambda_arn; + + SELECT sys.awslambda_fn (lambda_arn, var_xml) INTO return_message; + returncode := 0; + ELSE + returncode := 1; + RAISE 'Job not fount' USING ERRCODE := '50000'; + END IF; + +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_delete_job ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_originating_server varchar = NULL::character varying, + par_delete_history smallint = 1, + par_delete_unused_schedule smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_category_id INT; + var_job_owner_sid CHAR(85); + var_err INT; + var_schedule_id INT; +BEGIN + IF ((par_job_id IS NOT NULL) OR (par_job_name IS NOT NULL)) + THEN + SELECT t.par_job_name + , t.par_job_id + , t.par_owner_sid + , t.returncode + FROM sys.babelfish_sp_verify_job_identifiers( + '@job_name' + , '@job_id' + , par_job_name + , par_job_id + , 'TEST' + , var_job_owner_sid + ) t + INTO par_job_name + , par_job_id + , var_job_owner_sid + , var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := (1); + RETURN; + END IF; + END IF; + + /* Get category to see if it is a misc. replication agent. @category_id will be */ + /* NULL if there is no @job_id. */ + + SELECT category_id + INTO var_category_id + FROM sys.sysjobs + WHERE job_id = par_job_id; + + /* Do the delete (for a specific job) */ + IF (par_job_id IS NOT NULL) + THEN + --CREATE TEMPORARY TABLE "#temp_schedules_to_delete" (schedule_id INT NOT NULL); + + -- Delete all traces of the job + -- BEGIN TRANSACTION + -- Get the schedules to delete before deleting records from sysjobschedules + + + + --IF (par_delete_unused_schedule = 1) + --THEN + -- ZZZ optimize + -- Get the list of schedules to delete + --INSERT INTO "#temp_schedules_to_delete" + --SELECT DISTINCT schedule_id + -- FROM sys.sysschedules + -- WHERE schedule_id IN (SELECT schedule_id + -- FROM sys.sysjobschedules + -- WHERE job_id = par_job_id); + --INSERT INTO "#temp_schedules_to_delete" + SELECT schedule_id + FROM sys.sysjobschedules + WHERE job_id = par_job_id + INTO var_schedule_id; + + PERFORM sys.babelfish_sp_aws_del_jobschedule (par_job_id := par_job_id, par_schedule_id := var_schedule_id); + + +-- END IF; + + + --DELETE FROM sys.sysschedules + -- WHERE schedule_id IN (SELECT schedule_id FROM sys.sysjobschedules WHERE job_id = par_job_id); + + DELETE FROM sys.sysjobschedules + WHERE job_id = par_job_id; + + DELETE FROM sys.sysjobsteps + WHERE job_id = par_job_id; + + DELETE FROM sys.sysjobs + WHERE job_id = par_job_id; + + SELECT 0 /* @@ERROR */ INTO var_err; + + /* Delete the schedule(s) if requested to and it isn't being used by other jobs */ + IF (par_delete_unused_schedule = 1) + THEN + /* Now OK to delete the schedule */ + DELETE FROM sys.sysschedules + WHERE schedule_id = var_schedule_id; --IN (SELECT schedule_id FROM "#temp_schedules_to_delete"); + + --DELETE FROM sys.sysschedules + -- WHERE schedule_id IN (SELECT schedule_id + -- FROM "#temp_schedules_to_delete" AS sdel + -- WHERE NOT EXISTS (SELECT * + -- FROM sys.sysjobschedules AS js + -- WHERE js.schedule_id = sdel.schedule_id)); + END IF; + + /* Delete the job history if requested */ + IF (par_delete_history = 1) + THEN + DELETE FROM sys.sysjobhistory + WHERE job_id = par_job_id; + END IF; + + /* All done */ + /* COMMIT TRANSACTION */ + --DROP TABLE "#temp_schedules_to_delete"; + END IF; + + /* 0 means success */ + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_delete_jobschedule ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_name varchar = NULL::character varying, + par_keep_schedule integer = 0, + par_automatic_post smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_sched_count INT; + var_schedule_id INT; + var_job_owner_sid CHAR(85); +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_name)) INTO par_name; + + /* Check that we can uniquely identify the job */ + SELECT t.par_job_name + , t.par_job_id + , t.par_owner_sid + , t.returncode + FROM sys.babelfish_sp_verify_job_identifiers( + '@job_name' + , '@job_id' + , par_job_name + , par_job_id + , 'TEST' + , var_job_owner_sid + ) t + INTO par_job_name + , par_job_id + , var_job_owner_sid + , var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + IF (LOWER(UPPER(par_name)) = LOWER('ALL')) + THEN + SELECT - 1 INTO var_schedule_id; + + /* We use this in the call to sp_sqlagent_notify */ + /* Delete the schedule(s) if it isn't being used by other jobs */ + CREATE TEMPORARY TABLE "#temp_schedules_to_delete" (schedule_id INT NOT NULL) + /* If user requests that the schedules be removed (the legacy behavoir) */ + /* make sure it isnt being used by other jobs */; + + IF (par_keep_schedule = 0) + THEN + /* Get the list of schedules to delete */ + INSERT INTO "#temp_schedules_to_delete" + SELECT DISTINCT schedule_id + FROM sys.sysschedules + WHERE (schedule_id IN (SELECT schedule_id + FROM sys.sysjobschedules + WHERE (job_id = par_job_id))); + /* make sure no other jobs use these schedules */ + IF (EXISTS (SELECT * + FROM sys.sysjobschedules + WHERE (job_id <> par_job_id) + AND (schedule_id IN (SELECT schedule_id + FROM "#temp_schedules_to_delete")))) + THEN /* Failure */ + RAISE 'One or more schedules were not deleted because they are being used by at least one other job. Use "sp_detach_schedule" to remove schedules from a job.' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + /* OK to delete the jobschedule */ + DELETE FROM sys.sysjobschedules + WHERE (job_id = par_job_id); + + /* OK to delete the schedule - temp_schedules_to_delete is empty if @keep_schedule <> 0 */ + DELETE FROM sys.sysschedules + WHERE schedule_id IN (SELECT schedule_id FROM "#temp_schedules_to_delete"); + ELSE ---- IF (LOWER(UPPER(par_name)) = LOWER('ALL')) + + -- Need to use sp_detach_schedule to remove this ambiguous schedule name + IF(var_sched_count > 1) /* Failure */ + THEN + RAISE 'More than one schedule named "%" is attached to job "%". Use "sp_detach_schedule" to remove schedules from a job.', par_name, par_job_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + --If user requests that the schedule be removed (the legacy behavoir) + --make sure it isnt being used by another job + IF (par_keep_schedule = 0) + THEN + IF(EXISTS(SELECT * + FROM sys.sysjobschedules + WHERE (schedule_id = var_schedule_id) + AND (job_id <> par_job_id))) + THEN /* Failure */ + RAISE 'Schedule "%" was not deleted because it is being used by at least one other job. Use "sp_detach_schedule" to remove schedules from a job.', par_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + /* Delete the job schedule link first */ + DELETE FROM sys.sysjobschedules + WHERE (job_id = par_job_id) + AND (schedule_id = var_schedule_id); + + /* Delete schedule if required */ + IF (par_keep_schedule = 0) + THEN + /* Now delete the schedule if required */ + DELETE FROM sys.sysschedules + WHERE (schedule_id = var_schedule_id); + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_aws_del_jobschedule(par_job_id, var_schedule_id) t + INTO var_retval; + + + END IF; + + /* Update the job's version/last-modified information */ + UPDATE sys.sysjobs + SET version_number = version_number + 1 + -- , date_modified = GETDATE() / + WHERE job_id = par_job_id; + + DROP TABLE IF EXISTS "#temp_schedules_to_delete"; + + + /* 0 means success */ + returncode := var_retval; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_delete_jobstep ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_step_id integer = NULL::integer, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_max_step_id INT; + var_valid_range VARCHAR(50); + var_job_owner_sid CHAR(85); +BEGIN + SELECT t.par_job_name + , t.par_job_id + , t.par_owner_sid + , t.returncode + FROM sys.babelfish_sp_verify_job_identifiers( + '@job_name' + , '@job_id' + , par_job_name + , par_job_id + , 'TEST' + , var_job_owner_sid + ) t + INTO par_job_name + , par_job_id + , var_job_owner_sid + , var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + /* Get current maximum step id */ + SELECT COALESCE(MAX(step_id), 0) + INTO var_max_step_id + FROM sys.sysjobsteps + WHERE (job_id = par_job_id); + + /* Check step id */ + IF (par_step_id < 0) OR (par_step_id > var_max_step_id) + THEN + SELECT CONCAT('0 (all steps) ..', CAST (var_max_step_id AS VARCHAR(1))) + INTO var_valid_range; + RAISE 'The specified "%" is invalid (valid values are: %).', 'step_id', var_valid_range USING ERRCODE := '50000'; + returncode := 1; + RETURN; + /* Failure */ + END IF; + + /* BEGIN TRANSACTION */ + /* Delete either the specified step or ALL the steps (if step id is 0) */ + IF (par_step_id = 0) + THEN + DELETE FROM sys.sysjobsteps + WHERE (job_id = par_job_id); + ELSE + DELETE FROM sys.sysjobsteps + WHERE (job_id = par_job_id) AND (step_id = par_step_id); + END IF; + + IF (par_step_id <> 0) + THEN + /* Adjust step id's */ + UPDATE sys.sysjobsteps + SET step_id = step_id - 1 + WHERE (step_id > par_step_id) + AND (job_id = par_job_id); + + /* Clean up OnSuccess/OnFail references */ + UPDATE sys.sysjobsteps + SET on_success_step_id = on_success_step_id - 1 + WHERE (on_success_step_id > par_step_id) AND (job_id = par_job_id); + + UPDATE sys.sysjobsteps + SET on_fail_step_id = on_fail_step_id - 1 + WHERE (on_fail_step_id > par_step_id) AND (job_id = par_job_id); + + /* Quit With Success */ + UPDATE sys.sysjobsteps + SET on_success_step_id = 0 + , on_success_action = 1 + WHERE (on_success_step_id = par_step_id) + AND (job_id = par_job_id); + + /* Quit With Failure */ + UPDATE sys.sysjobsteps + SET on_fail_step_id = 0 + , on_fail_action = 2 + WHERE (on_fail_step_id = par_step_id) AND (job_id = par_job_id); + END IF; + + /* Update the job's version/last-modified information */ + UPDATE sys.sysjobs + SET version_number = version_number + 1 + --, date_modified = GETDATE() / + WHERE (job_id = par_job_id); + + /* COMMIT TRANSACTION */ + + /* Success */ + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_delete_schedule ( + par_schedule_id integer = NULL::integer, + par_schedule_name varchar = NULL::character varying, + par_force_delete smallint = 0, + par_automatic_post smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_job_count INT; +BEGIN + /* check if there are jobs using this schedule */ + SELECT COUNT(*) + INTO var_job_count + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id); + + /* If we aren't force deleting the schedule make sure no jobs are using it */ + IF ((par_force_delete = 0) AND (var_job_count > 0)) + THEN /* Failure */ + RAISE 'The schedule was not deleted because it is being used by one or more jobs.' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* OK to delete the job - schedule link */ + DELETE FROM sys.sysjobschedules + WHERE schedule_id = par_schedule_id; + + /* OK to delete the schedule */ + DELETE FROM sys.sysschedules + WHERE schedule_id = par_schedule_id; + + /* 0 means success */ + returncode := var_retval; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_detach_schedule ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_schedule_id integer = NULL::integer, + par_schedule_name varchar = NULL::character varying, + par_delete_unused_schedule smallint = 0, + par_automatic_post smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_sched_owner_sid CHAR(85); + var_job_owner_sid CHAR(85); +BEGIN + /* Check that we can uniquely identify the job */ + SELECT t.par_job_name + , t.par_job_id + , t.par_owner_sid + , t.returncode + FROM sys.babelfish_sp_verify_job_identifiers( + '@job_name' + , '@job_id' + , par_job_name + , par_job_id + , 'TEST' + , var_job_owner_sid + ) t + INTO par_job_name + , par_job_id + , var_job_owner_sid + , var_retval; + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + /* Check that we can uniquely identify the schedule */ + SELECT t.par_schedule_name + , t.par_schedule_id + , t.par_owner_sid + , t.par_orig_server_id + , t.returncode + FROM sys.babelfish_sp_verify_schedule_identifiers( + '@schedule_name' /* @name_of_name_parameter */ + , '@schedule_id' /* @name_of_id_parameter */ + , par_schedule_name /* @schedule_name */ + , par_schedule_id /* @schedule_id */ + , var_sched_owner_sid /* @owner_sid */ + , NULL /* @orig_server_id */ + , par_job_id + ) t + INTO par_schedule_name + , par_schedule_id + , var_sched_owner_sid + , var_retval; + -- job_id_filter + + IF (var_retval <> 0) THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + /* If the record doesn't exist raise an error */ + IF (NOT EXISTS ( + SELECT * + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id) + AND (job_id = par_job_id))) + THEN /* Failure */ + RAISE 'The specified schedule name "%s" is not associated with the job "%s".', par_schedule_name, par_job_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_aws_del_jobschedule(par_job_id, par_schedule_id) t + INTO var_retval; + + DELETE FROM sys.sysjobschedules + WHERE (job_id = par_job_id) + AND (schedule_id = par_schedule_id); + + SELECT /* @@ERROR */ 0 -- ZZZ + INTO var_retval; + + /* delete the schedule if requested and it isn't referenced */ + IF (var_retval = 0 AND par_delete_unused_schedule = 1) + THEN + IF (NOT EXISTS ( + SELECT * + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id))) + THEN + DELETE FROM sys.sysschedules + WHERE (schedule_id = par_schedule_id); + END IF; + END IF; + + /* Update the job's version/last-modified information */ + /* + UPDATE sys.sysjobs + SET version_number = version_number + 1 + -- , date_modified = GETDATE() + WHERE (job_id = par_job_id); + */ + + -- PERFORM sys.babelfish_sp_delete_job (par_job_id := par_job_id); + + /* 0 means success */ + returncode := var_retval; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_job_log ( + IN pid INTEGER + , IN pstatus INTEGER + , IN pmessage VARCHAR(255)) +RETURNS void AS +$BODY$ +BEGIN + PERFORM sys.babelfish_update_job (pid, pmessage); + + -- INSERT INTO ms_test.jobs_log(id, t, status, message) + -- VALUES (pid, CURRENT_TIMESTAMP, pstatus, pmessage); +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_schedule_to_cron ( + par_job_id integer, + par_schedule_id integer, + out cron_expression varchar +) +RETURNS VARCHAR AS +$body$ +DECLARE + var_enabled INTEGER; + var_freq_type INTEGER; + var_freq_interval INTEGER; + var_freq_subday_type INTEGER; + var_freq_subday_interval INTEGER; + var_freq_relative_interval INTEGER; + var_freq_recurrence_factor INTEGER; + var_active_start_date INTEGER; + var_active_end_date INTEGER; + var_active_start_time INTEGER; + var_active_end_time INTEGER; + + var_next_run_date date; + var_next_run_time time; + var_next_run_dt timestamp; + + var_tmp_interval varchar(50); + var_current_dt timestamp; + var_next_dt timestamp; +BEGIN + + SELECT enabled + , freq_type + , freq_interval + , freq_subday_type + , freq_subday_interval + , freq_relative_interval + , freq_recurrence_factor + , active_start_date + , active_end_date + , active_start_time + , active_end_time + FROM sys.sysschedules + INTO var_enabled + , var_freq_type + , var_freq_interval + , var_freq_subday_type + , var_freq_subday_interval + , var_freq_relative_interval + , var_freq_recurrence_factor + , var_active_start_date + , var_active_end_date + , var_active_start_time + , var_active_end_time + WHERE schedule_id = par_schedule_id; + + /* if enabled = 0 return */ + CASE var_freq_type + WHEN 1 THEN + NULL; + + WHEN 4 THEN + BEGIN + cron_expression := + CASE + /* WHEN var_freq_subday_type = 1 THEN var_freq_subday_interval::character varying || ' At the specified time' -- start time */ + /* WHEN var_freq_subday_type = 2 THEN var_freq_subday_interval::character varying || ' second' -- ADD var_freq_subday_interval SECOND */ + WHEN var_freq_subday_type = 4 THEN format('cron(*/%s * * * ? *)', var_freq_subday_interval::character varying) /* ADD var_freq_subday_interval MINUTE */ + WHEN var_freq_subday_type = 8 THEN format('cron(0 */%s * * ? *)', var_freq_subday_interval::character varying) /* ADD var_freq_subday_interval HOUR */ + ELSE '' + END; + END; + + WHEN 8 THEN + NULL; + + WHEN 16 THEN + NULL; + + WHEN 32 THEN + NULL; + + WHEN 64 THEN + NULL; + + WHEN 128 THEN + NULL; + + END CASE; + + -- return cron_expression; + +END; +$body$ +LANGUAGE 'plpgsql'; + +create or replace function sys.babelfish_sp_sequence_get_range( + in par_sequence_name text, + in par_range_size bigint, + out par_range_first_value bigint, + out par_range_last_value bigint, + out par_range_cycle_count bigint, + out par_sequence_increment bigint, + out par_sequence_min_value bigint, + out par_sequence_max_value bigint +) as +$body$ +declare + v_is_cycle character varying(3); + v_current_value bigint; +begin + select s.minimum_value, s.maximum_value, s.increment, s.cycle_option + from information_schema.sequences s + where s.sequence_name = $1 + into par_sequence_min_value, par_sequence_max_value, par_sequence_increment, v_is_cycle; + + par_range_first_value := sys.babelfish_get_sequence_value(par_sequence_name); + + if par_range_first_value > par_sequence_min_value then + par_range_first_value := par_range_first_value + 1; + end if; + + if v_is_cycle = 'YES' then + par_range_cycle_count := 0; + end if; + + for i in 1..$2 loop + select nextval(par_sequence_name) into v_current_value; + if (v_is_cycle = 'YES') and (v_current_value = par_sequence_min_value) and (par_range_first_value <> v_current_value) then + par_range_cycle_count := par_range_cycle_count + 1; + end if; + end loop; + + par_range_last_value := sys.babelfish_get_sequence_value(par_sequence_name); +end; +$body$ +language plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_set_next_run ( + par_job_id integer, + par_schedule_id integer +) +RETURNS void AS +$body$ +DECLARE + var_enabled INTEGER; + var_freq_type INTEGER; + var_freq_interval INTEGER; + var_freq_subday_type INTEGER; + var_freq_subday_interval INTEGER; + var_freq_relative_interval INTEGER; + var_freq_recurrence_factor INTEGER; + var_active_start_date INTEGER; + var_active_end_date INTEGER; + var_active_start_time INTEGER; + var_active_end_time INTEGER; + + var_next_run_date date; + var_next_run_time time; + var_next_run_dt timestamp; + + var_tmp_interval varchar(50); + var_current_dt timestamp; + var_next_dt timestamp; +BEGIN + + SELECT enabled + , freq_type + , freq_interval + , freq_subday_type + , freq_subday_interval + , freq_relative_interval + , freq_recurrence_factor + , active_start_date + , active_end_date + , active_start_time + , active_end_time + FROM sys.sysschedules + INTO var_enabled + , var_freq_type + , var_freq_interval + , var_freq_subday_type + , var_freq_subday_interval + , var_freq_relative_interval + , var_freq_recurrence_factor + , var_active_start_date + , var_active_end_date + , var_active_start_time + , var_active_end_time + WHERE schedule_id = par_schedule_id; + + SELECT next_run_date + , next_run_time + FROM sys.sysjobschedules + INTO var_next_run_date + , var_next_run_time + WHERE schedule_id = par_schedule_id + AND job_id = par_job_id; + + /* if enabled = 0 return */ + CASE var_freq_type + WHEN 1 THEN + NULL; + + WHEN 4 THEN + BEGIN + /* NULL start date & time or now */ + /* start date + start time or now() */ + IF (var_next_run_date IS NULL OR var_next_run_time IS NULL) + THEN + var_current_dt := now()::timestamp; + + UPDATE sys.sysjobschedules + SET next_run_date = var_current_dt::date + , next_run_time = var_current_dt::time + WHERE schedule_id = par_schedule_id + AND job_id = par_job_id; + RETURN; + ELSE + var_tmp_interval := + CASE + /* WHEN var_freq_subday_type = 1 THEN var_freq_subday_interval::character varying || ' At the specified time' -- start time */ + WHEN var_freq_subday_type = 2 THEN var_freq_subday_interval::character varying || ' second' /* ADD var_freq_subday_interval SECOND */ + WHEN var_freq_subday_type = 4 THEN var_freq_subday_interval::character varying || ' minute' /* ADD var_freq_subday_interval MINUTE */ + WHEN var_freq_subday_type = 8 THEN var_freq_subday_interval::character varying || ' hour' /* ADD var_freq_subday_interval HOUR */ + ELSE '' + END; + + var_next_dt := (var_next_run_date::date + var_next_run_time::time)::timestamp + var_tmp_interval::INTERVAL; + UPDATE sys.sysjobschedules + SET next_run_date = var_next_dt::date + , next_run_time = var_next_dt::time + WHERE schedule_id = par_schedule_id + AND job_id = par_job_id; + RETURN; + END IF; + END; + + WHEN 8 THEN + NULL; + + WHEN 16 THEN + NULL; + + WHEN 32 THEN + NULL; + + WHEN 64 THEN + NULL; + + WHEN 128 THEN + NULL; + + END CASE; + +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_update_job ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_new_name varchar = NULL::character varying, + par_enabled smallint = NULL::smallint, + par_description varchar = NULL::character varying, + par_start_step_id integer = NULL::integer, + par_category_name varchar = NULL::character varying, + par_owner_login_name varchar = NULL::character varying, + par_notify_level_eventlog integer = NULL::integer, + par_notify_level_email integer = NULL::integer, + par_notify_level_netsend integer = NULL::integer, + par_notify_level_page integer = NULL::integer, + par_notify_email_operator_name varchar = NULL::character varying, + par_notify_netsend_operator_name varchar = NULL::character varying, + par_notify_page_operator_name varchar = NULL::character varying, + par_delete_level integer = NULL::integer, + par_automatic_post smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_category_id INT; + var_notify_email_operator_id INT; + var_notify_netsend_operator_id INT; + var_notify_page_operator_id INT; + var_owner_sid CHAR(85); + var_alert_id INT; + var_cached_attribute_modified INT; + var_is_sysadmin INT; + var_current_owner VARCHAR(128); + var_enable_only_used INT; + var_x_new_name VARCHAR(128); + var_x_enabled SMALLINT; + var_x_description VARCHAR(512); + var_x_start_step_id INT; + var_x_category_name VARCHAR(128); + var_x_category_id INT; + var_x_owner_sid CHAR(85); + var_x_notify_level_eventlog INT; + var_x_notify_level_email INT; + var_x_notify_level_netsend INT; + var_x_notify_level_page INT; + var_x_notify_email_operator_name VARCHAR(128); + var_x_notify_netsnd_operator_name VARCHAR(128); + var_x_notify_page_operator_name VARCHAR(128); + var_x_delete_level INT; + var_x_originating_server_id INT; + var_x_master_server SMALLINT; +BEGIN + /* Not updatable */ + /* Remove any leading/trailing spaces from parameters (except @owner_login_name) */ + SELECT + LTRIM(RTRIM(par_job_name)) + INTO par_job_name; + SELECT + LTRIM(RTRIM(par_new_name)) + INTO par_new_name; + SELECT + LTRIM(RTRIM(par_description)) + INTO par_description; + SELECT + LTRIM(RTRIM(par_category_name)) + INTO par_category_name; + SELECT + LTRIM(RTRIM(par_notify_email_operator_name)) + INTO par_notify_email_operator_name; + SELECT + LTRIM(RTRIM(par_notify_netsend_operator_name)) + INTO par_notify_netsend_operator_name; + SELECT + LTRIM(RTRIM(par_notify_page_operator_name)) + INTO par_notify_page_operator_name + /* Are we modifying an attribute which tsql agent caches? */; + + IF ((par_new_name IS NOT NULL) OR (par_enabled IS NOT NULL) OR (par_start_step_id IS NOT NULL) OR (par_owner_login_name IS NOT NULL) OR (par_notify_level_eventlog IS NOT NULL) OR (par_notify_level_email IS NOT NULL) OR (par_notify_level_netsend IS NOT NULL) OR (par_notify_level_page IS NOT NULL) OR (par_notify_email_operator_name IS NOT NULL) OR (par_notify_netsend_operator_name IS NOT NULL) OR (par_notify_page_operator_name IS NOT NULL) OR (par_delete_level IS NOT NULL)) THEN + SELECT + 1 + INTO var_cached_attribute_modified; + ELSE + SELECT + 0 + INTO var_cached_attribute_modified; + END IF + /* Is @enable the only parameter used beside jobname and jobid? */; + + IF ((par_enabled IS NOT NULL) AND (par_new_name IS NULL) AND (par_description IS NULL) AND (par_start_step_id IS NULL) AND (par_category_name IS NULL) AND (par_owner_login_name IS NULL) AND (par_notify_level_eventlog IS NULL) AND (par_notify_level_email IS NULL) AND (par_notify_level_netsend IS NULL) AND (par_notify_level_page IS NULL) AND (par_notify_email_operator_name IS NULL) AND (par_notify_netsend_operator_name IS NULL) AND (par_notify_page_operator_name IS NULL) AND (par_delete_level IS NULL)) THEN + SELECT + 1 + INTO var_enable_only_used; + ELSE + SELECT + 0 + INTO var_enable_only_used; + END IF; + + IF (par_new_name = '') THEN + SELECT + NULL + INTO par_new_name; + END IF + /* Fill out the values for all non-supplied parameters from the existing values */; + + IF (par_new_name IS NULL) THEN + SELECT + var_x_new_name + INTO par_new_name; + END IF; + + IF (par_enabled IS NULL) THEN + SELECT + var_x_enabled + INTO par_enabled; + END IF; + + IF (par_description IS NULL) THEN + SELECT + var_x_description + INTO par_description; + END IF; + + IF (par_start_step_id IS NULL) THEN + SELECT + var_x_start_step_id + INTO par_start_step_id; + END IF; + + IF (par_category_name IS NULL) THEN + SELECT + var_x_category_name + INTO par_category_name; + END IF; + + IF (var_owner_sid IS NULL) THEN + SELECT + var_x_owner_sid + INTO var_owner_sid; + END IF; + + IF (par_notify_level_eventlog IS NULL) THEN + SELECT + var_x_notify_level_eventlog + INTO par_notify_level_eventlog; + END IF; + + IF (par_notify_level_email IS NULL) THEN + SELECT + var_x_notify_level_email + INTO par_notify_level_email; + END IF; + + IF (par_notify_level_netsend IS NULL) THEN + SELECT + var_x_notify_level_netsend + INTO par_notify_level_netsend; + END IF; + + IF (par_notify_level_page IS NULL) THEN + SELECT + var_x_notify_level_page + INTO par_notify_level_page; + END IF; + + IF (par_notify_email_operator_name IS NULL) THEN + SELECT + var_x_notify_email_operator_name + INTO par_notify_email_operator_name; + END IF; + + IF (par_notify_netsend_operator_name IS NULL) THEN + SELECT + var_x_notify_netsnd_operator_name + INTO par_notify_netsend_operator_name; + END IF; + + IF (par_notify_page_operator_name IS NULL) THEN + SELECT + var_x_notify_page_operator_name + INTO par_notify_page_operator_name; + END IF; + + IF (par_delete_level IS NULL) THEN + SELECT + var_x_delete_level + INTO par_delete_level; + END IF + /* Turn [nullable] empty string parameters into NULLs */; + + IF (LOWER(par_description) = LOWER('')) THEN + SELECT + NULL + INTO par_description; + END IF; + + IF (par_category_name = '') THEN + SELECT + NULL + INTO par_category_name; + END IF; + + IF (par_notify_email_operator_name = '') THEN + SELECT + NULL + INTO par_notify_email_operator_name; + END IF; + + IF (par_notify_netsend_operator_name = '') THEN + SELECT + NULL + INTO par_notify_netsend_operator_name; + END IF; + + IF (par_notify_page_operator_name = '') THEN + SELECT + NULL + INTO par_notify_page_operator_name; + END IF + /* Check new values */; + SELECT + t.par_owner_sid, t.par_notify_level_email, t.par_notify_level_netsend, t.par_notify_level_page, + t.par_category_id, t.par_notify_email_operator_id, t.par_notify_netsend_operator_id, t.par_notify_page_operator_id, t.par_originating_server, t.ReturnCode + FROM sys.babelfish_sp_verify_job(par_job_id, par_new_name, par_enabled, par_start_step_id, par_category_name, var_owner_sid, par_notify_level_eventlog, par_notify_level_email, par_notify_level_netsend, par_notify_level_page, par_notify_email_operator_name, par_notify_netsend_operator_name, par_notify_page_operator_name, par_delete_level, var_category_id, var_notify_email_operator_id, var_notify_netsend_operator_id, var_notify_page_operator_id, NULL) t + INTO var_owner_sid, par_notify_level_email, par_notify_level_netsend, par_notify_level_page, var_category_id, var_notify_email_operator_id, var_notify_netsend_operator_id, var_notify_page_operator_id, var_retval; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + /* Failure */ + /* BEGIN TRANSACTION */ + /* If the job is being re-assigned, modify sysjobsteps.database_user_name as necessary */; + + IF (par_owner_login_name IS NOT NULL) THEN + IF (EXISTS (SELECT + 1 + FROM sys.sysjobsteps + WHERE (job_id = par_job_id) AND (LOWER(subsystem) = LOWER('TSQL')))) THEN + /* The job is being re-assigned to an non-SA */ + UPDATE sys.sysjobsteps + SET database_user_name = NULL + WHERE (job_id = par_job_id) AND (LOWER(subsystem) = LOWER('TSQL')); + END IF; + END IF; + UPDATE sys.sysjobs + SET name = par_new_name, enabled = par_enabled, description = par_description, start_step_id = par_start_step_id, category_id = var_category_id + /* Returned from sp_verify_job */, owner_sid = var_owner_sid, notify_level_eventlog = par_notify_level_eventlog, notify_level_email = par_notify_level_email, notify_level_netsend = par_notify_level_netsend, notify_level_page = par_notify_level_page, notify_email_operator_id = var_notify_email_operator_id + /* Returned from sp_verify_job */, notify_netsend_operator_id = var_notify_netsend_operator_id + /* Returned from sp_verify_job */, notify_page_operator_id = var_notify_page_operator_id + /* Returned from sp_verify_job */, delete_level = par_delete_level, version_number = version_number + 1 + /* , -- Update the job's version */ + /* date_modified = GETDATE() -- Update the job's last-modified information */ + WHERE (job_id = par_job_id); + SELECT + 0 + INTO var_retval + /* @@error */ + /* COMMIT TRANSACTION */; + ReturnCode := (var_retval); + RETURN + /* 0 means success */; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_update_jobschedule ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_name varchar = NULL::character varying, + par_new_name varchar = NULL::character varying, + par_enabled smallint = NULL::smallint, + par_freq_type integer = NULL::integer, + par_freq_interval integer = NULL::integer, + par_freq_subday_type integer = NULL::integer, + par_freq_subday_interval integer = NULL::integer, + par_freq_relative_interval integer = NULL::integer, + par_freq_recurrence_factor integer = NULL::integer, + par_active_start_date integer = NULL::integer, + par_active_end_date integer = NULL::integer, + par_active_start_time integer = NULL::integer, + par_active_end_time integer = NULL::integer, + par_automatic_post smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_sched_count INT; + var_schedule_id INT; + var_job_owner_sid CHAR(85); + var_enable_only_used INT; + var_x_name VARCHAR(128); + var_x_enabled SMALLINT; + var_x_freq_type INT; + var_x_freq_interval INT; + var_x_freq_subday_type INT; + var_x_freq_subday_interval INT; + var_x_freq_relative_interval INT; + var_x_freq_recurrence_factor INT; + var_x_active_start_date INT; + var_x_active_end_date INT; + var_x_active_start_time INT; + var_x_active_end_time INT; + var_owner_sid CHAR(85); +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT + LTRIM(RTRIM(par_name)) + INTO par_name; + SELECT + LTRIM(RTRIM(par_new_name)) + INTO par_new_name + /* Turn [nullable] empty string parameters into NULLs */; + + IF (par_new_name = '') THEN + SELECT + NULL + INTO par_new_name; + END IF + /* Check that we can uniquely identify the job */; + SELECT + t.par_job_name, t.par_job_id, t.par_owner_sid, t.ReturnCode + FROM sys.babelfish_sp_verify_job_identifiers('@job_name', '@job_id', par_job_name, par_job_id, 'TEST', var_job_owner_sid) t + INTO par_job_name, par_job_id, var_job_owner_sid, var_retval; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + /* Failure */ + /* Is @enable the only parameter used beside jobname and jobid? */; + + IF ((par_enabled IS NOT NULL) AND (par_name IS NULL) AND (par_new_name IS NULL) AND (par_freq_type IS NULL) AND (par_freq_interval IS NULL) AND (par_freq_subday_type IS NULL) AND (par_freq_subday_interval IS NULL) AND (par_freq_relative_interval IS NULL) AND (par_freq_recurrence_factor IS NULL) AND (par_active_start_date IS NULL) AND (par_active_end_date IS NULL) AND (par_active_start_time IS NULL) AND (par_active_end_time IS NULL)) THEN + SELECT + 1 + INTO var_enable_only_used; + ELSE + SELECT + 0 + INTO var_enable_only_used; + END IF; + + IF (par_new_name IS NULL) THEN + SELECT + var_x_name + INTO par_new_name; + END IF; + + IF (par_enabled IS NULL) THEN + SELECT + var_x_enabled + INTO par_enabled; + END IF; + + IF (par_freq_type IS NULL) THEN + SELECT + var_x_freq_type + INTO par_freq_type; + END IF; + + IF (par_freq_interval IS NULL) THEN + SELECT + var_x_freq_interval + INTO par_freq_interval; + END IF; + + IF (par_freq_subday_type IS NULL) THEN + SELECT + var_x_freq_subday_type + INTO par_freq_subday_type; + END IF; + + IF (par_freq_subday_interval IS NULL) THEN + SELECT + var_x_freq_subday_interval + INTO par_freq_subday_interval; + END IF; + + IF (par_freq_relative_interval IS NULL) THEN + SELECT + var_x_freq_relative_interval + INTO par_freq_relative_interval; + END IF; + + IF (par_freq_recurrence_factor IS NULL) THEN + SELECT + var_x_freq_recurrence_factor + INTO par_freq_recurrence_factor; + END IF; + + IF (par_active_start_date IS NULL) THEN + SELECT + var_x_active_start_date + INTO par_active_start_date; + END IF; + + IF (par_active_end_date IS NULL) THEN + SELECT + var_x_active_end_date + INTO par_active_end_date; + END IF; + + IF (par_active_start_time IS NULL) THEN + SELECT + var_x_active_start_time + INTO par_active_start_time; + END IF; + + IF (par_active_end_time IS NULL) THEN + SELECT + var_x_active_end_time + INTO par_active_end_time; + END IF + /* Check schedule (frequency and owner) parameters */; + SELECT + t.par_freq_interval, t.par_freq_subday_type, t.par_freq_subday_interval, t.par_freq_relative_interval, t.par_freq_recurrence_factor, t.par_active_start_date, t.par_active_start_time, + t.par_active_end_date, t.par_active_end_time, t.ReturnCode + FROM sys.babelfish_sp_verify_schedule(var_schedule_id + /* @schedule_id */, par_new_name + /* @name */, par_enabled + /* @enabled */, par_freq_type + /* @freq_type */, par_freq_interval + /* @freq_interval */, par_freq_subday_type + /* @freq_subday_type */, par_freq_subday_interval + /* @freq_subday_interval */, par_freq_relative_interval + /* @freq_relative_interval */, par_freq_recurrence_factor + /* @freq_recurrence_factor */, par_active_start_date + /* @active_start_date */, par_active_start_time + /* @active_start_time */, par_active_end_date + /* @active_end_date */, par_active_end_time + /* @active_end_time */, var_owner_sid) t + INTO par_freq_interval, par_freq_subday_type, par_freq_subday_interval, par_freq_relative_interval, par_freq_recurrence_factor, par_active_start_date, par_active_start_time, par_active_end_date, par_active_end_time, var_retval /* @owner_sid */; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + /* Failure */ + /* Update the JobSchedule */; + UPDATE sys.sysschedules + SET name = par_new_name, enabled = par_enabled, freq_type = par_freq_type, freq_interval = par_freq_interval, freq_subday_type = par_freq_subday_type, freq_subday_interval = par_freq_subday_interval, freq_relative_interval = par_freq_relative_interval, freq_recurrence_factor = par_freq_recurrence_factor, active_start_date = par_active_start_date, active_end_date = par_active_end_date, active_start_time = par_active_start_time, active_end_time = par_active_end_time + /* date_modified = GETDATE(), */, version_number = version_number + 1 + WHERE (schedule_id = var_schedule_id); + SELECT + 0 + INTO var_retval + /* @@error */ + /* Update the job's version/last-modified information */; + UPDATE sys.sysjobs + SET version_number = version_number + 1 + /* date_modified = GETDATE() */ + WHERE (job_id = par_job_id); + ReturnCode := (var_retval); + RETURN + /* 0 means success */; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_update_jobstep ( + par_job_id integer = NULL::integer, + par_job_name varchar = NULL::character varying, + par_step_id integer = NULL::integer, + par_step_name varchar = NULL::character varying, + par_subsystem varchar = NULL::character varying, + par_command text = NULL::text, + par_additional_parameters text = NULL::text, + par_cmdexec_success_code integer = NULL::integer, + par_on_success_action smallint = NULL::smallint, + par_on_success_step_id integer = NULL::integer, + par_on_fail_action smallint = NULL::smallint, + par_on_fail_step_id integer = NULL::integer, + par_server varchar = NULL::character varying, + par_database_name varchar = NULL::character varying, + par_database_user_name varchar = NULL::character varying, + par_retry_attempts integer = NULL::integer, + par_retry_interval integer = NULL::integer, + par_os_run_priority integer = NULL::integer, + par_output_file_name varchar = NULL::character varying, + par_flags integer = NULL::integer, + par_proxy_id integer = NULL::integer, + par_proxy_name varchar = NULL::character varying, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_os_run_priority_code INT; + var_step_id_as_char VARCHAR(10); + var_new_step_name VARCHAR(128); + var_x_step_name VARCHAR(128); + var_x_subsystem VARCHAR(40); + var_x_command TEXT; + var_x_flags INT; + var_x_cmdexec_success_code INT; + var_x_on_success_action SMALLINT; + var_x_on_success_step_id INT; + var_x_on_fail_action SMALLINT; + var_x_on_fail_step_id INT; + var_x_server VARCHAR(128); + var_x_database_name VARCHAR(128); + var_x_database_user_name VARCHAR(128); + var_x_retry_attempts INT; + var_x_retry_interval INT; + var_x_os_run_priority INT; + var_x_output_file_name VARCHAR(200); + var_x_proxy_id INT; + var_x_last_run_outcome SMALLINT; + var_x_last_run_duration INT; + var_x_last_run_retries INT; + var_x_last_run_date INT; + var_x_last_run_time INT; + var_new_proxy_id INT; + var_subsystem_id INT; + var_auto_proxy_name VARCHAR(128); + var_job_owner_sid CHAR(85); + var_step_uid CHAR(85); +BEGIN + SELECT NULL INTO var_new_proxy_id; + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_step_name)) INTO par_step_name; + SELECT LTRIM(RTRIM(par_subsystem)) INTO par_subsystem; + SELECT LTRIM(RTRIM(par_command)) INTO par_command; + SELECT LTRIM(RTRIM(par_server)) INTO par_server; + SELECT LTRIM(RTRIM(par_database_name)) INTO par_database_name; + SELECT LTRIM(RTRIM(par_database_user_name)) INTO par_database_user_name; + SELECT LTRIM(RTRIM(par_output_file_name)) INTO par_output_file_name; + SELECT LTRIM(RTRIM(par_proxy_name)) INTO par_proxy_name; + /* Make sure Dts is translated into new subsystem's name SSIS */ + /* IF (@subsystem IS NOT NULL AND UPPER(@subsystem collate SQL_Latin1_General_CP1_CS_AS) = N'DTS') */ + /* BEGIN */ + /* SET @subsystem = N'SSIS' */ + /* END */ + SELECT + t.par_job_name, t.par_job_id, t.par_owner_sid, t.ReturnCode + FROM sys.babelfish_sp_verify_job_identifiers('@job_name' + /* @name_of_name_parameter */, '@job_id' + /* @name_of_id_parameter */, par_job_name + /* @job_name */, par_job_id + /* @job_id */, 'TEST' + /* @sqlagent_starting_test */, var_job_owner_sid) + INTO par_job_name, par_job_id, var_job_owner_sid, var_retval + /* @owner_sid */; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF; + /* Failure */ + /* Check that the step exists */ + + IF (NOT EXISTS (SELECT + * + FROM sys.sysjobsteps + WHERE (job_id = par_job_id) AND (step_id = par_step_id))) THEN + SELECT + CAST (par_step_id AS VARCHAR(10)) + INTO var_step_id_as_char; + RAISE 'Error %, severity %, state % was raised. Message: %. Argument: %. Argument: %', '50000', 0, 0, 'The specified %s ("%s") does not exist.', '@step_id', var_step_id_as_char USING ERRCODE := '50000'; + ReturnCode := (1); + RETURN; + /* Failure */ + END IF; + /* Set the x_ (existing) variables */ + SELECT + step_name, subsystem, command, flags, cmdexec_success_code, on_success_action, on_success_step_id, on_fail_action, on_fail_step_id, server, database_name, database_user_name, retry_attempts, retry_interval, os_run_priority, output_file_name, proxy_id, last_run_outcome, last_run_duration, last_run_retries, last_run_date, last_run_time + INTO var_x_step_name, var_x_subsystem, var_x_command, var_x_flags, var_x_cmdexec_success_code, var_x_on_success_action, var_x_on_success_step_id, var_x_on_fail_action, var_x_on_fail_step_id, var_x_server, var_x_database_name, var_x_database_user_name, var_x_retry_attempts, var_x_retry_interval, var_x_os_run_priority, var_x_output_file_name, var_x_proxy_id, var_x_last_run_outcome, var_x_last_run_duration, var_x_last_run_retries, var_x_last_run_date, var_x_last_run_time + FROM sys.sysjobsteps + WHERE (job_id = par_job_id) AND (step_id = par_step_id); + + IF ((par_step_name IS NOT NULL) AND (par_step_name <> var_x_step_name)) THEN + SELECT + par_step_name + INTO var_new_step_name; + END IF; + /* Fill out the values for all non-supplied parameters from the existing values */ + + IF (par_step_name IS NULL) THEN + SELECT var_x_step_name INTO par_step_name; + END IF; + + IF (par_subsystem IS NULL) THEN + SELECT var_x_subsystem INTO par_subsystem; + END IF; + + IF (par_command IS NULL) THEN + SELECT var_x_command INTO par_command; + END IF; + + IF (par_flags IS NULL) THEN + SELECT var_x_flags INTO par_flags; + END IF; + + IF (par_cmdexec_success_code IS NULL) THEN + SELECT var_x_cmdexec_success_code INTO par_cmdexec_success_code; + END IF; + + IF (par_on_success_action IS NULL) THEN + SELECT var_x_on_success_action INTO par_on_success_action; + END IF; + + IF (par_on_success_step_id IS NULL) THEN + SELECT var_x_on_success_step_id INTO par_on_success_step_id; + END IF; + + IF (par_on_fail_action IS NULL) THEN + SELECT var_x_on_fail_action INTO par_on_fail_action; + END IF; + + IF (par_on_fail_step_id IS NULL) THEN + SELECT var_x_on_fail_step_id INTO par_on_fail_step_id; + END IF; + + IF (par_server IS NULL) THEN + SELECT var_x_server INTO par_server; + END IF; + + IF (par_database_name IS NULL) THEN + SELECT var_x_database_name INTO par_database_name; + END IF; + + IF (par_database_user_name IS NULL) THEN + SELECT var_x_database_user_name INTO par_database_user_name; + END IF; + + IF (par_retry_attempts IS NULL) THEN + SELECT var_x_retry_attempts INTO par_retry_attempts; + END IF; + + IF (par_retry_interval IS NULL) THEN + SELECT var_x_retry_interval INTO par_retry_interval; + END IF; + + IF (par_os_run_priority IS NULL) THEN + SELECT var_x_os_run_priority INTO par_os_run_priority; + END IF; + + IF (par_output_file_name IS NULL) THEN + SELECT var_x_output_file_name INTO par_output_file_name; + END IF; + + IF (par_proxy_id IS NULL) THEN + SELECT var_x_proxy_id INTO var_new_proxy_id; + END IF; + /* if an empty proxy_name is supplied the proxy is removed */ + + IF par_proxy_name = '' THEN + SELECT NULL INTO var_new_proxy_id; + END IF; + /* Turn [nullable] empty string parameters into NULLs */ + + IF (LOWER(par_command) = LOWER('')) THEN + SELECT NULL INTO par_command; + END IF; + + IF (par_server = '') THEN + SELECT NULL INTO par_server; + END IF; + + IF (par_database_name = '') THEN + SELECT NULL INTO par_database_name; + END IF; + + IF (par_database_user_name = '') THEN + SELECT NULL INTO par_database_user_name; + END IF; + + IF (LOWER(par_output_file_name) = LOWER('')) THEN + SELECT NULL INTO par_output_file_name; + END IF + /* Check new values */; + SELECT + t.par_database_name, t.par_database_user_name, t.ReturnCode + FROM sys.babelfish_sp_verify_jobstep(par_job_id, par_step_id, var_new_step_name, par_subsystem, par_command, par_server, par_on_success_action, par_on_success_step_id, par_on_fail_action, par_on_fail_step_id, par_os_run_priority, par_database_name, par_database_user_name, par_flags, par_output_file_name, var_new_proxy_id) t + INTO par_database_name, par_database_user_name, var_retval; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + /* Failure */ + /* Update the job's version/last-modified information */; + UPDATE sys.sysjobs + SET version_number = version_number + 1 + /* date_modified = GETDATE() */ + WHERE (job_id = par_job_id) + /* Update the step */; + UPDATE sys.sysjobsteps + SET step_name = par_step_name, subsystem = par_subsystem, command = par_command, flags = par_flags, additional_parameters = par_additional_parameters, cmdexec_success_code = par_cmdexec_success_code, on_success_action = par_on_success_action, on_success_step_id = par_on_success_step_id, on_fail_action = par_on_fail_action, on_fail_step_id = par_on_fail_step_id, server = par_server, database_name = par_database_name, database_user_name = par_database_user_name, retry_attempts = par_retry_attempts, retry_interval = par_retry_interval, os_run_priority = par_os_run_priority, output_file_name = par_output_file_name, last_run_outcome = var_x_last_run_outcome, last_run_duration = var_x_last_run_duration, last_run_retries = var_x_last_run_retries, last_run_date = var_x_last_run_date, last_run_time = var_x_last_run_time, proxy_id = var_new_proxy_id + WHERE (job_id = par_job_id) AND (step_id = par_step_id); + + SELECT step_uid + FROM sys.sysjobsteps + WHERE job_id = par_job_id AND step_id = par_step_id + INTO var_step_uid; + + -- PERFORM sys.sp_jobstep_create_proc (var_step_uid); + + ReturnCode := (0); + RETURN + /* Success */; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_update_schedule ( + par_schedule_id integer = NULL::integer, + par_name varchar = NULL::character varying, + par_new_name varchar = NULL::character varying, + par_enabled smallint = NULL::smallint, + par_freq_type integer = NULL::integer, + par_freq_interval integer = NULL::integer, + par_freq_subday_type integer = NULL::integer, + par_freq_subday_interval integer = NULL::integer, + par_freq_relative_interval integer = NULL::integer, + par_freq_recurrence_factor integer = NULL::integer, + par_active_start_date integer = NULL::integer, + par_active_end_date integer = NULL::integer, + par_active_start_time integer = NULL::integer, + par_active_end_time integer = NULL::integer, + par_owner_login_name varchar = NULL::character varying, + par_automatic_post smallint = 1, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_retval INT; + var_owner_sid CHAR(85); + var_cur_owner_sid CHAR(85); + var_x_name VARCHAR(128); + var_enable_only_used INT; + var_x_enabled SMALLINT; + var_x_freq_type INT; + var_x_freq_interval INT; + var_x_freq_subday_type INT; + var_x_freq_subday_interval INT; + var_x_freq_relative_interval INT; + var_x_freq_recurrence_factor INT; + var_x_active_start_date INT; + var_x_active_end_date INT; + var_x_active_start_time INT; + var_x_active_end_time INT; + var_schedule_uid CHAR(38); +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT + LTRIM(RTRIM(par_name)) + INTO par_name; + SELECT + LTRIM(RTRIM(par_new_name)) + INTO par_new_name; + SELECT + LTRIM(RTRIM(par_owner_login_name)) + INTO par_owner_login_name + /* Turn [nullable] empty string parameters into NULLs */; + + IF (par_new_name = '') THEN + SELECT + NULL + INTO par_new_name; + END IF + /* Check that we can uniquely identify the schedule. This only returns a schedule that is visible to this user */; + SELECT + t.par_schedule_name, t.par_schedule_id, t.par_owner_sid, t.par_orig_server_id, t.ReturnCode + FROM sys.babelfish_sp_verify_schedule_identifiers('@name' + /* @name_of_name_parameter */, '@schedule_id' + /* @name_of_id_parameter */, par_name + /* @schedule_name */, par_schedule_id + /* @schedule_id */, var_cur_owner_sid + /* @owner_sid */, NULL + /* @orig_server_id */, NULL) t + INTO par_name, par_schedule_id, var_cur_owner_sid, var_retval + /* @job_id_filter */; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + /* Failure */ + /* Is @enable the only parameter used beside jobname and jobid? */; + + IF ((par_enabled IS NOT NULL) AND (par_new_name IS NULL) AND (par_freq_type IS NULL) AND (par_freq_interval IS NULL) AND (par_freq_subday_type IS NULL) AND (par_freq_subday_interval IS NULL) AND (par_freq_relative_interval IS NULL) AND (par_freq_recurrence_factor IS NULL) AND (par_active_start_date IS NULL) AND (par_active_end_date IS NULL) AND (par_active_start_time IS NULL) AND (par_active_end_time IS NULL) AND (par_owner_login_name IS NULL)) THEN + SELECT + 1 + INTO var_enable_only_used; + ELSE + SELECT + 0 + INTO var_enable_only_used; + END IF + /* If the param @owner_login_name is null or doesn't get resolved by SUSER_SID() set it to the current owner of the schedule */; + + IF (var_owner_sid IS NULL) THEN + SELECT + var_cur_owner_sid + INTO var_owner_sid; + END IF + /* Set the x_ (existing) variables */; + SELECT + name, enabled, freq_type, freq_interval, freq_subday_type, freq_subday_interval, freq_relative_interval, freq_recurrence_factor, active_start_date, active_end_date, active_start_time, active_end_time + INTO var_x_name, var_x_enabled, var_x_freq_type, var_x_freq_interval, var_x_freq_subday_type, var_x_freq_subday_interval, var_x_freq_relative_interval, var_x_freq_recurrence_factor, var_x_active_start_date, var_x_active_end_date, var_x_active_start_time, var_x_active_end_time + FROM sys.sysschedules + WHERE (schedule_id = par_schedule_id) + /* Fill out the values for all non-supplied parameters from the existing values */; + + IF (par_new_name IS NULL) THEN + SELECT + var_x_name + INTO par_new_name; + END IF; + + IF (par_enabled IS NULL) THEN + SELECT + var_x_enabled + INTO par_enabled; + END IF; + + IF (par_freq_type IS NULL) THEN + SELECT + var_x_freq_type + INTO par_freq_type; + END IF; + + IF (par_freq_interval IS NULL) THEN + SELECT + var_x_freq_interval + INTO par_freq_interval; + END IF; + + IF (par_freq_subday_type IS NULL) THEN + SELECT + var_x_freq_subday_type + INTO par_freq_subday_type; + END IF; + + IF (par_freq_subday_interval IS NULL) THEN + SELECT + var_x_freq_subday_interval + INTO par_freq_subday_interval; + END IF; + + IF (par_freq_relative_interval IS NULL) THEN + SELECT + var_x_freq_relative_interval + INTO par_freq_relative_interval; + END IF; + + IF (par_freq_recurrence_factor IS NULL) THEN + SELECT + var_x_freq_recurrence_factor + INTO par_freq_recurrence_factor; + END IF; + + IF (par_active_start_date IS NULL) THEN + SELECT + var_x_active_start_date + INTO par_active_start_date; + END IF; + + IF (par_active_end_date IS NULL) THEN + SELECT + var_x_active_end_date + INTO par_active_end_date; + END IF; + + IF (par_active_start_time IS NULL) THEN + SELECT + var_x_active_start_time + INTO par_active_start_time; + END IF; + + IF (par_active_end_time IS NULL) THEN + SELECT + var_x_active_end_time + INTO par_active_end_time; + END IF + /* Check schedule (frequency and owner) parameters */; + SELECT + t.par_freq_interval, t.par_freq_subday_type, t.par_freq_subday_interval, t.par_freq_relative_interval, t.par_freq_recurrence_factor, t.par_active_start_date, + t.par_active_start_time, t.par_active_end_date, t.par_active_end_time, t.ReturnCode + FROM sys.babelfish_sp_verify_schedule(par_schedule_id + /* @schedule_id */, par_new_name + /* @name */, par_enabled + /* @enabled */, par_freq_type + /* @freq_type */, par_freq_interval + /* @freq_interval */, par_freq_subday_type + /* @freq_subday_type */, par_freq_subday_interval + /* @freq_subday_interval */, par_freq_relative_interval + /* @freq_relative_interval */, par_freq_recurrence_factor + /* @freq_recurrence_factor */, par_active_start_date + /* @active_start_date */, par_active_start_time + /* @active_start_time */, par_active_end_date + /* @active_end_date */, par_active_end_time + /* @active_end_time */, var_owner_sid) t + INTO par_freq_interval, par_freq_subday_type, par_freq_subday_interval, par_freq_relative_interval, par_freq_recurrence_factor, par_active_start_date, par_active_start_time, par_active_end_date, par_active_end_time, var_retval /* @owner_sid */; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + /* Failure */ + /* Update the sysschedules table */; + UPDATE sys.sysschedules + SET name = par_new_name, owner_sid = var_owner_sid, enabled = par_enabled, freq_type = par_freq_type, freq_interval = par_freq_interval, freq_subday_type = par_freq_subday_type, freq_subday_interval = par_freq_subday_interval, freq_relative_interval = par_freq_relative_interval, freq_recurrence_factor = par_freq_recurrence_factor, active_start_date = par_active_start_date, active_end_date = par_active_end_date, active_start_time = par_active_start_time, active_end_time = par_active_end_time + /* date_modified = GETDATE(), */, version_number = version_number + 1 + WHERE (schedule_id = par_schedule_id); + SELECT + 0 + INTO var_retval; + + ReturnCode := (var_retval); + RETURN + /* 0 means success */; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_verify_job ( + par_job_id integer, + par_name varchar, + par_enabled smallint, + par_start_step_id integer, + par_category_name varchar, + inout par_owner_sid char, + par_notify_level_eventlog integer, + inout par_notify_level_email integer, + inout par_notify_level_netsend integer, + inout par_notify_level_page integer, + par_notify_email_operator_name varchar, + par_notify_netsend_operator_name varchar, + par_notify_page_operator_name varchar, + par_delete_level integer, + inout par_category_id integer, + inout par_notify_email_operator_id integer, + inout par_notify_netsend_operator_id integer, + inout par_notify_page_operator_id integer, + inout par_originating_server varchar, + out returncode integer +) +RETURNS record AS +$body$ +DECLARE + var_job_type INT; + var_retval INT; + var_current_date INT; + var_res_valid_range VARCHAR(200); + var_max_step_id INT; + var_valid_range VARCHAR(50); +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_name)) INTO par_name; + SELECT LTRIM(RTRIM(par_category_name)) INTO par_category_name; + SELECT UPPER(LTRIM(RTRIM(par_originating_server))) INTO par_originating_server; + + IF ( + EXISTS ( + SELECT * + FROM sys.sysjobs AS job + WHERE (name = par_name) + /* AND (job_id <> ISNULL(@job_id, 0x911)))) -- When adding a new job @job_id is NULL */ + ) + ) + THEN /* Failure */ + RAISE 'The specified % ("%") already exists.', 'par_name', par_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check enabled state */ + IF (par_enabled <> 0) AND (par_enabled <> 1) THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', 'par_enabled', '0, 1' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check start step */ + + IF (par_job_id IS NULL) THEN /* New job */ + IF (par_start_step_id <> 1) THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', 'par_start_step_id', '1' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + ELSE /* Existing job */ + /* Get current maximum step id */ + SELECT COALESCE(MAX(step_id), 0) + INTO var_max_step_id + FROM sys.sysjobsteps + WHERE (job_id = par_job_id); + + IF (par_start_step_id < 1) OR (par_start_step_id > var_max_step_id + 1) THEN /* Failure */ + SELECT '1..' || CAST (var_max_step_id + 1 AS VARCHAR(1)) + INTO var_valid_range; + RAISE 'The specified "%" is invalid (valid values are: %).', 'par_start_step_id', var_valid_range USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + /* Get the category_id, handling any special-cases as appropriate */ + SELECT NULL INTO par_category_id; + + IF (par_category_name = '[DEFAULT]') /* User wants to revert to the default job category */ + THEN + SELECT + CASE COALESCE(var_job_type, 1) + WHEN 1 THEN 0 /* [Uncategorized (Local)] */ + WHEN 2 THEN 2 /* [Uncategorized (Multi-Server)] */ + END + INTO par_category_id; + ELSE + SELECT 0 INTO par_category_id; + END IF; + + returncode := (0); /* Success */ + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_verify_job_date ( + par_date integer, + par_date_name varchar = 'date'::character varying, + out returncode integer +) +RETURNS integer AS +$body$ +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_date_name)) INTO par_date_name; + + /* Success */ + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_verify_job_identifiers ( + par_name_of_name_parameter varchar, + par_name_of_id_parameter varchar, + inout par_job_name varchar, + inout par_job_id integer, + par_sqlagent_starting_test varchar = 'TEST'::character varying, + inout par_owner_sid char = NULL::bpchar, + out returncode integer +) +RETURNS record AS +$body$ +DECLARE + var_retval INT; + var_job_id_as_char VARCHAR(36); +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_name_of_name_parameter)) INTO par_name_of_name_parameter; + SELECT LTRIM(RTRIM(par_name_of_id_parameter)) INTO par_name_of_id_parameter; + SELECT LTRIM(RTRIM(par_job_name)) INTO par_job_name; + + IF (par_job_name = '') + THEN + SELECT NULL INTO par_job_name; + END IF; + + IF ((par_job_name IS NULL) AND (par_job_id IS NULL)) OR ((par_job_name IS NOT NULL) AND (par_job_id IS NOT NULL)) + THEN /* Failure */ + RAISE 'Supply either % or % to identify the job.', par_name_of_id_parameter, par_name_of_name_parameter USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check job id */ + IF (par_job_id IS NOT NULL) + THEN + SELECT name + , owner_sid + INTO par_job_name + , par_owner_sid + FROM sys.sysjobs + WHERE (job_id = par_job_id); + + /* the view would take care of all the permissions issues. */ + IF (par_job_name IS NULL) + THEN /* Failure */ + SELECT CAST (par_job_id AS VARCHAR(36)) + INTO var_job_id_as_char; + + RAISE 'The specified % ("%") does not exist.', 'job_id', var_job_id_as_char USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + ELSE + /* Check job name */ + IF (par_job_name IS NOT NULL) + THEN + /* Check if the job name is ambiguous */ + IF (SELECT COUNT(*) FROM sys.sysjobs WHERE name = par_job_name) > 1 + THEN /* Failure */ + RAISE 'There are two or more jobs named "%". Specify % instead of % to uniquely identify the job.', par_job_name, par_name_of_id_parameter, par_name_of_name_parameter USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* The name is not ambiguous, so get the corresponding job_id (if the job exists) */ + SELECT job_id + , owner_sid + INTO par_job_id + , par_owner_sid + FROM sys.sysjobs + WHERE (name = par_job_name); + + /* the view would take care of all the permissions issues. */ + IF (par_job_id IS NULL) + THEN /* Failure */ + RAISE 'The specified % ("%") does not exist.', 'job_name', par_job_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + END IF; + + /* Success */ + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_verify_job_time ( + par_time integer, + par_time_name varchar = 'time'::character varying, + out returncode integer +) +RETURNS integer AS +$body$ +DECLARE + var_hour INT; + var_minute INT; + var_second INT; +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_time_name)) INTO par_time_name; + + IF ((par_time < 0) OR (par_time > 235959)) + THEN + RAISE 'The specified "%" is invalid (valid values are: %).', par_time_name, '000000..235959' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + SELECT (par_time / 10000) INTO var_hour; + SELECT (par_time % 10000) / 100 INTO var_minute; + SELECT (par_time % 100) INTO var_second; + + /* Check hour range */ + IF (var_hour > 23) THEN + RAISE 'The "%" supplied has an invalid %.', par_time_name, 'hour' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check minute range */ + IF (var_minute > 59) THEN + RAISE 'The "%" supplied has an invalid %.', par_time_name, 'minute' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check second range */ + IF (var_second > 59) THEN + RAISE 'The "%" supplied has an invalid %.', par_time_name, 'second' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_verify_jobstep ( + par_job_id integer, + par_step_id integer, + par_step_name varchar, + par_subsystem varchar, + par_command text, + par_server varchar, + par_on_success_action smallint, + par_on_success_step_id integer, + par_on_fail_action smallint, + par_on_fail_step_id integer, + par_os_run_priority integer, + par_flags integer, + par_output_file_name varchar, + par_proxy_id integer, + out returncode integer +) +AS +$body$ +DECLARE + var_max_step_id INT; + var_retval INT; + var_valid_values VARCHAR(50); + var_database_name_temp VARCHAR(258); + var_database_user_name_temp VARCHAR(256); + var_temp_command TEXT; + var_iPos INT; + var_create_count INT; + var_destroy_count INT; + var_is_olap_subsystem SMALLINT; + var_owner_sid CHAR(85); + var_owner_name VARCHAR(128); +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_subsystem)) INTO par_subsystem; + SELECT LTRIM(RTRIM(par_server)) INTO par_server; + SELECT LTRIM(RTRIM(par_output_file_name)) INTO par_output_file_name; + + /* Get current maximum step id */ + SELECT COALESCE(MAX(step_id), 0) + INTO var_max_step_id + FROM sys.sysjobsteps + WHERE (job_id = par_job_id); + + /* Check step id */ + IF (par_step_id < 1) OR (par_step_id > var_max_step_id + 1) /* Failure */ + THEN + SELECT '1..' || CAST (var_max_step_id + 1 AS VARCHAR(1)) INTO var_valid_values; + RAISE 'The specified "%" is invalid (valid values are: %).', '@step_id', var_valid_values USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check step name */ + IF ( + EXISTS ( + SELECT * + FROM sys.sysjobsteps + WHERE (job_id = par_job_id) AND (step_name = par_step_name) + ) + ) + THEN /* Failure */ + RAISE 'The specified % ("%") already exists.', 'step_name', par_step_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check on-success action/step */ + IF (par_on_success_action <> 1) /* Quit Qith Success */ + AND (par_on_success_action <> 2) /* Quit Qith Failure */ + AND (par_on_success_action <> 3) /* Goto Next Step */ + AND (par_on_success_action <> 4) /* Goto Step */ + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', 'on_success_action', '1, 2, 3, 4' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF (par_on_success_action = 4) AND ((par_on_success_step_id < 1) OR (par_on_success_step_id = par_step_id)) + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are greater than 0 but excluding %ld).', 'on_success_step', par_step_id USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check on-fail action/step */ + IF (par_on_fail_action <> 1) /* Quit With Success */ + AND (par_on_fail_action <> 2) /* Quit With Failure */ + AND (par_on_fail_action <> 3) /* Goto Next Step */ + AND (par_on_fail_action <> 4) /* Goto Step */ + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', 'on_failure_action', '1, 2, 3, 4' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF (par_on_fail_action = 4) AND ((par_on_fail_step_id < 1) OR (par_on_fail_step_id = par_step_id)) + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are greater than 0 but excluding %).', 'on_failure_step', par_step_id USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Warn the user about forward references */ + IF ((par_on_success_action = 4) AND (par_on_success_step_id > var_max_step_id)) + THEN + RAISE 'Warning: Non-existent step referenced by %.', 'on_success_step_id' USING ERRCODE := '50000'; + END IF; + + IF ((par_on_fail_action = 4) AND (par_on_fail_step_id > var_max_step_id)) + THEN + RAISE 'Warning: Non-existent step referenced by %.', '@on_fail_step_id' USING ERRCODE := '50000'; + END IF; + + /* Check run priority: must be a valid value to pass to SetThreadPriority: */ + /* [-15 = IDLE, -1 = BELOW_NORMAL, 0 = NORMAL, 1 = ABOVE_NORMAL, 15 = TIME_CRITICAL] */ + IF (par_os_run_priority NOT IN (- 15, - 1, 0, 1, 15)) + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', '@os_run_priority', '-15, -1, 0, 1, 15' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check flags */ + IF ((par_flags < 0) OR (par_flags > 114)) THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', '@flags', '0..114' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF (LOWER(UPPER(par_subsystem)) <> LOWER('TSQL')) THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', '@subsystem', 'TSQL' USING ERRCODE := '50000'; + returncode := (1); + RETURN; + END IF; + + /* Success */ + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_verify_schedule ( + par_schedule_id integer, + par_name varchar, + par_enabled smallint, + par_freq_type integer, + inout par_freq_interval integer, + inout par_freq_subday_type integer, + inout par_freq_subday_interval integer, + inout par_freq_relative_interval integer, + inout par_freq_recurrence_factor integer, + inout par_active_start_date integer, + inout par_active_start_time integer, + inout par_active_end_date integer, + inout par_active_end_time integer, + par_owner_sid char, + out returncode integer +) +RETURNS record AS +$body$ +DECLARE + var_return_code INT; + var_isAdmin INT; +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_name)) INTO par_name; + + /* Make sure that NULL input/output parameters - if NULL - are initialized to 0 */ + SELECT COALESCE(par_freq_interval, 0) INTO par_freq_interval; + SELECT COALESCE(par_freq_subday_type, 0) INTO par_freq_subday_type; + SELECT COALESCE(par_freq_subday_interval, 0) INTO par_freq_subday_interval; + SELECT COALESCE(par_freq_relative_interval, 0) INTO par_freq_relative_interval; + SELECT COALESCE(par_freq_recurrence_factor, 0) INTO par_freq_recurrence_factor; + SELECT COALESCE(par_active_start_date, 0) INTO par_active_start_date; + SELECT COALESCE(par_active_start_time, 0) INTO par_active_start_time; + SELECT COALESCE(par_active_end_date, 0) INTO par_active_end_date; + SELECT COALESCE(par_active_end_time, 0) INTO par_active_end_time; + + /* Verify name (we disallow schedules called 'ALL' since this has special meaning in sp_delete_jobschedules) */ + SELECT 0 INTO var_isAdmin; + + IF ( + EXISTS ( + SELECT * + FROM sys.sysschedules + WHERE (name = par_name) + ) + ) + THEN /* Failure */ + RAISE 'The specified % ("%") already exists.', 'par_name', par_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF (UPPER(par_name) = 'ALL') + THEN /* Failure */ + RAISE 'The specified "%" is invalid.', 'name' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Verify enabled state */ + IF (par_enabled <> 0) AND (par_enabled <> 1) + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', '@enabled', '0, 1' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Verify frequency type */ + IF (par_freq_type = 2) /* OnDemand is no longer supported */ + THEN /* Failure */ + RAISE 'Frequency Type 0x2 (OnDemand) is no longer supported.' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF (par_freq_type NOT IN (1, 4, 8, 16, 32, 64, 128)) + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', 'freq_type', '1, 4, 8, 16, 32, 64, 128' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Verify frequency sub-day type */ + IF (par_freq_subday_type <> 0) AND (par_freq_subday_type NOT IN (1, 2, 4, 8)) + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', 'freq_subday_type', '1, 2, 4, 8' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Default active start/end date/times (if not supplied, or supplied as NULLs or 0) */ + IF (par_active_start_date = 0) + THEN + SELECT date_part('year', NOW()::TIMESTAMP) * 10000 + date_part('month', NOW()::TIMESTAMP) * 100 + date_part('day', NOW()::TIMESTAMP) + INTO par_active_start_date; + END IF; + + /* This is an ISO format: "yyyymmdd" */ + IF (par_active_end_date = 0) + THEN + /* December 31st 9999 */ + SELECT 99991231 INTO par_active_end_date; + END IF; + + IF (par_active_start_time = 0) + THEN + /* 12:00:00 am */ + SELECT 000000 INTO par_active_start_time; + END IF; + + IF (par_active_end_time = 0) + THEN + /* 11:59:59 pm */ + SELECT 235959 INTO par_active_end_time; + END IF; + + /* Verify active start/end dates */ + IF (par_active_end_date = 0) + THEN + SELECT 99991231 INTO par_active_end_date; + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_verify_job_date(par_active_end_date, 'active_end_date') t + INTO var_return_code; + + IF (var_return_code <> 0) + THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_verify_job_date(par_active_start_date, '@active_start_date') t + INTO var_return_code; + + IF (var_return_code <> 0) + THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + IF (par_active_end_date < par_active_start_date) + THEN /* Failure */ + RAISE '% cannot be before %.', 'active_end_date', 'active_start_date' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_verify_job_time(par_active_end_time, '@active_end_time') t + INTO var_return_code; + + IF (var_return_code <> 0) + THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + SELECT t.returncode + FROM sys.babelfish_sp_verify_job_time(par_active_start_time, '@active_start_time') t + INTO var_return_code; + + IF (var_return_code <> 0) + THEN /* Failure */ + returncode := 1; + RETURN; + END IF; + + IF (par_active_start_time = par_active_end_time AND (par_freq_subday_type IN (2, 4, 8))) + THEN /* Failure */ + RAISE 'The specified "%" is invalid (valid values are: %).', 'active_end_time', 'before or after active_start_time' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF ((par_freq_type = 1) /* FREQTYPE_ONETIME */ + OR (par_freq_type = 64) /* FREQTYPE_AUTOSTART */ + OR (par_freq_type = 128)) /* FREQTYPE_ONIDLE */ + THEN /* Set standard defaults for non-required parameters */ + SELECT 0 INTO par_freq_interval; + SELECT 0 INTO par_freq_subday_type; + SELECT 0 INTO par_freq_subday_interval; + SELECT 0 INTO par_freq_relative_interval; + SELECT 0 INTO par_freq_recurrence_factor; + /* Success */ + returncode := 0; + RETURN; + END IF; + + IF (par_freq_subday_type = 0) /* FREQSUBTYPE_ONCE */ + THEN + SELECT 1 INTO par_freq_subday_type; + END IF; + + IF ((par_freq_subday_type <> 1) /* FREQSUBTYPE_ONCE */ + AND (par_freq_subday_type <> 2) /* FREQSUBTYPE_SECOND */ + AND (par_freq_subday_type <> 4) /* FREQSUBTYPE_MINUTE */ + AND (par_freq_subday_type <> 8)) /* FREQSUBTYPE_HOUR */ + THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: The specified @freq_subday_type is invalid (valid values are: 0x1, 0x2, 0x4, 0x8).).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF ((par_freq_subday_type <> 1) AND (par_freq_subday_interval < 1)) /* FREQSUBTYPE_ONCE and less than 1 interval */ + OR ((par_freq_subday_type = 2) AND (par_freq_subday_interval < 10)) /* FREQSUBTYPE_SECOND and less than 10 seconds (see MIN_SCHEDULE_GRANULARITY in SqlAgent source code) */ + THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: The specified @freq_subday_interval is invalid).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF (par_freq_type = 4) /* FREQTYPE_DAILY */ + THEN + SELECT 0 INTO par_freq_recurrence_factor; + + IF (par_freq_interval < 1) THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: @freq_interval must be at least 1 for a daily job.).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + IF (par_freq_type = 8) /* FREQTYPE_WEEKLY */ + THEN + IF (par_freq_interval < 1) OR (par_freq_interval > 127) /* (2^7)-1 [freq_interval is a bitmap (Sun=1..Sat=64)] */ + THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: @freq_interval must be a valid day of the week bitmask [Sunday = 1 .. Saturday = 64] for a weekly job.).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + IF (par_freq_type = 16) /* FREQTYPE_MONTHLY */ + THEN + IF (par_freq_interval < 1) OR (par_freq_interval > 31) + THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: @freq_interval must be between 1 and 31 for a monthly job.).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + IF (par_freq_type = 32) /* FREQTYPE_MONTHLYRELATIVE */ + THEN + IF (par_freq_relative_interval <> 1) /* RELINT_1ST */ + AND (par_freq_relative_interval <> 2) /* RELINT_2ND */ + AND (par_freq_relative_interval <> 4) /* RELINT_3RD */ + AND (par_freq_relative_interval <> 8) /* RELINT_4TH */ + AND (par_freq_relative_interval <> 16) /* RELINT_LAST */ + THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: @freq_relative_interval must be one of 1st (0x1), 2nd (0x2), 3rd [0x4], 4th (0x8) or Last (0x10).).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + IF (par_freq_type = 32) /* FREQTYPE_MONTHLYRELATIVE */ + THEN + IF (par_freq_interval <> 1) /* RELATIVE_SUN */ + AND (par_freq_interval <> 2) /* RELATIVE_MON */ + AND (par_freq_interval <> 3) /* RELATIVE_TUE */ + AND (par_freq_interval <> 4) /* RELATIVE_WED */ + AND (par_freq_interval <> 5) /* RELATIVE_THU */ + AND (par_freq_interval <> 6) /* RELATIVE_FRI */ + AND (par_freq_interval <> 7) /* RELATIVE_SAT */ + AND (par_freq_interval <> 8) /* RELATIVE_DAY */ + AND (par_freq_interval <> 9) /* RELATIVE_WEEKDAY */ + AND (par_freq_interval <> 10) /* RELATIVE_WEEKENDDAY */ + THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: @freq_interval must be between 1 and 10 (1 = Sunday .. 7 = Saturday, 8 = Day, 9 = Weekday, 10 = Weekend-day) for a monthly-relative job.).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + + IF ((par_freq_type = 8) /* FREQTYPE_WEEKLY */ + OR (par_freq_type = 16) /* FREQTYPE_MONTHLY */ + OR (par_freq_type = 32)) /* FREQTYPE_MONTHLYRELATIVE */ + AND (par_freq_recurrence_factor < 1) + THEN /* Failure */ + RAISE 'The schedule for this job is invalid (reason: @freq_recurrence_factor must be at least 1.).' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + /* Success */ + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_verify_schedule_identifiers ( + par_name_of_name_parameter varchar, + par_name_of_id_parameter varchar, + inout par_schedule_name varchar, + inout par_schedule_id integer, + inout par_owner_sid char, + inout par_orig_server_id integer, + par_job_id_filter integer = NULL::integer, + out returncode integer +) +AS +$body$ +DECLARE + var_retval INT; + var_schedule_id_as_char VARCHAR(36); + var_sch_name_count INT; +BEGIN + /* Remove any leading/trailing spaces from parameters */ + SELECT LTRIM(RTRIM(par_name_of_name_parameter)) INTO par_name_of_name_parameter; + SELECT LTRIM(RTRIM(par_name_of_id_parameter)) INTO par_name_of_id_parameter; + SELECT LTRIM(RTRIM(par_schedule_name)) INTO par_schedule_name; + SELECT 0 INTO var_sch_name_count; + + IF (par_schedule_name = '') + THEN + SELECT NULL INTO par_schedule_name; + END IF; + + IF ((par_schedule_name IS NULL) AND (par_schedule_id IS NULL)) OR ((par_schedule_name IS NOT NULL) AND (par_schedule_id IS NOT NULL)) + THEN /* Failure */ + RAISE 'Supply either % or % to identify the schedule.', par_name_of_id_parameter, par_name_of_name_parameter USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* Check schedule id */ + IF (par_schedule_id IS NOT NULL) + THEN + /* Look at all schedules */ + SELECT name + , owner_sid + , originating_server_id + INTO par_schedule_name + , par_owner_sid + , par_orig_server_id + FROM sys.sysschedules + WHERE (schedule_id = par_schedule_id); + + IF (par_schedule_name IS NULL) + THEN /* Failure */ + SELECT CAST (par_schedule_id AS VARCHAR(36)) + INTO var_schedule_id_as_char; + + RAISE 'The specified % ("%") does not exist.', 'schedule_id', var_schedule_id_as_char USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + ELSE + IF (par_schedule_name IS NOT NULL) + THEN + /* Check if the schedule name is ambiguous */ + IF (SELECT COUNT(*) FROM sys.sysschedules WHERE name = par_schedule_name) > 1 + THEN /* Failure */ + RAISE 'There are two or more sysschedules named "%". Specify % instead of % to uniquely identify the sysschedules.', par_job_name, par_name_of_id_parameter, par_name_of_name_parameter USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + /* The name is not ambiguous, so get the corresponding job_id (if the job exists) */ + SELECT schedule_id + , owner_sid + INTO par_schedule_id, par_owner_sid + FROM sys.sysschedules + WHERE (name = par_schedule_name); + + /* the view would take care of all the permissions issues. */ + IF (par_schedule_id IS NULL) + THEN /* Failure */ + RAISE 'The specified % ("%") does not exist.', 'par_schedule_name', par_schedule_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + END IF; + + /* Success */ + returncode := 0; + RETURN; +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_xml_preparedocument(IN XmlDocument TEXT,OUT DocHandle BIGINT) +AS +$BODY$ +DECLARE + XmlDocument$data XML; +BEGIN + /*Create temporary structure for xmldocument saving*/ + CREATE TEMPORARY SEQUENCE IF NOT EXISTS sys$seq_openmxl_id MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 INCREMENT BY 1 CACHE 5; + + CREATE TEMPORARY TABLE IF NOT EXISTS sys$openxml + (DocID BigInt NOT NULL DEFAULT NEXTVAL('sys$seq_openmxl_id'), + XmlData XML not NULL, + CONSTRAINT pk_sys$doc_id PRIMARY KEY(DocID) + ) ON COMMIT PRESERVE ROWS; + + IF xml_is_well_formed(XmlDocument) THEN + XmlDocument$data := XmlDocument::XML; + ELSE + RAISE EXCEPTION '%','The XML parse error occurred'; + END IF; + + INSERT INTO sys$openxml(XmlData) + VALUES (XmlDocument$data) + RETURNING DocID INTO DocHandle; +END; +$BODY$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_sp_xml_removedocument(IN DocHandle BIGINT) RETURNS VOID +AS +$BODY$ +DECLARE + lt_error_text TEXT := 'Could not find prepared statement with handle '||CASE + WHEN DocHandle IS NULL THEN 'null' + ELSE DocHandle::TEXT + END; +BEGIN + DELETE FROM sys$openxml t + WHERE t.DocID = DocHandle; + + IF NOT FOUND THEN + RAISE EXCEPTION '%', lt_error_text; + END IF; + + EXCEPTION + WHEN SQLSTATE '42P01' THEN + RAISE EXCEPTION '%',lt_error_text; +END; +$BODY$ +LANGUAGE plpgsql; + +/* *********************************************** +EXTENSION PACK function STRPOS3(x) +schema sys +**************************************************/ +create or replace function sys.babelfish_STRPOS3(p_str text, p_substr text, p_loc int)returns int +AS +$body$ +DECLARE + v_loc int := case when p_loc > 0 then p_loc else 1 end; + v_cnt int := length(p_str) - v_loc + 1; +BEGIN +/*************************************************************** +EXTENSION PACK function STRPOS3(x) +***************************************************************/ + if v_cnt > 0 then + return case when 0!= strpos(substr(p_str, v_loc, v_cnt), p_substr) + then strpos(substr(p_str, v_loc, v_cnt), p_substr) + v_loc - 1 + else strpos(substr(p_str, v_loc, v_cnt), p_substr) + end; + else + return 0; + end if; +END; +$body$ +language plpgsql; + +CREATE OR REPLACE FUNCTION sys.babelfish_tomsbit(in_str NUMERIC) +RETURNS SMALLINT +AS +$BODY$ +BEGIN + CASE + WHEN in_str < 0 OR in_str > 0 THEN RETURN 1; + ELSE RETURN 0; + END CASE; +END; +$BODY$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_tomsbit(in_str VARCHAR) +RETURNS SMALLINT +AS +$BODY$ +BEGIN + CASE + WHEN LOWER(in_str) = 'true' OR in_str = '1' THEN RETURN 1; + WHEN LOWER(in_str) = 'false' OR in_str = '0' THEN RETURN 0; + ELSE RETURN 0; + END CASE; +END; +$BODY$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_date_to_string(IN p_datatype TEXT, + IN p_dateval DATE, + IN p_style NUMERIC DEFAULT 20) +RETURNS TEXT +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_date_to_string(p_datatype, + p_dateval, + p_style); +EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_datetime_to_string(IN p_datatype TEXT, + IN p_src_datatype TEXT, + IN p_datetimeval TIMESTAMP WITHOUT TIME ZONE, + IN p_style NUMERIC DEFAULT -1) +RETURNS TEXT +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_datetime_to_string(p_datatype, + p_src_datatype, + p_datetimeval, + p_style); +EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_string_to_date(IN p_datestring TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS DATE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_string_to_date(p_datestring, + p_style); +EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_string_to_datetime(IN p_datatype TEXT, + IN p_datetimestring TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_string_to_datetime(p_datatype, + p_datetimestring , + p_style); +EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_string_to_time(IN p_datatype TEXT, + IN p_timestring TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIME WITHOUT TIME ZONE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_string_to_time(p_datatype, + p_timestring, + p_style); +EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_time_to_string(IN p_datatype TEXT, + IN p_src_datatype TEXT, + IN p_timeval TIME WITHOUT TIME ZONE, + IN p_style NUMERIC DEFAULT 25) +RETURNS TEXT +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_time_to_string(p_datatype, + p_src_datatype, + p_timeval, + p_style); +EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +-- convertion to date +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_date(IN arg TEXT, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS DATE +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_string_to_date(arg, p_style); + ELSE + RETURN sys.babelfish_conv_string_to_date(arg, p_style); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_date(IN arg anyelement, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS DATE +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_to_date(arg); + ELSE + RETURN CAST(arg AS DATE); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_to_date(IN arg anyelement) +RETURNS DATE +AS +$BODY$ +BEGIN + RETURN CAST(arg AS DATE); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +-- convertion to time +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_time(IN arg TEXT, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIME +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_string_to_time('TIME', arg, p_style); + ELSE + RETURN sys.babelfish_conv_string_to_time('TIME', arg, p_style); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_time(IN arg anyelement, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIME +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_to_time(arg); + ELSE + RETURN CAST(arg AS TIME); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_to_time(IN arg anyelement) +RETURNS TIME +AS +$BODY$ +BEGIN + RETURN CAST(arg AS TIME); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +-- convertion to datetime +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_datetime(IN arg TEXT, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIMESTAMP +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_string_to_datetime('DATETIME', arg, p_style); + ELSE + RETURN sys.babelfish_conv_string_to_datetime('DATETIME', arg, p_style); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_datetime(IN arg anyelement, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS TIMESTAMP +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_to_datetime(arg); + ELSE + RETURN CAST(arg AS TIMESTAMP); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_to_datetime(IN arg anyelement) +RETURNS TIMESTAMP +AS +$BODY$ +BEGIN + RETURN CAST(arg AS TIMESTAMP); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +-- convertion to varchar +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_varchar(IN typename TEXT, + IN arg TEXT, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS sys.VARCHAR +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_to_varchar(typename, arg, p_style); + ELSE + RETURN sys.babelfish_conv_to_varchar(typename, arg, p_style); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_helper_to_varchar(IN typename TEXT, + IN arg ANYELEMENT, + IN try BOOL, + IN p_style NUMERIC DEFAULT 0) +RETURNS sys.VARCHAR +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_conv_to_varchar(typename, arg, p_style); + ELSE + RETURN sys.babelfish_conv_to_varchar(typename, arg, p_style); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_to_varchar(IN typename TEXT, + IN arg TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS sys.VARCHAR +AS +$BODY$ +BEGIN + RETURN CAST(arg AS sys.VARCHAR); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_to_varchar(IN typename TEXT, + IN arg anyelement, + IN p_style NUMERIC DEFAULT 0) +RETURNS sys.VARCHAR +AS +$BODY$ +BEGIN + CASE pg_typeof(arg) + WHEN 'date'::regtype THEN + RETURN sys.babelfish_try_conv_date_to_string(typename, arg, p_style); + WHEN 'time'::regtype THEN + RETURN sys.babelfish_try_conv_time_to_string(typename, 'TIME', arg, p_style); + WHEN 'sys.datetime'::regtype THEN + RETURN sys.babelfish_try_conv_datetime_to_string(typename, 'DATETIME', arg::timestamp, p_style); + WHEN 'float'::regtype THEN + RETURN sys.babelfish_try_conv_float_to_string(typename, arg, p_style); + WHEN 'sys.money'::regtype THEN + RETURN sys.babelfish_try_conv_money_to_string(typename, arg::numeric(19,4)::pg_catalog.money, p_style); + ELSE + RETURN CAST(arg AS sys.VARCHAR); + END CASE; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_to_varchar(IN typename TEXT, + IN arg TEXT, + IN p_style NUMERIC DEFAULT 0) +RETURNS sys.VARCHAR +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_to_varchar(typename, arg, p_style); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_to_varchar(IN typename TEXT, + IN arg anyelement, + IN p_style NUMERIC DEFAULT 0) +RETURNS sys.VARCHAR +AS +$BODY$ +BEGIN + RETURN sys.babelfish_conv_to_varchar(typename, arg, p_style); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_parse_helper_to_date(IN arg TEXT, IN try BOOL, IN culture TEXT DEFAULT '') +RETURNS DATE +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_parse_to_date(arg, culture); + ELSE + RETURN sys.babelfish_parse_to_date(arg, culture); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_parse_helper_to_time(IN arg TEXT, IN try BOOL, IN culture TEXT DEFAULT '') +RETURNS TIME WITHOUT TIME ZONE +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_parse_to_time('TIME', arg, culture); + ELSE + RETURN sys.babelfish_parse_to_time('TIME', arg, culture); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_parse_helper_to_datetime(IN arg TEXT, IN try BOOL, IN culture TEXT DEFAULT '') +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +BEGIN + IF try THEN + RETURN sys.babelfish_try_parse_to_datetime('DATETIME', arg, culture); + ELSE + RETURN sys.babelfish_parse_to_datetime('DATETIME', arg, culture); + END IF; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_money_to_string(IN p_datatype TEXT, + IN p_moneyval PG_CATALOG.MONEY, + IN p_style NUMERIC DEFAULT 0) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_style SMALLINT; + v_format VARCHAR; + v_moneyval NUMERIC(19,4) := p_moneyval::NUMERIC(19,4); + v_moneysign NUMERIC(19,4) := sign(v_moneyval); + v_moneyabs NUMERIC(19,4) := abs(v_moneyval); + v_digits SMALLINT; + v_integral_digits SMALLINT; + v_decimal_digits SMALLINT; + v_res_length SMALLINT; + MASK_REGEXP CONSTANT VARCHAR := '^\s*(?:character varying)\s*\(\s*(\d+|MAX)\s*\)\s*$'; + v_result TEXT; +BEGIN + v_style := floor(p_style)::SMALLINT; + v_digits := length(v_moneyabs::TEXT); + v_decimal_digits := scale(v_moneyabs); + IF (v_decimal_digits > 0) THEN + v_integral_digits := v_digits - v_decimal_digits - 1; + ELSE + v_integral_digits := v_digits; + END IF; + IF (v_style = 0) THEN + v_format := (pow(10, v_integral_digits)-1)::TEXT || 'D99'; + v_result := to_char(v_moneyval, v_format); + ELSIF (v_style = 1) THEN + IF (v_moneysign::SMALLINT = 1) THEN + v_result := substring(p_moneyval::TEXT, 2); + ELSE + v_result := substring(p_moneyval::TEXT, 1, 1) || substring(p_moneyval::TEXT, 3); + END IF; + ELSIF (v_style = 2) THEN + v_format := (pow(10, v_integral_digits)-1)::TEXT || 'D9999'; + v_result := to_char(v_moneyval, v_format); + ELSE + RAISE invalid_parameter_value; + END IF; + v_res_length := substring(p_datatype, MASK_REGEXP)::SMALLINT; + IF v_res_length IS NULL THEN + RETURN v_result; + ELSE + RETURN rpad(v_result, v_res_length, ' '); + END IF; +EXCEPTION + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('%s is not a valid style number when converting from MONEY to a character string.', v_style), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_float_to_string(IN p_datatype TEXT, + IN p_floatval FLOAT, + IN p_style NUMERIC DEFAULT 0) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_style SMALLINT; + v_format VARCHAR; + v_floatval NUMERIC := abs(p_floatval); + v_digits SMALLINT; + v_integral_digits SMALLINT; + v_decimal_digits SMALLINT; + v_sign SMALLINT := sign(p_floatval); + v_result TEXT; + v_res_length SMALLINT; + MASK_REGEXP CONSTANT VARCHAR := '^\s*(?:character varying)\s*\(\s*(\d+|MAX)\s*\)\s*$'; +BEGIN + v_style := floor(p_style)::SMALLINT; + IF (v_style = 0) THEN + v_digits := length(v_floatval::NUMERIC::TEXT); + v_decimal_digits := scale(v_floatval); + IF (v_decimal_digits > 0) THEN + v_integral_digits := v_digits - v_decimal_digits - 1; + ELSE + v_integral_digits := v_digits; + END IF; + IF (v_floatval >= 999999.5) THEN + v_format := '9D99999EEEE'; + v_result := to_char(v_sign * ceiling(v_floatval), v_format); + v_result := to_char(substring(v_result, 1, 8)::NUMERIC, 'FM9D99999')::NUMERIC::TEXT || substring(v_result, 9); + ELSE + if (6 - v_integral_digits < v_decimal_digits) THEN + v_decimal_digits := 6 - v_integral_digits; + END IF; + v_format := (pow(10, v_integral_digits)-1)::TEXT || 'D'; + IF (v_decimal_digits > 0) THEN + v_format := v_format || (pow(10, v_decimal_digits)-1)::TEXT; + END IF; + v_result := to_char(p_floatval, v_format); + END IF; + ELSIF (v_style = 1) THEN + v_format := '9D9999999EEEE'; + v_result := to_char(p_floatval, v_format); + ELSIF (v_style = 2) THEN + v_format := '9D999999999999999EEEE'; + v_result := to_char(p_floatval, v_format); + ELSIF (v_style = 3) THEN + v_format := '9D9999999999999999EEEE'; + v_result := to_char(p_floatval, v_format); + ELSE + RAISE invalid_parameter_value; + END IF; + + v_res_length := substring(p_datatype, MASK_REGEXP)::SMALLINT; + IF v_res_length IS NULL THEN + RETURN v_result; + ELSE + RETURN rpad(v_result, v_res_length, ' '); + END IF; +EXCEPTION + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('%s is not a valid style number when converting from FLOAT to a character string.', v_style), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_parse_to_date(IN p_datestring TEXT, + IN p_culture TEXT DEFAULT NULL) +RETURNS DATE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_parse_to_date(p_datestring, p_culture); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_parse_to_datetime(IN p_datatype TEXT, + IN p_datetimestring TEXT, + IN p_culture TEXT DEFAULT '') +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_parse_to_datetime(p_datatype, p_datetimestring, p_culture); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_try_parse_to_time(IN p_datatype TEXT, + IN p_srctimestring TEXT, + IN p_culture TEXT DEFAULT '') +RETURNS TIME WITHOUT TIME ZONE +AS +$BODY$ +BEGIN + RETURN sys.babelfish_parse_to_time(p_datatype, p_srctimestring, p_culture); + EXCEPTION + WHEN OTHERS THEN + RETURN NULL; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_update_job ( + p_job integer, + p_error_message varchar +) +RETURNS void AS +$body$ +DECLARE + var_enabled smallint; + var_freq_type integer; + var_freq_interval integer; + var_freq_subday_type integer; + var_freq_subday_interval integer; + var_freq_relative_interval integer; + var_freq_recurrence_factor integer; + var_tmp_interval varchar(50); + var_job_id integer; + var_schedule_id integer; + var_job_step_id integer; + var_step_id integer; + var_step_name VARCHAR(128); +BEGIN + /* + var_job_step_id := p_job; + + SELECT jst.job_id, jsc.schedule_id, jst.step_name, jst.step_id + FROM sys.sysjobsteps jst + INNER JOIN sys.sysjobschedules jsc + ON jsc.job_id = jst.job_id + INTO var_job_id, var_schedule_id, var_step_name, var_step_id + WHERE jst.job_step_id = var_job_step_id; + */ + INSERT + INTO sys.sysjobhistory ( + job_id + , step_id + , step_name + , sql_message_id + , sql_severity + , message + , run_status + , run_date + , run_time + , run_duration + , operator_id_emailed + , operator_id_netsent + , operator_id_paged + , retries_attempted + , server) + VALUES ( + p_job + , 0 -- var_step_id + , ''--var_step_name + , 0 + , 0 + , p_error_message + , 0 + , now()::date + , now()::time + , 0 + , 0 + , 0 + , 0 + , 0 + , ''::character varying); + + -- PERFORM sys.babelfish_sp_set_next_run (var_job_id, var_schedule_id); + +END; +$body$ +LANGUAGE 'plpgsql'; + +CREATE OR REPLACE FUNCTION sys.babelfish_waitfor_delay(time_to_pass TEXT) +RETURNS void AS +$BODY$ + SELECT pg_sleep(EXTRACT(HOUR FROM $1::time)*60*60 + + EXTRACT(MINUTE FROM $1::time)*60 + + TRUNC(EXTRACT(SECOND FROM $1::time)) + + sys.babelfish_round_fractseconds( + ( + EXTRACT(MILLISECONDS FROM $1::time) + - TRUNC(EXTRACT(SECOND FROM $1::time)) * 1000 + )::numeric + )/1000::numeric); +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.babelfish_waitfor_delay(time_to_pass TIMESTAMP WITHOUT TIME ZONE) +RETURNS void AS +$BODY$ + SELECT pg_sleep(EXTRACT(HOUR FROM $1::time)*60*60 + + EXTRACT(MINUTE FROM $1::time)*60 + + TRUNC(EXTRACT(SECOND FROM $1::time)) + + sys.babelfish_round_fractseconds( + ( + EXTRACT(MILLISECONDS FROM $1::time) + - TRUNC(EXTRACT(SECOND FROM $1::time)) * 1000 + )::numeric + )/1000::numeric); +$BODY$ +LANGUAGE SQL; + +-- internal table function for sp_cursor_list and sp_decribe_cursor +CREATE OR REPLACE FUNCTION sys.babelfish_cursor_list(cursor_source integer) +RETURNS table ( + reference_name text, + cursor_name text, + cursor_scope smallint, + status smallint, + model smallint, + concurrency smallint, + scrollable smallint, + open_status smallint, + cursor_rows bigint, + fetch_status smallint, + column_count smallint, + row_count bigint, + last_operation smallint, + cursor_handle int, + cursor_source smallint +) AS 'babelfishpg_tsql', 'cursor_list' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.babelfish_get_datetimeoffset_tzoffset(SYS.DATETIMEOFFSET) +RETURNS SMALLINT +AS 'babelfishpg_common', 'get_datetimeoffset_tzoffset_internal' +LANGUAGE C IMMUTABLE STRICT; + +-- internal table function for querying the registered ENRs +CREATE OR REPLACE FUNCTION sys.babelfish_get_enr_list() +RETURNS table ( + reloid int, + relname text +) AS 'babelfishpg_tsql', 'get_enr_list' LANGUAGE C; + +-- internal table function for collation_list +CREATE OR REPLACE FUNCTION sys.babelfish_collation_list() +RETURNS table ( + oid int, + collation_name text, + l1_priority int, + l2_priority int, + l3_priority int, + l4_priority int, + l5_priority int +) AS 'babelfishpg_tsql', 'collation_list' LANGUAGE C; + +-- internal function to truncate long identifier +CREATE OR REPLACE FUNCTION sys.babelfish_truncate_identifier(IN object_name TEXT) +RETURNS text +AS 'babelfishpg_tsql', 'pltsql_truncate_identifier_func' LANGUAGE C IMMUTABLE STRICT; + +-- internal functions for debuggig/testing purpose +CREATE OR REPLACE FUNCTION sys.babelfish_pltsql_cursor_show_textptr_only_column_indexes(cursor_handle INT) +RETURNS text +AS 'babelfishpg_tsql', 'pltsql_cursor_show_textptr_only_column_indexes' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.babelfish_pltsql_get_last_cursor_handle() +RETURNS INT +AS 'babelfishpg_tsql', 'pltsql_get_last_cursor_handle' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.babelfish_pltsql_get_last_stmt_handle() +RETURNS INT +AS 'babelfishpg_tsql', 'pltsql_get_last_stmt_handle' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.get_babel_server_collation_oid() RETURNS OID +LANGUAGE C +AS 'babelfishpg_tsql', 'get_server_collation_oid'; diff --git a/contrib/babelfishpg_tsql/sql/sys_functions.sql b/contrib/babelfishpg_tsql/sql/sys_functions.sql new file mode 100644 index 00000000000..e2ae177b456 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_functions.sql @@ -0,0 +1,1594 @@ +-- Helper functions to support the FOR XML clause +CREATE OR REPLACE FUNCTION sys.tsql_query_to_xml(query text, mode int, element_name text, + binary_base64 boolean, root_name text) +RETURNS xml +AS 'babelfishpg_tsql', 'tsql_query_to_xml' +LANGUAGE C IMMUTABLE STRICT COST 100; + +CREATE OR REPLACE FUNCTION sys.tsql_query_to_xml_text(query text, mode int, element_name text, + binary_base64 boolean, root_name text) +RETURNS ntext +AS 'babelfishpg_tsql', 'tsql_query_to_xml_text' +LANGUAGE C IMMUTABLE STRICT COST 100; + +-- User and Login Functions +CREATE OR REPLACE FUNCTION sys.user_name(IN id OID DEFAULT NULL) +RETURNS sys.NVARCHAR(128) +AS 'babelfishpg_tsql', 'user_name' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.user_id(IN user_name TEXT DEFAULT NULL) +RETURNS OID +AS 'babelfishpg_tsql', 'user_id' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.suser_name(IN server_user_id OID DEFAULT NULL) +RETURNS sys.NVARCHAR(128) +AS 'babelfishpg_tsql', 'suser_name' +LANGUAGE C IMMUTABLE PARALLEL RESTRICTED; + +CREATE OR REPLACE FUNCTION sys.suser_id(IN login TEXT DEFAULT NULL) +RETURNS OID +AS 'babelfishpg_tsql', 'suser_id' +LANGUAGE C IMMUTABLE PARALLEL RESTRICTED; + +-- Matches and returns object name to Oid +CREATE OR REPLACE FUNCTION sys.OBJECT_NAME(IN object_id INT, IN database_id INT DEFAULT NULL) +RETURNS sys.SYSNAME AS +$BODY$ +DECLARE + object_name TEXT; + object_oid Oid; + cur_dat_id Oid; +BEGIN + IF database_id is not NULL THEN + SELECT Oid INTO cur_dat_id FROM pg_database WHERE datname = current_database(); + IF database_id::Oid != cur_dat_id THEN + RAISE EXCEPTION 'Can only do lookup in current database.'; + END IF; + END IF; + + SELECT CAST(object_id AS Oid) INTO object_oid; + + -- First check for tables, sequences, views, etc. + SELECT relname INTO object_name FROM pg_class WHERE Oid = object_oid; + IF object_name IS NOT NULL THEN + RETURN object_name::sys.SYSNAME; + END IF; + + -- Check ENR for any matches + SELECT relname INTO object_name FROM sys.babelfish_get_enr_list() WHERE reloid = object_oid; + IF object_name IS NOT NULL THEN + RETURN object_name::sys.SYSNAME; + END IF; + + -- Next check for functions + SELECT proname INTO object_name FROM pg_proc WHERE Oid = object_oid; + IF object_name IS NOT NULL THEN + RETURN object_name::sys.SYSNAME; + END IF; + + -- Next check for types + SELECT typname INTO object_name FROM pg_type WHERE Oid = object_oid; + IF object_name IS NOT NULL THEN + RETURN object_name::sys.SYSNAME; + END IF; + + -- Apparently SYSNAME cannot be null so returning empty string + RETURN ''; +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.scope_identity() +RETURNS numeric(38,0) AS +$BODY$ + SELECT sys.babelfish_get_last_identity_numeric()::numeric(38,0); +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.ident_seed(IN tablename TEXT) +RETURNS numeric(38,0) AS +$BODY$ + SELECT sys.babelfish_get_identity_param(tablename, 'start'::text)::numeric(38,0); +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.ident_incr(IN tablename TEXT) +RETURNS numeric(38,0) AS +$BODY$ + SELECT sys.babelfish_get_identity_param(tablename, 'increment'::text)::numeric(38,0); +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.ident_current(IN tablename TEXT) +RETURNS numeric(38,0) AS +$BODY$ + SELECT sys.babelfish_get_identity_current(tablename)::numeric(38,0); +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.checksum(IN _input TEXT) RETURNS INTEGER +AS +$BODY$ + SELECT ('x'||SUBSTR(MD5(_input),1,8))::pg_catalog.BIT(32)::INTEGER; +$BODY$ +LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datetime2fromparts(IN p_year NUMERIC, + IN p_month NUMERIC, + IN p_day NUMERIC, + IN p_hour NUMERIC, + IN p_minute NUMERIC, + IN p_seconds NUMERIC, + IN p_fractions NUMERIC, + IN p_precision NUMERIC) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_fractions VARCHAR; + v_precision SMALLINT; + v_err_message VARCHAR; + v_calc_seconds NUMERIC; +BEGIN + v_fractions := floor(p_fractions)::INTEGER::VARCHAR; + v_precision := p_precision::SMALLINT; + + IF (scale(p_precision) > 0) THEN + RAISE most_specific_type_mismatch; + ELSIF ((p_year::SMALLINT NOT BETWEEN 1 AND 9999) OR + (p_month::SMALLINT NOT BETWEEN 1 AND 12) OR + (p_day::SMALLINT NOT BETWEEN 1 AND 31) OR + (p_hour::SMALLINT NOT BETWEEN 0 AND 23) OR + (p_minute::SMALLINT NOT BETWEEN 0 AND 59) OR + (p_seconds::SMALLINT NOT BETWEEN 0 AND 59) OR + (p_fractions::SMALLINT NOT BETWEEN 0 AND 9999999) OR + (p_fractions::SMALLINT != 0 AND char_length(v_fractions) > p_precision)) + THEN + RAISE invalid_datetime_format; + ELSIF (v_precision NOT BETWEEN 0 AND 7) THEN + RAISE invalid_parameter_value; + END IF; + + v_calc_seconds := format('%s.%s', + floor(p_seconds)::SMALLINT, + substring(rpad(lpad(v_fractions, v_precision, '0'), 7, '0'), 1, 6))::NUMERIC; + + RETURN make_timestamp(floor(p_year)::SMALLINT, + floor(p_month)::SMALLINT, + floor(p_day)::SMALLINT, + floor(p_hour)::SMALLINT, + floor(p_minute)::SMALLINT, + v_calc_seconds); +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Scale argument is not valid. Valid expressions for data type DATETIME2 scale argument are integer constants and integer constant expressions.', + DETAIL := 'Use of incorrect "precision" parameter value during conversion process.', + HINT := 'Change "precision" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('Specified scale %s is invalid.', v_precision), + DETAIL := 'Use of incorrect "precision" parameter value during conversion process.', + HINT := 'Change "precision" parameter to the proper value and try again.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := 'Cannot construct data type DATETIME2, some of the arguments have values which are not valid.', + DETAIL := 'Possible use of incorrect value of date or time part (which lies outside of valid range).', + HINT := 'Check each input argument belongs to the valid range and try again.'; + + WHEN numeric_value_out_of_range THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := upper(split_part(v_err_message, ' ', 1)); + + RAISE USING MESSAGE := format('Error while trying to cast to %s data type.', v_err_message), + DETAIL := format('Source value is out of %s data type range.', v_err_message), + HINT := format('Correct the source value you are trying to cast to %s data type and try again.', + v_err_message); +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.datetime2fromparts(IN p_year TEXT, + IN p_month TEXT, + IN p_day TEXT, + IN p_hour TEXT, + IN p_minute TEXT, + IN p_seconds TEXT, + IN p_fractions TEXT, + IN p_precision TEXT) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_err_message VARCHAR; +BEGIN + RETURN sys.datetime2fromparts(p_year::NUMERIC, p_month::NUMERIC, p_day::NUMERIC, + p_hour::NUMERIC, p_minute::NUMERIC, p_seconds::NUMERIC, + p_fractions::NUMERIC, p_precision::NUMERIC); +EXCEPTION + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'numeric\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to NUMERIC data type.', v_err_message), + DETAIL := 'Supplied string value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.datetimefromparts(IN p_year NUMERIC, + IN p_month NUMERIC, + IN p_day NUMERIC, + IN p_hour NUMERIC, + IN p_minute NUMERIC, + IN p_seconds NUMERIC, + IN p_milliseconds NUMERIC) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_err_message VARCHAR; + v_calc_seconds NUMERIC; + v_milliseconds SMALLINT; + v_resdatetime TIMESTAMP WITHOUT TIME ZONE; +BEGIN + -- Check if arguments are out of range + IF ((floor(p_year)::SMALLINT NOT BETWEEN 1753 AND 9999) OR + (floor(p_month)::SMALLINT NOT BETWEEN 1 AND 12) OR + (floor(p_day)::SMALLINT NOT BETWEEN 1 AND 31) OR + (floor(p_hour)::SMALLINT NOT BETWEEN 0 AND 23) OR + (floor(p_minute)::SMALLINT NOT BETWEEN 0 AND 59) OR + (floor(p_seconds)::SMALLINT NOT BETWEEN 0 AND 59) OR + (floor(p_milliseconds)::SMALLINT NOT BETWEEN 0 AND 999)) + THEN + RAISE invalid_datetime_format; + END IF; + + v_milliseconds := sys.babelfish_round_fractseconds(p_milliseconds::INTEGER); + + v_calc_seconds := format('%s.%s', + floor(p_seconds)::SMALLINT, + CASE v_milliseconds + WHEN 1000 THEN '0' + ELSE lpad(v_milliseconds::VARCHAR, 3, '0') + END)::NUMERIC; + + v_resdatetime := make_timestamp(floor(p_year)::SMALLINT, + floor(p_month)::SMALLINT, + floor(p_day)::SMALLINT, + floor(p_hour)::SMALLINT, + floor(p_minute)::SMALLINT, + v_calc_seconds); + RETURN CASE + WHEN (v_milliseconds != 1000) THEN v_resdatetime + ELSE v_resdatetime + INTERVAL '1 second' + END; +EXCEPTION + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := 'Cannot construct data type datetime, some of the arguments have values which are not valid.', + DETAIL := 'Possible use of incorrect value of date or time part (which lies outside of valid range).', + HINT := 'Check each input argument belongs to the valid range and try again.'; + + WHEN numeric_value_out_of_range THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := upper(split_part(v_err_message, ' ', 1)); + + RAISE USING MESSAGE := format('Error while trying to cast to %s data type.', v_err_message), + DETAIL := format('Source value is out of %s data type range.', v_err_message), + HINT := format('Correct the source value you are trying to cast to %s data type and try again.', + v_err_message); +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.datetimefromparts(IN p_year TEXT, + IN p_month TEXT, + IN p_day TEXT, + IN p_hour TEXT, + IN p_minute TEXT, + IN p_seconds TEXT, + IN p_milliseconds TEXT) +RETURNS TIMESTAMP WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_err_message VARCHAR; +BEGIN + RETURN sys.datetimefromparts(p_year::NUMERIC, p_month::NUMERIC, p_day::NUMERIC, + p_hour::NUMERIC, p_minute::NUMERIC, + p_seconds::NUMERIC, p_milliseconds::NUMERIC); +EXCEPTION + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'numeric\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to NUMERIC data type.', v_err_message), + DETAIL := 'Supplied string value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +IMMUTABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.isnumeric(IN expr ANYELEMENT) RETURNS INTEGER AS +$BODY$ +DECLARE + x NUMERIC; + y MONEY; +BEGIN + IF (expr IS NULL) THEN + RETURN 0; + END IF; + IF ($1::VARCHAR ~ '^\s*$') THEN + RETURN 0; + END IF; + IF pg_typeof(expr) IN ('bigint'::regtype, 'int'::regtype, 'smallint'::regtype,'sys.tinyint'::regtype, + 'numeric'::regtype, 'float'::regtype, 'real'::regtype, 'sys.money'::regtype) + THEN + RETURN 1; + END IF; + x = $1::NUMERIC; + RETURN 1; +EXCEPTION WHEN others THEN + BEGIN + y = $1::sys.MONEY; + RETURN 1; + EXCEPTION WHEN others THEN + RETURN 0; + END; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE CALLED ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.isnumeric(IN expr TEXT) RETURNS INTEGER AS +$BODY$ +DECLARE + x NUMERIC; + y MONEY; +BEGIN + IF (expr IS NULL) THEN + RETURN 0; + END IF; + IF ($1::VARCHAR ~ '^\s*$') THEN + RETURN 0; + END IF; + IF pg_typeof(expr) IN ('bigint'::regtype, 'int'::regtype, 'smallint'::regtype,'sys.tinyint'::regtype, + 'numeric'::regtype, 'float'::regtype, 'real'::regtype, 'sys.money'::regtype) + THEN + RETURN 1; + END IF; + x = $1::NUMERIC; + RETURN 1; +EXCEPTION WHEN others THEN + BEGIN + y = $1::sys.MONEY; + RETURN 1; + EXCEPTION WHEN others THEN + RETURN 0; + END; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE CALLED ON NULL INPUT; + +-- Return the object ID given the object name. Can specify optional type. +CREATE OR REPLACE FUNCTION sys.object_id(IN object_name TEXT, IN object_type char(2) DEFAULT '') +RETURNS INTEGER AS +$BODY$ +DECLARE + id oid; + lower_object_name text; + names text[2]; + counter int; + cur_pos int; + db_name text; + input_schema_name text; + schema_name text; + schema_oid oid; + obj_name text; + is_temp_object boolean; +BEGIN + id = null; + lower_object_name = lower(trim(object_name)); + counter = 1; + cur_pos = position('.' in lower_object_name); + schema_oid = NULL; + + -- Parse user input into names split by '.' + WHILE cur_pos > 0 LOOP + IF counter > 3 THEN + -- Too many names provided + RETURN NULL; + END IF; + names[counter] = sys.babelfish_single_unbracket_name(left(lower_object_name, cur_pos - 1)); + lower_object_name = substring(lower_object_name from cur_pos + 1); + counter = counter + 1; + cur_pos = position('.' in lower_object_name); + END LOOP; + + -- Assign each name accordingly + obj_name = sys.babelfish_truncate_identifier(sys.babelfish_single_unbracket_name(lower_object_name)); + CASE counter + WHEN 1 THEN + db_name = NULL; + schema_name = NULL; + WHEN 2 THEN + db_name = NULL; + input_schema_name = sys.babelfish_truncate_identifier(names[1]); + schema_name = sys.bbf_get_current_physical_schema_name(input_schema_name); + WHEN 3 THEN + db_name = sys.babelfish_truncate_identifier(names[1]); + input_schema_name = sys.babelfish_truncate_identifier(names[2]); + schema_name = sys.bbf_get_current_physical_schema_name(input_schema_name); + ELSE + RETURN NULL; + END CASE; + + -- Check if looking for temp object. + is_temp_object = left(obj_name, 1) = '#'; + + -- Can only search in current database. Allowing tempdb for temp objects. + IF db_name IS NOT NULL AND db_name <> current_database() AND db_name <> 'tempdb' THEN + RAISE EXCEPTION 'Can only do lookup in current database.'; + END IF; + + IF schema_name IS NOT NULL AND schema_name <> '' THEN + -- Searching within a schema. Get schema oid. + schema_oid = (SELECT oid FROM pg_namespace WHERE nspname = schema_name); + IF schema_oid IS NULL THEN + RETURN NULL; + END IF; + + if object_type <> '' then + case + -- Schema does not apply as much to temp objects. + when upper(object_type) in ('S', 'U', 'V', 'IT', 'ET', 'SO') and is_temp_object then + id := (select reloid from sys.babelfish_get_enr_list() where lower(relname) = obj_name limit 1); + + when upper(object_type) in ('S', 'U', 'V', 'IT', 'ET', 'SO') and not is_temp_object then + id := (select oid from pg_class where lower(relname) = obj_name + and relnamespace = schema_oid limit 1); + + when upper(object_type) in ('C', 'D', 'F', 'PK', 'UQ') then + id := (select oid from pg_constraint where lower(conname) = obj_name + and connamespace = schema_oid limit 1); + + when upper(object_type) in ('AF', 'FN', 'FS', 'FT', 'IF', 'P', 'PC', 'TF', 'RF', 'X') then + id := (select oid from pg_proc where lower(proname) = obj_name + and pronamespace = schema_oid limit 1); + + when upper(object_type) in ('TR', 'TA') then + id := (select oid from pg_trigger where lower(tgname) = obj_name limit 1); + + -- Throwing exception as a reminder to add support in the future. + when upper(object_type) in ('R', 'EC', 'PG', 'SN', 'SQ', 'TT') then + RAISE EXCEPTION 'Object type currently unsupported.'; + + -- unsupported object_type + else id := null; + end case; + else + if not is_temp_object then id := ( + select oid from pg_class where lower(relname) = obj_name + and relnamespace = schema_oid + union + select oid from pg_constraint where lower(conname) = obj_name + and connamespace = schema_oid + union + select oid from pg_proc where lower(proname) = obj_name + and pronamespace = schema_oid + union + select oid from pg_trigger where lower(tgname) = obj_name + limit 1); + else + -- temp object without "object_type" in-argument + id := (select reloid from sys.babelfish_get_enr_list() where lower(relname) = obj_name limit 1); + end if; + end if; + ELSE + -- Schema not specified. + if object_type <> '' then + case + when upper(object_type) in ('S', 'U', 'V', 'IT', 'ET', 'SO') and is_temp_object then + id := (select reloid from sys.babelfish_get_enr_list() where lower(relname) = obj_name limit 1); + + when upper(object_type) in ('S', 'U', 'V', 'IT', 'ET', 'SO') and not is_temp_object then + id := (select oid from pg_class where lower(relname) = obj_name limit 1); + + when upper(object_type) in ('C', 'D', 'F', 'PK', 'UQ') then + id := (select oid from pg_constraint where lower(conname) = obj_name limit 1); + + when upper(object_type) in ('AF', 'FN', 'FS', 'FT', 'IF', 'P', 'PC', 'TF', 'RF', 'X') then + id := (select oid from pg_proc where lower(proname) = obj_name limit 1); + + when upper(object_type) in ('TR', 'TA') then + id := (select oid from pg_trigger where lower(tgname) = obj_name limit 1); + + -- Throwing exception as a reminder to add support in the future. + when upper(object_type) in ('R', 'EC', 'PG', 'SN', 'SQ', 'TT') then + RAISE EXCEPTION 'Object type currently unsupported.'; + + -- unsupported object_type + else id := null; + end case; + else + if not is_temp_object then id := ( + select oid from pg_class where lower(relname) = obj_name + union + select oid from pg_constraint where lower(conname) = obj_name + union + select oid from pg_proc where lower(proname) = obj_name + union + select oid from pg_trigger where lower(tgname) = obj_name + limit 1); + else + -- temp object without "object_type" in-argument + id := (select reloid from sys.babelfish_get_enr_list() where lower(relname) = obj_name limit 1); + end if; + end if; + END IF; + + RETURN id::integer; +END; +$BODY$ +LANGUAGE plpgsql STABLE RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.parsename ( + object_name VARCHAR + ,object_piece INT + ) +RETURNS VARCHAR AS $$ +/*************************************************************** +EXTENSION PACK function PARSENAME(x) +***************************************************************/ +SELECT CASE + WHEN char_length($1) < char_length(replace($1, '.', '')) + 4 + AND $2 BETWEEN 1 + AND 4 + THEN reverse(split_part(reverse($1), '.', $2)) + ELSE NULL + END $$ immutable LANGUAGE 'sql'; + +CREATE OR REPLACE FUNCTION sys.timefromparts(IN p_hour NUMERIC, + IN p_minute NUMERIC, + IN p_seconds NUMERIC, + IN p_fractions NUMERIC, + IN p_precision NUMERIC) +RETURNS TIME WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_fractions VARCHAR; + v_precision SMALLINT; + v_err_message VARCHAR; + v_calc_seconds NUMERIC; +BEGIN + v_fractions := floor(p_fractions)::INTEGER::VARCHAR; + v_precision := p_precision::SMALLINT; + + IF (scale(p_precision) > 0) THEN + RAISE most_specific_type_mismatch; + ELSIF ((p_hour::SMALLINT NOT BETWEEN 0 AND 23) OR + (p_minute::SMALLINT NOT BETWEEN 0 AND 59) OR + (p_seconds::SMALLINT NOT BETWEEN 0 AND 59) OR + (p_fractions::SMALLINT NOT BETWEEN 0 AND 9999999) OR + (p_fractions::SMALLINT != 0 AND char_length(v_fractions) > p_precision)) + THEN + RAISE invalid_datetime_format; + ELSIF (v_precision NOT BETWEEN 0 AND 7) THEN + RAISE numeric_value_out_of_range; + END IF; + + v_calc_seconds := format('%s.%s', + floor(p_seconds)::SMALLINT, + substring(rpad(lpad(v_fractions, v_precision, '0'), 7, '0'), 1, 6))::NUMERIC; + + RETURN make_time(floor(p_hour)::SMALLINT, + floor(p_minute)::SMALLINT, + v_calc_seconds); +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Scale argument is not valid. Valid expressions for data type DATETIME2 scale argument are integer constants and integer constant expressions.', + DETAIL := 'Use of incorrect "precision" parameter value during conversion process.', + HINT := 'Change "precision" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := format('Specified scale %s is invalid.', v_precision), + DETAIL := 'Use of incorrect "precision" parameter value during conversion process.', + HINT := 'Change "precision" parameter to the proper value and try again.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := 'Cannot construct data type time, some of the arguments have values which are not valid.', + DETAIL := 'Possible use of incorrect value of time part (which lies outside of valid range).', + HINT := 'Check each input argument belongs to the valid range and try again.'; + + WHEN numeric_value_out_of_range THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := upper(split_part(v_err_message, ' ', 1)); + + RAISE USING MESSAGE := format('Error while trying to cast to %s data type.', v_err_message), + DETAIL := format('Source value is out of %s data type range.', v_err_message), + HINT := format('Correct the source value you are trying to cast to %s data type and try again.', + v_err_message); +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.timefromparts(IN p_hour TEXT, + IN p_minute TEXT, + IN p_seconds TEXT, + IN p_fractions TEXT, + IN p_precision TEXT) +RETURNS TIME WITHOUT TIME ZONE +AS +$BODY$ +DECLARE + v_err_message VARCHAR; +BEGIN + RETURN sys.timefromparts(p_hour::NUMERIC, p_minute::NUMERIC, + p_seconds::NUMERIC, p_fractions::NUMERIC, + p_precision::NUMERIC); +EXCEPTION + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(lower(v_err_message), 'numeric\:\s\"(.*)\"'); + + RAISE USING MESSAGE := format('Error while trying to convert "%s" value to NUMERIC data type.', v_err_message), + DETAIL := 'Supplied string value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters and try again.'; +END; +$BODY$ +LANGUAGE plpgsql +VOLATILE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.has_dbaccess(database_name PG_CATALOG.TEXT) RETURNS INTEGER AS $$ +DECLARE has_access BOOLEAN; +BEGIN + has_access = has_database_privilege(database_name, 'CONNECT'); + IF has_access THEN + RETURN 1; + ELSE + RETURN 0; + END IF; +EXCEPTION WHEN others THEN + RETURN NULL; +END; +$$ +STRICT +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.is_srvrolemember(role PG_CATALOG.TEXT, login PG_CATALOG.TEXT DEFAULT CURRENT_USER) RETURNS INTEGER AS $$ +DECLARE has_role BOOLEAN; +BEGIN + has_role = pg_has_role(login, role, 'MEMBER'); + IF has_role THEN + return 1; + ELSE + RETURN 0; + END IF; +EXCEPTION WHEN others THEN + RETURN NULL; +END; +$$ +STRICT +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.datefromparts(IN year INT, IN month INT, IN day INT) +RETURNS DATE AS +$BODY$ +SELECT make_date(year, month, day); +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.charindex(expressionToFind PG_CATALOG.TEXT, + expressionToSearch PG_CATALOG.TEXT, + start_location INTEGER DEFAULT 0) +RETURNS INTEGER AS +$BODY$ +SELECT +CASE +WHEN start_location <= 0 THEN + strpos(expressionToSearch, expressionToFind) +ELSE + CASE + WHEN strpos(substr(expressionToSearch, start_location), expressionToFind) = 0 THEN + 0 + ELSE + strpos(substr(expressionToSearch, start_location), expressionToFind) + start_location - 1 + END +END; +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE; + +-- Duplicate functions with arg TEXT since ANYELEMNT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.stuff(expr TEXT, start INTEGER, length INTEGER, replace_expr TEXT) +RETURNS TEXT AS +$BODY$ +SELECT +CASE +WHEN start <= 0 or start > length(expr) or length < 0 THEN + NULL +WHEN replace_expr is NULL THEN + overlay (expr placing '' from start for length) +ELSE + overlay (expr placing replace_expr from start for length) +END; +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.stuff(expr ANYELEMENT, start INTEGER, length INTEGER, replace_expr ANYELEMENT) +RETURNS ANYELEMENT AS +$BODY$ +SELECT +CASE +WHEN start <= 0 or start > length(expr) or length < 0 THEN + NULL +WHEN replace_expr is NULL THEN + overlay (expr placing '' from start for length) +ELSE + overlay (expr placing replace_expr from start for length) +END; +$BODY$ +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.len(expr TEXT) RETURNS INTEGER AS +$BODY$ +SELECT length(trim(trailing from expr)); +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE; + +-- DATALENGTH +CREATE OR REPLACE FUNCTION sys.datalength(ANYELEMENT) RETURNS INTEGER +AS 'babelfishpg_tsql', 'datalength' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- provide both additional functions here to avoid implicit casting between string literals with/without N'' +CREATE OR REPLACE FUNCTION sys.datalength(text) RETURNS INTEGER +AS 'babelfishpg_tsql', 'datalength' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION sys.datalength(char) RETURNS INTEGER +AS 'babelfishpg_tsql', 'datalength' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +-- TODO: in MSSQL datalength against varchar(max) will return BIGINT instead of INTEGER. However in PG we ignore typmods in functions. +-- However this is not a critical issue so we will just leave it. We may come back to this difference later once we find out solution to typmods. + +CREATE OR REPLACE FUNCTION sys.round(number PG_CATALOG.NUMERIC, length INTEGER) +RETURNS NUMERIC AS 'babelfishpg_common', 'tsql_numeric_round' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.round(number PG_CATALOG.NUMERIC, length INTEGER, function INTEGER) +RETURNS NUMERIC AS 'babelfishpg_common', 'tsql_numeric_trunc' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.day(date ANYELEMENT) +RETURNS INTEGER AS +$BODY$ +SELECT sys.datepart('day', date); +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.month(date ANYELEMENT) +RETURNS INTEGER AS +$BODY$ +SELECT sys.datepart('month', date); +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.year(date ANYELEMENT) +RETURNS INTEGER AS +$BODY$ +SELECT sys.datepart('year', date); +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.space(IN number INTEGER, OUT result SYS.VARCHAR) AS $$ +-- sys.varchar has default length of 1, so we have to pass in 'number' to be the +-- type modifier. +BEGIN + EXECUTE format(E'SELECT repeat(\' \', %s)::SYS.VARCHAR(%s)', number, number) INTO result; +END; +$$ +STRICT +LANGUAGE plpgsql; + +create or replace function sys.isdate(v text) +returns integer +as +$body$ +begin + if v is NULL THEN + return 0; + else + perform v::date; + return 1; + end if; + EXCEPTION WHEN others THEN + RETURN 0; +end +$body$ +language 'plpgsql'; + +create or replace function sys.PATINDEX(in pattern character varying, in expression character varying) returns bigint as +$body$ +declare + v_find_result character varying; + v_pos bigint; + v_regexp_pattern character varying; +begin + v_pos := null; + if left(pattern, 1) = '%' then + v_regexp_pattern := regexp_replace(pattern, '^%', '%#"'); + else + v_regexp_pattern := '#"' || pattern; + end if; + + if right(pattern, 1) = '%' then + v_regexp_pattern := regexp_replace(v_regexp_pattern, '%$', '#"%'); + else + v_regexp_pattern := v_regexp_pattern || '#"'; + end if; + v_find_result := substring(expression from v_regexp_pattern for '#'); + if v_find_result <> '' then + v_pos := strpos(expression, v_find_result); + end if; + return v_pos; +end; +$body$ +language plpgsql returns null on null input; + +create or replace function sys.RAND(x in int)returns double precision +AS 'babelfishpg_tsql', 'tsql_random' +LANGUAGE C IMMUTABLE STRICT COST 1 PARALLEL RESTRICTED; + +CREATE OR REPLACE FUNCTION sys.datepart(IN datepart PG_CATALOG.TEXT, IN arg anyelement) RETURNS INTEGER +AS +$body$ +BEGIN + IF pg_typeof(arg) = 'sys.DATETIMEOFFSET'::regtype THEN + return sys.datepart_internal(datepart, arg::timestamp, + sys.babelfish_get_datetimeoffset_tzoffset(arg)::integer); + ELSE + return sys.datepart_internal(datepart, arg); + END IF; +END; +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff(IN datepart PG_CATALOG.TEXT, IN startdate PG_CATALOG.date, IN enddate PG_CATALOG.date) RETURNS INTEGER +AS +$body$ +BEGIN + return sys.datediff_internal(datepart, startdate, enddate); +END +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff(IN datepart PG_CATALOG.TEXT, IN startdate sys.datetime, IN enddate sys.datetime) RETURNS INTEGER +AS +$body$ +BEGIN + return sys.datediff_internal(datepart, startdate, enddate); +END +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff(IN datepart PG_CATALOG.TEXT, IN startdate sys.datetimeoffset, IN enddate sys.datetimeoffset) RETURNS INTEGER +AS +$body$ +BEGIN + return sys.datediff_internal_df(datepart, startdate, enddate); +END +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff(IN datepart PG_CATALOG.TEXT, IN startdate sys.datetime2, IN enddate sys.datetime2) RETURNS INTEGER +AS +$body$ +BEGIN + return sys.datediff_internal(datepart, startdate, enddate); +END +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff(IN datepart PG_CATALOG.TEXT, IN startdate sys.smalldatetime, IN enddate sys.smalldatetime) RETURNS INTEGER +AS +$body$ +BEGIN + return sys.datediff_internal(datepart, startdate, enddate); +END +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff(IN datepart PG_CATALOG.TEXT, IN startdate PG_CATALOG.time, IN enddate PG_CATALOG.time) RETURNS INTEGER +AS +$body$ +BEGIN + return sys.datediff_internal(datepart, startdate, enddate); +END +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.dateadd(IN datepart PG_CATALOG.TEXT, IN num INTEGER, IN startdate ANYELEMENT) RETURNS ANYELEMENT +AS +$body$ +BEGIN + IF pg_typeof(startdate) = 'sys.DATETIMEOFFSET'::regtype THEN + return sys.dateadd_internal_df(datepart, num, + startdate); + ELSE + return sys.dateadd_internal(datepart, num, + startdate); + END IF; +END; +$body$ +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datepart_internal(IN datepart PG_CATALOG.TEXT, IN arg anyelement,IN df_tz INTEGER DEFAULT 0) RETURNS INTEGER AS $$ +DECLARE + result INTEGER; + first_day DATE; + first_week_end INTEGER; + day INTEGER; +BEGIN + CASE datepart + WHEN 'dow' THEN + result = (date_part(datepart, arg)::INTEGER - current_setting('babelfishpg_tsql.datefirst')::INTEGER + 7) % 7 + 1; + WHEN 'tsql_week' THEN + first_day = make_date(date_part('year', arg)::INTEGER, 1, 1); + first_week_end = 8 - sys.datepart_internal('dow', first_day)::INTEGER; + day = date_part('doy', arg)::INTEGER; + IF day <= first_week_end THEN + result = 1; + ELSE + result = 2 + (day - first_week_end - 1) / 7; + END IF; + WHEN 'second' THEN + result = TRUNC(date_part(datepart, arg))::INTEGER; + WHEN 'millisecond' THEN + result = right(date_part(datepart, arg)::TEXT, 3)::INTEGER; + WHEN 'microsecond' THEN + result = right(date_part(datepart, arg)::TEXT, 6)::INTEGER; + WHEN 'nanosecond' THEN + -- Best we can do - Postgres does not support nanosecond precision + result = right(date_part('microsecond', arg)::TEXT, 6)::INTEGER * 1000; + WHEN 'tzoffset' THEN + -- timezone for datetimeoffset + result = df_tz; + ELSE + result = date_part(datepart, arg)::INTEGER; + END CASE; + RETURN result; +EXCEPTION WHEN invalid_parameter_value THEN + -- date_part() throws an exception when trying to get day/month/year etc. from + -- TIME, so we just need to catch the exception in this case + -- date_part() returns 0 when trying to get hour/minute/second etc. from + -- DATE, which is the desirable behavior for datepart() as well. + -- If the date argument data type does not have the specified datepart, + -- date_part() will return the default value for that datepart. + CASE datepart + -- Case for datepart is year, yy and yyyy, all mappings are defined in gram.y. + WHEN 'year' THEN RETURN 1900; + -- Case for datepart is quater, qq and q + WHEN 'quarter' THEN RETURN 1; + -- Case for datepart is month, mm and m + WHEN 'month' THEN RETURN 1; + -- Case for datepart is day, dd and d + WHEN 'day' THEN RETURN 1; + -- Case for datepart is dayofyear, dy + WHEN 'doy' THEN RETURN 1; + -- Case for datepart is y(also refers to dayofyear) + WHEN 'y' THEN RETURN 1; + -- Case for datepart is week, wk and ww + WHEN 'tsql_week' THEN RETURN 1; + -- Case for datepart is iso_week, isowk and isoww + WHEN 'week' THEN RETURN 1; + -- Case for datepart is tzoffset and tz + WHEN 'tzoffset' THEN RETURN 0; + -- Case for datepart is weekday and dw, return dow according to datefirst + WHEN 'dow' THEN + RETURN (1 - current_setting('babelfishpg_tsql.datefirst')::INTEGER + 7) % 7 + 1 ; + ELSE + RAISE EXCEPTION '''%'' is not a recognized datepart option', datepart; + RETURN -1; + END CASE; +END; +$$ +STRICT +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.dateadd_internal_df(IN datepart PG_CATALOG.TEXT, IN num INTEGER, IN startdate datetimeoffset) RETURNS datetimeoffset AS $$ +BEGIN + CASE datepart + WHEN 'year' THEN + RETURN startdate OPERATOR(sys.+) make_interval(years => num); + WHEN 'quarter' THEN + RETURN startdate OPERATOR(sys.+) make_interval(months => num * 3); + WHEN 'month' THEN + RETURN startdate OPERATOR(sys.+) make_interval(months => num); + WHEN 'dayofyear', 'y' THEN + RETURN startdate OPERATOR(sys.+) make_interval(days => num); + WHEN 'day' THEN + RETURN startdate OPERATOR(sys.+) make_interval(days => num); + WHEN 'week' THEN + RETURN startdate OPERATOR(sys.+) make_interval(weeks => num); + WHEN 'weekday' THEN + RETURN startdate OPERATOR(sys.+) make_interval(days => num); + WHEN 'hour' THEN + RETURN startdate OPERATOR(sys.+) make_interval(hours => num); + WHEN 'minute' THEN + RETURN startdate OPERATOR(sys.+) make_interval(mins => num); + WHEN 'second' THEN + RETURN startdate OPERATOR(sys.+) make_interval(secs => num); + WHEN 'millisecond' THEN + RETURN startdate OPERATOR(sys.+) make_interval(secs => num * 0.001); + WHEN 'microsecond' THEN + RETURN startdate OPERATOR(sys.+) make_interval(secs => num * 0.000001); + WHEN 'nanosecond' THEN + -- Best we can do - Postgres does not support nanosecond precision + RETURN startdate; + ELSE + RAISE EXCEPTION '"%" is not a recognized dateadd option.', datepart; + END CASE; +END; +$$ +STRICT +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.dateadd_internal(IN datepart PG_CATALOG.TEXT, IN num INTEGER, IN startdate ANYELEMENT) RETURNS ANYELEMENT AS $$ +BEGIN + IF pg_typeof(startdate) = 'date'::regtype AND + datepart IN ('hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond') THEN + RAISE EXCEPTION 'The datepart % is not supported by date function dateadd for data type date.', datepart; + END IF; + IF pg_typeof(startdate) = 'time'::regtype AND + datepart IN ('year', 'quarter', 'month', 'doy', 'day', 'week', 'weekday') THEN + RAISE EXCEPTION 'The datepart % is not supported by date function dateadd for data type time.', datepart; + END IF; + + CASE datepart + WHEN 'year' THEN + RETURN startdate + make_interval(years => num); + WHEN 'quarter' THEN + RETURN startdate + make_interval(months => num * 3); + WHEN 'month' THEN + RETURN startdate + make_interval(months => num); + WHEN 'dayofyear', 'y' THEN + RETURN startdate + make_interval(days => num); + WHEN 'day' THEN + RETURN startdate + make_interval(days => num); + WHEN 'week' THEN + RETURN startdate + make_interval(weeks => num); + WHEN 'weekday' THEN + RETURN startdate + make_interval(days => num); + WHEN 'hour' THEN + RETURN startdate + make_interval(hours => num); + WHEN 'minute' THEN + RETURN startdate + make_interval(mins => num); + WHEN 'second' THEN + RETURN startdate + make_interval(secs => num); + WHEN 'millisecond' THEN + RETURN startdate + make_interval(secs => num * 0.001); + WHEN 'microsecond' THEN + RETURN startdate + make_interval(secs => num * 0.000001); + WHEN 'nanosecond' THEN + -- Best we can do - Postgres does not support nanosecond precision + RETURN startdate; + ELSE + RAISE EXCEPTION '"%" is not a recognized dateadd option.', datepart; + END CASE; +END; +$$ +STRICT +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff_internal_df(IN datepart PG_CATALOG.TEXT, IN startdate anyelement, IN enddate anyelement) RETURNS INTEGER AS $$ +DECLARE + result INTEGER; + year_diff INTEGER; + month_diff INTEGER; + day_diff INTEGER; + hour_diff INTEGER; + minute_diff INTEGER; + second_diff INTEGER; + millisecond_diff INTEGER; + microsecond_diff INTEGER; +BEGIN + CASE datepart + WHEN 'year' THEN + year_diff = sys.datepart('year', enddate) - sys.datepart('year', startdate); + result = year_diff; + WHEN 'quarter' THEN + year_diff = sys.datepart('year', enddate) - sys.datepart('year', startdate); + month_diff = sys.datepart('month', enddate) - sys.datepart('month', startdate); + result = (year_diff * 12 + month_diff) / 3; + WHEN 'month' THEN + year_diff = sys.datepart('year', enddate) - sys.datepart('year', startdate); + month_diff = sys.datepart('month', enddate) - sys.datepart('month', startdate); + result = year_diff * 12 + month_diff; + WHEN 'doy', 'y' THEN + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + result = day_diff; + WHEN 'day' THEN + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + result = day_diff; + WHEN 'week' THEN + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + result = day_diff / 7; + WHEN 'hour' THEN + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + hour_diff = sys.datepart('hour', enddate OPERATOR(sys.-) startdate); + result = day_diff * 24 + hour_diff; + WHEN 'minute' THEN + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + hour_diff = sys.datepart('hour', enddate OPERATOR(sys.-) startdate); + minute_diff = sys.datepart('minute', enddate OPERATOR(sys.-) startdate); + result = (day_diff * 24 + hour_diff) * 60 + minute_diff; + WHEN 'second' THEN + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + hour_diff = sys.datepart('hour', enddate OPERATOR(sys.-) startdate); + minute_diff = sys.datepart('minute', enddate OPERATOR(sys.-) startdate); + second_diff = TRUNC(sys.datepart('second', enddate OPERATOR(sys.-) startdate)); + result = ((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60 + second_diff; + WHEN 'millisecond' THEN + -- millisecond result from date_part by default contains second value, + -- so we don't need to add second_diff again + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + hour_diff = sys.datepart('hour', enddate OPERATOR(sys.-) startdate); + minute_diff = sys.datepart('minute', enddate OPERATOR(sys.-) startdate); + second_diff = TRUNC(sys.datepart('second', enddate OPERATOR(sys.-) startdate)); + millisecond_diff = TRUNC(sys.datepart('millisecond', enddate OPERATOR(sys.-) startdate)); + result = (((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60) * 1000 + millisecond_diff; + WHEN 'microsecond' THEN + -- microsecond result from date_part by default contains second and millisecond values, + -- so we don't need to add second_diff and millisecond_diff again + day_diff = sys.datepart('day', enddate OPERATOR(sys.-) startdate); + hour_diff = sys.datepart('hour', enddate OPERATOR(sys.-) startdate); + minute_diff = sys.datepart('minute', enddate OPERATOR(sys.-) startdate); + second_diff = TRUNC(sys.datepart('second', enddate OPERATOR(sys.-) startdate)); + millisecond_diff = TRUNC(sys.datepart('millisecond', enddate OPERATOR(sys.-) startdate)); + microsecond_diff = TRUNC(sys.datepart('microsecond', enddate OPERATOR(sys.-) startdate)); + result = ((((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60) * 1000) * 1000 + microsecond_diff; + WHEN 'nanosecond' THEN + -- Best we can do - Postgres does not support nanosecond precision + day_diff = sys.datepart('day', enddate - startdate); + hour_diff = sys.datepart('hour', enddate OPERATOR(sys.-) startdate); + minute_diff = sys.datepart('minute', enddate OPERATOR(sys.-) startdate); + second_diff = TRUNC(sys.datepart('second', enddate OPERATOR(sys.-) startdate)); + millisecond_diff = TRUNC(sys.datepart('millisecond', enddate OPERATOR(sys.-) startdate)); + microsecond_diff = TRUNC(sys.datepart('microsecond', enddate OPERATOR(sys.-) startdate)); + result = (((((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60) * 1000) * 1000 + microsecond_diff) * 1000; + ELSE + RAISE EXCEPTION '"%" is not a recognized datediff option.', datepart; + END CASE; + + return result; +END; +$$ +STRICT +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datediff_internal(IN datepart PG_CATALOG.TEXT, IN startdate anyelement, IN enddate anyelement) RETURNS INTEGER AS $$ +DECLARE + result INTEGER; + year_diff INTEGER; + month_diff INTEGER; + day_diff INTEGER; + hour_diff INTEGER; + minute_diff INTEGER; + second_diff INTEGER; + millisecond_diff INTEGER; + microsecond_diff INTEGER; +BEGIN + CASE datepart + WHEN 'year' THEN + year_diff = date_part('year', enddate)::INTEGER - date_part('year', startdate)::INTEGER; + result = year_diff; + WHEN 'quarter' THEN + year_diff = date_part('year', enddate)::INTEGER - date_part('year', startdate)::INTEGER; + month_diff = date_part('month', enddate)::INTEGER - date_part('month', startdate)::INTEGER; + result = (year_diff * 12 + month_diff) / 3; + WHEN 'month' THEN + year_diff = date_part('year', enddate)::INTEGER - date_part('year', startdate)::INTEGER; + month_diff = date_part('month', enddate)::INTEGER - date_part('month', startdate)::INTEGER; + result = year_diff * 12 + month_diff; + WHEN 'doy', 'y' THEN + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + result = day_diff; + WHEN 'day' THEN + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + result = day_diff; + WHEN 'week' THEN + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + result = day_diff / 7; + WHEN 'hour' THEN + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + hour_diff = date_part('hour', enddate OPERATOR(sys.-) startdate)::INTEGER; + result = day_diff * 24 + hour_diff; + WHEN 'minute' THEN + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + hour_diff = date_part('hour', enddate OPERATOR(sys.-) startdate)::INTEGER; + minute_diff = date_part('minute', enddate OPERATOR(sys.-) startdate)::INTEGER; + result = (day_diff * 24 + hour_diff) * 60 + minute_diff; + WHEN 'second' THEN + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + hour_diff = date_part('hour', enddate OPERATOR(sys.-) startdate)::INTEGER; + minute_diff = date_part('minute', enddate OPERATOR(sys.-) startdate)::INTEGER; + second_diff = TRUNC(date_part('second', enddate OPERATOR(sys.-) startdate)); + result = ((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60 + second_diff; + WHEN 'millisecond' THEN + -- millisecond result from date_part by default contains second value, + -- so we don't need to add second_diff again + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + hour_diff = date_part('hour', enddate OPERATOR(sys.-) startdate)::INTEGER; + minute_diff = date_part('minute', enddate OPERATOR(sys.-) startdate)::INTEGER; + second_diff = TRUNC(date_part('second', enddate OPERATOR(sys.-) startdate)); + millisecond_diff = TRUNC(date_part('millisecond', enddate OPERATOR(sys.-) startdate)); + result = (((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60) * 1000 + millisecond_diff; + WHEN 'microsecond' THEN + -- microsecond result from date_part by default contains second and millisecond values, + -- so we don't need to add second_diff and millisecond_diff again + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + hour_diff = date_part('hour', enddate OPERATOR(sys.-) startdate)::INTEGER; + minute_diff = date_part('minute', enddate OPERATOR(sys.-) startdate)::INTEGER; + second_diff = TRUNC(date_part('second', enddate OPERATOR(sys.-) startdate)); + millisecond_diff = TRUNC(date_part('millisecond', enddate OPERATOR(sys.-) startdate)); + microsecond_diff = TRUNC(date_part('microsecond', enddate OPERATOR(sys.-) startdate)); + result = ((((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60) * 1000) * 1000 + microsecond_diff; + WHEN 'nanosecond' THEN + -- Best we can do - Postgres does not support nanosecond precision + day_diff = date_part('day', enddate OPERATOR(sys.-) startdate)::INTEGER; + hour_diff = date_part('hour', enddate OPERATOR(sys.-) startdate)::INTEGER; + minute_diff = date_part('minute', enddate OPERATOR(sys.-) startdate)::INTEGER; + second_diff = TRUNC(date_part('second', enddate OPERATOR(sys.-) startdate)); + millisecond_diff = TRUNC(date_part('millisecond', enddate OPERATOR(sys.-) startdate)); + microsecond_diff = TRUNC(date_part('microsecond', enddate OPERATOR(sys.-) startdate)); + result = (((((day_diff * 24 + hour_diff) * 60 + minute_diff) * 60) * 1000) * 1000 + microsecond_diff) * 1000; + ELSE + RAISE EXCEPTION '"%" is not a recognized datediff option.', datepart; + END CASE; + + return result; +END; +$$ +STRICT +LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.datename(IN dp PG_CATALOG.TEXT, IN arg anyelement) RETURNS TEXT AS +$BODY$ +SELECT + CASE + WHEN dp = 'month'::text THEN + to_char(arg::date, 'TMMonth') + -- '1969-12-28' is a Sunday + WHEN dp = 'dow'::text THEN + to_char(arg::date, 'TMDay') + ELSE + sys.datepart(dp, arg)::TEXT + END +$BODY$ +STRICT +LANGUAGE sql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.GETUTCDATE() RETURNS sys.DATETIME AS +$BODY$ +SELECT CAST(CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS sys.DATETIME); +$BODY$ +LANGUAGE SQL PARALLEL SAFE; + +-- These come from the built-in pg_catalog.count in pg_aggregate.dat +CREATE AGGREGATE sys.count(*) +( + sfunc = int8inc, + combinefunc = int8pl, + msfunc = int8inc, + minvfunc = int8dec, + stype = int8, + mstype = int8, + initcond = 0, + minitcond = 0, + finalfunc = int4, + mfinalfunc = int4, + parallel = safe +); + +CREATE AGGREGATE sys.count("any") +( + sfunc = int8inc_any, + combinefunc = int8pl, + msfunc = int8inc_any, + minvfunc = int8dec_any, + stype = int8, + mstype = int8, + initcond = 0, + minitcond = 0, + finalfunc = int4, + mfinalfunc = int4, + parallel = safe +); + +CREATE AGGREGATE sys.count_big(*) +( + sfunc = int8inc, + combinefunc = int8pl, + msfunc = int8inc, + minvfunc = int8dec, + stype = int8, + mstype = int8, + initcond = 0, + minitcond = 0, + parallel = safe +); + +CREATE AGGREGATE sys.count_big("any") +( + sfunc = int8inc_any, + combinefunc = int8pl, + msfunc = int8inc_any, + minvfunc = int8dec_any, + stype = int8, + mstype = int8, + initcond = 0, + minitcond = 0, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.REPLICATE(string TEXT, number INTEGER) +RETURNS VARCHAR AS +$BODY$ +SELECT + CASE + WHEN number >= 0 THEN repeat(string, number) + ELSE null + END; +$BODY$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +-- @@ functions +CREATE OR REPLACE FUNCTION sys.rowcount() +RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.error() + RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.pgerror() + RETURNS VARCHAR AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.trancount() + RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.datefirst() + RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.options() + RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.version() + RETURNS sys.NVARCHAR(255) AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.servername() + RETURNS sys.NVARCHAR(128) AS 'babelfishpg_tsql' LANGUAGE C; + +-- In tsql @@max_precision represents max precision that server supports +-- As of now, we do not support change in max_precision. So, returning default value +CREATE OR REPLACE FUNCTION sys.max_precision() +RETURNS sys.TINYINT AS +$$ +BEGIN + RETURN 38; +END; +$$ +LANGUAGE plpgsql; + +-- not supported, only syntax support +CREATE OR REPLACE FUNCTION sys.PROCID() + RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.spid() +RETURNS INTEGER AS +$BODY$ +SELECT pg_backend_pid(); +$BODY$ +STRICT +LANGUAGE SQL; + +CREATE OR REPLACE FUNCTION sys.nestlevel() RETURNS INTEGER AS +$$ +DECLARE + stack text; + result integer; +BEGIN + GET DIAGNOSTICS stack = PG_CONTEXT; + result := array_length(string_to_array(stack, 'function'), 1) - 2; + IF result < 0 THEN + RAISE EXCEPTION 'Invalid output, check stack trace %', stack; + ELSE + RETURN result; + END IF; +END; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.fetch_status() +RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.cursor_rows() +RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.cursor_status(text, text) +RETURNS INT AS 'babelfishpg_tsql' LANGUAGE C; + +-- Floor for bit +CREATE OR REPLACE FUNCTION sys.floor(sys.bit) RETURNS DOUBLE PRECISION +AS 'babelfishpg_tsql', 'bit_floor' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- Floor overloading for all int types +CREATE OR REPLACE FUNCTION sys.floor(bigint) RETURNS BIGINT +AS 'babelfishpg_tsql', 'int_floor' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.floor(int) RETURNS INT +AS 'babelfishpg_tsql', 'int_floor' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.floor(smallint) RETURNS SMALLINT +AS 'babelfishpg_tsql', 'int_floor' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.floor(tinyint) RETURNS TINYINT +AS 'babelfishpg_tsql', 'int_floor' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- Ceiling for bit +CREATE OR REPLACE FUNCTION sys.ceiling(sys.bit) RETURNS DOUBLE PRECISION +AS 'babelfishpg_tsql', 'bit_ceiling' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- Ceiling overloading for all int types +CREATE OR REPLACE FUNCTION sys.ceiling(bigint) RETURNS BIGINT +AS 'babelfishpg_tsql', 'int_ceiling' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.ceiling(int) RETURNS INT +AS 'babelfishpg_tsql', 'int_ceiling' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.ceiling(smallint) RETURNS SMALLINT +AS 'babelfishpg_tsql', 'int_ceiling' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.ceiling(tinyint) RETURNS TINYINT +AS 'babelfishpg_tsql', 'int_ceiling' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.microsoftversion() +RETURNS INTEGER AS +$BODY$ + SELECT NULL::INTEGER; +$BODY$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION sys.APPLOCK_MODE(IN "@dbprincipal" varchar(32), + IN "@resource" varchar(255), + IN "@lockowner" varchar(32) DEFAULT 'TRANSACTION') +RETURNS TEXT +AS 'babelfishpg_tsql', 'APPLOCK_MODE' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.APPLOCK_TEST(IN "@dbprincipal" varchar(32), + IN "@resource" varchar(255), + IN "@lockmode" varchar(32), + IN "@lockowner" varchar(32) DEFAULT 'TRANSACTION') +RETURNS SMALLINT +AS 'babelfishpg_tsql', 'APPLOCK_TEST' LANGUAGE C; + +-- Error handling functions +CREATE OR REPLACE FUNCTION sys.xact_state() +RETURNS SMALLINT +AS 'babelfishpg_tsql', 'xact_state' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.error_line() +RETURNS INT +AS 'babelfishpg_tsql', 'pltsql_error_line' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.error_message() +RETURNS sys.NVARCHAR(4000) +AS 'babelfishpg_tsql', 'pltsql_error_message' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.error_number() +RETURNS INT +AS 'babelfishpg_tsql', 'pltsql_error_number' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.error_procedure() +RETURNS sys.NVARCHAR(128) +AS 'babelfishpg_tsql', 'pltsql_error_procedure' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.error_severity() +RETURNS INT +AS 'babelfishpg_tsql', 'pltsql_error_severity' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.error_state() +RETURNS INT +AS 'babelfishpg_tsql', 'pltsql_error_state' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.rand() RETURNS FLOAT AS +$$ + SELECT random(); +$$ +LANGUAGE SQL VOLATILE STRICT PARALLEL RESTRICTED; + +CREATE OR REPLACE FUNCTION sys.DEFAULT_DOMAIN() +RETURNS TEXT +AS 'babelfishpg_tsql', 'default_domain' LANGUAGE C; + +CREATE OR REPLACE FUNCTION sys.db_id(sys.nvarchar(128)) RETURNS SMALLINT +AS 'babelfishpg_tsql', 'babelfish_db_id' +LANGUAGE C PARALLEL SAFE IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.db_id() RETURNS SMALLINT +AS 'babelfishpg_tsql', 'babelfish_db_id' +LANGUAGE C PARALLEL SAFE IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.db_name(int) RETURNS sys.nvarchar(128) +AS 'babelfishpg_tsql', 'babelfish_db_name' +LANGUAGE C PARALLEL SAFE IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.db_name() RETURNS sys.nvarchar(128) +AS 'babelfishpg_tsql', 'babelfish_db_name' +LANGUAGE C PARALLEL SAFE IMMUTABLE; + +-- BABEL-1783: (partial) support for sys.fn_listextendedproperty +create table if not exists sys.extended_properties ( +class sys.tinyint, +class_desc sys.nvarchar(60), +major_id int, +minor_id int, +name sys.sysname, +value sys.sql_variant +); +GRANT SELECT ON sys.extended_properties TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.fn_listextendedproperty ( +property_name varchar(128), +level0_object_type varchar(128), +level0_object_name varchar(128), +level1_object_type varchar(128), +level1_object_name varchar(128), +level2_object_type varchar(128), +level2_object_name varchar(128) +) +returns table ( +objtype sys.sysname, +objname sys.sysname, +name sys.sysname, +value sys.sql_variant +) +as $$ +begin +-- currently only support COLUMN property +IF (((SELECT coalesce(property_name, '')) = '') or + ((SELECT coalesce(property_name, '')) = 'COLUMN')) THEN + IF (((SELECT coalesce(level0_object_type, '')) = 'schema') and + ((SELECT coalesce(level1_object_type, '')) = 'table') and + ((SELECT coalesce(level2_object_type, '')) = 'column')) THEN + RETURN query + select CAST('COLUMN' AS sys.sysname) as objtype, + CAST(t3.column_name AS sys.sysname) as objname, + t1.name as name, + t1.value as value + from sys.extended_properties t1, pg_catalog.pg_class t2, information_schema.columns t3 + where t1.major_id = t2.oid and + t2.relname = t3.table_name and + t2.relname = (SELECT coalesce(level1_object_name, '')) and + t3.column_name = (SELECT coalesce(level2_object_name, '')); + END IF; +END IF; +RETURN; +end; +$$ +LANGUAGE plpgsql; +GRANT EXECUTE ON FUNCTION sys.fn_listextendedproperty( + varchar(128), varchar(128), varchar(128), varchar(128), varchar(128), varchar(128), varchar(128) +) TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/sys_languages.sql b/contrib/babelfishpg_tsql/sql/sys_languages.sql new file mode 100644 index 00000000000..6fcdfbcb5f5 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_languages.sql @@ -0,0 +1,2025 @@ +/* Tsql DMLs*/ +INSERT INTO sys.syslanguages + VALUES (1, + 'ENGLISH', + 'ENGLISH (AUSTRALIA)', + NULL, + NULL, + 'AUSTRALIA', + 'EN-AU', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (2, + 'ENGLISH', + 'ENGLISH (BELGIUM)', + NULL, + NULL, + 'BELGIUM', + 'EN-BE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (3, + 'ENGLISH', + 'ENGLISH (BELIZE)', + NULL, + NULL, + 'BELIZE', + 'EN-BZ', + jsonb_build_object('date_format', 'MDY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (4, + 'ENGLISH', + 'ENGLISH (BOTSWANA)', + NULL, + NULL, + 'BOTSWANA', + 'EN-BW', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (5, + 'ENGLISH', + 'ENGLISH (CAMEROON)', + NULL, + NULL, + 'CAMEROON', + 'EN-CM', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (6, + 'ENGLISH', + 'ENGLISH (CANADA)', + NULL, + NULL, + 'CANADA', + 'EN-CA', + jsonb_build_object('date_format', 'YMD', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (7, + 'ENGLISH', + 'ENGLISH (ERITREA)', + NULL, + NULL, + 'ERITREA', + 'EN-ER', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (8, + 'ENGLISH', + 'ENGLISH (INDIA)', + NULL, + NULL, + 'INDIA', + 'EN-IN', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (9, + 'ENGLISH', + 'ENGLISH (IRELAND)', + NULL, + NULL, + 'IRELAND', + 'EN-IE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (10, + 'ENGLISH', + 'ENGLISH (JAMAICA)', + NULL, + NULL, + 'JAMAICA', + 'EN-IM', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (11, + 'ENGLISH', + 'ENGLISH (KENYA)', + NULL, + NULL, + 'KENYA', + 'EN-KE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (12, + 'ENGLISH', + 'ENGLISH (MALAYSIA)', + NULL, + NULL, + 'MALAYSIA', + 'EN-MY', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (13, + 'ENGLISH', + 'ENGLISH (MALTA)', + NULL, + NULL, + 'MALTA', + 'EN-MT', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (14, + 'ENGLISH', + 'ENGLISH (NEW ZEALAND)', + NULL, + NULL, + 'NEW ZEALAND', + 'EN-NZ', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (15, + 'ENGLISH', + 'ENGLISH (NIGERIA)', + NULL, + NULL, + 'NIGERIA', + 'EN-NG', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (16, + 'ENGLISH', + 'ENGLISH (PAKISTAN)', + NULL, + NULL, + 'PAKISTAN', + 'EN-PK', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (17, + 'ENGLISH', + 'ENGLISH (PHILIPPINES)', + NULL, + NULL, + 'PHILIPPINES', + 'EN-PH', + jsonb_build_object('date_format', 'MDY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (18, + 'ENGLISH', + 'ENGLISH (PUERTO RICO)', + NULL, + NULL, + 'PUERTO RICO', + 'EN-PR', + jsonb_build_object('date_format', 'MDY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (19, + 'ENGLISH', + 'ENGLISH (SINGAPORE)', + NULL, + NULL, + 'SINGAPORE', + 'EN-SG', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (20, + 'ENGLISH', + 'ENGLISH (SOUTH AFRICA)', + NULL, + NULL, + 'SOUTH AFRICA', + 'EN-ZA', + jsonb_build_object('date_format', 'YMD', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (21, + 'ENGLISH', + 'ENGLISH (TRINIDAD & TOBAGO)', + NULL, + NULL, + 'TRINIDAD & TOBAGO', + 'EN-TT', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (22, + 'ENGLISH', + 'ENGLISH (GREAT BRITAIN)', + 'BRITISH', + 'BRITISH ENGLISH', + 'GREAT BRITAIN', + 'EN-GB', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (23, + 'ENGLISH', + 'ENGLISH (UNITED KINGDOM)', + NULL, + NULL, + 'UNITED KINGDOM', + 'EN-UK', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (24, + 'ENGLISH', + 'ENGLISH (ENGLAND)', + NULL, + NULL, + 'ENGLAND', + 'EN-EN', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (25, + 'ENGLISH', + 'ENGLISH (UNITED STATES)', + 'US_ENGLISH', + 'ENGLISH', + 'UNITED STATES', + 'EN-US', + jsonb_build_object('date_format', 'MDY', + 'date_first', 7, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (26, + 'ENGLISH', + 'ENGLISH (ZIMBABWE)', + NULL, + NULL, + 'ZIMBABWE', + 'EN-ZW', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (27, + 'GERMAN', + 'GERMAN (AUSTRIA)', + NULL, + NULL, + 'AUSTRIA', + 'DE-AT', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), + 'days_names', jsonb_build_array('Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (28, + 'GERMAN', + 'GERMAN (BELGIUM)', + NULL, + NULL, + 'BELGIUM', + 'DE-BE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), + 'days_names', jsonb_build_array('Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (29, + 'GERMAN', + 'GERMAN (GERMANY)', + 'DEUTSCH', + 'GERMAN', + 'GERMANY', + 'DE-DE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (30, + 'GERMAN', + 'GERMAN (LIECHTENSTEIN)', + NULL, + NULL, + 'LIECHTENSTEIN', + 'DE-LI', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), + 'days_names', jsonb_build_array('Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (31, + 'GERMAN', + 'GERMAN (LUXEMBOURG)', + NULL, + NULL, + 'LUXEMBOURG', + 'DE-LU', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), + 'days_names', jsonb_build_array('Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (32, + 'GERMAN', + 'GERMAN (SWITZERLAND)', + NULL, + NULL, + 'SWITZERLAND', + 'DE-CH', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'), + 'days_names', jsonb_build_array('Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (33, + 'FRENCH', + 'FRENCH (ALGERIA)', + NULL, + NULL, + 'ALGERIA', + 'FR-DZ', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (34, + 'FRENCH', + 'FRENCH (BELGIUM)', + NULL, + NULL, + 'BELGIUM', + 'FR-BE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (35, + 'FRENCH', + 'FRENCH (CAMEROON)', + NULL, + NULL, + 'CAMEROON', + 'FR-CM', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (36, + 'FRENCH', + 'FRENCH (CANADA)', + NULL, + NULL, + 'CANADA', + 'FR-CA', + jsonb_build_object('date_format', 'YMD', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (37, + 'FRENCH', + 'FRENCH (FRANCE)', + 'FRANÇAIS', + 'FRENCH', + 'FRANCE', + 'FR-FR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (38, + 'FRENCH', + 'FRENCH (HAITI)', + NULL, + NULL, + 'HAITI', + 'FR-HT', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (39, + 'FRENCH', + 'FRENCH (LUXEMBOURG)', + NULL, + NULL, + 'LUXEMBOURG', + 'FR-LU', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (40, + 'FRENCH', + 'FRENCH (MALI)', + NULL, + NULL, + 'MALI', + 'FR-ML', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (41, + 'FRENCH', + 'FRENCH (MONACO)', + NULL, + NULL, + 'MONACO', + 'FR-MC', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (42, + 'FRENCH', + 'FRENCH (MOROCCO)', + NULL, + NULL, + 'MOROCCO', + 'FR-MA', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (43, + 'FRENCH', + 'FRENCH (SENEGAL)', + NULL, + NULL, + 'SENEGAL', + 'FR-SN', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (44, + 'FRENCH', + 'FRENCH (SWITZERLAND)', + NULL, + NULL, + 'SWITZERLAND', + 'FR-CH', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (45, + 'FRENCH', + 'FRENCH (SYRIA)', + NULL, + NULL, + 'SYRIA', + 'FR-SY', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (46, + 'FRENCH', + 'FRENCH (TUNISIA)', + NULL, + NULL, + 'TUNISIA', + 'FR-TN', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'), + 'months_shortnames', jsonb_build_array('janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'), + 'days_names', jsonb_build_array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (47, + 'JAPANESE', + 'JAPANESE (JAPAN)', + '日本語', + 'JAPANESE', + 'JAPAN', + 'JA-JP', + jsonb_build_object('date_format', 'YMD', + 'date_first', 7, + 'months_names', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日', '日曜日'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (48, + 'DANISH', + 'DANISH (DENMARK)', + 'DANSK', + 'DANISH', + 'DENMARK', + 'DA-DK', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', 'søndag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (49, + 'DANISH', + 'DANISH (GREENLAND)', + NULL, + NULL, + 'GREENLAND', + 'DA-GL', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'), + 'days_names', jsonb_build_array('mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', 'søndag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (50, + 'SPANISH', + 'SPANISH (ARGENTINA)', + NULL, + NULL, + 'ARGENTINA', + 'ES-AR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (51, + 'SPANISH', + 'SPANISH (BOLIVIA)', + NULL, + NULL, + 'BOLIVIA', + 'ES-BO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (52, + 'SPANISH', + 'SPANISH (CHILE)', + NULL, + NULL, + 'CHILE', + 'ES-CL', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (53, + 'SPANISH', + 'SPANISH (COLOMBIA)', + NULL, + NULL, + 'COLOMBIA', + 'ES-CO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (54, + 'SPANISH', + 'SPANISH (COSTA RICA)', + NULL, + NULL, + 'COSTA RICA', + 'ES-CR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (55, + 'SPANISH', + 'SPANISH (CUBA)', + NULL, + NULL, + 'CUBA', + 'ES-CU', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (56, + 'SPANISH', + 'SPANISH (DOMINICAN REPUBLIC)', + NULL, + NULL, + 'DOMINICAN REPUBLIC', + 'ES-DO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (57, + 'SPANISH', + 'SPANISH (ECUADOR)', + NULL, + NULL, + 'ECUADOR', + 'ES-EC', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (58, + 'SPANISH', + 'SPANISH (EL SALVADOR)', + NULL, + NULL, + 'EL SALVADOR', + 'ES-SV', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (59, + 'SPANISH', + 'SPANISH (GUATEMALA)', + NULL, + NULL, + 'GUATEMALA', + 'ES-GT', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (60, + 'SPANISH', + 'SPANISH (HONDURASALA)', + NULL, + NULL, + 'HONDURAS', + 'ES-HN', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (61, + 'SPANISH', + 'SPANISH (MEXICO)', + NULL, + NULL, + 'MEXICO', + 'ES-MX', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (62, + 'SPANISH', + 'SPANISH (NICARAGUA)', + NULL, + NULL, + 'NICARAGUA', + 'ES-NI', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (63, + 'SPANISH', + 'SPANISH (PANAMA)', + NULL, + NULL, + 'PANAMA', + 'ES-PA', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (64, + 'SPANISH', + 'SPANISH (PARAGUAY)', + NULL, + NULL, + 'PARAGUAY', + 'ES-PY', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (65, + 'SPANISH', + 'SPANISH (PERU)', + NULL, + NULL, + 'PERU', + 'ES-PE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (66, + 'SPANISH', + 'SPANISH (PHILIPPINES)', + NULL, + NULL, + 'PHILIPPINES', + 'ES-PH', + jsonb_build_object('date_format', 'MDY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (67, + 'SPANISH', + 'SPANISH (PUERTO RICO)', + NULL, + NULL, + 'PUERTO RICO', + 'ES-PR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (68, + 'SPANISH', + 'SPANISH (SPAIN)', + 'ESPAÑOL', + 'SPANISH', + 'SPAIN', + 'ES-ES', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (69, + 'SPANISH', + 'SPANISH (UNITED STATES)', + NULL, + NULL, + 'UNITED STATES', + 'ES-US', + jsonb_build_object('date_format', 'MDY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (70, + 'SPANISH', + 'SPANISH (URUGUAY)', + NULL, + NULL, + 'URUGUAY', + 'ES-UY', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (71, + 'SPANISH', + 'SPANISH (VENEZUELA)', + NULL, + NULL, + 'VENEZUELA', + 'ES-VE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'), + 'months_shortnames', jsonb_build_array('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'), + 'days_names', jsonb_build_array('Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (72, + 'ITALIAN', + 'ITALIAN (ITALY)', + 'ITALIANO', + 'ITALIAN', + 'ITALY', + 'IT-IT', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'), + 'months_shortnames', jsonb_build_array('gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato', 'domenica'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (73, + 'ITALIAN', + 'ITALIAN (SWITZERLAND)', + NULL, + NULL, + 'SWITZERLAND', + 'IT-CH', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'), + 'months_shortnames', jsonb_build_array('gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'), + 'days_names', jsonb_build_array('lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato', 'domenica'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (74, + 'DUTCH', + 'DUTCH (BELGIUM)', + NULL, + NULL, + 'BELGIUM', + 'NL-BE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'), + 'days_names', jsonb_build_array('maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag', 'zondag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (75, + 'DUTCH', + 'DUTCH (NETHERLANDS)', + 'NEDERLANDS', + 'DUTCH', + 'NETHERLANDS', + 'NL-NL', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag', 'zondag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (76, + 'NORWEGIAN', + 'NORWEGIAN (NORWAY)', + NULL, + NULL, + 'NORWAY', + 'NO-NO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'), + 'days_names', jsonb_build_array('mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', 'søndag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (77, + 'NORWEGIAN (MS SQL)', + 'NORWEGIAN NYNORSK (NORWAY)', + 'NORSK', + 'NORWEGIAN', + 'NORWAY', + 'NN-NO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januar', 'februar', 'mars', 'april', 'mai', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'desember'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('mandag', 'tirsdag', 'onsdag', 'torsdag', 'fredag', 'lørdag', 'søndag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (78, + 'PORTUGUESE', + 'PORTUGUESE (BRAZIL)', + 'PORTUGUESE', + 'BRAZILIAN', + 'BRAZIL', + 'PT-BR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 7, + 'months_names', jsonb_build_array('janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'), + 'months_shortnames', jsonb_build_array('jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('segunda-feira', 'terça-feira', 'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado', 'domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (79, + 'PORTUGUESE', + 'PORTUGUESE (PORTUGAL)', + 'PORTUGUÊS', + 'PORTUGUESE', + 'PORTUGAL', + 'PT-PT', + jsonb_build_object('date_format', 'DMY', + 'date_first', 7, + 'months_names', jsonb_build_array('janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'), + 'months_shortnames', jsonb_build_array('jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('segunda-feira', 'terça-feira', 'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado', 'domingo'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (80, + 'FINNISH', + 'FINNISH (FINLAND)', + NULL, + NULL, + 'FINLAND', + 'FI-FI', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta', 'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta', 'lokakuuta', 'marraskuuta', 'joulukuuta'), + 'months_shortnames', jsonb_build_array('tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä', 'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'), + 'days_names', jsonb_build_array('maanantai', 'tiistai', 'keskiviikko', 'torstai', 'perjantai', 'lauantai', 'sunnuntai'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (81, + 'FINNISH (MS SQL)', + 'FINNISH (FINLAND)', + 'SUOMI', + 'FINNISH', + 'FINLAND', + 'FI', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('tammikuuta', 'helmikuuta', 'maaliskuuta', 'huhtikuuta', 'toukokuuta', 'kesäkuuta', 'heinäkuuta', 'elokuuta', 'syyskuuta', 'lokakuuta', 'marraskuuta', 'joulukuuta'), + 'months_shortnames', jsonb_build_array('tammi', 'helmi', 'maalis', 'huhti', 'touko', 'kesä', 'heinä', 'elo', 'syys', 'loka', 'marras', 'joulu'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('maanantai', 'tiistai', 'keskiviikko', 'torstai', 'perjantai', 'lauantai', 'sunnuntai'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (82, + 'SWEDISH', + 'SWEDISH (FINLAND)', + NULL, + NULL, + 'FINLAND', + 'SV-FI', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'), + 'days_names', jsonb_build_array('måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag', 'söndag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (83, + 'SWEDISH', + 'SWEDISH (SWEDEN)', + 'SVENSKA', + 'SWEDISH', + 'SWEDEN', + 'SV-SE', + jsonb_build_object('date_format', 'YMD', + 'date_first', 1, + 'months_names', jsonb_build_array('januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag', 'söndag'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (84, + 'CZECH', + 'CZECH (CZECH REPUBLIC)', + 'ČEŠTINA', + 'CZECH', + 'CZECHIA', + 'CS-CZ', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('leden', 'únor', 'březen', 'duben', 'květen', 'červen', 'červenec', 'srpen', 'září', 'říjen', 'listopad', 'prosinec'), + 'months_shortnames', jsonb_build_array('I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('pondělí', 'úterý', 'středa', 'čtvrtek', 'pátek', 'sobota', 'neděle'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (85, + 'HUNGARIAN', + 'HUNGARIAN (HUNGARY)', + 'MAGYAR', + 'HUNGARIAN', + 'HUNGARY', + 'HU-HU', + jsonb_build_object('date_format', 'YMD', + 'date_first', 1, + 'months_names', jsonb_build_array('január', 'február', 'március', 'április', 'május', 'június', 'július', 'augusztus', 'szeptember', 'október', 'november', 'december'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'days_names', jsonb_build_array('hétfő', 'kedd', 'szerda', 'csütörtök', 'péntek', 'szombat', 'vasárnap'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (86, + 'POLISH', + 'POLISH (POLAND)', + 'POLSKI', + 'POLISH', + 'POLAND', + 'PL-PL', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('styczeń', 'luty', 'marzec', 'kwiecień', 'maj', 'czerwiec', 'lipiec', 'sierpień', 'wrzesień', 'październik', 'listopad', 'grudzień'), + 'months_shortnames', jsonb_build_array('I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('poniedziałek', 'wtorek', 'środa', 'czwartek', 'piątek', 'sobota', 'niedziela'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (87, + 'ROMANIAN', + 'ROMANIAN (MOLDOVA)', + NULL, + NULL, + 'MOLDOVA', + 'RO-MD', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie', 'iulie', 'august', 'septembrie', 'octombrie', 'noiembrie', 'decembrie'), + 'months_shortnames', jsonb_build_array('Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('luni', 'marţi', 'miercuri', 'joi', 'vineri', 'sîmbătă', 'duminică'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (88, + 'ROMANIAN', + 'ROMANIAN (ROMANIA)', + 'ROMÂNĂ', + 'ROMANIAN', + 'ROMANIA', + 'RO-RO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie', 'iulie', 'august', 'septembrie', 'octombrie', 'noiembrie', 'decembrie'), + 'months_shortnames', jsonb_build_array('Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('luni', 'marţi', 'miercuri', 'joi', 'vineri', 'sîmbătă', 'duminică'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (89, + 'CROATIAN', + 'CROATIAN (CROATIA)', + 'HRVATSKI', + 'CROATIAN', + 'CROATIA', + 'HR-HR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('siječanj', 'veljača', 'ožujak', 'travanj', 'svibanj', 'lipanj', 'srpanj', 'kolovoz', 'rujan', 'listopad', 'studeni', 'prosinac'), + 'months_shortnames', jsonb_build_array('sij', 'vel', 'ožu', 'tra', 'svi', 'lip', 'srp', 'kol', 'ruj', 'lis', 'stu', 'pro'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('ponedjeljak', 'utorak', 'srijeda', 'četvrtak', 'petak', 'subota', 'nedjelja'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (90, + 'SLOVAK', + 'SLOVAK (SLOVAKIA)', + 'SLOVENČINA', + 'SLOVAK', + 'SLOVAKIA', + 'SK-SK', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('január', 'február', 'marec', 'apríl', 'máj', 'jún', 'júl', 'august', 'september', 'október', 'november', 'december'), + 'months_shortnames', jsonb_build_array('I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X', 'XI', 'XII'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('pondelok', 'utorok', 'streda', 'štvrtok', 'piatok', 'sobota', 'nedeľa'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (91, + 'SLOVENIAN', + 'SLOVENIAN (SLOVENIA)', + 'SLOVENSKI', + 'SLOVENIAN', + 'SLOVENIA', + 'SL-SI', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('januar', 'februar', 'marec', 'april', 'maj', 'junij', 'julij', 'avgust', 'september', 'oktober', 'november', 'december'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'avg', 'sept', 'okt', 'nov', 'dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('ponedeljek', 'torek', 'sreda', 'četrtek', 'petek', 'sobota', 'nedelja'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (92, + 'GREEK', + 'GREEK (GREECE)', + 'ΕΛΛΗΝΙΚΆ', + 'GREEK', + 'GREECE', + 'EL-GR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Ιανουαρίου', 'Φεβρουαρίου', 'Μαρτίου', 'Απριλίου', 'Μα_ου', 'Ιουνίου', 'Ιουλίου', 'Αυγούστου', 'Σεπτεμβρίου', 'Οκτωβρίου', 'Νοεμβρίου', 'Δεκεμβρίου'), + 'months_shortnames', jsonb_build_array('Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μαϊ', 'Ιουν', 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Δευτέρα', 'Τρίτη', 'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο', 'Κυριακή'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (93, + 'BULGARIAN', + 'BULGARIAN (BULGARIA)', + 'БЪЛГАРСКИ', + 'BULGARIAN', + 'BULGARIA', + 'BG-BG', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('януари', 'февруари', 'март', 'април', 'май', 'юни', 'юли', 'август', 'септември', 'октомври', 'ноември', 'декември'), + 'months_shortnames', jsonb_build_array('януари', 'февруари', 'март', 'април', 'май', 'юни', 'юли', 'август', 'септември', 'октомври', 'ноември', 'декември'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('понеделник', 'вторник', 'сряда', 'четвъртък', 'петък', 'събота', 'неделя'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (94, + 'RUSSIAN', + 'RUSSIAN (BELARUS)', + NULL, + NULL, + 'BELARUS', + 'RU-BY', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'), + 'months_shortnames', jsonb_build_array('янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'), + 'days_names', jsonb_build_array('понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота', 'воскресенье'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (95, + 'RUSSIAN', + 'RUSSIAN (KAZAKHSTAN)', + NULL, + NULL, + 'KAZAKHSTAN', + 'RU-KZ', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'), + 'months_shortnames', jsonb_build_array('янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'), + 'days_names', jsonb_build_array('понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота', 'воскресенье'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (96, + 'RUSSIAN', + 'RUSSIAN (KYRGYZSTAN)', + NULL, + NULL, + 'KYRGYZSTAN', + 'RU-KG', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'), + 'months_shortnames', jsonb_build_array('янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'), + 'days_names', jsonb_build_array('понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота', 'воскресенье'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (97, + 'RUSSIAN', + 'RUSSIAN (MOLDOVA)', + NULL, + NULL, + 'MOLDOVA', + 'RU-MD', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'), + 'months_shortnames', jsonb_build_array('янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'), + 'days_names', jsonb_build_array('понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота', 'воскресенье'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (98, + 'RUSSIAN', + 'RUSSIAN (RUSSIA)', + 'РУССКИЙ', + 'RUSSIAN', + 'RUSSIA', + 'RU-RU', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'), + 'months_shortnames', jsonb_build_array('янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота', 'воскресенье'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (99, + 'RUSSIAN', + 'RUSSIAN (UKRAINE)', + NULL, + NULL, + 'UKRAINE', + 'RU-UA', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'), + 'months_shortnames', jsonb_build_array('янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'), + 'days_names', jsonb_build_array('понедельник', 'вторник', 'среда', 'четверг', 'пятница', 'суббота', 'воскресенье'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (100, + 'TURKISH', + 'TURKISH (TURKEY)', + 'TÜRKÇE', + 'TURKISH', + 'TURKEY', + 'TR-TR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'), + 'months_shortnames', jsonb_build_array('Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi', 'Pazar'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (101, + 'ESTONIAN', + 'ESTONIAN (ESTONIA)', + 'EESTI', + 'ESTONIAN', + 'ESTONIA', + 'ET-EE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'), + 'months_shortnames', jsonb_build_array('jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni', 'juuli', 'aug', 'sept', 'okt', 'nov', 'dets'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('esmaspäev', 'teisipäev', 'kolmapäev', 'neljapäev', 'reede', 'laupäev', 'pühapäev'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (102, + 'LATVIAN', + 'LATVIAN (LATVIA)', + 'LATVIEŠU', + 'LATVIAN', + 'LATVIA', + 'LV-LV', + jsonb_build_object('date_format', 'YMD', + 'date_first', 1, + 'months_names', jsonb_build_array('janvāris', 'februāris', 'marts', 'aprīlis', 'maijs', 'jūnijs', 'jūlijs', 'augusts', 'septembris', 'oktobris', 'novembris', 'decembris'), + 'months_shortnames', jsonb_build_array('jan', 'feb', 'mar', 'apr', 'mai', 'jūn', 'jūl', 'aug', 'sep', 'okt', 'nov', 'dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('pirmdiena', 'otrdiena', 'trešdiena', 'ceturtdiena', 'piektdiena', 'sestdiena', 'svētdiena'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (103, + 'LITHUANIAN', + 'LITHUANIAN (LITHUANIA)', + 'LIETUVIŲ', + 'LITHUANIAN', + 'LITHUANIA', + 'LT-LT', + jsonb_build_object('date_format', 'YMD', + 'date_first', 1, + 'months_names', jsonb_build_array('sausis', 'vasaris', 'kovas', 'balandis', 'gegužė', 'birželis', 'liepa', 'rugpjūtis', 'rugsėjis', 'spalis', 'lapkritis', 'gruodis'), + 'months_shortnames', jsonb_build_array('sau', 'vas', 'kov', 'bal', 'geg', 'bir', 'lie', 'rgp', 'rgs', 'spl', 'lap', 'grd'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('pirmadienis', 'antradienis', 'trečiadienis', 'ketvirtadienis', 'penktadienis', 'šeštadienis', 'sekmadienis'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (104, + 'CHINESE (TRADITIONAL)', + 'CHINESE (TRADITIONAL, CHINA)', + '繁體中文', + 'TRADITIONAL CHINESE', + 'CHINA', + 'ZH-TW', + jsonb_build_object('date_format', 'YMD', + 'date_first', 7, + 'months_names', jsonb_build_array('一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'), + 'months_shortnames', jsonb_build_array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (105, + 'KOREAN', + 'KOREAN (NORTH KOREA)', + NULL, + NULL, + 'NORTH KOREA', + 'KO-KP', + jsonb_build_object('date_format', 'YMD', + 'date_first', 7, + 'months_names', jsonb_build_array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'), + 'months_shortnames', jsonb_build_array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'), + 'days_names', jsonb_build_array('월요일', '화요일', '수요일', '목요일', '금요일', '토요일', '일요일'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (106, + 'KOREAN', + 'KOREAN (SOUTH KOREA)', + '한국어', + 'KOREAN', + 'KOREA', + 'KO-KR', + jsonb_build_object('date_format', 'YMD', + 'date_first', 7, + 'months_names', jsonb_build_array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'), + 'months_shortnames', jsonb_build_array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('월요일', '화요일', '수요일', '목요일', '금요일', '토요일', '일요일'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (107, + 'CHINESE (SIMPLIFIED)', + 'CHINESE (SIMPLIFIED, CHINA)', + '简体中文', + 'SIMPLIFIED CHINESE', + 'CHINA', + 'ZH-CN', + jsonb_build_object('date_format', 'YMD', + 'date_first', 7, + 'months_names', jsonb_build_array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'), + 'months_shortnames', jsonb_build_array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), + 'days_extrashortnames', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'))); + +INSERT INTO sys.syslanguages + VALUES (108, + 'ARABIC (MS SQL)', + 'ARABIC (ARABIC)', + 'GENERAL ARABIC', + 'GENERAL ARABIC', + 'ARABIC', + 'AR', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (109, + 'ARABIC', + 'ARABIC (ALGERIA)', + NULL, + NULL, + 'ALGERIA', + 'AR-DZ', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (110, + 'ARABIC', + 'ARABIC (BAHRAIN)', + NULL, + NULL, + 'BAHRAIN', + 'AR-BH', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (111, + 'ARABIC', + 'ARABIC (EGYPT)', + NULL, + NULL, + 'EGYPT', + 'AR-EG', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (112, + 'ARABIC', + 'ARABIC (ERITREA)', + NULL, + NULL, + 'ERITREA', + 'AR-ER', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (113, + 'ARABIC', + 'ARABIC (IRAQ)', + NULL, + NULL, + 'IRAQ', + 'AR-IQ', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (114, + 'ARABIC', + 'ARABIC (ISRAEL)', + NULL, + NULL, + 'ISRAEL', + 'AR-IL', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (115, + 'ARABIC', + 'ARABIC (JORDAN)', + NULL, + NULL, + 'JORDAN', + 'AR-JO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (116, + 'ARABIC', + 'ARABIC (KUWAIT)', + NULL, + NULL, + 'KUWAIT', + 'AR-KW', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (117, + 'ARABIC', + 'ARABIC (LEBANON)', + NULL, + NULL, + 'LEBANON', + 'AR-LB', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (118, + 'ARABIC', + 'ARABIC (LIBYA)', + NULL, + NULL, + 'LIBYA', + 'AR-LY', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (119, + 'ARABIC', + 'ARABIC (MOROCCO)', + NULL, + NULL, + 'MOROCCO', + 'AR-MA', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (120, + 'ARABIC', + 'ARABIC (OMAN)', + NULL, + NULL, + 'OMAN', + 'AR-OM', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (121, + 'ARABIC', + 'ARABIC (QATAR)', + NULL, + NULL, + 'QATAR', + 'AR-QA', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (122, + 'ARABIC', + 'ARABIC (SAUDI ARABIA)', + 'ARABIC', + 'ARABIC', + 'SAUDI ARABIA', + 'AR-SA', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (123, + 'ARABIC', + 'ARABIC (SOMALIA)', + NULL, + NULL, + 'SOMALIA', + 'AR-SO', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (124, + 'ARABIC', + 'ARABIC (SYRIA)', + NULL, + NULL, + 'SYRIA', + 'AR-SY', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (125, + 'ARABIC', + 'ARABIC (TUNISIA)', + NULL, + NULL, + 'TUNISIA', + 'AR-TN', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (126, + 'ARABIC', + 'ARABIC (UNITED ARAB EMIRATES)', + NULL, + NULL, + 'UNITED ARAB EMIRATES', + 'AR-AE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (127, + 'ARABIC', + 'ARABIC (YEMEN)', + NULL, + NULL, + 'YEMEN', + 'AR-YE', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('Muharram', 'Safar', 'Rabie I', 'Rabie II', 'Jumada I', 'Jumada II', 'Rajab', 'Shaaban', 'Ramadan', 'Shawwal', 'Thou Alqadah', 'Thou Alhajja'), + 'months_shortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (128, + 'THAI', + 'THAI (THAILAND)', + 'ไทย', + 'THAI', + 'THAILAND', + 'TH-TH', + jsonb_build_object('date_format', 'DMY', + 'date_first', 7, + 'months_names', jsonb_build_array('มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'), + 'months_shortnames', jsonb_build_array('ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.', 'ก.ย.', 'ต.ค.', 'พ.ย.', 'ธ.ค.'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('จันทร์', 'อังคาร', 'พุธ', 'พฤหัสบดี', 'ศุกร์', 'เสาร์', 'อาทิตย์'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); + +INSERT INTO sys.syslanguages + VALUES (129, + 'HIJRI', + 'HIJRI (ISLAMIC)', + 'HIJRI', + 'ISLAMIC', + 'ISLAMIC', + 'HI-IS', + jsonb_build_object('date_format', 'DMY', + 'date_first', 1, + 'months_names', jsonb_build_array('محرم', 'صفر', 'ربيع الاول', 'ربيع الثاني', 'جمادى الاولى', 'جمادى الثانية', 'رجب', 'شعبان', 'رمضان', 'شوال', 'ذو القعدة', 'ذو الحجة'), + 'months_shortnames', jsonb_build_array('محرم', 'صفر', 'ربيع الاول', 'ربيع الثاني', 'جمادى الاولى', 'جمادى الثانية', 'رجب', 'شعبان', 'رمضان', 'شوال', 'ذو القعدة', 'ذو الحجة'), + 'months_extranames', jsonb_build_array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'), + 'months_extrashortnames', jsonb_build_array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'), + 'days_names', jsonb_build_array('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'), + 'days_shortnames', jsonb_build_array('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'))); diff --git a/contrib/babelfishpg_tsql/sql/sys_procedures.sql b/contrib/babelfishpg_tsql/sql/sys_procedures.sql new file mode 100644 index 00000000000..fe4d054dafd --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_procedures.sql @@ -0,0 +1,153 @@ +CREATE PROCEDURE sys.sp_unprepare(IN prep_handle INTEGER) +AS 'babelfishpg_tsql', 'sp_unprepare' +LANGUAGE C; +GRANT EXECUTE ON PROCEDURE sys.sp_unprepare(IN INTEGER) TO PUBLIC; + +CREATE PROCEDURE sys.sp_prepare(INOUT prep_handle INTEGER, IN params varchar(8000), + IN stmt varchar(8000), IN options int default 1) +AS 'babelfishpg_tsql', 'sp_prepare' +LANGUAGE C; +GRANT EXECUTE ON PROCEDURE sys.sp_prepare( + INOUT INTEGER, IN varchar(8000), IN varchar(8000), IN int +) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sp_getapplock_function (IN "@resource" varchar(255), + IN "@lockmode" varchar(32), + IN "@lockowner" varchar(32) DEFAULT 'TRANSACTION', + IN "@locktimeout" INTEGER DEFAULT -99, + IN "@dbprincipal" varchar(32) DEFAULT 'dbo') +RETURNS INTEGER +AS 'babelfishpg_tsql', 'sp_getapplock_function' LANGUAGE C; +GRANT EXECUTE ON FUNCTION sys.sp_getapplock_function( + IN varchar(255), IN varchar(32), IN varchar(32), IN INTEGER, IN varchar(32) +) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sp_releaseapplock_function(IN "@resource" varchar(255), + IN "@lockowner" varchar(32) DEFAULT 'TRANSACTION', + IN "@dbprincipal" varchar(32) DEFAULT 'dbo') +RETURNS INTEGER +AS 'babelfishpg_tsql', 'sp_releaseapplock_function' LANGUAGE C; +GRANT EXECUTE ON FUNCTION sys.sp_releaseapplock_function( + IN varchar(255), IN varchar(32), IN varchar(32) +) TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_cursor_list (INOUT "@cursor_return" refcursor, + IN "@cursor_scope" INTEGER) +AS $$ +DECLARE + cur refcursor; +BEGIN + IF "@cursor_scope" >= 1 AND "@cursor_scope" <= 3 THEN + OPEN cur FOR EXECUTE 'SELECT reference_name::name, cursor_name::name, cursor_scope::smallint, status::smallint, model::smallint, concurrency::smallint, scrollable::smallint, open_status::smallint, cursor_rows::numeric(10,0), fetch_status::smallint, column_count::smallint, row_count::numeric(10,0), last_operation::smallint, cursor_handle::int FROM sys.babelfish_cursor_list($1)' USING "@cursor_scope"; + ELSE + RAISE 'invalid @cursor_scope: %', "@cursor_scope"; + END IF; + + -- PG cursor evaluates the query at first fetch. We need to evaluate table function now because cursor_list() depeneds on "current" tsql_estate(). + -- Running MOVE fowrard and backward to force evaluating sys.babelfish_cursor_list() now. + MOVE NEXT FROM cur; + MOVE PRIOR FROM cur; + SELECT cur INTO "@cursor_return"; +END; +$$ LANGUAGE plpgsql; +GRANT EXECUTE ON PROCEDURE sys.sp_cursor_list(INOUT refcursor, IN INTEGER) TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_describe_cursor (INOUT "@cursor_return" refcursor, + IN "@cursor_source" nvarchar(30), + IN "@cursor_identity" nvarchar(30)) +AS $$ +DECLARE + cur refcursor; + cursor_source int; +BEGIN + IF lower("@cursor_source") = 'local' THEN + cursor_source := 1; + ELSIF lower("@cursor_source") = 'global' THEN + cursor_source := 2; + ELSIF lower("@cursor_source") = 'variable' THEN + cursor_source := 3; + ELSE + RAISE 'invalid @cursor_source: %', "@cursor_source"; + END IF; + + OPEN cur FOR EXECUTE 'SELECT reference_name::name, cursor_name::name, cursor_scope::smallint, status::smallint, model::smallint, concurrency::smallint, scrollable::smallint, open_status::smallint, cursor_rows::numeric(10,0), fetch_status::smallint, column_count::smallint, row_count::numeric(10,0), last_operation::smallint, cursor_handle::int FROM sys.babelfish_cursor_list($1) WHERE cursor_source = $1 and reference_name = $2' USING cursor_source, "@cursor_identity"; + + -- PG cursor evaluates the query at first fetch. We need to evaluate table function now because cursor_list() depeneds on "current" tsql_estate(). + -- Running MOVE fowrard and backward to force evaluating sys.babelfish_cursor_list() now. + MOVE NEXT FROM cur; + MOVE PRIOR FROM cur; + SELECT cur INTO "@cursor_return"; +END; +$$ LANGUAGE plpgsql; +GRANT EXECUTE ON PROCEDURE sys.sp_describe_cursor( + INOUT refcursor, IN nvarchar(30), IN nvarchar(30) +) TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_babelfish_configure() +AS 'babelfishpg_tsql', 'sp_babelfish_configure' +LANGUAGE C; +GRANT EXECUTE ON PROCEDURE sys.sp_babelfish_configure() TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_babelfish_configure(IN "@option_name" varchar(128)) +AS 'babelfishpg_tsql', 'sp_babelfish_configure' +LANGUAGE C; +GRANT EXECUTE ON PROCEDURE sys.sp_babelfish_configure(IN varchar(128)) TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_babelfish_configure(IN "@option_name" varchar(128), IN "@option_value" varchar(128)) +AS $$ +BEGIN + CALL sys.sp_babelfish_configure("@option_name", "@option_value", ''); +END; +$$ LANGUAGE plpgsql; +GRANT EXECUTE ON PROCEDURE sys.sp_babelfish_configure(IN varchar(128), IN varchar(128)) TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_babelfish_configure(IN "@option_name" varchar(128), IN "@option_value" varchar(128), IN "@option_scope" varchar(128)) +AS $$ +DECLARE + normalized_name varchar(256); + cnt int; + cur refcursor; + eh_name varchar(256); + server boolean := false; + prev_user text; +BEGIN + IF lower("@option_name") like 'babelfishpg_tsql.%' THEN + SELECT "@option_name" INTO normalized_name; + ELSE + SELECT concat('babelfishpg_tsql.',"@option_name") INTO normalized_name; + END IF; + + IF lower("@option_scope") = 'server' THEN + server := true; + ELSIF btrim("@option_scope") != '' THEN + RAISE EXCEPTION 'invalid option: %', "@option_scope"; + END IF; + + SELECT COUNT(*) INTO cnt FROM pg_catalog.pg_settings WHERE name like normalized_name and name like '%escape_hatch%'; + IF cnt = 0 THEN + RAISE EXCEPTION 'unknown configuration: %', normalized_name; + END IF; + + OPEN cur FOR SELECT name FROM pg_catalog.pg_settings WHERE name like normalized_name and name like '%escape_hatch%'; + + LOOP + FETCH NEXT FROM cur into eh_name; + exit when not found; + + PERFORM pg_catalog.set_config(eh_name, "@option_value", 'false'); + IF server THEN + SELECT current_user INTO prev_user; + PERFORM sys.babelfish_set_role(session_user); + -- store the setting in PG master database so that it can be applied to all bbf databases + EXECUTE format('ALTER DATABASE %s SET %s = %s', CURRENT_DATABASE(), eh_name, "@option_value"); + PERFORM sys.babelfish_set_role(prev_user); + END IF; + END LOOP; + + CLOSE cur; + +END; +$$ LANGUAGE plpgsql; +GRANT EXECUTE ON PROCEDURE sys.sp_babelfish_configure( + IN varchar(128), IN varchar(128), IN varchar(128) +) TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/sys_views.sql b/contrib/babelfishpg_tsql/sql/sys_views.sql new file mode 100644 index 00000000000..cd12a7db9ec --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_views.sql @@ -0,0 +1,778 @@ +/* Tsql system catalog views */ +create or replace view sys.tables as +select + t.relname as name + , t.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , 0 as parent_object_id + , 'U'::varchar(2) as type + , 'USER_TABLE'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published + , case reltoastrelid when 0 then 0 else 1 end as lob_data_space_id + , null::integer as filestream_data_space_id + , relnatts as max_column_id_used + , 0 as lock_on_bulk_load + , 1 as uses_ansi_nulls + , 0 as is_replicated + , 0 as has_replication_filter + , 0 as is_merge_published + , 0 as is_sync_tran_subscribed + , 0 as has_unchecked_assembly_data + , 0 as text_in_row_limit + , 0 as large_value_types_out_of_row + , 0 as is_tracked_by_cdc + , 0 as lock_escalation + , 'TABLE'::varchar(60) as lock_escalation_desc + , 0 as is_filetable + , 0 as durability + , 'SCHEMA_AND_DATA'::varchar(60) as durability_desc + , 0 as is_memory_optimized + , case relpersistence when 't' then 2 else 0 end as temporal_type + , case relpersistence when 't' then 'SYSTEM_VERSIONED_TEMPORAL_TABLE' else 'NON_TEMPORAL_TABLE' end as temporal_type_desc + , null::integer as history_table_id + , 0 as is_remote_data_archive_enabled + , 0 as is_external +from pg_class t inner join pg_namespace s on s.oid = t.relnamespace +where t.relpersistence in ('p', 'u', 't') +and t.relkind = 'r' +and has_table_privilege(quote_ident(s.nspname) ||'.'||quote_ident(t.relname), 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER') +and s.nspname not in ('information_schema', 'pg_catalog'); +GRANT SELECT ON sys.tables TO PUBLIC; + +create or replace view sys.views as +select + t.relname as name + , t.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , 0 as parent_object_id + , 'V'::varchar(2) as type + , 'VIEW'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published + , 0 as with_check_option + , 0 as is_date_correlation_view + , 0 as is_tracked_by_cdc +from pg_class t inner join pg_namespace s on s.oid = t.relnamespace +where t.relkind = 'v' +and has_table_privilege(quote_ident(s.nspname) ||'.'||quote_ident(t.relname), 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER') +and s.nspname not in ('information_schema', 'pg_catalog'); +GRANT SELECT ON sys.views TO PUBLIC; + +create or replace view sys.all_columns as +select c.oid as object_id + , a.attname as name + , a.attnum as column_id + , t.oid as system_type_id + , t.oid as user_type_id + , a.attlen as max_length + , null::integer as precision + , null::integer as scale + , coll.collname as collation_name + , case when a.attnotnull then 0 else 1 end as is_nullable + , 0 as is_ansi_padded + , 0 as is_rowguidcol + , 0 as is_identity + , 0 as is_computed + , 0 as is_filestream + , 0 as is_replicated + , 0 as is_non_sql_subscribed + , 0 as is_merge_published + , 0 as is_dts_replicated + , 0 as is_xml_document + , 0 as xml_collection_id + , coalesce(d.oid, 0) as default_object_id + , coalesce((select oid from pg_constraint where conrelid = t.oid and contype = 'c' and a.attnum = any(conkey) limit 1), 0) as rule_object_id + , 0 as is_sparse + , 0 as is_column_set + , 0 as generated_always_type + , 'NOT_APPLICABLE'::varchar(60) as generated_always_type_desc + , null::integer as encryption_type + , null::varchar(64) as encryption_type_desc + , null::varchar as encryption_algorithm_name + , null::integer as column_encryption_key_id + , null::varchar as column_encryption_key_database_name + , 0 as is_hidden + , 0 as is_masked +from pg_attribute a +inner join pg_class c on c.oid = a.attrelid +inner join pg_type t on t.oid = a.atttypid +inner join pg_namespace s on s.oid = c.relnamespace +left join pg_attrdef d on c.oid = d.adrelid and a.attnum = d.adnum +left join pg_collation coll on coll.oid = t.typcollation +where not a.attisdropped +-- r = ordinary table, i = index, S = sequence, t = TOAST table, v = view, m = materialized view, c = composite type, f = foreign table, p = partitioned table +and c.relkind in ('r', 'v', 'm', 'f', 'p') +and has_column_privilege(quote_ident(s.nspname) ||'.'||quote_ident(c.relname), a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +GRANT SELECT ON sys.all_columns TO PUBLIC; + +create or replace view sys.all_views as +select + t.relname as name + , t.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , 0 as parent_object_id + , 'V'::varchar(2) as type + , 'VIEW'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published + , 0 as with_check_option + , 0 as is_date_correlation_view + , 0 as is_tracked_by_cdc +from pg_class t inner join pg_namespace s on s.oid = t.relnamespace +where t.relkind = 'v' +and has_table_privilege(quote_ident(s.nspname) ||'.'||quote_ident(t.relname), 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +GRANT SELECT ON sys.all_views TO PUBLIC; + +create or replace view sys.columns as +select c.oid as object_id + , a.attname as name + , a.attnum as column_id + , t.oid as system_type_id + , t.oid as user_type_id + , a.attlen as max_length + , null::integer as precision + , null::integer as scale + , coll.collname as collation_name + , case when a.attnotnull then 0 else 1 end as is_nullable + , 0 as is_ansi_padded + , 0 as is_rowguidcol + , 0 as is_identity + , 0 as is_computed + , 0 as is_filestream + , 0 as is_replicated + , 0 as is_non_sql_subscribed + , 0 as is_merge_published + , 0 as is_dts_replicated + , 0 as is_xml_document + , 0 as xml_collection_id + , coalesce(d.oid, 0) as default_object_id + , coalesce((select oid from pg_constraint where conrelid = t.oid and contype = 'c' and a.attnum = any(conkey) limit 1), 0) as rule_object_id + , 0 as is_sparse + , 0 as is_column_set + , 0 as generated_always_type + , 'NOT_APPLICABLE'::varchar(60) as generated_always_type_desc + , null::integer as encryption_type + , null::varchar(64) as encryption_type_desc + , null::varchar as encryption_algorithm_name + , null::integer as column_encryption_key_id + , null::varchar as column_encryption_key_database_name + , 0 as is_hidden + , 0 as is_masked +from pg_attribute a +inner join pg_class c on c.oid = a.attrelid +inner join pg_type t on t.oid = a.atttypid +inner join pg_namespace s on s.oid = c.relnamespace +left join pg_attrdef d on c.oid = d.adrelid and a.attnum = d.adnum +left join pg_collation coll on coll.oid = t.typcollation +where not a.attisdropped +-- r = ordinary table, i = index, S = sequence, t = TOAST table, v = view, m = materialized view, c = composite type, f = foreign table, p = partitioned table +and c.relkind in ('r', 'v', 'm', 'f', 'p') +and s.nspname not in ('information_schema', 'pg_catalog') +and has_column_privilege(quote_ident(s.nspname) ||'.'||quote_ident(c.relname), a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +GRANT SELECT ON sys.columns TO PUBLIC; + +create or replace view sys.foreign_key_columns as +select distinct + c.oid as constraint_object_id + , c.confkey as constraint_column_id + , c.conrelid as parent_object_id + , a_con.attnum as parent_column_id + , c.confrelid as referenced_object_id + , a_conf.attnum as referenced_column_id +from pg_constraint c +inner join pg_attribute a_con on a_con.attrelid = c.conrelid and a_con.attnum = any(c.conkey) +inner join pg_attribute a_conf on a_conf.attrelid = c.confrelid and a_conf.attnum = any(c.confkey) +where c.contype = 'f'; +GRANT SELECT ON sys.foreign_key_columns TO PUBLIC; + +create or replace view sys.foreign_keys as +select + c.conname as name + , c.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , c.conrelid as parent_object_id + , 'F'::varchar(2) as type + , 'FOREIGN_KEY_CONSTRAINT'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published + , c.confrelid as referenced_object_id + , c.confkey as key_index_id + , 0 as is_disabled + , 0 as is_not_for_replication + , 0 as is_not_trusted + , case c.confdeltype + when 'a' then 0 + when 'r' then 0 + when 'c' then 1 + when 'n' then 2 + when 'd' then 3 + end as delete_referential_action + , case c.confdeltype + when 'a' then 'NO_ACTION' + when 'r' then 'NO_ACTION' + when 'c' then 'CASCADE' + when 'n' then 'SET_NULL' + when 'd' then 'SET_DEFAULT' + end as delete_referential_action_desc + , case c.confupdtype + when 'a' then 0 + when 'r' then 0 + when 'c' then 1 + when 'n' then 2 + when 'd' then 3 + end as update_referential_action + , case c.confupdtype + when 'a' then 'NO_ACTION' + when 'r' then 'NO_ACTION' + when 'c' then 'CASCADE' + when 'n' then 'SET_NULL' + when 'd' then 'SET_DEFAULT' + end as update_referential_action_desc + , 1 as is_system_named +from pg_constraint c +inner join pg_namespace s on s.oid = c.connamespace +where c.contype = 'f' +and s.nspname not in ('information_schema', 'pg_catalog'); +GRANT SELECT ON sys.foreign_keys TO PUBLIC; + +create or replace view sys.identity_columns as +select + sys.babelfish_get_id_by_name(c.oid::text||a.attname) as object_id + , a.attname as name + , a.attnum as column_id + , t.oid as system_type_id + , t.oid as user_type_id + , a.attlen as max_length + , null::integer as precision + , null::integer as scale + , coll.collname as collation_name + , case when a.attnotnull then 0 else 1 end as is_nullable + , 0 as is_ansi_padded + , 0 as is_rowguidcol + , 1 as is_identity + , 0 as is_computed + , 0 as is_filestream + , 0 as is_replicated + , 0 as is_non_sql_subscribed + , 0 as is_merge_published + , 0 as is_dts_replicated + , 0 as is_xml_document + , 0 as xml_collection_id + , coalesce(d.oid, 0) as default_object_id + , coalesce((select oid from pg_constraint where conrelid = t.oid and contype = 'c' and a.attnum = any(conkey) limit 1), 0) as rule_object_id + , 0 as is_sparse + , 0 as is_column_set + , 0 as generated_always_type + , 'NOT_APPLICABLE'::varchar(60) as generated_always_type_desc + , null::integer as encryption_type + , null::varchar(64) as encryption_type_desc + , null::varchar as encryption_algorithm_name + , null::integer as column_encryption_key_id + , null::varchar as column_encryption_key_database_name + , 0 as is_hidden + , 0 as is_masked + , null::bigint as seed_value + , null::bigint as increment_value + , sys.babelfish_get_sequence_value(pg_get_serial_sequence(quote_ident(s.nspname)||'.'||quote_ident(c.relname), a.attname)) as last_value +from pg_attribute a +left join pg_attrdef d on a.attrelid = d.adrelid and a.attnum = d.adnum +inner join pg_class c on c.oid = a.attrelid +inner join pg_namespace s on s.oid = c.relnamespace +left join pg_type t on t.oid = a.atttypid +left join pg_collation coll on coll.oid = t.typcollation +where not a.attisdropped +and pg_get_serial_sequence(quote_ident(s.nspname)||'.'||quote_ident(c.relname), a.attname) is not null +and s.nspname not in ('information_schema', 'pg_catalog') +and has_sequence_privilege(pg_get_serial_sequence(quote_ident(s.nspname)||'.'||quote_ident(c.relname), a.attname), 'USAGE,SELECT,UPDATE'); +GRANT SELECT ON sys.identity_columns TO PUBLIC; + +create or replace view sys.indexes as +select + i.indrelid as object_id + , c.relname as name + , case when i.indisclustered then 1 else 2 end as type + , case when i.indisclustered then 'CLUSTERED'::varchar(60) else 'NONCLUSTERED'::varchar(60) end as type_desc + , case when i.indisunique then 1 else 0 end as is_unique + , c.reltablespace as data_space_id + , 0 as ignore_dup_key + , case when i.indisprimary then 1 else 0 end as is_primary_key + , case when constr.oid is null then 0 else 1 end as is_unique_constraint + , 0 as fill_factor + , case when i.indpred is null then 0 else 1 end as is_padded + , case when i.indisready then 0 else 1 end is_disabled + , 0 as is_hypothetical + , 1 as allow_row_locks + , 1 as allow_page_locks + , 0 as has_filter + , null::varchar as filter_definition + , 0 as auto_created +from pg_class c +inner join pg_namespace s on s.oid = c.relnamespace +inner join pg_index i on i.indexrelid = c.oid +left join pg_constraint constr on constr.conindid = c.oid +where c.relkind = 'i' and i.indislive +and s.nspname not in ('information_schema', 'pg_catalog'); +GRANT SELECT ON sys.indexes TO PUBLIC; + +create or replace view sys.key_constraints as +select + c.conname as name + , c.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , c.conrelid as parent_object_id + , case contype + when 'p' then 'PK'::varchar(2) + when 'u' then 'UQ'::varchar(2) + end as type + , case contype + when 'p' then 'PRIMARY_KEY_CONSTRAINT'::varchar(60) + when 'u' then 'UNIQUE_CONSTRAINT'::varchar(60) + end as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , c.conindid as unique_index_id + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_constraint c +inner join pg_namespace s on s.oid = c.connamespace +where c.contype = 'p'; +GRANT SELECT ON sys.key_constraints TO PUBLIC; + +create or replace view sys.procedures as +select + p.proname as name + , p.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , 0 as parent_object_id + , case format_type(p.prorettype, null) + when 'void' then 'P'::varchar(2) + else + case format_type(p.prorettype, null) when 'trigger' + then 'TR'::varchar(2) + else 'FN'::varchar(2) + end + end as type + , case format_type(p.prorettype, null) + when 'void' then 'SQL_STORED_PROCEDURE'::varchar(60) + else + case format_type(p.prorettype, null) when 'trigger' + then 'SQL_TRIGGER'::varchar(60) + else 'SQL_SCALAR_FUNCTION'::varchar(60) + end + end as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_proc p +inner join pg_namespace s on s.oid = p.pronamespace +where s.nspname not in ('information_schema', 'pg_catalog') +and has_function_privilege(p.oid, 'EXECUTE'); +GRANT SELECT ON sys.procedures TO PUBLIC; + +create or replace view sys.sql_modules as +select + p.oid as object_id + , pg_get_functiondef(p.oid) as definition + , 1 as uses_ansi_nulls + , 1 as uses_quoted_identifier + , 0 as is_schema_bound + , 0 as uses_database_collation + , 0 as is_recompiled + , case when p.proisstrict then 1 else 0 end as null_on_null_input + , null::integer as execute_as_principal_id + , 0 as uses_native_compilation +from pg_proc p +inner join pg_namespace s on s.oid = p.pronamespace +inner join pg_type t on t.oid = p.prorettype +left join pg_collation c on c.oid = t.typcollation +where s.nspname not in ('information_schema', 'pg_catalog') +and has_function_privilege(p.oid, 'EXECUTE'); +GRANT SELECT ON sys.sql_modules TO PUBLIC; + +create or replace view sys.sysforeignkeys as +select + c.conname as name + , c.oid as object_id + , c.conrelid as fkeyid + , c.confrelid as rkeyid + , a_con.attnum as fkey + , a_conf.attnum as rkey + , a_conf.attnum as keyno +from pg_constraint c +inner join pg_namespace s on s.oid = c.connamespace +inner join pg_attribute a_con on a_con.attrelid = c.conrelid and a_con.attnum = any(c.conkey) +inner join pg_attribute a_conf on a_conf.attrelid = c.confrelid and a_conf.attnum = any(c.confkey) +where c.contype = 'f' +and s.nspname not in ('information_schema', 'pg_catalog'); +GRANT SELECT ON sys.sysforeignkeys TO PUBLIC; + +create or replace view sys.sysindexes as +select + i.object_id as id + , null::integer as status + , null::oid as first + , i.type as indid + , null::oid as root + , 0 as minlen + , 1 as keycnt + , 0 as groupid + , 0 as dpages + , 0 as reserved + , 0 as used + , 0 as rowcnt + , 0 as rowmodctr + , 0 as reserved3 + , 0 as reserved4 + , 0 as xmaxlen + , null::integer as maxirow + , 0 as OrigFillFactor + , 0 as StatVersion + , 0 as reserved2 + , null::integer as FirstIAM + , 0 as impid + , 0 as lockflags + , 0 as pgmodctr + , null::bytea as keys + , i.name + , null::bytea as statblob + , 800 as maxlen + , 0 as rows +from sys.indexes i; +GRANT SELECT ON sys.sysindexes TO PUBLIC; + +create or replace view sys.sysprocesses as +select + a.pid as spid + , null::integer as kpid + , coalesce(blocking_activity.pid, 0) as blocked + , null::bytea as waittype + , 0 as waittime + , a.wait_event_type as lastwaittype + , null::text as waitresource + , a.datid as dbid + , a.usesysid as uid + , 0 as cpu + , 0 as physical_io + , 0 as memusage + , a.backend_start as login_time + , a.query_start as last_batch + , 0 as ecid + , 0 as open_tran + , a.state as status + , null::bytea as sid + , a.client_hostname as hostname + , a.application_name as program_name + , null::varchar(10) as hostprocess + , a.query as cmd + , null::varchar(128) as nt_domain + , null::varchar(128) as nt_username + , null::varchar(12) as net_address + , null::varchar(12) as net_library + , a.usename as loginname + , null::bytea as context_info + , null::bytea as sql_handle + , 0 as stmt_start + , 0 as stmt_end + , 0 as request_id +from pg_stat_activity a +left join pg_catalog.pg_locks as blocked_locks on a.pid = blocked_locks.pid +left join pg_catalog.pg_locks blocking_locks + ON blocking_locks.locktype = blocked_locks.locktype + AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE + AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation + AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page + AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple + AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid + AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid + AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid + AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid + AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid + AND blocking_locks.pid != blocked_locks.pid + left join pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid; +GRANT SELECT ON sys.sysprocesses TO PUBLIC; + +create or replace view sys.types As +select format_type(t.oid, null) as name + , t.oid as system_type_id + , t.oid as user_type_id + , s.oid as schema_id + , null::integer as principal_id + , t.typlen as max_length + , 0 as precision + , 0 as scale + , c.collname as collation_name + , case when typnotnull then 0 else 1 end as is_nullable + , case typcategory when 'U' then 1 else 0 end as is_user_defined + , 0 as is_assembly_type + , 0 as default_object_id + , 0 as rule_object_id + , 0 as is_table_type +from pg_type t +inner join pg_namespace s on s.oid = t.typnamespace +left join pg_collation c on c.oid = t.typcollation; +GRANT SELECT ON sys.types TO PUBLIC; + +create view sys.objects as +select + t.name + , t.object_id + , t.principal_id + , t.schema_id + , t.parent_object_id + , 'U' as type + , 'USER_TABLE' as type_desc + , t.create_date + , t.modify_date + , t.is_ms_shipped + , t.is_published + , t.is_schema_published +from sys.tables t +where has_schema_privilege(t.schema_id, 'USAGE') +union all +select + v.name + , v.object_id + , v.principal_id + , v.schema_id + , v.parent_object_id + , 'V' as type + , 'VIEW' as type_desc + , v.create_date + , v.modify_date + , v.is_ms_shipped + , v.is_published + , v.is_schema_published +from sys.views v +where has_schema_privilege(v.schema_id, 'USAGE') +union all +select + f.name + , f.object_id + , f.principal_id + , f.schema_id + , f.parent_object_id + , 'F' as type + , 'FOREIGN_KEY_CONSTRAINT' + , f.create_date + , f.modify_date + , f.is_ms_shipped + , f.is_published + , f.is_schema_published + from sys.foreign_keys f +where has_schema_privilege(f.schema_id, 'USAGE') + union all +select + p.name + , p.object_id + , p.principal_id + , p.schema_id + , p.parent_object_id + , 'PK' as type + , 'PRIMARY_KEY_CONSTRAINT' as type_desc + , p.create_date + , p.modify_date + , p.is_ms_shipped + , p.is_published + , p.is_schema_published + from sys.key_constraints p +where has_schema_privilege(p.schema_id, 'USAGE') +union all +select + pr.name + , pr.object_id + , pr.principal_id + , pr.schema_id + , pr.parent_object_id + , pr.type + , pr.type_desc + , pr.create_date + , pr.modify_date + , pr.is_ms_shipped + , pr.is_published + , pr.is_schema_published + from sys.procedures pr +where has_schema_privilege(pr.schema_id, 'USAGE') +union all +select + p.relname as name + ,p.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , 0 as parent_object_id + , 'SO'::varchar(2) as type + , 'SEQUENCE_OBJECT'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_class p +inner join pg_namespace s on s.oid = p.relnamespace +where s.nspname not in ('information_schema', 'pg_catalog') +and p.relkind = 'S' +and has_schema_privilege(s.oid, 'USAGE'); +GRANT SELECT ON sys.objects TO PUBLIC; + +create or replace view sys.sysobjects as +select + s.name + , s.object_id as id + , s.type as xtype + , s.schema_id as uid + , 0 as info + , 0 as status + , 0 as base_schema_ver + , 0 as replinfo + , s.parent_object_id as parent_obj + , s.create_date as crdate + , 0 as ftcatid + , 0 as schema_ver + , 0 as stats_schema_ver + , s.type + , 0 as userstat + , 0 as sysstat + , 0 as indexdel + , s.modify_date as refdate + , 0 as version + , 0 as deltrig + , 0 as instrig + , 0 as updtrig + , 0 as seltrig + , 0 as category + , 0 as cache +from sys.objects s; +GRANT SELECT ON sys.sysobjects TO PUBLIC; + +create view sys.all_objects as +select + t.name + , t.object_id + , t.principal_id + , t.schema_id + , t.parent_object_id + , 'U' as type + , 'USER_TABLE' as type_desc + , t.create_date + , t.modify_date + , t.is_ms_shipped + , t.is_published + , t.is_schema_published +from sys.tables t +union all +select + v.name + , v.object_id + , v.principal_id + , v.schema_id + , v.parent_object_id + , 'V' as type + , 'VIEW' as type_desc + , v.create_date + , v.modify_date + , v.is_ms_shipped + , v.is_published + , v.is_schema_published +from sys.all_views v +union all +select + f.name + , f.object_id + , f.principal_id + , f.schema_id + , f.parent_object_id + , 'F' as type + , 'FOREIGN_KEY_CONSTRAINT' + , f.create_date + , f.modify_date + , f.is_ms_shipped + , f.is_published + , f.is_schema_published + from sys.foreign_keys f + union all +select + p.name + , p.object_id + , p.principal_id + , p.schema_id + , p.parent_object_id + , 'PK' as type + , 'PRIMARY_KEY_CONSTRAINT' as type_desc + , p.create_date + , p.modify_date + , p.is_ms_shipped + , p.is_published + , p.is_schema_published + from sys.key_constraints p +union all +select + pr.name + , pr.object_id + , pr.principal_id + , pr.schema_id + , pr.parent_object_id + , pr.type + , pr.type_desc + , pr.create_date + , pr.modify_date + , pr.is_ms_shipped + , pr.is_published + , pr.is_schema_published + from sys.procedures pr +union all +select + p.relname as name + ,p.oid as object_id + , null::integer as principal_id + , s.oid as schema_id + , 0 as parent_object_id + , 'SO'::varchar(2) as type + , 'SEQUENCE_OBJECT'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_class p +inner join pg_namespace s on s.oid = p.relnamespace +where p.relkind = 'S'; +GRANT SELECT ON sys.all_objects TO PUBLIC; + +create or replace view sys.system_objects as +select * from sys.all_objects o +inner join pg_namespace s on s.oid = o.schema_id +where s.nspname in ('information_schema', 'pg_catalog'); +GRANT SELECT ON sys.system_objects TO PUBLIC; + +CREATE VIEW sys.syscharsets +AS +SELECT 1001 as type, + 1 as id, + 0 as csid, + 0 as status, + NULL::nvarchar(128) as name, + NULL::nvarchar(255) as description , + NULL::varbinary(6000) binarydefinition , + NULL::image definition; +GRANT SELECT ON sys.syscharsets TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/upgrades/.gitignore b/contrib/babelfishpg_tsql/sql/upgrades/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/babelfishpg_tsql/src/analyzer.c b/contrib/babelfishpg_tsql/src/analyzer.c new file mode 100644 index 00000000000..340d7747075 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/analyzer.c @@ -0,0 +1,318 @@ +#include "postgres.h" +#include "analyzer.h" +#include "dynastack.h" +#include "stmt_walker.h" + +#define ANALYZER_INITIAL_STACK_SIZE 8 + +/*********************************************************************************** + * VISITOR ACTIONS DEFINITIONS + **********************************************************************************/ +static bool analyzer_try_catch_act(Walker_context *ctx, PLtsql_stmt_try_catch *stmt); +static bool analyzer_goto_act(Walker_context *ctx, PLtsql_stmt_goto *stmt); +static bool analyzer_label_act(Walker_context *ctx, PLtsql_stmt_label *stmt); +static bool analyzer_while_act(Walker_context *ctx, PLtsql_stmt_while *stmt); +static bool analyzer_exit_act(Walker_context *ctx, PLtsql_stmt_exit *stmt); +static bool analyzer_return_act(Walker_context *ctx, PLtsql_stmt_return *stmt); + +/*********************************************************************************** + * ANALYZER CONTEXT + **********************************************************************************/ +static Walker_context *make_analyzer_context(CompileContext *cmpl_ctx); +static void destroy_analyzer_context(void *ctx); + +/* all items MUST be destoryed in destroy_template_context */ +typedef struct +{ + /* for invalid GOTO check */ + DynaVec *trycatch_info_stack; /* current nesting stmt_try_catch */ + DynaVec *loop_stack; /* current nesting loops */ + DynaVec *gotos; /* store all user input goto stmts */ + + /* compile context */ + CompileContext *cmpl_ctx; +} AnalyzerContext; + +static Walker_context *make_analyzer_context(CompileContext *cmpl_ctx) +{ + Walker_context *walker = make_template_context(); + AnalyzerContext *analyzer = palloc(sizeof(AnalyzerContext)); + + analyzer->trycatch_info_stack = create_stack2(sizeof(TryCatchInfo), ANALYZER_INITIAL_STACK_SIZE); + analyzer->loop_stack = create_stack2(sizeof(PLtsql_stmt_while *), ANALYZER_INITIAL_STACK_SIZE); + analyzer->gotos = create_stack2(sizeof(PLtsql_stmt_goto *), ANALYZER_INITIAL_STACK_SIZE); + + /* compile context */ + analyzer->cmpl_ctx = cmpl_ctx; + + /* Regster actions */ + walker->try_catch_act = &analyzer_try_catch_act; + walker->goto_act = &analyzer_goto_act; + walker->label_act = &analyzer_label_act; + walker->while_act = &analyzer_while_act; + walker->exit_act = &analyzer_exit_act; + walker->return_act = &analyzer_return_act; + + /* Extra context */ + walker->extra_ctx = (void *) analyzer; + walker->destroy_extra_ctx = &destroy_analyzer_context; + return walker; +} + +static void destroy_analyzer_context(void *ctx) +{ + AnalyzerContext *analyzer_ctx = (AnalyzerContext *) ctx; + + destroy_vector(analyzer_ctx->trycatch_info_stack); + destroy_vector(analyzer_ctx->loop_stack); + destroy_vector(analyzer_ctx->gotos); + + pfree(analyzer_ctx); +} + +/*********************************************************************************** + * VISITOR ACTIONS IMPLEMENTATION + **********************************************************************************/ +static void save_scope(PLtsql_stmt *stmt, AnalyzerContext *analyzer_ctx) +{ + CompileContext *cmpl_ctx = analyzer_ctx->cmpl_ctx; + ScopeContext *scope_context = + hash_search(cmpl_ctx->stmt_scope_context, &stmt, HASH_ENTER, NULL); + + scope_context->nesting_trycatch_infos = + create_vector_copy(analyzer_ctx->trycatch_info_stack); + scope_context->nesting_loops = + create_vector_copy(analyzer_ctx->loop_stack); +} + +static bool analyzer_try_catch_act(Walker_context *ctx, PLtsql_stmt_try_catch *stmt) +{ + AnalyzerContext *analyzer_ctx = (AnalyzerContext*) ctx->extra_ctx; + TryCatchInfo try_catch_info; + TryCatchInfo *try_catch_info_ptr; + + try_catch_info.stmt = (PLtsql_stmt *) stmt; + try_catch_info.in_try_block = true; + vec_push_back(analyzer_ctx->trycatch_info_stack, &try_catch_info); + + general_walker_func(stmt->body, ctx); /* visit try block */ + + try_catch_info_ptr = (TryCatchInfo *) vec_back(analyzer_ctx->trycatch_info_stack); + try_catch_info_ptr->in_try_block = false; + + general_walker_func(stmt->handler, ctx); /* visit right chid */ + + vec_pop_back(analyzer_ctx->trycatch_info_stack); + + return false; +} + +static bool analyzer_goto_act(Walker_context *ctx, PLtsql_stmt_goto *stmt) +{ + AnalyzerContext *analyzer_ctx = (AnalyzerContext*) ctx->extra_ctx; + + save_scope((PLtsql_stmt*) stmt, analyzer_ctx); + vec_push_back(analyzer_ctx->gotos, &stmt); + return stmt_walker((PLtsql_stmt*)stmt, &general_walker_func, ctx); +} + +static bool analyzer_label_act(Walker_context *ctx, PLtsql_stmt_label *stmt) +{ + AnalyzerContext *analyzer_ctx = (AnalyzerContext*) ctx->extra_ctx; + CompileContext *cmpl_ctx = analyzer_ctx->cmpl_ctx; + bool found = false; + LabelStmtEntry *label_entry = + hash_search(cmpl_ctx->label_stmt_map, stmt->label, HASH_ENTER, &found); + + if (found) + { + /* label not unique within one procedure */ + PLtsql_stmt_label *label = label_entry->stmt; + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Label %s not unique wihtin one procedure in line %d, previous defined in line %d", + stmt->label, stmt->lineno, label->lineno))); + } + label_entry->stmt = stmt; + + save_scope((PLtsql_stmt*) stmt, analyzer_ctx); + + return stmt_walker((PLtsql_stmt*)stmt, &general_walker_func, ctx); +} + +static bool analyzer_while_act(Walker_context *ctx, PLtsql_stmt_while *stmt) +{ + AnalyzerContext *analyzer_ctx = (AnalyzerContext*) ctx->extra_ctx; + ListCell *s; + + vec_push_back(analyzer_ctx->loop_stack, &stmt); + save_scope((PLtsql_stmt*) stmt, analyzer_ctx); + + /* visit all children */ + foreach(s, stmt->body) + general_walker_func((PLtsql_stmt *) lfirst(s), ctx); + + vec_pop_back(analyzer_ctx->loop_stack); + return false; +} + +static bool analyzer_exit_act(Walker_context *ctx, PLtsql_stmt_exit *stmt) +{ + AnalyzerContext *analyzer_ctx = (AnalyzerContext*) ctx->extra_ctx; + + if (vec_size(analyzer_ctx->loop_stack) == 0) + { + if (stmt->is_exit) /* break */ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Do not support BREAK outside of a WHILE loop, line %d", stmt->lineno))); + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Do not support CONTINUE outside of a WHILE loop, line %d", stmt->lineno))); + } + save_scope((PLtsql_stmt*) stmt, analyzer_ctx); + + return stmt_walker((PLtsql_stmt*)stmt, &general_walker_func, ctx); +} + +static bool analyzer_return_act(Walker_context *ctx, PLtsql_stmt_return *stmt) +{ + AnalyzerContext *analyzer_ctx = (AnalyzerContext*) ctx->extra_ctx; + + save_scope((PLtsql_stmt*) stmt, analyzer_ctx); + return stmt_walker((PLtsql_stmt*)stmt, &general_walker_func, ctx); +} + +/*********************************************************************************** + * CHECKING FUNCTIONS + **********************************************************************************/ + +static bool check_goto_try_catch(DynaVec *src_stack, DynaVec *dest_stack); +static bool check_goto_loop(DynaVec *src_stack, DynaVec *dest_stack); + +static void check_unsupported_goto(AnalyzerContext *analyzer_ctx) +{ + CompileContext *cmpl_ctx = analyzer_ctx->cmpl_ctx; + size_t size = vec_size(analyzer_ctx->gotos); + PLtsql_stmt_label *label; + DynaVec *src_nesting_trycatch_infos, *dest_nesting_trycatch_infos; + DynaVec *src_nesting_loops, *dest_nesting_loops; + LabelStmtEntry *label_entry; + ScopeContext *scope_context; + size_t i; + + for (i = 0; i < size; i++) + { + PLtsql_stmt_goto *stmt_goto = + *(PLtsql_stmt_goto **) vec_at(analyzer_ctx->gotos, i); + + label_entry = + hash_search(cmpl_ctx->label_stmt_map, stmt_goto->target_label, + HASH_FIND, NULL); + + /* check existence of target label */ + if (!label_entry) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("GOTO target Label %s not defined", + stmt_goto->target_label))); + + /* source context */ + scope_context = + hash_search(cmpl_ctx->stmt_scope_context, &stmt_goto, HASH_FIND, NULL); + src_nesting_trycatch_infos = scope_context->nesting_trycatch_infos; + src_nesting_loops = scope_context->nesting_loops; + + /* destination context */ + label = label_entry->stmt; + scope_context = + hash_search(cmpl_ctx->stmt_scope_context, &label, HASH_FIND, NULL); + dest_nesting_trycatch_infos = scope_context->nesting_trycatch_infos; + dest_nesting_loops = scope_context->nesting_loops; + + /* check if goto a loop or try catch block */ + if (!check_goto_try_catch(src_nesting_trycatch_infos, dest_nesting_trycatch_infos)) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("GOTO into an try catch block not supported, label %s", + stmt_goto->target_label))); + + if (!check_goto_loop(src_nesting_loops, dest_nesting_loops)) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("GOTO into an while loop not supported, label %s", + stmt_goto->target_label))); + } +} + +static bool check_goto_try_catch(DynaVec *src_stack, DynaVec *dest_stack) +{ + if (vec_size(src_stack) < vec_size(dest_stack)) + return false; /* goto deeper try-catch block */ + else + { + size_t goto_stack_size = vec_size(src_stack); + size_t label_stack_size = vec_size(dest_stack); + size_t i; + for (i = 0; i < goto_stack_size && i < label_stack_size; i++) + { + TryCatchInfo *info1 = (TryCatchInfo *) vec_at(src_stack, i); + TryCatchInfo *info2 = (TryCatchInfo *) vec_at(dest_stack, i); + if (info1->stmt != info2->stmt || info1->in_try_block != info2->in_try_block) + return false; /* goto differen upper / sibling try-catch block */ + } + } + return true; +} + +static bool check_goto_loop(DynaVec *src_stack, DynaVec *dest_stack) +{ + if (vec_size(src_stack) < vec_size(dest_stack)) + return false; /* goto deeper loop */ + else + { + size_t goto_stack_size = vec_size(src_stack); + size_t label_stack_size = vec_size(dest_stack); + size_t i; + for (i = 0; i < goto_stack_size && i < label_stack_size; i++) + { + PLtsql_stmt *stmt1 = *(PLtsql_stmt **) vec_at(src_stack, i); + PLtsql_stmt *stmt2 = *(PLtsql_stmt **) vec_at(dest_stack, i); + if (stmt1 != stmt2) + return false; /* goto different upper / sibling loop block */ + } + } + return true; +} + +/*********************************************************************************** + * PLTSQL ANALYZER + **********************************************************************************/ + +void analyze(PLtsql_function *func, CompileContext *cmpl_ctx) +{ + Walker_context *walker; + AnalyzerContext *analyzer_ctx; + + if ((!func) || func->exec_codes) /* cached plan */ + return; + + walker = make_analyzer_context(cmpl_ctx); + analyzer_ctx = (AnalyzerContext *) walker->extra_ctx; + + PG_TRY(); + { + /* general checks through traversal */ + stmt_walker((PLtsql_stmt *) func->action, general_walker_func, walker); + + /* extra checks */ + check_unsupported_goto(analyzer_ctx); + } + PG_CATCH(); + { + destroy_template_context(walker); + PG_RE_THROW(); + } + PG_END_TRY(); + + destroy_template_context(walker); +} diff --git a/contrib/babelfishpg_tsql/src/analyzer.h b/contrib/babelfishpg_tsql/src/analyzer.h new file mode 100644 index 00000000000..0ba543a702b --- /dev/null +++ b/contrib/babelfishpg_tsql/src/analyzer.h @@ -0,0 +1,8 @@ +#ifndef ANALYZER_H +#define ANALYZER_H +#include "pltsql.h" +#include "compile_context.h" + +void analyze(PLtsql_function *func, CompileContext *cmpl_ctx); + +#endif /* ANALYZE_H */ diff --git a/contrib/babelfishpg_tsql/src/antlrTests/decl.sql b/contrib/babelfishpg_tsql/src/antlrTests/decl.sql new file mode 100644 index 00000000000..a009a5d1984 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/decl.sql @@ -0,0 +1,14 @@ +DO $$ +DECLARE @X INT = 42 +DECLARE @Y INT = @X * 2 + +PRINT @X +PRINT @Y + +BEGIN + DECLARE @INNER INT = @Y - 1 + PRINT @INNER +END + + PRINT @INNER +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/ifelse.sql b/contrib/babelfishpg_tsql/src/antlrTests/ifelse.sql new file mode 100644 index 00000000000..314284fe3f2 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/ifelse.sql @@ -0,0 +1,17 @@ +DO $$ + +DECLARE @X INT = 4 + +IF (@X = 3) + PRINT '3' +ELSE IF (@X = 4) + BEGIN + PRINT 'the answer is: ' + PRINT '4' + END +ELSE IF (@X = 5) + PRINT '5' +ELSE + PRINT 'unknown' + +$$LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/inval.sql b/contrib/babelfishpg_tsql/src/antlrTests/inval.sql new file mode 100644 index 00000000000..34bc8caee01 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/inval.sql @@ -0,0 +1,23 @@ +DROP TABLE foo +GO + +DROP FUNCTION needs_foo() +GO + +CREATE TABLE foo(pkey int) +GO + +CREATE FUNCTION needs_foo() RETURNS bool AS +$$ +DECLARE + x foo%ROWTYPE; +BEGIN + + FOR x IN SELECT * FROM foo LOOP + x.pkey := 4; + END LOOP; + + RETURN true; + +END; +$$ LANGUAGE plpgsql; diff --git a/contrib/babelfishpg_tsql/src/antlrTests/nestedBlocks.sql b/contrib/babelfishpg_tsql/src/antlrTests/nestedBlocks.sql new file mode 100644 index 00000000000..d015c69179f --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/nestedBlocks.sql @@ -0,0 +1,15 @@ +DO $$ + PRINT 'line 1' + BEGIN + PRINT 2 + END + + BEGIN + PRINT 3 * 1 + PRINT [upper]('four score and seven years ago') + BEGIN + PRINT 4 + END + END + +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/oneBlock.sql b/contrib/babelfishpg_tsql/src/antlrTests/oneBlock.sql new file mode 100644 index 00000000000..ecbaa5332df --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/oneBlock.sql @@ -0,0 +1,6 @@ +DO $$ +BEGIN + PRINT 1 + PRINT 2 +END +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/print.sql b/contrib/babelfishpg_tsql/src/antlrTests/print.sql new file mode 100644 index 00000000000..bd6e5f1f233 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/print.sql @@ -0,0 +1,14 @@ +DO $$ +PRINT 1 +PRINT 2 +PRINT 3 +PRINT 4 +PRINT 5 +-- IF (@X < 10) +--BEGIN +-- PRINT 6 +-- PRINT 7 +-- PRINT 8 +-- END +PRINT 9 +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/print2.sql b/contrib/babelfishpg_tsql/src/antlrTests/print2.sql new file mode 100644 index 00000000000..35d1b054512 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/print2.sql @@ -0,0 +1,13 @@ +DO $$ +BEGIN + PRINT 1 + PRINT 2 + + BEGIN + PRINT 3 + PRINT 4 + END + + DROP TABLE [foo] +END +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/proc_simple.sql b/contrib/babelfishpg_tsql/src/antlrTests/proc_simple.sql new file mode 100644 index 00000000000..0cd9187663b --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/proc_simple.sql @@ -0,0 +1,6 @@ +DO $$ +CREATE FUNCTION RETURN_42() RETURNS INT AS +BEGIN + RETURN 42 +END +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/returnsTable.sql b/contrib/babelfishpg_tsql/src/antlrTests/returnsTable.sql new file mode 100644 index 00000000000..0ed02b892c7 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/returnsTable.sql @@ -0,0 +1,5 @@ +DO $$ +CREATE FUNCTION CLASSES (@MINPAGES AS INT) RETURNS TABLE AS + RETURN (SELECT 42 AS VALUE) + -- RELNAME, RELPAGES FROM PG_CLASS WHERE RELPAGES > @MINPAGES +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/series.sql b/contrib/babelfishpg_tsql/src/antlrTests/series.sql new file mode 100644 index 00000000000..688830a973d --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/series.sql @@ -0,0 +1,11 @@ +DO $$ + + PRINT 'line 1' + + INSERT INTO [foo] VALUES(1,2,3*4) + + THROW + + RETURN 42 + +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/simple.sql b/contrib/babelfishpg_tsql/src/antlrTests/simple.sql new file mode 100644 index 00000000000..cbef5f9255c --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/simple.sql @@ -0,0 +1,6 @@ +DO $$ + PRINT 'line 1' + PRINT 2 + PRINT 3 * 2 + +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/simple2.sql b/contrib/babelfishpg_tsql/src/antlrTests/simple2.sql new file mode 100644 index 00000000000..0ba7d4da539 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/simple2.sql @@ -0,0 +1,10 @@ +DO $$ + PRINT 'line 1' + BEGIN + PRINT 'nested' + PRINT 'block' + END + PRINT 2 + PRINT 3 * 2 + +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/single.sql b/contrib/babelfishpg_tsql/src/antlrTests/single.sql new file mode 100644 index 00000000000..7239907898a --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/single.sql @@ -0,0 +1,3 @@ +DO $$ + PRINT 'line 1' +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/throw.sql b/contrib/babelfishpg_tsql/src/antlrTests/throw.sql new file mode 100644 index 00000000000..41cb6ef035e --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/throw.sql @@ -0,0 +1,3 @@ +DO $$ +THROW 52000, 'error message goes here', 200 +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/tryCatch.sql b/contrib/babelfishpg_tsql/src/antlrTests/tryCatch.sql new file mode 100644 index 00000000000..f83878f940a --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/tryCatch.sql @@ -0,0 +1,26 @@ +DO $$ + DECLARE @VAL INT + + BEGIN TRY + PRINT 'inside try' + + SET @VAL = @VAL / 0 + + PRINT 'never reached' + + END TRY + BEGIN CATCH + PRINT 'inside catch' + END CATCH + + PRINT 'between' + + BEGIN TRY + PRINT 'inside second try' + END TRY + BEGIN CATCH + PRINT 'never reached' + END CATCH + + PRINT 'final' +$$ LANGUAGE 'pltsql' diff --git a/contrib/babelfishpg_tsql/src/antlrTests/while.sql b/contrib/babelfishpg_tsql/src/antlrTests/while.sql new file mode 100644 index 00000000000..82efcc2fd50 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/antlrTests/while.sql @@ -0,0 +1,23 @@ +DO $$ +DECLARE @X INT = 0 + +WHILE( @X < 10 ) +BEGIN + PRINT @X + SET @X = @X + 1 + + IF @X = 5 + BEGIN + PRINT 'skipping 5' + CONTINUE + END + + IF @X = 7 + BREAK + +END + +PRINT 'yyy' + +$$ LANGUAGE 'pltsql' + diff --git a/contrib/babelfishpg_tsql/src/applock.c b/contrib/babelfishpg_tsql/src/applock.c new file mode 100644 index 00000000000..b49f8602bae --- /dev/null +++ b/contrib/babelfishpg_tsql/src/applock.c @@ -0,0 +1,884 @@ +/*------------------------------------------------------------------------- + * + * applock.c + * Application Lock Functionality for Babelfish + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/xact.h" +#include "executor/spi.h" +#include "fmgr.h" +#include "miscadmin.h" +#include "parser/parser.h" +#include "pltsql.h" +#include "storage/lmgr.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/timeout.h" +#include "datatypes.h" + + +PG_FUNCTION_INFO_V1(sp_getapplock_function); +PG_FUNCTION_INFO_V1(sp_releaseapplock_function); +PG_FUNCTION_INFO_V1(APPLOCK_MODE); +PG_FUNCTION_INFO_V1(APPLOCK_TEST); + +/* + * Applock local and global hashmaps. The local one keeps track of applock + * that the current session owns. The global one resolves hash conflict if + * two different lock resource name are hashed to the same integer key. + * Both uses the same cache entry structure for convenience. + */ +static HTAB * appLockCacheLocal = NULL; +static HTAB * appLockCacheGlobal = NULL; + +/* Max length of applock resource name string (including the ending '\0') */ +#define APPLOCK_MAX_RESOURCE_LENGTH 256 +/* + * Max number of retries to search for usable key when hash collision happens. + * The chance of multiple strings being hashed to the same key is roughly + * (1/2^63)*(#_of_strings-1). So a small APPLOCK_MAX_TRY_SEARCH_KEY should be + * enough. Also, because we have to scan all the possible candidate keys when + * looking for a usable key (see ApplockGetUsableKey()), a small + * APPLOCK_MAX_TRY_SEARCH_KEY is preferred too. + */ +#define APPLOCK_MAX_TRY_SEARCH_KEY 5 + +typedef struct applockcacheent +{ + int64 key; /* (hashed) key integer of the lock */ + char resource[APPLOCK_MAX_RESOURCE_LENGTH]; /* Resource name string of the lock */ + uint32_t refcount; /* Currently how many times this lock is being held. + Note the count may be different locally/globally.*/ + slist_head mode_head; /* lock mode list, keeping track of all lock modes + currently being held with this lock resource . + Only used in local cache. */ + bool is_session; /* If it's session lock or transaction lock */ +} AppLockCacheEnt; + +/* Linked-list struct for keeping track of the lockmodes one owns */ +typedef struct +{ + slist_node sn; + short mode; +} AppLockModeNode; + +/* + * Applock modes + * + * Table of compatibility ('Yes' indicates compatible): + * + * mode IS S U IX X + * Intent shared (IS) Yes Yes Yes Yes No + * Shared (S) Yes Yes Yes No No + * Update (U) Yes Yes No No No + * Intent exclusive (IX) Yes No No Yes No + * Exclusive (X) No No No No No + * + * Note that APPLOCKMODE_SHAREDINTENTEXCLUSIVE and + * APPLOCKMODE_UPDATEINTENTEXCLUSIVE are special lockmodes that + * are NOT acquirable by sp_getapplock but can be returned by + * APPLOCK_MODE(). See comments for APPLOCK_MODE(). + */ +typedef enum { + APPLOCKMODE_NOLOCK, + APPLOCKMODE_INTENTSHARED, + APPLOCKMODE_SHARED, + APPLOCKMODE_UPDATE, + APPLOCKMODE_INTENTEXCLUSIVE, + APPLOCKMODE_EXCLUSIVE, + APPLOCKMODE_SHAREDINTENTEXCLUSIVE, + APPLOCKMODE_UPDATEINTENTEXCLUSIVE, +} Applock_All_Lockmode; + +/* + * Strings for Applock modes. The order MUST match the mode enum in + * Applock_All_Lockmode. + */ +static const char *AppLockModeStrings[] = +{ + "NoLock", + "IntentShared", + "Shared", + "Update", + "IntentExclusive", + "Exclusive" + "SharedIntentExclusive", + "UpdateIntentExclusive", +}; + +static void ApplockPrintMessage(const char *fmt, ...) { + char msg[128]; + va_list args; + + va_start(args, fmt); + vsprintf(msg, fmt, args); + + ereport(WARNING, errmsg_internal("%s", msg)); + if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->send_info) + ((*pltsql_protocol_plugin_ptr)->send_info) (0, 1, 0, msg, 0); + + va_end (args); +} + +/* Helper macro to validate and get a string argument */ +#define ApplockGetStringArg(argnum, OUT) \ + do { \ + if (fcinfo->args[argnum].isnull) { \ + ApplockPrintMessage("parameter cannot be null"); \ + return -999; \ + } \ + OUT = text_to_cstring(DatumGetVarCharPP(PG_GETARG_DATUM(argnum))); \ + } while (0); + +#define SET_LOCKTAG_APPLOCK(locktag,id1,id2,id3,id4) \ + ((locktag).locktag_field1 = (id1), \ + (locktag).locktag_field2 = (id2), \ + (locktag).locktag_field3 = (id3), \ + (locktag).locktag_field4 = (id4), \ + (locktag).locktag_type = LOCKTAG_ADVISORY, \ + (locktag).locktag_lockmethodid = APPLOCK_LOCKMETHOD) + +/* + * PG advisory lock uses 0 and 1 for field4 (see comments for SET_LOCKTAG_INT64). + * We use 2 to avoid conflict with it. + */ +#define ApplockSetLocktag(tag, key64) \ + SET_LOCKTAG_APPLOCK(tag, \ + MyDatabaseId, \ + (uint32) ((key64) >> 32), \ + (uint32) (key64), \ + 2) + +#define AppLockCacheInsert(ID, ENTRY) \ + do { \ + bool found; \ + (ENTRY) = (AppLockCacheEnt*) hash_search(appLockCacheLocal, \ + (void *) &(ID), \ + HASH_ENTER, &found); \ + if (!found) { \ + (ENTRY)->refcount = 0; \ + strcpy((ENTRY)->resource, ""); \ + slist_init(&(ENTRY)->mode_head); \ + } \ +} while(0) + +#define AppLockCacheLookup(ID, ENTRY) \ + do { \ + (ENTRY) = (AppLockCacheEnt *) hash_search(appLockCacheLocal, \ + (void *) &(ID), \ + HASH_FIND, NULL); \ +} while(0) + +#define AppLockCacheDelete(ID) \ + do { \ + AppLockCacheEnt *hentry; \ + hentry = (AppLockCacheEnt *) hash_search(appLockCacheLocal, \ + (void *) &(ID), \ + HASH_REMOVE, NULL); \ + if (hentry == NULL) \ + ApplockPrintMessage("failed to delete app lock entry for key %ld", ID); \ +} while(0) + +#define ApplockSetLockTimeout(val) \ + do { \ + if (timeout != -99) { \ + char timeout_str[16]; \ + sprintf(timeout_str, "%d", val); \ + SetConfigOption("lock_timeout", timeout_str, \ + PGC_USERSET, PGC_S_OVERRIDE); \ + } \ +} while (0); + +#define ApplockCheckParallelMode(suppress_warning) \ + do { \ + if (IsInParallelMode()) { \ + if (!suppress_warning) \ + ApplockPrintMessage("cannot use advisory locks during a parallel operation"); \ + return -999; \ + } \ + } while (0); + +#define ApplockCheckLockmode(IN, OUT, suppress_warning) \ + do { \ + if (pg_strcasecmp(IN, "IntentShared") == 0) \ + (OUT) = APPLOCKMODE_INTENTSHARED; \ + else if (pg_strcasecmp(IN, "Shared") == 0) \ + (OUT) = APPLOCKMODE_SHARED; \ + else if (pg_strcasecmp(IN, "Update") == 0) \ + (OUT) = APPLOCKMODE_UPDATE; \ + else if (pg_strcasecmp(IN, "IntentExclusive") == 0) \ + (OUT) = APPLOCKMODE_INTENTEXCLUSIVE; \ + else if (pg_strcasecmp(IN, "Exclusive") == 0) \ + (OUT) = APPLOCKMODE_EXCLUSIVE; \ + else { \ + if (!suppress_warning) \ + ApplockPrintMessage("Option \'%s\' not recognized for \'@LockMode\' parameter", IN); \ + return -999; \ + } \ + } while (0) + +#define ApplockCheckLockowner(IN, OUT, suppress_warning) \ + do { \ + if (pg_strcasecmp(IN, "Session") == 0) \ + (OUT) = true; \ + else if (pg_strcasecmp(IN, "Transaction") == 0) \ + (OUT) = false; \ + else { \ + if (!suppress_warning) \ + ApplockPrintMessage("Option \'%s\' not recognized for \'@LockOwner\' parameter", IN); \ + return -999; \ + } \ + } while (0) + +/* + * We accept any input of dbprincipal until we decide otherwise. + * Also a placeholder to escape unused variable error for dbprincipal. + */ +#define ApplockCheckDbPrincipal(IN) \ + do { \ + if (pg_strcasecmp((IN), "dbo")) \ + ; \ + } while (0); + +static void ApplockRemoveCache(bool release_session); + +/* + * Simple consistent hashing function to convert a string to an int. + * We'll avoid return non-negative values because that will be used for errors. + * The chance of 2 strings colliding with the same key is about 1/2^63. + * See https://cp-algorithms.com/string/string-hashing.html + */ +static int64 +applock_simple_hash(char *str) +{ + const int p = 31; + const int64 m = INT64_MAX; + uint64 hash_value = 0; + int64 p_pow = 1; + char c; + + c = *str; + while (c) { + hash_value = (hash_value + (c - 'a' + 1) * p_pow) % m; + p_pow = (p_pow * p) % m; + c = *++str; + } + return hash_value; +} + +/* + * Get PG Lock mode for corresponding Applock mode. + * See AppLockConflicts[] defined in backend/storage/lmgr/lock.c. + */ +static short getPGLockMode(short applockmode) +{ + short mode = 0; + + if (applockmode == APPLOCKMODE_EXCLUSIVE) + mode = ExclusiveLock; + else if (applockmode == APPLOCKMODE_SHARED) + mode = ShareLock; + else if (applockmode == APPLOCKMODE_UPDATE) + mode = ShareUpdateExclusiveLock; + else if (applockmode == APPLOCKMODE_INTENTSHARED) + mode = RowShareLock; + else if (applockmode == APPLOCKMODE_INTENTEXCLUSIVE) + mode = RowExclusiveLock; + else + ApplockPrintMessage("wrong application lock mode %d", applockmode); + + return mode; +} + +/* Initialize both local and global hashmaps */ +static void initApplockCache() +{ + HASHCTL ctl; + + /* Local cache */ + MemSet(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(int64); + ctl.entrysize = sizeof(AppLockCacheEnt); + appLockCacheLocal = hash_create("Applock Cache", 16, + &ctl, HASH_ELEM | HASH_BLOBS); + + /* Global cache */ + LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); + MemSet(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(int64); + ctl.entrysize = sizeof(AppLockCacheEnt); + appLockCacheGlobal = (HTAB*)ShmemInitHash("Applock", + /*table size*/ 32, + /*max table size*/ 32, + &ctl, + HASH_ELEM); + LWLockRelease(AddinShmemInitLock); + + /* + * Init this function handler to be called when PG implicitly + * release locks at the end of transaction/session. + */ + applock_release_func_handler = (void*) ApplockRemoveCache; +} + +/* Search a key corresponding to a resource name in local hashmap. */ +static int64 AppLockSearchKeyLocal(char *resource) +{ + AppLockCacheEnt *entry; + int64 key; + int try_search = 0; + + key = applock_simple_hash(resource); + while (try_search++ < APPLOCK_MAX_TRY_SEARCH_KEY) { + entry = (AppLockCacheEnt*) hash_search(appLockCacheLocal, + (void *) &key, + HASH_FIND, NULL); + if (entry && strcmp(entry->resource, resource) == 0) + return key; + /* be mindful of overflow */ + key = (key % INT64_MAX) + 1; + } + + return -1; +} + +/* Search a key corresponding to a resource name in global hashmap. */ +static int64 AppLockSearchKeyGlobal(char *resource) +{ + AppLockCacheEnt *entry; + int64 key; + int try_search = 0; + + LWLockAcquire(TsqlApplockSyncLock, LW_SHARED); + + key = applock_simple_hash(resource); + while (try_search++ < APPLOCK_MAX_TRY_SEARCH_KEY) { + entry = (AppLockCacheEnt*) hash_search(appLockCacheGlobal, + (void *) &key, + HASH_FIND, NULL); + if (entry && strcmp(entry->resource, resource) == 0) { + LWLockRelease(TsqlApplockSyncLock); + return key; + } + /* be mindful of overflow */ + key = (key % INT64_MAX) + 1; + } + + LWLockRelease(TsqlApplockSyncLock); + return -1; +} + +/* + * Un-reference an entry in the appLockCacheGlobal. + * Delete it if its refcount is reduced to 0. + */ +static void ApplockUnrefGlobalCache(int64 key) +{ + AppLockCacheEnt *entry; + + LWLockAcquire(TsqlApplockSyncLock, LW_EXCLUSIVE); + entry = (AppLockCacheEnt *) hash_search(appLockCacheGlobal, + (void *) &key, + HASH_FIND, NULL); + if (entry && --entry->refcount == 0) { + hash_search(appLockCacheGlobal, + (void *) &key, + HASH_REMOVE, NULL); + strcpy(entry->resource, ""); + } + LWLockRelease(TsqlApplockSyncLock); +} + +/* + * Get a usable key from the resource string that doesn't collide + * with existing ones. + * Return a usable key (non-negative integer) if found, or -1 if couldn't. + */ +static int64 ApplockGetUsableKey(char *resource) +{ + int64 key, usable_key; + bool found; + AppLockCacheEnt *entry; + int try_search = 0; + + /* Firstly, try search in the global cache to see if it's available already*/ + if ((key = AppLockSearchKeyGlobal(resource)) != -1) { + LWLockAcquire(TsqlApplockSyncLock, LW_EXCLUSIVE); + entry = (AppLockCacheEnt*) hash_search(appLockCacheGlobal, + (void *) &key, + HASH_ENTER, &found); + /* Someone might've just deleted it. So check it before modify.*/ + if (found) { + ++entry->refcount; + LWLockRelease(TsqlApplockSyncLock); + return key; + } + LWLockRelease(TsqlApplockSyncLock); + } + + /* Otherwise, try generating a new key for this resource */ + + /* convert resource string to key integer */ + key = applock_simple_hash(resource); + usable_key = -1; + + LWLockAcquire(TsqlApplockSyncLock, LW_EXCLUSIVE); + + /* + * Some different resource name may have been hashed to the same key. + * In that case, we keep incrementing key until we find a usable one. + * + * NB: it's not very meaningful to try too many times because if it + * turns out that a couple of random keys have somehow all been used, + * we probably have a bug somewhere so it's better to error out. + * Also, we have to search all the possible candidate keys for the resource + * to make sure someone else did not just insert the same resource with + * some key unknown to the caller. + */ + while (try_search++ < APPLOCK_MAX_TRY_SEARCH_KEY) { + entry = (AppLockCacheEnt*) hash_search(appLockCacheGlobal, + (void *) &key, + HASH_FIND, NULL); + /* Someone might've just inserted an entry for this resource. */ + if (entry && strcmp(entry->resource, resource) == 0) { + entry->refcount++; + LWLockRelease(TsqlApplockSyncLock); + return key; + } + /* Key usable, record it if not done so. */ + if (!entry && usable_key == -1) + usable_key = key; + + /* Keep searching, be mindful of overflow */ + key = (key % INT64_MAX) + 1; + } + + if (usable_key != -1) { + entry = (AppLockCacheEnt*) hash_search(appLockCacheGlobal, + (void *) &usable_key, + HASH_ENTER, &found); + /* It must be non-existing at this point. */ + Assert(!found); + + entry->key = usable_key; + entry->refcount = 1; + strcpy(entry->resource, resource); + } + + LWLockRelease(TsqlApplockSyncLock); + return usable_key; +} + +/* + * Common function for sp_getapplock_function() and APPLOCK_TEST(). + * + * Returns: + * 0: lock acquired successfully. + * -999: lock request attempt failed. + * 1: lock acquired successfully but after waiting. + * -1: timed out. + * -2: lock request canceled. + * -3: lock request was chosen as a deadlock victim. + */ +static int _sp_getapplock_internal (char *resource, char *lockmode, + char *lockowner, int32_t timeout, + char *dbprincipal, bool suppress_warning) +{ + int32_t cur_timeout; + int64 key; + LOCKTAG tag; + short mode; + bool is_session; + bool lock_timeout_occurred = false; + bool no_wait = false; + AppLockCacheEnt *entry; + volatile TimestampTz start_time; + AppLockModeNode *node; + + /* a few sanity checks */ + ApplockCheckParallelMode(suppress_warning); + ApplockCheckLockmode(lockmode, mode, suppress_warning); + ApplockCheckLockowner(lockowner, is_session, suppress_warning); + ApplockCheckDbPrincipal(dbprincipal); + + if (pg_strcasecmp(lockowner, "Transaction") == 0 && !IsTransactionBlockActive()) + { + if (!suppress_warning) + ApplockPrintMessage("You attempted to acquire a transactional application lock without an active transaction."); + return -999; + } + if ((key = ApplockGetUsableKey(resource)) < 0) + { + if (!suppress_warning) + ApplockPrintMessage("could not find usable key for lock resource %s.",resource); + return -999; + } + + ApplockSetLocktag(tag, key); + + /* + * Setting timeout if timeout is not the meaningless default value (-99). + * Note some special cases in timeout: in TSQL -1 means wait forever + * and 0 means do not wait at all. But in PG, 0 means wait forever and + * -1 is meaningless. To make PG not wait at all, we need to pass + * no_wait=true to LockAcquire(). + */ + if (timeout == 0) + no_wait = true; + timeout = (timeout == -1 ? 0 : timeout); + cur_timeout = atoi(GetConfigOption("lock_timeout", false, false)); + ApplockSetLockTimeout(timeout); + + start_time = GetCurrentTimestamp(); + /* finally, attempt to acquire the lock.*/ + PG_TRY(); + { + /* If lock is unavailable, throw an error to let the catch block deal with it */ + if (LockAcquire(&tag, getPGLockMode(mode), is_session, no_wait) == LOCKACQUIRE_NOT_AVAIL) + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("Applock resource \'%s\' unavailable", resource))); + } + PG_CATCH(); + { + /* + * Exceptions during lock acquiring. This could be timeout, deadlock + * or other failures. Note that we have to return something here + * instead of throwing the errors out because otherwise the caller + * won't be able to get the return code as defined in TSQL standard. + * Therefore, we unfortunately can't print PG's nice deadlock report. + */ + + /* Un-referencing the global cache entry associated with this key. */ + ApplockUnrefGlobalCache(key); + + /* + * Did timeout occur? + * + * NB: ERRCODE_LOCK_NOT_AVAILABLE is not just for timeout, so we + * have to check the elapse time to really make sure. + * Also, although get_timeout_indicator(LOCK_TIMEOUT, if_reset) can + * check the same but when timeout happens, ProcessInterrupts() always + * reset the indicator, thus we have to use another way. + */ + lock_timeout_occurred = timeout >= 0 && + get_timeout_finish_time(LOCK_TIMEOUT) + - start_time > (int64)timeout * 1e3 && + geterrcode() == ERRCODE_LOCK_NOT_AVAILABLE; + + /* reset timeout back */ + ApplockSetLockTimeout(cur_timeout); + + if (lock_timeout_occurred) + { + if (!suppress_warning) + ApplockPrintMessage("Applock request for \'%s\' timed out", resource); + return -1; + } + /* Not timed out, but it's still due to lock unavailable. */ + else if (geterrcode() == ERRCODE_LOCK_NOT_AVAILABLE) + { + if (!suppress_warning) + ApplockPrintMessage("Applock resource \'%s\' unavailable", resource); + return -999; + } + /* Did deadlock occur? */ + else if (geterrcode() == ERRCODE_T_R_DEADLOCK_DETECTED) + { + if (!suppress_warning) + ApplockPrintMessage("Deadlock detected in applock request for \'%s\' ", resource); + return -3; + } + /* + * Regard all other exceptions as lock request being canceled (e.g. + * the calling query was interrupted and terminated.) + */ + else + { + if (!suppress_warning) + ApplockPrintMessage("Applock request for \'%s\' is canceled", resource); + return -2; + } + } + PG_END_TRY(); + + ApplockSetLockTimeout(cur_timeout); + + /* lock aquired, we can insert or update the local cache entry now. */ + AppLockCacheInsert(key, entry); + strcpy(entry->resource, resource); + entry->refcount++; + node = malloc(sizeof(AppLockModeNode)); + node->mode = mode; + slist_push_head(&entry->mode_head, &node->sn); + entry->is_session = is_session; + + return 0; +} + +/* + * Common function for sp_releaseapplock_function() and APPLOCK_TEST(). + * + * Returns: + * 0: lock released successfully. + * -999: lock release attempt failed. + */ +static int _sp_releaseapplock_internal(char *resource, char *lockowner, + char *dbprincipal, bool suppress_warning) +{ + int64 key; + LOCKTAG tag; + short mode; + bool is_session; + AppLockCacheEnt *entry; + AppLockModeNode *node; + + /* a few sanity checks */ + ApplockCheckParallelMode(suppress_warning); + ApplockCheckLockowner(lockowner, is_session, suppress_warning); + ApplockCheckDbPrincipal(dbprincipal); + + /* Search in the global cache for the key. */ + if ((key = AppLockSearchKeyGlobal(resource)) == -1) { + if (!suppress_warning) + ApplockPrintMessage("No lock resource \'%s\' acquired before.", resource); + LWLockRelease(TsqlApplockSyncLock); + return -999; + } + + /* verify the key in the local cache, and if the lock owner matches */ + AppLockCacheLookup(key, entry); + if (entry == NULL) { + if (!suppress_warning) + ApplockPrintMessage("No lock resource \'%s\' acquired before.", resource); + return -999; + } + if (is_session != entry->is_session) { + if (!suppress_warning) + ApplockPrintMessage("Wrong LockOwner for lock resource \'%s\', it is a %s lock.", + resource, entry->is_session ? "Session" : "Transaction"); + return -999; + } + + /* Set tag according to key. */ + ApplockSetLocktag(tag, key); + + /* get the same lock mode as recorded */ + mode = ((AppLockModeNode*)entry->mode_head.head.next)->mode; + + if (!LockRelease(&tag, getPGLockMode(mode), is_session)) + return -999; + + /* Un-referencing the local cache entry and delete it if needed. */ + node = (AppLockModeNode*)slist_pop_head_node((slist_head*)&entry->mode_head); + free(node); + if (--entry->refcount == 0) + AppLockCacheDelete(key); + + /* Un-referencing the global cache entry associated with this key. */ + ApplockUnrefGlobalCache(key); + + return 0; +} + +/* + * Get application lock function, to be called by procedure sp_getapplock + */ +Datum +sp_getapplock_function(PG_FUNCTION_ARGS) +{ + char *resource, *lockmode, *lockowner, *dbprincipal; + int32_t timeout; + int ret; + + /* Init applock hash table if we haven't done so. */ + if (!appLockCacheLocal) + initApplockCache(); + + ApplockGetStringArg(0, resource); + ApplockGetStringArg(1, lockmode); + ApplockGetStringArg(2, lockowner); + timeout = DatumGetInt32(PG_GETARG_DATUM(3)); + ApplockGetStringArg(4, dbprincipal); + + ret = _sp_getapplock_internal(resource, lockmode, lockowner, timeout, dbprincipal, false); + + PG_RETURN_INT32(ret); +} + +/* + * Release application lock function, to be called by procedure sp_releaseapplock + */ +Datum +sp_releaseapplock_function(PG_FUNCTION_ARGS) +{ + char *resource, *lockowner, *dbprincipal; + int ret; + + /* Init applock hash table if we haven't done so. */ + if (!appLockCacheLocal) + initApplockCache(); + + ApplockGetStringArg(0, resource); + ApplockGetStringArg(1, lockowner); + ApplockGetStringArg(2, dbprincipal); + + ret = _sp_releaseapplock_internal(resource, lockowner, dbprincipal, false); + + PG_RETURN_INT32(ret); +} + +/* + * Get lockmode of the applock the caller holds and return the mode in string. + * + * NB: when there are more than one lock modes, the mode to return is the 'highest' + * lockmode among them. The main order is: from lowest (most relaxed) to + * highest (most strict): IntentShared < Shared < Update < Exclusive. + * A special case is IntentExclusive which if is held, there could be 3 + * different return modes depending on what's the other mode being held: + * 1. IntentExclusive + IntentExclusive = IntentExclusive + * 2. IntentExclusive + IntentShared = SharedIntentExclusive + * 3. IntentExclusive + Update = UpdateIntentExclusive + */ +Datum +APPLOCK_MODE(PG_FUNCTION_ARGS) +{ + char *resource; + short high_mode, ret_mode; + AppLockCacheEnt *entry; + int64 key; + slist_iter iter; + bool has_intent_exc; + + /* Init applock hash table if not yet done. */ + if (!appLockCacheLocal) + initApplockCache(); + + ApplockGetStringArg(1, resource); + + /* If we don't own the lock, just return NoLock */ + if ((key = AppLockSearchKeyLocal(resource)) < 0) + PG_RETURN_VARCHAR_P(tsql_varchar_input(AppLockModeStrings[APPLOCKMODE_NOLOCK], + strlen(AppLockModeStrings[APPLOCKMODE_NOLOCK]), + -1)); + + /* + * Loop all the lock modes I've owned this resource with, and find the + * correct string to return. + */ + AppLockCacheLookup(key, entry); + high_mode = APPLOCKMODE_NOLOCK; + has_intent_exc = false; + slist_foreach(iter, &entry->mode_head) { + AppLockModeNode *node = slist_container(AppLockModeNode, sn, iter.cur); + if (node->mode == APPLOCKMODE_INTENTEXCLUSIVE) + has_intent_exc = true; + if (node->mode > high_mode) + high_mode = node->mode; + } + if (has_intent_exc && high_mode == APPLOCKMODE_INTENTSHARED) + ret_mode = APPLOCKMODE_SHAREDINTENTEXCLUSIVE; + else if (has_intent_exc && high_mode == APPLOCKMODE_UPDATE) + ret_mode = APPLOCKMODE_UPDATEINTENTEXCLUSIVE; + else + ret_mode = high_mode; + + PG_RETURN_VARCHAR_P(tsql_varchar_input(AppLockModeStrings[ret_mode], + strlen(AppLockModeStrings[ret_mode]), + -1)); +} + +/* + * Test if an applock can be acquired. We took a simple approach where we + * try aqcuiring the lock and releasing it immediately. + * The alternative is to remember all lockmodes and who owns them in the global + * hashmap, which entails too much of invasiveness and additional shared + * memory management. + * + * Returns: + * 1 - the lock is grantable. + * 0 - the lock is not grantable. + */ +Datum +APPLOCK_TEST(PG_FUNCTION_ARGS) +{ + char *resource, *lockmode, *lockowner, *dbprincipal; + + /* Init applock hash table if not yet done. */ + if (!appLockCacheLocal) + initApplockCache(); + + ApplockGetStringArg(0, dbprincipal); + ApplockGetStringArg(1, resource); + ApplockGetStringArg(2, lockmode); + ApplockGetStringArg(3, lockowner); + + /* + * Pass the arguments and a time out of 0 (no wait) to the internal + * getapplock function. Suppress the warning messages as they would be + * normal during testing a lock. If anything happened besides having + * acquired the lock successfully, just return 0. + */ + if (_sp_getapplock_internal(resource, lockmode, lockowner, 0, dbprincipal, true) != 0) + PG_RETURN_INT32(0); + + /* + * PANIC: we've acquired the lock but can't release it for some reason. + * Unlike previous case, we need to print messages clearly indicating + * such, so user is aware of the dangling lock, and error out to prevent + * any inconsistent state. + */ + if (_sp_releaseapplock_internal(resource, lockowner, dbprincipal, false) != 0) + ereport(PANIC, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("Lock acuiqred during APPLOCK_TEST for resource \'%s\'" + "but couldn't release it.", + resource))); + + /* Lock can be acquired now. */ + PG_RETURN_INT32(1); +} + +/* + * Function to be called by a hook in the backend. + * Remove all hash entries for application locks of either transaction-only + * or transaction+session too. + * + * @release_session: if we remove session locks as well as transaction locks. + */ +static void +ApplockRemoveCache(bool release_session) +{ + HASH_SEQ_STATUS hash_seq; + AppLockCacheEnt *entry; + + /* + * If we are not using TSQL dialect or applock cache is not initialized, + * don't bother. + */ + if (sql_dialect != SQL_DIALECT_TSQL || !appLockCacheLocal) + return; + + hash_seq_init(&hash_seq, appLockCacheLocal); + + while ((entry = hash_seq_search(&hash_seq)) != NULL) + { + int i; + if (!release_session && entry->is_session) + continue; + + /* unreferencing my entries in global hashmap */ + for (i = 0; i < entry->refcount; i++) + ApplockUnrefGlobalCache(entry->key); + + /* free allocated space, and the entry itself. */ + hash_search(appLockCacheLocal, (void *) &entry->key, HASH_REMOVE, NULL); + } + + /* Release all applocks too. */ + LockReleaseAll(APPLOCK_LOCKMETHOD, release_session); +} diff --git a/contrib/babelfishpg_tsql/src/babelfish_version.h b/contrib/babelfishpg_tsql/src/babelfish_version.h new file mode 100644 index 00000000000..64620646c75 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/babelfish_version.h @@ -0,0 +1,15 @@ +/*------------------------------------------------------------------------- + * + * babelfish_version.h + * Defines the Babelfish version string. + * Defines the Babel Compatibility version string. + * Defines the Babel Compatibility major version string. + * + *------------------------------------------------------------------------- + */ + +#define BABELFISH_VERSION_STR "1.0.0" +#define BABELFISH_INTERNAL_VERSION_STR "Babelfish 13.4.0.15" +#define BABEL_COMPATIBILITY_VERSION "12.0.2000.8" +#define BABEL_COMPATIBILITY_MAJOR_VERSION "12" + diff --git a/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-decl.y b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-decl.y new file mode 100644 index 00000000000..10378cbb869 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-decl.y @@ -0,0 +1,108 @@ +%expect 1 + +%debug +%verbose + +%initial-action +{ + yydebug = false; + + YYDPRINTF((stderr, "starting base SQL parser\n")); + + YYDPRINTF((stderr, " %s\n", pg_yyget_extra(yyscanner)->core_yy_extra.scanbuf)); +} + +%type tsql_stmt + +%type tsql_CreateFunctionStmt tsql_VariableSetStmt tsql_CreateTrigStmt tsql_TransactionStmt tsql_UpdateStmt tsql_DeleteStmt tsql_IndexStmt +%type tsql_DropIndexStmt tsql_InsertStmt +%type tsql_CreateLoginStmt tsql_AlterLoginStmt tsql_DropLoginStmt +%type tsql_nchar +%type tsql_login_option_list1 tsql_login_option_list2 +%type tsql_alter_login_option_list +%type tsql_login_option_elem tsql_alter_login_option_elem +%type tsql_enable_disable +%type tsql_createfunc_options tsql_createfunc_opt_list tsql_IsolationLevel +%type tsql_func_name +%type tsql_func_opt_item + +%type tsql_qualified_func_name + +%type tsql_opt_arg_dflt +%type tsql_opt_null_keyword +%type tsql_proc_arg tsql_func_arg +%type tsql_proc_args_list tsql_func_args_list + +%type tsql_ExecStmt tsql_output_ExecStmt +%type tsql_actual_args +%type tsql_actual_arg +%type tsql_opt_output tsql_opt_readonly + +%type tsql_OptTranName tsql_IsolationLevelStr + +%type tsql_without_login + +%type tsql_alter_table_cmd + +%type tsql_TriggerActionTime +%type tsql_TriggerEvents tsql_TriggerOneEvent + +%type tsql_stmtmulti +%type columnListWithOptAscDesc + +%type tsql_cluster tsql_opt_cluster + +%type tsql_OptParenthesizedIdentList tsql_IdentList + +%type TSQL_computed_column +%type columnElemWithOptAscDesc + +%type tsql_ColConstraint tsql_ColConstraintElem + +%type TSQL_Typename TSQL_SimpleTypename TSQL_GenericType + +%type datepart_arg datediff_arg dateadd_arg +%type tsql_type_function_name +%type tsql_createproc_args tsql_createfunc_args + +%type tsql_top_clause opt_top_clause + +%type tokens_remaining +%type tsql_table_hint_kw_no_with +%type tsql_table_hint_expr tsql_opt_table_hint_expr tsql_table_hint_list +%type tsql_table_hint +%type tsql_for_clause tsql_xml_common_directive +%type tsql_xml_common_directives + +%type tsql_output_insert_rest tsql_output_insert_rest_no_paren + +%type tsql_output_simple_select tsql_values_clause +%type tsql_output_clause tsql_output_into_target_columns +%type tsql_alter_server_role + +%token TSQL_ATAT TSQL_ALLOW_SNAPSHOT_ISOLATION + TSQL_CALLER TSQL_CHOOSE TSQL_CLUSTERED TSQL_COLUMNSTORE TSQL_CONVERT + TSQL_DATENAME TSQL_DATEPART TSQL_DATEDIFF TSQL_DATEADD TSQL_ISNULL + TSQL_D TSQL_DAYOFYEAR TSQL_DD TSQL_DW TSQL_DY TSQL_HH TSQL_ISO_WEEK TSQL_ISOWK + TSQL_ISOWW TSQL_LOGIN TSQL_M TSQL_MCS TSQL_MICROSECOND TSQL_MILLISECOND TSQL_MM TSQL_MS + TSQL_N TSQL_NANOSECOND TSQL_NONCLUSTERED TSQL_NS TSQL_OUTPUT TSQL_OUT TSQL_PARSE TSQL_Q + TSQL_QQ TSQL_QUARTER TSQL_READONLY TSQL_ROWGUIDCOL TSQL_S + TSQL_SAVE TSQL_SS TSQL_TRAN TSQL_TRY_CAST TSQL_TRY_CONVERT TSQL_TRY_PARSE + TSQL_TEXTIMAGE_ON TSQL_TZ TSQL_TZOFFSET TSQL_WEEK TSQL_WEEKDAY TSQL_WK TSQL_WW TSQL_YY TSQL_YYYY + TSQL_SCHEMABINDING TSQL_IDENTITY_INSERT + TSQL_EXEC TSQL_PROC TSQL_IIF TSQL_REPLICATION TSQL_PERSISTED + TSQL_NOCHECK TSQL_NOLOCK TSQL_READUNCOMMITTED TSQL_UPDLOCK TSQL_REPEATABLEREAD + TSQL_READCOMMITTED TSQL_TABLOCK TSQL_TABLOCKX TSQL_PAGLOCK TSQL_ROWLOCK + TSQL_TOP TSQL_PERCENT + TSQL_AUTO TSQL_EXPLICIT TSQL_RAW TSQL_PATH TSQL_FOR TSQL_BASE64 TSQL_ROOT TSQL_READPAST TSQL_XLOCK TSQL_NOEXPAND + TSQL_MEMBER TSQL_SERVER + TSQL_WINDOWS CERTIFICATE DEFAULT_DATABASE DEFAULT_LANGUAGE HASHED + MUST_CHANGE CHECK_EXPIRATION CHECK_POLICY CREDENTIAL SID OLD_PASSWORD + UNLOCK TSQL_VALUES + +/* + * WITH_paren is added to support table hints syntax WITH ( [[,]...n]), + * otherwise the parser cannot tell between 'WITH' and 'WITH (' and thus + * lead to a shift/reduce conflict. + */ +%token WITH_paren TSQL_HINT_START_BRACKET diff --git a/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-epilogue.y.c b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-epilogue.y.c new file mode 100644 index 00000000000..57084380418 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-epilogue.y.c @@ -0,0 +1,1269 @@ +void +pgtsql_parser_init(base_yy_extra_type *yyext) +{ + parser_init(yyext); +} + +static void +pgtsql_base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg) +{ + base_yyerror(yylloc, yyscanner, msg); +} + +static Node * +makeTSQLHexStringConst(char *str, int location) +{ + A_Const *n = makeNode(A_Const); + + n->val.type = T_TSQL_HexString; + n->val.val.str = str; + n->location = location; + + return (Node *)n; +} + +/* tsql_completeDefaultValues + * fill NULL as default value for any trailing params + * following the first param with default value + */ +static void +tsql_completeDefaultValues(List *parameters) +{ + ListCell *i; + bool fill = false; + foreach(i, parameters) + { + FunctionParameter *p = (FunctionParameter *) lfirst(i); + if (p->defexpr) + fill = true; + + if (fill && !p->defexpr) + p->defexpr = makeNullAConst(0); /* no location available */ + } +} + +/* TsqlSystemFuncName() + * Build a properly-qualified reference to a tsql built-in function. + */ +List * +TsqlSystemFuncName(char *name) +{ + return list_make2(makeString("sys"), makeString(name)); +} + +/* TsqlSystemFuncName2() + * Build a properly-qualified reference to a tsql built-in function. + */ +List * +TsqlSystemFuncName2(char *name) +{ + return list_make2(makeString("sys"), makeString(name)); +} + +char * +construct_unique_index_name(char *index_name, char *relation_name) { + char md5[MD5_HASH_LEN + 1]; + char buf[2 * NAMEDATALEN + MD5_HASH_LEN + 1]; + char* name; + bool success; + int full_len; + int new_len; + int index_len; + int relation_len; + + if (index_name == NULL || relation_name == NULL) { + return index_name; + } + index_len = strlen(index_name); + relation_len = strlen(relation_name); + + success = pg_md5_hash(index_name, index_len, md5); + if (unlikely(!success)) { /* OOM */ + ereport( + ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg( + "constructing unique index name failed: index = \"%s\", relation = \"%s\", ", + index_name, + relation_name + ) + ) + ); + } + + memcpy(buf, index_name, index_len); + memcpy(buf + index_len, relation_name, relation_len); + memcpy(buf + index_len + relation_len, md5, MD5_HASH_LEN + 1); + + full_len = index_len + relation_len + MD5_HASH_LEN; + buf[full_len] = '\0'; + + truncate_identifier(buf, full_len, false); + + new_len = strlen(buf); + Assert(new_len < NAMEDATALEN); /* result new_len is below max */ + + name = palloc(new_len + 1); + memcpy(name, buf, new_len + 1); + + return name; +} + +/* + * Convert a list of (dotted) names for a table type to a RangeVar. + * This differs from makeRangeVarFromAnyName in that it only allows 1 prefix, + * instead of 2. + */ +static RangeVar * +makeRangeVarFromAnyNameForTableType(List *names, int position, core_yyscan_t yyscanner) +{ + RangeVar *r = makeNode(RangeVar); + + switch (list_length(names)) + { + case 1: + r->catalogname = NULL; + r->schemaname = NULL; + r->relname = strVal(linitial(names)); + break; + case 2: + r->catalogname = NULL; + r->schemaname = strVal(linitial(names)); + r->relname = strVal(lsecond(names)); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The type name '%s' contains more than the maximum number of prefixes. The maximum is 1.", + NameListToString(names)), + parser_errposition(position))); + break; + } + + r->relpersistence = RELPERSISTENCE_PERMANENT; + r->location = position; + + return r; +} + +Node +*TsqlFunctionChoose(Node *int_expr, List *choosable, int location) +{ + CaseExpr *c = makeNode(CaseExpr); + ListCell *lc; + int i = 1; + + TSQLInstrumentation(INSTR_TSQL_FUNCTION_CHOOSE); + + if (choosable == NIL) + elog(ERROR, + "Function 'choose' requires at least 2 argument(s)"); + + foreach(lc, choosable) + { + CaseWhen *w = makeNode(CaseWhen); + w->expr = (Expr *) makeIntConst(i, location); + w->result = (Expr *) lfirst(lc); + w->location = location; + c->args = lappend(c->args, w); + i++; + } + + c->casetype = InvalidOid; + c->arg = (Expr *) makeTypeCast(int_expr, SystemTypeName("int4"), -1); + c->location = location; + + return (Node *) c; +} + + +/* TsqlFunctionConvert -- Implements the CONVERT and TRY_CONVERT functions. + * Takes in target type, expression, style, try boolean, location. + * + * Converts any input type to any type with different styles. + * Uses try boolean to determine returning an error or null if cast fails. + */ +Node * +TsqlFunctionConvert(TypeName *typename, Node *arg, Node *style, bool try, int location) +{ + Node *result; + List *args; + int32 typmod; + Oid type_oid; + char *typename_string; + + /* For handling try boolean logic on babelfishpg_tsql side */ + Node *try_const = makeBoolAConst(try, location); + if (style) + args = list_make3(arg, try_const, style); + else + args = list_make2(arg, try_const); + + typenameTypeIdAndMod(NULL, typename, &type_oid, &typmod); + typename_string = TypeNameToString(typename); + + TSQLInstrumentation(INSTR_TSQL_FUNCTION_CONVERT); + + if (type_oid == DATEOID) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_conv_helper_to_date"), args, location); + else if (type_oid == TIMEOID) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_conv_helper_to_time"), args, location); + else if (type_oid == typenameTypeId(NULL, makeTypeName("datetime"))) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_conv_helper_to_datetime"), args, location); + else if (strcmp(typename_string, "varchar") == 0) + { + Node *helperFuncCall; + + typename_string = format_type_extended(VARCHAROID, typmod, FORMAT_TYPE_TYPEMOD_GIVEN); + args = lcons(makeStringConst(typename_string, typename->location), args); + helperFuncCall = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_conv_helper_to_varchar"), args, location); + /* BABEL-1661, add a type cast on top of the CONVERT helper function so typmod can be applied */ + result = makeTypeCast(helperFuncCall, typename, location); + } + else + { + if (try) + { + result = TsqlFunctionTryCast(arg, typename, location); + } + else + { + result = makeTypeCast(arg, typename, location); + } + } + + return result; +} + +/* TsqlFunctionParse -- Implements the PARSE and TRY_PARSE functions. + * Takes in expression, target type, regional culture, try boolean, location. + * + * Parses text input to date/time and number types. Uses try boolean to determine returning + * an error or null if cast fails. + */ +Node * +TsqlFunctionParse(Node *arg, TypeName *typename, Node *culture, bool try, int location) +{ + Node *result; + List *args; + int32 typmod; + Oid type_oid; + + /* So far only date, time, and datetime need try_const and culture if not null since + * only they have specialized functions implemented in PG TSQL. + */ + Node *try_const = makeBoolAConst(try, location); + if (culture) + args = list_make3(arg, try_const, culture); + else + args = list_make2(arg, try_const); + + typenameTypeIdAndMod(NULL, typename, &type_oid, &typmod); + + TSQLInstrumentation(INSTR_TSQL_FUNCTION_PARSE); + + if (type_oid == DATEOID) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_parse_helper_to_date"), args, location); + else if (type_oid == TIMEOID) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_parse_helper_to_time"), args, location); + else if (type_oid == typenameTypeId(NULL, makeTypeName("datetime"))) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_parse_helper_to_datetime"), args, location); + else + { + if (try) + result = TsqlFunctionTryCast(arg, typename, location); + else + result = makeTypeCast(arg, typename, location); + } + + return result; +} + +/* TsqlFunctionTryCast -- Implements the TRY_CAST function. + * Takes in expression, target type, location. + * + * Behaves like CAST except return NULL instead of error in most cases. + */ +Node * +TsqlFunctionTryCast(Node *arg, TypeName *typename, int location) +{ + Node *result; + int32 typmod; + Oid type_oid; + + typenameTypeIdAndMod(NULL, typename, &type_oid, &typmod); + + TSQLInstrumentation(INSTR_TSQL_FUNCTION_TRY_CAST); + + /* Going case-by-case since it seems we cannot define a wrapper try_cast function that takes in an + * arg of any type and returns any type. Can reduce cases to handle by having a generic cast at the end + * that casts the arg to TEXT then casts to the target type. Works for most cases but not all such as casting + * float to int. + */ + if (type_oid == INT2OID) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_try_cast_floor_smallint"), list_make1(arg), location); + else if (type_oid == INT4OID) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_try_cast_floor_int"), list_make1(arg), location); + else if (type_oid == INT8OID) + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_try_cast_floor_bigint"), list_make1(arg), location); + else + { + Node *arg_const = makeTypeCast(arg, SystemTypeName("text"), location); + + /* Cast null to typename to take advantage of polymorphic types in Postgres. */ + Node *null_const = makeTypeCast(makeNullAConst(location), typename, location); + + List *args = list_make3(arg_const, null_const, makeIntConst(typmod, location)); + result = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_try_cast_to_any"), args, location); + } + + return result; +} + +Node * +TsqlFunctionIIF(Node *bool_expr, Node *arg1, Node *arg2, int location) +{ + CaseExpr *c = makeNode(CaseExpr); + CaseWhen *w = makeNode(CaseWhen); + + TSQLInstrumentation(INSTR_TSQL_FUNCTION_IIF); + + w->expr = (Expr *) bool_expr; + w->result = (Expr *) arg1; + w->location = location; + + c->casetype = InvalidOid; + c->arg = NULL; + c->args = list_make1((Node *) w); + c->defresult = (Expr *) arg2; + c->location = location; + + return (Node *) c; +} + +/* tsql_check_param_readonly --- check the usage of READONLY on parameter + * + * READONLY Indicates that the parameter cannot be updated or modified + * within the definition of the function. READONLY is required for + * user-defined table type parameters (TVPs), and cannot be used for + * any other parameter type. + * + * It's easiest to do the check here, to avoid having to add a field + * to the FunctionParameter struct. + */ +static void +tsql_check_param_readonly(const char *paramname, TypeName *typename, bool readonly) +{ + TypeName *typeclone = copyObjectImpl(typename); + + /* work on the cloned object to avoid double rewriting */ + rewrite_plain_name(typeclone->names); + if (typeidTypeRelid(typenameTypeId(NULL, typeclone)) == InvalidOid) + { + /* Not table-valued parameter - must not be READONLY */ + if (readonly) + elog(ERROR, + "The parameter \"%s\" can not be declared READONLY since it is not a table-valued parameter.", + paramname); + } + else + { + /* Table-valued parameter - must be READONLY */ + if (!readonly) + elog(ERROR, + "The table-valued parameter \"%s\" must be declared with the READONLY option.", + paramname); + } +} + +/* + * Make a function call to tsql_query_to_xml for FOR XML clause. + * For example, it does the following transformation: + * select a from t for xml path => + * select tsql_query_to_xml('select a from t', ... ) + * The first argument of the function is the query string without the for xml clause. + * The rest of the arguments passe in options and directives allowed by tsql. + * + * If the for xml clause has TSQL variables/identifiers that needs to be binded + * (e.g. @varName that's used as a procedure parameter) during parse analysis, + * we transform the query to use PG's function FORMAT in order to support the + * variable binding, for example: + * select a from t where id = @pid for xml path => + * select tsql_query_toxml(FORMAT('select a from t where id = %s', @pid) ... ) + */ +ResTarget * +TsqlForXMLMakeFuncCall(TSQL_ForClause* forclause, char* src_query, size_t start_location, core_yyscan_t yyscanner) +{ + ResTarget *rt = makeNode(ResTarget); + FuncCall *fc; + size_t len = (forclause)->location - start_location; + char *query = palloc(len + 1); + List *func_name; + List *func_args; + int begin_index; + int end_index; + char *begin_param; + char *end_param; + StringInfo format_query = makeStringInfo(); + List *params = NIL; + bool binary_base64 = false; + bool return_xml_type = false; + char* root_name = NULL; + Node* arg1; + + /* Resolve the XML common directive list if provided */ + if (forclause->commonDirectives != NIL) + { + ListCell *lc; + foreach (lc, forclause->commonDirectives) + { + Node *myNode = lfirst(lc); + A_Const *myConst; + + /* commonDirective is either integer const or string const */ + Assert(IsA(myNode, A_Const)); + myConst = (A_Const *)myNode; + Assert(myConst->val.type == T_Integer || myConst->val.type == T_String); + if (myConst->val.type == T_Integer) + { + if (myConst->val.val.ival == TSQL_XML_DIRECTIVE_BINARY_BASE64) + binary_base64 = true; + else if (myConst->val.val.ival == TSQL_XML_DIRECTIVE_TYPE) + return_xml_type = true; + } + else if (myConst->val.type == T_String) + { + root_name = myConst->val.val.str; + } + } + } + + query = memcpy(query, + src_query + start_location, + len); + query[len] = '\0'; + + /* + * Transform query with tsql identifiers (@pname) to PG's FORMAT function so + * variable binding still works. + * For example: + * select a from t where id = @pid for xml path => + * select tsql_query_toxml(FORMAT('select a from t where id = %s', @pid) ... ) + * Notice the tsql identifiers in the query string has already been + * processed by the babelfishpg_tsql parser at this point and is surrounded by quote + * such as in \"@pid"\. + * + * We achieve the above transformation by extacting all parameters starting + * with \"@ from the query string, and replace them with %s. The StringInfo + * variable format_query is used to assemble the new query in this process. + * end_param points the remaiming query string after the parameter, initially + * we set it to the query string. begin_param points to the begining of a + * parameter or tsql identifer, starting from left to right in the query string. + */ + end_param = query; + while ((begin_param = strstr(end_param, "\"@")) != NULL) + { + char *before_param; + + begin_index = begin_param - end_param; + before_param = palloc(begin_index + 1); + before_param = memcpy(before_param, end_param, begin_index); + before_param[begin_index] = '\0'; + if ((end_param = strstr(begin_param+2, "\"")) != NULL) + { + char *param; + + end_index = (end_param - begin_param) + begin_index; + appendStringInfoString(format_query, before_param); + appendStringInfoString(format_query, "%L"); + param = palloc(end_index - begin_index); + param = memcpy(param, begin_param + 1, end_index - begin_index - 1); + param[end_index - begin_index - 1] = '\0'; + params = lappend(params, makeColumnRef(param, NIL, -1, yyscanner)); + /* Move end_param pass the \", so it points to the rest of the query */ + end_param++; + } + else + { + /* + * Unmatched quote around the tsql identification, it shouldn't happen + * because proper quoting is done in babelfishpg_tsql parser. + * But just in case report an error. + */ + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("Unmatched quote around TSQL identifier"))); + } + } + + /* + * Make a function call to Function FORMAT if format_query is built from the + * above process. + */ + if (format_query->len > 0) + { + FuncCall *format_fc; + List *format_func_args; + appendStringInfoString(format_query, end_param); + format_func_args = list_concat(list_make1(makeStringConst(format_query->data, -1)), + params); + format_fc = makeFuncCall(list_make1(makeString("format")), format_func_args, -1); + arg1 = (Node *) format_fc; + } + else + arg1 = makeStringConst(query, -1); + + /* + * Finally make funtion call to tsql_query_to_xml or tsql_query_to_xml_text + * depending on the return_xml_type flag (TYPE option in the FOR XML clause). + * The only difference of the two functions is the return type. tsql_query_to_xml + * returns XML type, tsql_query_to_xml_text returns text type. + */ + if (return_xml_type) + func_name= list_make2(makeString("sys"), makeString("tsql_query_to_xml")); + else + func_name= list_make2(makeString("sys"), makeString("tsql_query_to_xml_text")); + func_args = list_make4(arg1, + makeIntConst(forclause->mode, -1), + forclause->elementName ? makeStringConst(forclause->elementName, -1) : + makeStringConst("row", -1), + makeBoolAConst(binary_base64, -1)); + func_args = lappend(func_args, root_name ? makeStringConst(root_name, -1) : makeStringConst("", -1)); + fc = makeFuncCall(func_name, func_args, -1); + + rt->name = NULL; + rt->indirection = NIL; + rt->val = (Node *) fc; + rt->location = -1; + return rt; +} + +/* + * helper macro to compare relname in + * function tsql_update_delete_stmt_with_join + */ +#define TSQL_COMP_REL_NAME(l, r) \ + (r != NULL && strcmp(l->relname, r->relation->relname) == 0 \ + && ( (!r->relation->schemaname && !l->schemaname) \ + || (l->schemaname && r->relation->schemaname && \ + strcmp(l->schemaname, r->relation->schemaname) == 0))\ + && ( (!r->relation->catalogname && !l->catalogname)\ + || (l->catalogname && r->relation->catalogname && \ + strcmp(l->catalogname, r->relation->catalogname) == 0))) + +static Node * +tsql_update_delete_stmt_with_join(Node *n, List* from_clause, Node* + where_clause, Node *top_clause, + RangeVar *relation, core_yyscan_t yyscanner) +{ + DeleteStmt* n_d = NULL; + UpdateStmt* n_u = NULL; + RangeVar* target_table = NULL; + RangeVar* larg = NULL; + RangeVar* rarg = NULL; + JoinExpr* jexpr = linitial(from_clause); + SubLink * link; + List* indirect; + SelectStmt *selectstmt; + ResTarget *resTarget; + /* use queue to go over all join expr and find target table */ + List* queue = list_make1(jexpr); + ListCell *queue_item; + if(IsA(n, DeleteStmt)) + n_d = (DeleteStmt*)n; + else + n_u = (UpdateStmt*)n; + + foreach(queue_item, queue) + { + jexpr = (JoinExpr*)lfirst(queue_item); + if(IsA(jexpr->larg, JoinExpr)) + { + queue = lappend(queue, jexpr->larg); + } + else if(IsA(jexpr->larg, RangeVar)) + { + larg = (RangeVar*)(jexpr->larg); + } + if(IsA(jexpr->rarg, JoinExpr)) + { + queue = lappend(queue, jexpr->rarg); + } + else if(IsA(jexpr->rarg, RangeVar)) + { + rarg = (RangeVar*)(jexpr->rarg); + } + if(larg && (TSQL_COMP_REL_NAME(larg,n_d) || TSQL_COMP_REL_NAME(larg,n_u))) + { + target_table = larg; + break; + } + if(rarg && (TSQL_COMP_REL_NAME(rarg,n_d) || TSQL_COMP_REL_NAME(rarg,n_u))) + { + target_table = rarg; + break; + } + larg = NULL; + rarg = NULL; + } + /* if target table doesn't show in JoinExpr, + * it indicates delete/update the whole table + * the original statement doesn't need to be changed + */ + if(!target_table) + { + /* + * if we don't end up creating a subquery for JOIN, deal with TOP clause + * separately as it might require a subquery. + */ + if(n_d) + { + n_d -> usingClause = from_clause; + n_d -> whereClause = tsql_update_delete_stmt_with_top(top_clause, + relation, where_clause, yyscanner); + return (Node*)n_d; + } + else + { + n_u -> fromClause = from_clause; + n_u -> whereClause = tsql_update_delete_stmt_with_top(top_clause, + relation, where_clause, yyscanner); + return (Node*)n_u; + } + } + /* construct select statment->target */ + resTarget = makeNode(ResTarget); + resTarget->name = NULL; + resTarget->indirection = NIL; + indirect = list_make1((Node *) makeString("ctid")); + if(target_table->alias) + { + resTarget->val = makeColumnRef(target_table->alias->aliasname, + indirect,-1,yyscanner); + } + else + { + resTarget->val = makeColumnRef(target_table->relname, + indirect,-1,yyscanner); + } + + selectstmt = makeNode(SelectStmt); + selectstmt->targetList = list_make1(resTarget); + /* assign fromClause and whereClause from JoinExpr */ + selectstmt->fromClause = from_clause; + selectstmt->whereClause = where_clause; + /* if we end up createing a subquery for JOIN, attach TOP clause to it */ + selectstmt->limitCount = top_clause; + /* construct where_clause(subLink) */ + link = makeNode(SubLink); + link->subselect = (Node*)selectstmt; + link->subLinkType = ANY_SUBLINK; + link->subLinkId = 0; + link->testexpr = (Node*)makeColumnRef(pstrdup("ctid"), + NIL, -1, yyscanner);; + link->operName = NIL; /* show it's IN not = ANY */ + link->location = -1; + if(n_d) + { + n_d->whereClause = (Node*)link; + return (Node*)n_d; + } + else + + { + n_u->whereClause = (Node*)link; + return (Node*)n_u; + } +} + +/* + * Similar to JOIN, we rewrite TOP clause into a subquery, while attaching the + * TOP as a LIMIT in the subquery, for UPDATE/DELETE. + * + * original query: + * UPDATE/DELETE + * + * rewritten query: + * UPDATE/DELETE
WHERE ctid IN (SELECT ctid FROM
) + */ +static Node * +tsql_update_delete_stmt_with_top(Node *top_clause, RangeVar *relation, Node + *where_clause, core_yyscan_t yyscanner) +{ + SubLink * link; + List* indirect; + SelectStmt *selectstmt; + ResTarget *resTarget; + + if (top_clause == NULL) + return where_clause; + + /* construct select statment->target */ + resTarget = makeNode(ResTarget); + resTarget->name = NULL; + resTarget->indirection = NIL; + indirect = list_make1((Node *) makeString("ctid")); + if(relation->alias) + { + resTarget->val = makeColumnRef(relation->alias->aliasname, + indirect,-1,yyscanner); + } + else + { + resTarget->val = makeColumnRef(relation->relname, + indirect,-1,yyscanner); + } + + /* construct select statement */ + selectstmt = makeNode(SelectStmt); + selectstmt->targetList = list_make1(resTarget); + selectstmt->fromClause = list_make1(relation); + selectstmt->whereClause = where_clause; + selectstmt->limitCount = top_clause; + + /* construct where_clause(subLink) */ + link = makeNode(SubLink); + link->subselect = (Node*)selectstmt; + link->subLinkType = ANY_SUBLINK; + link->subLinkId = 0; + link->testexpr = (Node*)makeColumnRef(pstrdup("ctid"), + NIL, -1, yyscanner);; + link->operName = NIL; /* show it's IN not = ANY */ + link->location = -1; + + return (Node *)link; +} + +static Node * +tsql_insert_output_into_cte_transformation(WithClause *opt_with_clause, RangeVar *insert_target, + List *insert_column_list, List *tsql_output_clause, RangeVar *output_target, List *tsql_output_into_target_columns, + InsertStmt *tsql_output_insert_rest, int select_location) +{ + + CommonTableExpr *cte = makeNode(CommonTableExpr); + WithClause *w = makeNode(WithClause); + SelectStmt *n = makeNode(SelectStmt); + InsertStmt *i = makeNode(InsertStmt); + char* internal_ctename = NULL; + char ctename[NAMEDATALEN]; + ListCell *expr; + char col_alias_arr[NAMEDATALEN]; + char *col_alias = NULL; + List *output_list = NIL, *queue = NIL; + ListCell *lc; + Node *field1; + char *qualifier = NULL; + + sprintf(ctename, "internal_output_cte##sys_gen##%p", (void*) i); + internal_ctename = pstrdup(ctename); + + // PreparableStmt inside CTE + i->cols = NIL; + i->selectStmt = tsql_output_insert_rest->selectStmt; + i->relation = insert_target; + i->onConflictClause = NULL; + i->returningList = get_transformed_output_list(tsql_output_clause); + i->withClause = NULL; + i->override = false; + + /* + * Make sure we do not pass inserted qualifier to the SELECT target list. + * Instead, we add an alias for column names qualified by inserted, and remove + * the inserted qualifier from *. We also make sure only one * is left in + * the output list inside the CTE. + */ + output_list = copyObject(tsql_output_clause); + foreach(lc, output_list) + { + ResTarget *res = (ResTarget *) lfirst(lc); + queue = NIL; + queue = list_make1(res->val); + + foreach(expr, queue) + { + Node *node = (Node *) lfirst(expr); + if (IsA(node, ColumnRef)) + { + ColumnRef *cref = (ColumnRef *) node; + if (list_length(cref->fields) >= 2) + { + field1 = (Node *) linitial(cref->fields); + qualifier = strVal(field1); + + if (!strcmp(qualifier, "inserted")) + { + if (IsA((Node*) llast(cref->fields), String)) + { + snprintf(col_alias_arr, NAMEDATALEN, "sys_gen##%pins_%s", (void*) tsql_output_clause, strVal(llast(cref->fields))); + col_alias = pstrdup(col_alias_arr); + } + else + cref->fields = list_delete_first(cref->fields); + } + if (col_alias) + cref->fields = list_make1(makeString(col_alias)); + } + } + else if(IsA(node, A_Expr)) + { + A_Expr *a_expr = (A_Expr *) node; + if (a_expr->lexpr) + queue = lappend(queue, a_expr->lexpr); + if (a_expr->rexpr) + queue = lappend(queue, a_expr->rexpr); + } + else if(IsA(node, FuncCall)) + { + FuncCall *func_call = (FuncCall*) node; + if (func_call->args) + queue = list_concat(queue, func_call->args); + } + } + } + + // SelectStmt inside outer InsertStmt + n->limitCount = NULL; + n->targetList = output_list; + n->intoClause = NULL; + n->fromClause = list_make1(makeRangeVar(NULL, internal_ctename, select_location)); + + // Outer InsertStmt + tsql_output_insert_rest->selectStmt = (Node*) n; + tsql_output_insert_rest->relation = output_target; + tsql_output_insert_rest->onConflictClause = NULL; + tsql_output_insert_rest->returningList = NULL; + if (tsql_output_into_target_columns == NIL) + tsql_output_insert_rest->cols = insert_column_list; + else + tsql_output_insert_rest->cols = tsql_output_into_target_columns; + + // CTE + cte->ctename = internal_ctename; + cte->aliascolnames = NULL; + cte->ctematerialized = CTEMaterializeDefault; + cte->ctequery = (Node *) i; + cte->location = 1; + + if(opt_with_clause) + { + opt_with_clause->ctes = lappend(opt_with_clause->ctes, (Node*) cte); + tsql_output_insert_rest->withClause = opt_with_clause; + } + else + { + w->ctes = list_make1((Node *) cte); + w->recursive = false; + w->location = 1; + tsql_output_insert_rest->withClause = w; + } + return (Node *) tsql_output_insert_rest; +} + +static Node * +tsql_delete_output_into_cte_transformation(WithClause *opt_with_clause, Node *opt_top_clause, + RangeVar *relation_expr_opt_alias, List *tsql_output_clause, RangeVar *insert_target, + List *tsql_output_into_target_columns, List *from_clause, Node *where_or_current_clause, + core_yyscan_t yyscanner) +{ + CommonTableExpr *cte = makeNode(CommonTableExpr); + WithClause *w = makeNode(WithClause); + SelectStmt *n = makeNode(SelectStmt); + DeleteStmt *d = makeNode(DeleteStmt); + InsertStmt *i = makeNode(InsertStmt); + ListCell *lc; + Node *field1; + char *qualifier = NULL; + List *output_list = NIL, *queue = NIL; + char *internal_ctename = NULL; + char ctename[NAMEDATALEN]; + ListCell *expr; + char col_alias_arr[NAMEDATALEN]; + char *col_alias = NULL; + + sprintf(ctename, "internal_output_cte##sys_gen##%p", (void*) i); + internal_ctename = pstrdup(ctename); + + // PreparableStmt inside CTE + d->relation = relation_expr_opt_alias; + if (from_clause != NULL && IsA(linitial(from_clause), JoinExpr)) + { + d = (DeleteStmt*)tsql_update_delete_stmt_with_join( + (Node*)d, from_clause, where_or_current_clause, opt_top_clause, + relation_expr_opt_alias, yyscanner); + output_update_transformation = true; + } + else + { + d->usingClause = from_clause; + d->whereClause = tsql_update_delete_stmt_with_top(opt_top_clause, + relation_expr_opt_alias, where_or_current_clause, yyscanner); + if (from_clause != NULL && (IsA(linitial(from_clause), RangeSubselect) || IsA(linitial(from_clause), RangeVar))) + output_update_transformation = true; + } + d->returningList = get_transformed_output_list(tsql_output_clause); + d->withClause = opt_with_clause; + + /* + * Make sure we do not pass deleted qualifier to the SELECT target list. + * Instead, we add an alias for column names qualified bydeleted, and remove + * the deleted qualifier from *. + */ + output_list = copyObject(tsql_output_clause); + foreach(lc, output_list) + { + ResTarget *res = (ResTarget *) lfirst(lc); + queue = NIL; + queue = list_make1(res->val); + + foreach(expr, queue) + { + Node *node = (Node *) lfirst(expr); + if (IsA(node, ColumnRef)) + { + ColumnRef *cref = (ColumnRef *) node; + if (list_length(cref->fields) >= 2) + { + field1 = (Node *) linitial(cref->fields); + qualifier = strVal(field1); + + if (!strcmp(qualifier, "deleted")) + { + if (IsA((Node*) llast(cref->fields), String)) + { + snprintf(col_alias_arr, NAMEDATALEN, "sys_gen##%pdel_%s", (void*) tsql_output_clause, strVal(llast(cref->fields))); + col_alias = pstrdup(col_alias_arr); + } + else + cref->fields = list_delete_first(cref->fields); + } + if (col_alias) + cref->fields = list_make1(makeString(col_alias)); + } + } + else if(IsA(node, A_Expr)) + { + A_Expr *a_expr = (A_Expr *) node; + if (a_expr->lexpr) + queue = lappend(queue, a_expr->lexpr); + if (a_expr->rexpr) + queue = lappend(queue, a_expr->rexpr); + } + else if(IsA(node, FuncCall)) + { + FuncCall *func_call = (FuncCall*) node; + if (func_call->args) + queue = list_concat(queue, func_call->args); + } + } + } + + // SelectStmt inside outer InsertStmt + n->limitCount = NULL; + n->targetList = output_list; + n->intoClause = NULL; + n->fromClause = list_make1(makeRangeVar(NULL, internal_ctename, 4)); + + // Outer InsertStmt + i->selectStmt = (Node*) n; + i->relation = insert_target; + i->onConflictClause = NULL; + i->returningList = NULL; + i->cols = tsql_output_into_target_columns; + + // CTE + cte->ctename = internal_ctename; + cte->aliascolnames = NULL; + cte->ctematerialized = CTEMaterializeDefault; + cte->ctequery = (Node *) d; + cte->location = 1; + + if(opt_with_clause) + { + opt_with_clause->ctes = lappend(opt_with_clause->ctes, (Node*) cte); + i->withClause = opt_with_clause; + } + else + { + w->ctes = list_make1((Node *) cte); + w->recursive = false; + w->location = 1; + i->withClause = w; + } + return (Node *) i; +} + +static void +tsql_check_update_output_transformation(List *tsql_output_clause) +{ + ListCell *lc; + bool deleted = false; + + /* + * Check for deleted qualifier in OUTPUT list. If there is no deleted qualifier, + * there is no need for parse tree rewrite because PG already supports + * returning modified (inserted) values. + */ + foreach(lc, tsql_output_clause) + { + ResTarget *res = (ResTarget *) lfirst(lc); + if (IsA(res->val, ColumnRef)) + { + ColumnRef *cref = (ColumnRef *) res->val; + if(!strcmp(strVal((Node *) linitial(cref->fields)), "deleted")) + { + deleted = true; + break; + } + } + } + if (deleted) + output_update_transformation = true; +} + +static Node * +tsql_update_output_into_cte_transformation(WithClause *opt_with_clause, Node *opt_top_clause, + RangeVar *relation_expr_opt_alias, List *set_clause_list, + List *tsql_output_clause, RangeVar *insert_target, List *tsql_output_into_target_columns, + List *from_clause, Node *where_or_current_clause, core_yyscan_t yyscanner) +{ + CommonTableExpr *cte = makeNode(CommonTableExpr); + WithClause *w = makeNode(WithClause); + SelectStmt *n = makeNode(SelectStmt); + UpdateStmt *u = makeNode(UpdateStmt); + InsertStmt *i = makeNode(InsertStmt); + ListCell *lc; + Node *field1; + char *qualifier = NULL; + List *output_list = NIL, *queue = NIL; + char *internal_ctename = NULL; + char ctename[NAMEDATALEN]; + ListCell *expr; + char col_alias_arr[NAMEDATALEN]; + char *col_alias = NULL; + + sprintf(ctename, "internal_output_cte##sys_gen##%p", (void*) i); + internal_ctename = pstrdup(ctename); + + // PreparableStmt inside CTE + u->relation = relation_expr_opt_alias; + u->targetList = set_clause_list; + if (from_clause != NULL && IsA(linitial(from_clause), JoinExpr)) + { + u = (UpdateStmt*)tsql_update_delete_stmt_with_join( + (Node*)u, from_clause, where_or_current_clause, opt_top_clause, + relation_expr_opt_alias, yyscanner); + } + else + { + u->fromClause = from_clause; + u->whereClause = where_or_current_clause; + } + u->returningList = get_transformed_output_list(tsql_output_clause); + u->withClause = opt_with_clause; + + tsql_check_update_output_transformation(tsql_output_clause); + + /* + * Make sure we do not pass deleted or inserted qualifier to the SELECT target list. + * Instead, we add an alias for column names qualified by inserted/deleted, and remove + * the inserted/deleted qualifier from *. + */ + output_list = copyObject(tsql_output_clause); + foreach(lc, output_list) + { + ResTarget *res = (ResTarget *) lfirst(lc); + queue = NIL; + queue = list_make1(res->val); + + foreach(expr, queue) + { + Node *node = (Node *) lfirst(expr); + if (IsA(node, ColumnRef)) + { + ColumnRef *cref = (ColumnRef *) node; + if (list_length(cref->fields) >= 2) + { + field1 = (Node *) linitial(cref->fields); + qualifier = strVal(field1); + + if(!strcmp(qualifier, "deleted")) + { + if (IsA((Node*) llast(cref->fields), String)) + { + snprintf(col_alias_arr, NAMEDATALEN, "sys_gen##%pdel_%s", (void*) tsql_output_clause, strVal(llast(cref->fields))); + col_alias = pstrdup(col_alias_arr); + } + else + cref->fields = list_delete_first(cref->fields); + } + else if (!strcmp(qualifier, "inserted")) + { + if (IsA((Node*) llast(cref->fields), String)) + { + snprintf(col_alias_arr, NAMEDATALEN, "sys_gen##%pins_%s", (void*) tsql_output_clause, strVal(llast(cref->fields))); + col_alias = pstrdup(col_alias_arr); + } + else + cref->fields = list_delete_first(cref->fields); + } + if (col_alias) + cref->fields = list_make1(makeString(col_alias)); + } + } + else if(IsA(node, A_Expr)) + { + A_Expr *a_expr = (A_Expr *) node; + if (a_expr->lexpr) + queue = lappend(queue, a_expr->lexpr); + if (a_expr->rexpr) + queue = lappend(queue, a_expr->rexpr); + } + else if(IsA(node, FuncCall)) + { + FuncCall *func_call = (FuncCall*) node; + if (func_call->args) + queue = list_concat(queue, func_call->args); + } + } + } + + // SelectStmt inside outer InsertStmt + n->limitCount = NULL; + n->targetList = output_list; + n->intoClause = NULL; + n->fromClause = list_make1(makeRangeVar(NULL, internal_ctename, -1)); + + // Outer InsertStmt + i->selectStmt = (Node*) n; + i->relation = insert_target; + i->onConflictClause = NULL; + i->returningList = NULL; + i->cols = tsql_output_into_target_columns; + + // CTE + cte->ctename = internal_ctename; + cte->aliascolnames = NULL; + cte->ctematerialized = CTEMaterializeDefault; + cte->ctequery = (Node *) u; + cte->location = 1; + + if(opt_with_clause) + { + opt_with_clause->ctes = lappend(opt_with_clause->ctes, (Node*) cte); + i->withClause = opt_with_clause; + } + else + { + w->ctes = list_make1((Node *) cte); + w->recursive = false; + w->location = 1; + i->withClause = w; + } + return (Node *) i; +} + +/* +* get_transformed_output_list() extracts the ColumnRefs from functions and +* expressions so that the returning list in the rewritten CTE for OUTPUT INTO +* transformation does not contain functions and expressions. It also adds an +* alias to columns qualified by inserted or deleted. +*/ +static List * +get_transformed_output_list(List *tsql_output_clause) +{ + List *transformed_returning_list = NIL, *queue = NIL, *output_list = NIL; + ListCell *o_target, *expr; + char col_alias_arr[NAMEDATALEN]; + char *col_alias = NULL; + PLtsql_execstate *estate; + int i = 0; + bool local_variable = false, ins_star = false, del_star = false; + + estate = get_current_tsql_estate(); + + output_list = copyObject(tsql_output_clause); + foreach(o_target, output_list) + { + ResTarget *res = (ResTarget *) lfirst(o_target); + queue = NIL; + queue = list_make1(res->val); + + foreach(expr, queue) + { + Node *node = (Node *) lfirst(expr); + if (IsA(node, ColumnRef)) + { + ResTarget *target = makeNode(ResTarget); + ColumnRef *cref = (ColumnRef *) node; + + if(!strcmp(strVal(linitial(cref->fields)), "deleted") && list_length(cref->fields) >= 2) + { + if (IsA((Node*) llast(cref->fields), String)) + { + snprintf(col_alias_arr, NAMEDATALEN, "sys_gen##%pdel_%s", (void*) tsql_output_clause, strVal(llast(cref->fields))); + col_alias = pstrdup(col_alias_arr); + target->name = col_alias; + } + else if (IsA((Node*) llast(cref->fields), A_Star)) + ins_star = true; + + } + else if(!strcmp(strVal(linitial(cref->fields)), "inserted") && list_length(cref->fields) >= 2) + { + if (IsA((Node*) llast(cref->fields), String)) + { + snprintf(col_alias_arr, NAMEDATALEN, "sys_gen##%pins_%s", (void*) tsql_output_clause, strVal(llast(cref->fields))); + col_alias = pstrdup(col_alias_arr); + target->name = col_alias; + } + else if (IsA((Node*) llast(cref->fields), A_Star)) + del_star = true; + } + else + { + local_variable = false; + if(!strncmp(strVal(linitial(cref->fields)), "@", 1) && estate) + { + for (i = 0; i < estate->ndatums; i++) + { + PLtsql_datum *d = estate->datums[i]; + if (!strcmp(strVal(linitial(cref->fields)), ((PLtsql_variable*) d)->refname)) + { + local_variable = true; + break; + } + } + } + } + if (ins_star && del_star) + ereport( + ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("OUTPUT INTO does not support both inserted.* and deleted.* in target list") + ) + ); + if (!local_variable) + { + target->val = (Node*) cref; + transformed_returning_list = lappend(transformed_returning_list, target); + } + } + else if(IsA(node, A_Expr)) + { + A_Expr *a_expr = (A_Expr *) node; + + if (a_expr->lexpr) + queue = lappend(queue, a_expr->lexpr); + if (a_expr->rexpr) + queue = lappend(queue, a_expr->rexpr); + } + else if(IsA(node, FuncCall)) + { + FuncCall *func_call = (FuncCall*) node; + if (func_call->args) + queue = list_concat(queue, func_call->args); + } + } + } + return transformed_returning_list; +} diff --git a/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-nonassoc-ident-tokens b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-nonassoc-ident-tokens new file mode 100644 index 00000000000..8310e24409a --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-nonassoc-ident-tokens @@ -0,0 +1 @@ +IDENTITY_P TSQL_PERSISTED TSQL_ROWGUIDCOL /* these tokens can follow b_expr. To resolve ambiguity, we need to assign the same priority with IDENT. please see the comment in gram.y for details */ diff --git a/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-prologue.y.h b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-prologue.y.h new file mode 100644 index 00000000000..38da81ca17c --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-prologue.y.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * + * Start of T-SQL specific prologue (will be moved to separate file) + * + *****************************************************************************/ + +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "parser/parse_type.h" +#include "parser/scansup.h" +#include "utils/builtins.h" +#include "common/md5.h" + +#include "src/backend_parser/gramparse.h" +#include "src/pltsql_instr.h" +#include "src/multidb.h" + +#define MD5_HASH_LEN 32 + +static void pgtsql_base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg); + +List *TsqlSystemFuncName(char *name); +List *TsqlSystemFuncName2(char *name); + +/* Private struct for the result of tsql_for_clause production */ +typedef struct TSQL_ForClause +{ + int mode; + char *elementName; + List *commonDirectives; + int location; /* token location of FOR, or -1 if unknown */ +} TSQL_ForClause; + +extern bool output_update_transformation; +extern PLtsql_execstate *get_current_tsql_estate(void); + +static Node *makeTSQLHexStringConst(char *str, int location); +static RangeVar *makeRangeVarFromAnyNameForTableType(List *names, int position, core_yyscan_t yyscanner); + +static Node *TsqlFunctionTryCast(Node *arg, TypeName *typename, int location); +static Node *TsqlFunctionConvert(TypeName *typename, Node *arg, Node *style, bool try, int location); +static Node *TsqlFunctionParse(Node *arg, TypeName *typename, Node *culture, bool try, int location); + +static Node *TsqlFunctionIIF(Node *bool_expr, Node *arg1, Node *arg2, int location); +static Node *TsqlFunctionChoose(Node *int_expr, List *choosable, int location); +static void tsql_check_param_readonly(const char* paramname, TypeName *typename, bool readonly); +static void tsql_completeDefaultValues(List *parameters); +static ResTarget *TsqlForXMLMakeFuncCall(TSQL_ForClause *forclause, char *src_query, size_t start_location, core_yyscan_t yyscanner); + +char * construct_unique_index_name(char *index_name, char *relation_name); + +static Node *tsql_update_delete_stmt_with_join(Node *n, List* from_clause, Node* + where_clause, Node *top_clause, RangeVar *relation, + core_yyscan_t yyscanner); +static Node *tsql_update_delete_stmt_with_top(Node *top_clause, RangeVar + *relation, Node *where_clause, core_yyscan_t yyscanner); +static Node *tsql_insert_output_into_cte_transformation(WithClause *opt_with_clause, RangeVar *insert_target, + List *insert_column_list, List *tsql_output_clause, RangeVar *output_target, List *tsql_output_into_target_columns, + InsertStmt *tsql_output_insert_rest, int select_location); +static Node *tsql_delete_output_into_cte_transformation(WithClause *opt_with_clause, Node *opt_top_clause, + RangeVar *relation_expr_opt_alias, List *tsql_output_clause, RangeVar *insert_target, + List *tsql_output_into_target_columns, List *from_clause, Node *where_or_current_clause, + core_yyscan_t yyscanner); +static void tsql_check_update_output_transformation(List *tsql_output_clause); +static Node *tsql_update_output_into_cte_transformation(WithClause *opt_with_clause, Node *opt_top_clause, + RangeVar *relation_expr_opt_alias, List *set_clause_list, + List *tsql_output_clause, RangeVar *insert_target, List *tsql_output_into_target_columns, + List *from_clause, Node *where_or_current_clause, core_yyscan_t yyscanner); +static List *get_transformed_output_list(List * tsql_output_clause); diff --git a/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-rule.y b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-rule.y new file mode 100644 index 00000000000..48b658cc2f7 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-rule.y @@ -0,0 +1,3749 @@ +/* + * Please note that this file is appended to gram.y so that existing PG backend parser rule can be extended + * because if there are multiple rules with the same name then they can be ORed into one rule. + * i.e. + * ruleX: XXX {} + * ruleX: XXX_TSQL {} + * <=> + * ruleX: XXX {} + * | XXX_TSQL {} + */ + +/* Start of exsiting grammar rule in gram.y */ + +stmtblock: + DIALECT_TSQL tsql_stmtmulti + { + pg_yyget_extra(yyscanner)->parsetree = $2; + } + ; + +tsql_CreateLoginStmt: + CREATE TSQL_LOGIN RoleId FROM tsql_login_sources + { + CreateRoleStmt *n = makeNode(CreateRoleStmt); + n->stmt_type = ROLESTMT_USER; + n->role = $3; + n->options = list_make1(makeDefElem("islogin", + (Node *)makeInteger(true), + @1)); /* Must be first */ + n->options = lappend(n->options, + makeDefElem("createdb", + (Node *)makeInteger(true), + @1)); + n->options = lappend(n->options, + makeDefElem("createrole", + (Node *)makeInteger(true), + @1)); + n->options = lappend(n->options, + makeDefElem("inherit", + (Node *)makeInteger(true), + @1)); + n->options = lappend(n->options, + makeDefElem("canlogin", + (Node *)makeInteger(true), + @1)); + $$ = (Node *)n; + } + | CREATE TSQL_LOGIN RoleId tsql_login_option_list1 + { + CreateRoleStmt *n = makeNode(CreateRoleStmt); + n->stmt_type = ROLESTMT_USER; + n->role = $3; + n->options = list_make1(makeDefElem("islogin", + (Node *)makeInteger(true), + @1)); /* Must be first */ + n->options = lappend(n->options, + makeDefElem("createdb", + (Node *)makeInteger(true), + @1)); + n->options = lappend(n->options, + makeDefElem("createrole", + (Node *)makeInteger(true), + @1)); + n->options = lappend(n->options, + makeDefElem("inherit", + (Node *)makeInteger(true), + @1)); + n->options = lappend(n->options, + makeDefElem("canlogin", + (Node *)makeInteger(true), + @1)); + n->options = list_concat(n->options, $4); + $$ = (Node *)n; + } + ; + +tsql_login_option_list1: + WITH PASSWORD '=' tsql_nchar opt_must_change + { + $$ = list_make1(makeDefElem("password", $4, @1)); + } + | WITH PASSWORD '=' tsql_nchar opt_must_change tsql_login_option_list2 + { + $$ = lcons(makeDefElem("password", $4, @1), $6); + } + | WITH PASSWORD '=' TSQL_XCONST HASHED opt_must_change + { + $$ = list_make1(makeDefElem("password", NULL, @1)); + } + | WITH PASSWORD '=' TSQL_XCONST HASHED opt_must_change tsql_login_option_list2 + { + $$ = lcons(makeDefElem("password", NULL, @1), $7); + } + ; + +tsql_login_option_list2: + ',' tsql_login_option_elem + { + if ($2 != NULL) + $$ = list_make1($2); + else + $$ = NIL; + } + | tsql_login_option_list2 ',' tsql_login_option_elem + { + if ($3 != NULL) + $$ = lappend($1, $3); + } + ; + +tsql_login_option_elem: + SID '=' TSQL_XCONST + { + $$ = NULL; + } + | DEFAULT_DATABASE '=' NonReservedWord + { + $$ = makeDefElem("default_database", + (Node *)makeString($3), + @1); + } + | DEFAULT_LANGUAGE '=' NonReservedWord + { + $$ = NULL; + } + | CHECK_EXPIRATION '=' opt_boolean_or_string + { + $$ = NULL; + } + | CHECK_POLICY '=' opt_boolean_or_string + { + $$ = NULL; + } + | CREDENTIAL '=' NonReservedWord + { + $$ = NULL; + } + ; + +opt_must_change: + MUST_CHANGE + | /*EMPTY*/ + ; + +tsql_login_sources: + TSQL_WINDOWS + | TSQL_WINDOWS WITH tsql_windows_options_list + | CERTIFICATE NonReservedWord + | ASYMMETRIC KEY NonReservedWord + ; + +tsql_windows_options_list: + tsql_windows_options + | tsql_windows_options_list ',' tsql_windows_options + ; + +tsql_windows_options: + DEFAULT_DATABASE '=' NonReservedWord + | DEFAULT_LANGUAGE '=' NonReservedWord + ; + +CreateUserStmt: + CREATE USER RoleId tsql_without_login opt_with OptRoleList + { + CreateRoleStmt *n = makeNode(CreateRoleStmt); + n->stmt_type = ROLESTMT_USER; + n->role = $3; + n->options = $6; + if ($4) + { + if ($6) + { + n->options = lappend(n->options, + list_make1(makeDefElem("canlogin", (Node *)makeInteger(false), @1))); + } + else + { + n->options = list_make1(makeDefElem("canlogin", (Node *)makeInteger(false), @1)); + } + } + $$ = (Node *)n; + } + ; + +tsql_AlterLoginStmt: + ALTER TSQL_LOGIN RoleSpec tsql_enable_disable + { + AlterRoleStmt *n = makeNode(AlterRoleStmt); + n->role = $3; + n->action = +1; /* add, if there are members */ + n->options = list_make1(makeDefElem("islogin", + (Node *)makeInteger(true), + @1)); /* Must be first */ + if ($4) + n->options = lappend(n->options, + makeDefElem("canlogin", + (Node *)makeInteger(true), + @1)); + else + n->options = lappend(n->options, + makeDefElem("canlogin", + (Node *)makeInteger(false), + @1)); + $$ = (Node *)n; + } + | ALTER TSQL_LOGIN RoleSpec WITH tsql_alter_login_option_list + { + AlterRoleStmt *n = makeNode(AlterRoleStmt); + n->role = $3; + n->action = +1; /* add, if there are members */ + n->options = list_make1(makeDefElem("islogin", + (Node *)makeInteger(true), + @1)); /* Must be first */ + if ($5 != NIL) + n->options = list_concat(n->options, $5); + $$ = (Node *)n; + } + | ALTER TSQL_LOGIN RoleSpec add_drop CREDENTIAL NonReservedWord + { + AlterRoleStmt *n = makeNode(AlterRoleStmt); + n->role = $3; + n->action = +1; /* add, if there are members */ + n->options = list_make1(makeDefElem("islogin", + (Node *)makeInteger(true), + @1)); /* Must be first */ + $$ = (Node *)n; + } + ; + +tsql_enable_disable: + ENABLE_P + { + $$ = true; + } + | DISABLE_P + { + $$ = false; + } + ; + +tsql_alter_login_option_list: + tsql_alter_login_option_elem + { + if ($1 != NULL) + $$ = list_make1($1); + else + $$ = NIL; + } + | tsql_alter_login_option_list ',' tsql_alter_login_option_elem + { + if ($3 != NULL) + $$ = lappend($1, $3); + } + ; + +tsql_alter_login_option_elem: + PASSWORD '=' tsql_nchar tsql_alter_login_password_option1 + { + $$ = makeDefElem("password", $3, @1); + } + | PASSWORD '=' TSQL_XCONST HASHED tsql_alter_login_password_option1 + { + $$ = makeDefElem("password", NULL, @1); + } + | DEFAULT_DATABASE '=' NonReservedWord + { + $$ = makeDefElem("default_database", + (Node *)makeString($3), + @1);; + } + | DEFAULT_LANGUAGE '=' NonReservedWord + { + $$ = NULL; + } + | NAME_P '=' RoleSpec + { + $$ = NULL; + } + | CHECK_EXPIRATION '=' opt_boolean_or_string + { + $$ = NULL; + } + | CHECK_POLICY '=' opt_boolean_or_string + { + $$ = NULL; + } + | CREDENTIAL '=' NonReservedWord + { + $$ = NULL; + } + | NO CREDENTIAL + { + $$ = NULL; + } + ; + +tsql_alter_login_password_option1: + OLD_PASSWORD '=' tsql_nchar + | tsql_alter_login_password_option2_list + | /*EMPTY*/ + ; + +tsql_alter_login_password_option2_list: + tsql_alter_login_password_option2 + | tsql_alter_login_password_option2_list tsql_alter_login_password_option2 + ; + +tsql_alter_login_password_option2: + MUST_CHANGE + | UNLOCK + ; + +tsql_DropLoginStmt: + DROP TSQL_LOGIN role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = false; + n->roles = $3; + $$ = (Node *)n; + } + ; + +tsql_nchar: + VARCHAR Sconst { $$ = (Node *)makeString($2); } + | Sconst { $$ = (Node *)makeString($1); } + ; + +AlterOptRoleElem: + PASSWORD '=' Sconst + { + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@2))); + $$ = makeDefElem("password", + (Node *)makeString($3), @1); + } + ; + +alter_table_cmds: /* extend it allow to consume nullable (tsql_alter_table_cmd) + /* alter_table_cmd */ + /* | alter_table_cmds ',' alter_table_cmd */ + tsql_alter_table_cmd { $$ = (($1 != NULL) ? list_make1($1) : NIL); } + | alter_table_cmds ',' tsql_alter_table_cmd { $$ = (($3 != NULL) ? lappend($1, $3) : $1); } + ; + +opt_reloptions: + WITH_paren reloptions { $$ = $2; } + ; + +PartitionBoundSpec: + /* a HASH partition */ + FOR TSQL_VALUES WITH_paren '(' hash_partbound ')' + { + ListCell *lc; + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->strategy = PARTITION_STRATEGY_HASH; + n->modulus = n->remainder = -1; + + foreach (lc, $5) + { + DefElem *opt = lfirst_node(DefElem, lc); + + if (strcmp(opt->defname, "modulus") == 0) + { + if (n->modulus != -1) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("modulus for hash partition provided more than once"), + parser_errposition(opt->location))); + n->modulus = defGetInt32(opt); + } + else if (strcmp(opt->defname, "remainder") == 0) + { + if (n->remainder != -1) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("remainder for hash partition provided more than once"), + parser_errposition(opt->location))); + n->remainder = defGetInt32(opt); + } + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized hash partition bound specification \"%s\"", + opt->defname), + parser_errposition(opt->location))); + } + + if (n->modulus == -1) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("modulus for hash partition must be specified"))); + if (n->remainder == -1) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("remainder for hash partition must be specified"))); + + n->location = @3; + + $$ = n; + } + ; + +CopyStmt: COPY opt_binary qualified_name opt_column_list + copy_from opt_program copy_file_name copy_delimiter WITH_paren + copy_options where_clause + { + CopyStmt *n = makeNode(CopyStmt); + n->relation = $3; + n->query = NULL; + n->attlist = $4; + n->is_from = $5; + n->is_program = $6; + n->filename = $7; + n->whereClause = $11; + + if (n->is_program && n->filename == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("STDIN/STDOUT not allowed with PROGRAM"), + parser_errposition(@8))); + + if (!n->is_from && n->whereClause != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("WHERE clause not allowed with COPY TO"), + parser_errposition(@11))); + + n->options = NIL; + /* Concatenate user-supplied flags */ + if ($2) + n->options = lappend(n->options, $2); + if ($8) + n->options = lappend(n->options, $8); + if ($10) + n->options = list_concat(n->options, $10); + $$ = (Node *)n; + } + | COPY '(' PreparableStmt ')' TO opt_program + copy_file_name WITH_paren copy_options + { + CopyStmt *n = makeNode(CopyStmt); + n->relation = NULL; + n->query = $3; + n->attlist = NIL; + n->is_from = false; + n->is_program = $6; + n->filename = $7; + n->options = $9; + + if (n->is_program && n->filename == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("STDIN/STDOUT not allowed with PROGRAM"), + parser_errposition(@5))); + + $$ = (Node *)n; + } + ; + +OptTableElementList: + TableElementList ',' { $$ = $1; } /* For TSQL compatibility */ + ; + +columnDef: + ColId TSQL_computed_column ColQualList + { + ColumnDef *n = makeNode(ColumnDef); + + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@2))); + + TSQLInstrumentation(INSTR_TSQL_COMPUTED_COLUMN); + n->colname = $1; + + /* + * For computed columns, user doesn't provide a datatype. + * But, PG expects a datatype. Hence, we just assign a + * valid datatype temporarily. Later, we'll evaluate + * expression to detect the actual datatype. + */ + n->typeName = makeTypeName("varchar"); + n->inhcount = 0; + n->is_local = true; + n->is_not_null = false; + n->is_from_type = false; + n->storage = 0; + n->raw_default = NULL; + n->cooked_default = NULL; + n->collOid = InvalidOid; + n->fdwoptions = NULL; + n->location = @1; + + $3 = lappend($3, $2); + + SplitColQualList($3, &n->constraints, &n->collClause, + yyscanner); + + $$ = (Node *)n; + } + ; + +ColQualList: /* extend it allow to consume nullable (tsql_ColConstraint) */ + /* ColQualList ColConstraint */ + /* | EMPTY */ + ColQualList tsql_ColConstraint { $$ = (($2 != NULL) ? lappend($1, $2) : $1); } + ; +ConstraintElem: + UNIQUE tsql_cluster '(' columnList ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_opt_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = $4; + n->including = $6; + n->options = $7; + n->indexname = NULL; + n->indexspace = $8; + processCASbits($9, @9, "UNIQUE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | UNIQUE tsql_cluster '(' columnListWithOptAscDesc ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_opt_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = $4; + n->including = $6; + n->options = $7; + n->indexname = NULL; + n->indexspace = $8; + processCASbits($9, @9, "UNIQUE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | UNIQUE '(' columnListWithOptAscDesc ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_opt_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = $3; + n->including = $5; + n->options = $6; + n->indexname = NULL; + n->indexspace = $7; + processCASbits($8, @8, "UNIQUE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | UNIQUE '(' columnList ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = $3; + n->including = $5; + n->options = $6; + n->indexname = NULL; + n->indexspace = $7; + processCASbits($8, @8, "UNIQUE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | UNIQUE tsql_cluster ExistingIndex ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_UNIQUE; + n->location = @1; + n->keys = NIL; + n->including = NIL; + n->options = NIL; + n->indexname = $3; + n->indexspace = NULL; + processCASbits($4, @4, "UNIQUE", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | PRIMARY KEY tsql_cluster '(' columnList ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_opt_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = $5; + n->including = $7; + n->options = $8; + n->indexname = NULL; + n->indexspace = $9; + processCASbits($10, @10, "PRIMARY KEY", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | PRIMARY KEY tsql_cluster '(' columnListWithOptAscDesc ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_opt_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = $5; + n->including = $7; + n->options = $8; + n->indexname = NULL; + n->indexspace = $9; + processCASbits($10, @10, "PRIMARY KEY", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | PRIMARY KEY '(' columnListWithOptAscDesc ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_opt_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = $4; + n->including = $6; + n->options = $7; + n->indexname = NULL; + n->indexspace = $8; + processCASbits($9, @9, "PRIMARY KEY", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | PRIMARY KEY '(' columnList ')' opt_c_include opt_definition OptConsTableSpace + ConstraintAttributeSpec tsql_on_filegroup + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = $4; + n->including = $6; + n->options = $7; + n->indexname = NULL; + n->indexspace = $8; + processCASbits($9, @9, "PRIMARY KEY", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + | PRIMARY KEY tsql_cluster ExistingIndex ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_PRIMARY; + n->location = @1; + n->keys = NIL; + n->including = NIL; + n->options = NIL; + n->indexname = $4; + n->indexspace = NULL; + processCASbits($5, @5, "PRIMARY KEY", + &n->deferrable, &n->initdeferred, NULL, + NULL, yyscanner); + $$ = (Node *)n; + } + ; + +OptWith: + WITH_paren reloptions { $$ = $2; } + ; + +OnCommitOption: + OptFileGroup { $$ = ONCOMMIT_NOOP; } + | OptFileGroup OptFileGroup { $$ = ONCOMMIT_NOOP; } + ; + +DefineStmt: + /* + * TSQL supports table type, and we handle it by creating a template + * table so that later when variables of this type are created, they + * are created like the template table. + */ + CREATE TYPE_P any_name AS TABLE '(' OptTableElementList ')' + { + CreateStmt *n = makeNode(CreateStmt); + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@1))); + TSQLInstrumentation(INSTR_TSQL_CREATE_TEMP_TABLE); + n->relation = makeRangeVarFromAnyNameForTableType($3, @3, yyscanner); + n->tableElts = $7; + n->inhRelations = NIL; + n->partspec = NULL; + n->ofTypename = NULL; + n->constraints = NIL; + n->options = NIL; + n->oncommit = ONCOMMIT_NOOP; + n->tablespacename = NULL; + n->if_not_exists = false; + n->tsql_tabletype = true; + $$ = (Node *)n; + } + | CREATE TYPE_P any_name FROM Typename + { + CreateDomainStmt *n = makeNode(CreateDomainStmt); + n->domainname = $3; + n->typeName = $5; + n->constraints = NIL; + + $$ = (Node *)n; + } + | CREATE TYPE_P any_name FROM Typename NOT NULL_P + { + CreateDomainStmt *n = makeNode(CreateDomainStmt); + Constraint *c = makeNode(Constraint); + + n->domainname = $3; + n->typeName = $5; + n->constraints = list_make1(c); + + c->contype = CONSTR_NOTNULL; + c->location = @6; + + $$ = (Node *)n; + + } + | CREATE TYPE_P any_name FROM Typename NULL_P + { + CreateDomainStmt *n = makeNode(CreateDomainStmt); + Constraint *c = makeNode(Constraint); + + n->domainname = $3; + n->typeName = $5; + n->constraints = list_make1(c); + + c->contype = CONSTR_NULL; + c->location = @6; + + $$ = (Node *)n; + + } + ; + +func_arg: + param_name func_type arg_class + { + FunctionParameter *n = makeNode(FunctionParameter); + n->name = $1; + n->argType = $2; + n->mode = $3; + n->defexpr = NULL; + $$ = n; + } + ; + +arg_class: + TSQL_OUT { $$ = FUNC_PARAM_OUT; } + | TSQL_OUTPUT { $$ = FUNC_PARAM_INOUT; } + | IN_P TSQL_OUT { $$ = FUNC_PARAM_INOUT; } + ; + +/* Note: any simple identifier will be returned as a type name! + * TSQL support for and : + * CREATE TABLE... WITH () + * CREATE INDEX... WITH () + */ +def_arg: func_type tsql_on_ident_partitions_list { $$ = (Node *)$1; } + | reserved_keyword tsql_paren_extra_relopt_list { $$ = (Node *)makeString(pstrdup($1)); } + | NumericOnly tsql_ident { $$ = (Node *)$1; } + | NONE tsql_on_ident_partitions_list { $$ = (Node *)makeString(pstrdup($1)); } + | ROW tsql_opt_on_partitions_list { $$ = (Node *)makeString(pstrdup($1)); } + ; + +DropStmt: + DROP drop_type_name_on_any_name name + { + DropStmt *n = makeNode(DropStmt); + + if(sql_dialect != SQL_DIALECT_TSQL || $2 != OBJECT_TRIGGER) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL for DROP TRIGGER"), + parser_errposition(@1))); + } + n->removeType = OBJECT_TRIGGER; + n->objects = list_make1(list_make1(makeString($3))); + n->behavior = DROP_CASCADE; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP drop_type_name_on_any_name IF_P EXISTS name + { + DropStmt *n = makeNode(DropStmt); + + if(sql_dialect != SQL_DIALECT_TSQL || $2 != OBJECT_TRIGGER) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL for DROP TRIGGER"), + parser_errposition(@1))); + } + n->removeType = OBJECT_TRIGGER; + n->objects = list_make1(list_make1(makeString($5))); + n->behavior = DROP_CASCADE; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *) n; + } + ; + +tsql_DropIndexStmt: + DROP drop_type_any_name index_name ON name_list + { + DropStmt *n = makeNode(DropStmt); + if(sql_dialect != SQL_DIALECT_TSQL) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@1))); + } + if($2 != OBJECT_INDEX) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid for DROP INDEX ... on ..."), + parser_errposition(@2))); + } + n->removeType = $2; + n->missing_ok = false; + n->objects = list_make1(list_make1(makeString(construct_unique_index_name($3, makeRangeVarFromAnyName($5, @5, yyscanner)->relname)))); + n->behavior = DROP_CASCADE; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP drop_type_any_name IF_P EXISTS index_name ON name_list + { + DropStmt *n = makeNode(DropStmt); + if(sql_dialect != SQL_DIALECT_TSQL) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@1))); + } + if($2 != OBJECT_INDEX) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid for DROP INDEX ... on ..."), + parser_errposition(@2))); + } + n->removeType = $2; + n->missing_ok = true; + n->objects = list_make1(list_make1(makeString(construct_unique_index_name($5, makeRangeVarFromAnyName($7, @5, yyscanner)->relname)))); + n->behavior = DROP_CASCADE; + n->concurrent = false; + $$ = (Node *)n; + } + ; + +opt_definition: + WITH_paren definition { $$ = $2; } + | WITH IDENT '=' NumericOnly + { + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@1))); + $$ = NIL; + } + ; + +RemoveFuncStmt: + DROP TSQL_PROC function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_PROCEDURE; + n->objects = $3; + n->behavior = $4; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP TSQL_PROC IF_P EXISTS function_with_argtypes_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_PROCEDURE; + n->objects = $5; + n->behavior = $6; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *)n; + } + ; + +DeleteStmt: opt_with_clause DELETE_P FROM relation_expr_opt_alias + tsql_table_hint_expr using_clause where_or_current_clause + returning_clause + { + DeleteStmt *n = makeNode(DeleteStmt); + n->relation = $4; + n->usingClause = $6; + n->whereClause = $7; + n->returningList = $8; + n->withClause = $1; + $$ = (Node *)n; + } + ; + +tsql_UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias + SET set_clause_list + from_clause + where_or_current_clause + returning_clause + { + UpdateStmt *n = makeNode(UpdateStmt); + n->relation = $3; + n->targetList = $5; + if ($6 != NULL && IsA(linitial($6), JoinExpr)) + { + n = (UpdateStmt*)tsql_update_delete_stmt_with_join( + (Node*)n, $6, $7, NULL, $3, + yyscanner); + } + else + { + n->fromClause = $6; + n->whereClause = $7; + } + n->returningList = $8; + n->withClause = $1; + $$ = (Node *)n; + } + | opt_with_clause UPDATE relation_expr_opt_alias + tsql_table_hint_expr + SET set_clause_list + from_clause + where_or_current_clause + returning_clause + { + UpdateStmt *n = makeNode(UpdateStmt); + n->relation = $3; + n->targetList = $6; + n->fromClause = $7; + n->whereClause = $8; + n->returningList = $9; + n->withClause = $1; + $$ = (Node *)n; + } + | opt_with_clause UPDATE tsql_top_clause relation_expr_opt_alias + tsql_opt_table_hint_expr + SET set_clause_list + from_clause + where_or_current_clause + returning_clause + { + UpdateStmt *n = makeNode(UpdateStmt); + n->relation = $4; + n->targetList = $7; + if ($8 != NULL && IsA(linitial($8), JoinExpr)) + { + n = (UpdateStmt*)tsql_update_delete_stmt_with_join( + (Node*)n, $8, $9, $3, $4, + yyscanner); + } + else + { + n->fromClause = $8; + n->whereClause = tsql_update_delete_stmt_with_top($3, + $4, $9, yyscanner); + } + n->returningList = $10; + n->withClause = $1; + $$ = (Node *)n; + } + /* OUTPUT syntax */ + | opt_with_clause UPDATE relation_expr_opt_alias + SET set_clause_list + tsql_output_clause + from_clause + where_or_current_clause + { + UpdateStmt *n = makeNode(UpdateStmt); + n->relation = $3; + n->targetList = $5; + if ($7 != NULL && IsA(linitial($7), JoinExpr)) + { + n = (UpdateStmt*)tsql_update_delete_stmt_with_join( + (Node*)n, $7, $8, NULL, $3, + yyscanner); + + } + else + { + n->fromClause = $7; + n->whereClause = $8; + } + tsql_check_update_output_transformation($6); + n->returningList = $6; + n->withClause = $1; + $$ = (Node *)n; + } + | opt_with_clause UPDATE relation_expr_opt_alias + tsql_table_hint_expr + SET set_clause_list + tsql_output_clause + from_clause + where_or_current_clause + { + UpdateStmt *n = makeNode(UpdateStmt); + n->relation = $3; + n->targetList = $6; + n->fromClause = $8; + n->whereClause = $9; + tsql_check_update_output_transformation($7); + n->returningList = $7; + n->withClause = $1; + $$ = (Node *)n; + } + | opt_with_clause UPDATE tsql_top_clause relation_expr_opt_alias + tsql_opt_table_hint_expr + SET set_clause_list + tsql_output_clause + from_clause + where_or_current_clause + { + UpdateStmt *n = makeNode(UpdateStmt); + n->relation = $4; + n->targetList = $7; + if ($8 != NULL && IsA(linitial($8), JoinExpr)) + { + n = (UpdateStmt*)tsql_update_delete_stmt_with_join( + (Node*)n, $9, $10, $3, $4, + yyscanner); + } + else + { + n->fromClause = $8; + n->whereClause = tsql_update_delete_stmt_with_top($3, + $4, $10, yyscanner); + } + tsql_check_update_output_transformation($8); + n->returningList = $8; + n->withClause = $1; + $$ = (Node *)n; + } + /* OUTPUT INTO syntax with OUTPUT target column list */ + | opt_with_clause UPDATE relation_expr_opt_alias + SET set_clause_list + tsql_output_clause INTO insert_target tsql_output_into_target_columns + from_clause + where_or_current_clause + { + $$ = tsql_update_output_into_cte_transformation($1, NULL, $3, $5, $6, $8, + $9, $10, $11, yyscanner); + } + | opt_with_clause UPDATE relation_expr_opt_alias + tsql_table_hint_expr + SET set_clause_list + tsql_output_clause INTO insert_target tsql_output_into_target_columns + from_clause + where_or_current_clause + { + $$ = tsql_update_output_into_cte_transformation($1, NULL, $3, $6, $7, $9, + $10, $11, $12, yyscanner); + } + | opt_with_clause UPDATE tsql_top_clause relation_expr_opt_alias + tsql_opt_table_hint_expr + SET set_clause_list + tsql_output_clause INTO insert_target tsql_output_into_target_columns + from_clause + where_or_current_clause + { + $$ = tsql_update_output_into_cte_transformation($1, $3, $4, $7, $8, $10, + $11, $12, $13, yyscanner); + } + /* Without OUTPUT target column list */ + | opt_with_clause UPDATE relation_expr_opt_alias + SET set_clause_list + tsql_output_clause INTO insert_target + from_clause + where_or_current_clause + { + $$ = tsql_update_output_into_cte_transformation($1, NULL, $3, $5, $6, $8, + NIL, $9, $10, yyscanner); + } + | opt_with_clause UPDATE relation_expr_opt_alias + tsql_table_hint_expr + SET set_clause_list + tsql_output_clause INTO insert_target + from_clause + where_or_current_clause + { + $$ = tsql_update_output_into_cte_transformation($1, NULL, $3, $6, $7, $9, + NIL, $10, $11, yyscanner); + } + | opt_with_clause UPDATE tsql_top_clause relation_expr_opt_alias + tsql_opt_table_hint_expr + SET set_clause_list + tsql_output_clause INTO insert_target + from_clause + where_or_current_clause + { + $$ = tsql_update_output_into_cte_transformation($1, $3, $4, $7, $8, $10, + NIL, $11, $12, yyscanner); + } + ; + +select_no_parens: + select_clause tsql_for_clause + { + base_yy_extra_type *yyextra = pg_yyget_extra(yyscanner); + char *src_query = yyextra->core_yy_extra.scanbuf; + /* + * We can free the SelectStmt because we will process the transformed + * FOR XML query by calling function tsql_query_to_xml(). + */ + pfree($1); + $1 = (Node *) makeNode(SelectStmt); + ((SelectStmt *)$1)->targetList = list_make1(TsqlForXMLMakeFuncCall((TSQL_ForClause *) $2, src_query, @1, yyscanner)); + $$ = $1; + } + | select_clause sort_clause tsql_for_clause + { + if ($3 == NULL) + insertSelectOptions((SelectStmt *) $1, $2, NIL, + NULL, NULL, + yyscanner); + else + { + base_yy_extra_type *yyextra = pg_yyget_extra(yyscanner); + char *src_query = yyextra->core_yy_extra.scanbuf; + /* + * We can free the SelectStmt because we will process the transformed + * FOR XML query by calling function tsql_query_to_xml(). + */ + pfree($1); + $1 = (Node *) makeNode(SelectStmt); + ((SelectStmt *)$1)->targetList = list_make1(TsqlForXMLMakeFuncCall((TSQL_ForClause *) $3, src_query, @1, yyscanner)); + } + $$ = $1; + } + | with_clause select_clause tsql_for_clause + { + if ($3 == NULL) + insertSelectOptions((SelectStmt *) $2, NULL, NIL, + NULL, + $1, + yyscanner); + else + { + base_yy_extra_type *yyextra = pg_yyget_extra(yyscanner); + char *src_query = yyextra->core_yy_extra.scanbuf; + /* + * We can free the SelectStmt because we will process the transformed + * FOR XML query by calling function tsql_query_to_xml(). + */ + pfree($2); + $2 = (Node *) makeNode(SelectStmt); + ((SelectStmt *)$2)->targetList = list_make1(TsqlForXMLMakeFuncCall((TSQL_ForClause *) $3, src_query, @1, yyscanner)); + } + $$ = $2; + } + | with_clause select_clause sort_clause tsql_for_clause + { + if ($4 == NULL) + insertSelectOptions((SelectStmt *) $2, $3, NIL, + NULL, + $1, + yyscanner); + else + { + base_yy_extra_type *yyextra = pg_yyget_extra(yyscanner); + char *src_query = yyextra->core_yy_extra.scanbuf; + /* + * We can free the SelectStmt because we will process the transformed + * FOR XML query by calling function tsql_query_to_xml(). + */ + pfree($2); + $2 = (Node *) makeNode(SelectStmt); + ((SelectStmt *)$2)->targetList = list_make1(TsqlForXMLMakeFuncCall((TSQL_ForClause *) $4, src_query, @1, yyscanner)); + } + $$ = $2; + } + ; + +simple_select: + SELECT opt_all_clause tsql_top_clause opt_target_list + into_clause from_clause where_clause + group_clause having_clause window_clause + { + SelectStmt *n = makeNode(SelectStmt); + + n->limitCount = $3; + n->targetList = $4; + if ($3 != NULL && $4 == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Target list missing from TOP clause"), + errhint("For example, TOP n COLUMNS ..."), + parser_errposition(@3))); + n->intoClause = $5; + n->fromClause = $6; + n->whereClause = $7; + n->groupClause = $8; + n->havingClause = $9; + n->windowClause = $10; + $$ = (Node *)n; + } + | SELECT distinct_clause tsql_top_clause target_list + into_clause from_clause where_clause + group_clause having_clause window_clause + { + SelectStmt *n = makeNode(SelectStmt); + + n->distinctClause = $2; + n->limitCount = $3; + n->targetList = $4; + if ($3 != NULL && $4 == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Target list missing from TOP clause"), + errhint("For example, TOP n COLUMNS ..."), + parser_errposition(@3))); + n->intoClause = $5; + n->fromClause = $6; + n->whereClause = $7; + n->groupClause = $8; + n->havingClause = $9; + n->windowClause = $10; + $$ = (Node *)n; + } + ; + +table_ref: relation_expr tsql_table_hint_expr + { + $$ = (Node *) $1; + } + | relation_expr alias_clause tsql_table_hint_expr + { + $1->alias = $2; + $$ = (Node *) $1; + } + | relation_expr tsql_table_hint_expr alias_clause + { + $1->alias = $3; + $$ = (Node *) $1; + } + | relation_expr opt_alias_clause tablesample_clause tsql_table_hint_expr + { + RangeTableSample *n = (RangeTableSample *) $3; + $1->alias = $2; + /* relation_expr goes inside the RangeTableSample node */ + n->relation = (Node *) $1; + $$ = (Node *) n; + } + ; + +func_expr_common_subexpr: + TSQL_TRY_CAST '(' a_expr AS Typename ')' + { + $$ = TsqlFunctionTryCast($3, $5, @1); + } + | TSQL_CONVERT '(' Typename ',' a_expr ')' + { + $$ = TsqlFunctionConvert($3, $5, NULL, false, @1); + } + | TSQL_CHOOSE '(' a_expr ',' expr_list ')' + { + $$ = TsqlFunctionChoose($3, $5, @1); + } + | TSQL_CONVERT '(' Typename ',' a_expr ',' a_expr ')' + { + $$ = TsqlFunctionConvert($3, $5, $7, false, @1); + } + | TSQL_TRY_CONVERT '(' Typename ',' a_expr ')' + { + $$ = TsqlFunctionConvert($3, $5, NULL, true, @1); + } + | TSQL_TRY_CONVERT '(' Typename ',' a_expr ',' a_expr ')' + { + TSQLInstrumentation(INSTR_TSQL_TRY_CONVERT); + $$ = TsqlFunctionConvert($3, $5, $7, true, @1); + } + | TSQL_DATEADD '(' dateadd_arg ',' a_expr ',' a_expr ')' + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName2("dateadd"), + list_make3(makeStringConst($3, @3), + $5, $7), + @1); + } + | TSQL_PARSE '(' a_expr AS Typename ')' + { + TSQLInstrumentation(INSTR_TSQL_PARSE); + $$ = TsqlFunctionParse($3, $5, NULL, false, @1); + } + | TSQL_PARSE '(' a_expr AS Typename USING a_expr ')' + { + TSQLInstrumentation(INSTR_TSQL_PARSE); + $$ = TsqlFunctionParse($3, $5, $7, false, @1); + } + | TSQL_TRY_PARSE '(' a_expr AS Typename ')' + { + $$ = TsqlFunctionParse($3, $5, NULL, true, @1); + } + | TSQL_TRY_PARSE '(' a_expr AS Typename USING a_expr ')' + { + $$ = TsqlFunctionParse($3, $5, $7, true, @1); + } + | TSQL_DATEDIFF '(' datediff_arg ',' a_expr ',' a_expr ')' + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName2("datediff"), + list_make3(makeStringConst($3, @3), $5, $7), + @1); + } + | TSQL_DATEPART '(' datepart_arg ',' a_expr ')' + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName2("datepart"), + list_make2(makeStringConst($3, @3), $5), + @1); + } + | TSQL_DATENAME '(' datepart_arg ',' a_expr ')' + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName2("datename"), + list_make2(makeStringConst($3, @3), $5), + @1); + } + | TSQL_ISNULL '(' a_expr ',' a_expr ')' + { + CoalesceExpr *c = makeNode(CoalesceExpr); + c->args=list_make2($3, $5); + c->location = @1; + $$ = (Node *)c; + } + | TSQL_IIF '(' a_expr ',' a_expr ',' a_expr ')' + { + $$ = TsqlFunctionIIF($3, $5, $7, @1); + } + | TSQL_ATAT IDENT + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName2($2), NIL, @1); + } + | TSQL_ATAT VERSION_P + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName2("version"),NIL, @1); + } + | TSQL_ATAT IDENTITY_P + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName("babelfish_get_last_identity_numeric"), NIL, @1); + } + ; + +target_el: + a_expr AS Sconst + { + $$ = makeNode(ResTarget); + if (strlen($3) >= NAMEDATALEN) + { + char *name = pstrdup($3); + truncate_identifier(name, strlen(name), true); + $$->name = downcaseIfTsqlAndCaseInsensitive(name); + } + else + $$->name = downcaseIfTsqlAndCaseInsensitive($3); + $$->name_location = @3; + $$->indirection = NIL; + $$->val = (Node *)$1; + $$->location = @1; + } + | a_expr AS VARCHAR Sconst + /* + * This rule is to support SELECT 1 AS N'col' query in Babelfish. + * For vanilla PG, the syntax is valid as well + */ + { + $$ = makeNode(ResTarget); + if (strlen($4) >= NAMEDATALEN) + { + char *name = pstrdup($4); + truncate_identifier(name, strlen(name), true); + $$->name = downcaseIfTsqlAndCaseInsensitive(name); + } + else + $$->name = downcaseIfTsqlAndCaseInsensitive($4); + $$->name_location = @4; + $$->indirection = NIL; + $$->val = (Node *)$1; + $$->location = @1; + } + ; + +AexprConst: + TSQL_XCONST + { + $$ = makeTSQLHexStringConst($1, @1); + } + ; + + +/* Start of T-SQL specific grammar rule. */ + +tsql_stmtmulti: tsql_stmtmulti ';' tsql_stmt + { + if ($1 != NIL) + { + /* update length of previous stmt */ + updateRawStmtEnd(llast_node(RawStmt, $1), @2); + } + if ($3 != NULL) + $$ = lappend($1, makeRawStmt($3, @2 + 1)); + else + $$ = $1; + } + | tsql_stmt + { + if ($1 != NULL) + $$ = list_make1(makeRawStmt($1, 0)); + else + $$ = NIL; + } + ; + +/* --------------------------------- */ +/* Rules for OUTPUT clause support */ +tsql_output_insert_rest: + tsql_output_simple_select opt_sort_clause + { + SelectStmt *s = makeNode(SelectStmt); + $$ = makeNode(InsertStmt); + $$->cols = NIL; + s = (SelectStmt*) $1; + s->sortClause = $2; + $$->selectStmt = (Node*) s; + } + | '(' tsql_output_simple_select opt_sort_clause ')' + { + SelectStmt *s = makeNode(SelectStmt); + $$ = makeNode(InsertStmt); + $$->cols = NIL; + s = (SelectStmt*) $2; + s->sortClause = $3; + $$->selectStmt = (Node*) s; + } + | tsql_ExecStmt + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->selectStmt = NULL; + $$->execStmt = $1; + } + ; + +tsql_output_insert_rest_no_paren: + tsql_output_simple_select + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->selectStmt = $1; + } + | tsql_output_ExecStmt + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->selectStmt = NULL; + $$->execStmt = $1; + } + ; + +tsql_output_simple_select: + SELECT opt_all_clause opt_target_list + into_clause from_clause where_clause + group_clause having_clause window_clause + { + SelectStmt *n = makeNode(SelectStmt); + n->targetList = $3; + n->intoClause = $4; + n->fromClause = $5; + n->whereClause = $6; + n->groupClause = $7; + n->havingClause = $8; + n->windowClause = $9; + $$ = (Node *)n; + } + | SELECT distinct_clause target_list + into_clause from_clause where_clause + group_clause having_clause window_clause + { + SelectStmt *n = makeNode(SelectStmt); + n->distinctClause = $2; + n->targetList = $3; + n->intoClause = $4; + n->fromClause = $5; + n->whereClause = $6; + n->groupClause = $7; + n->havingClause = $8; + n->windowClause = $9; + $$ = (Node *)n; + } + | tsql_values_clause { $$ = $1; } + | tsql_output_simple_select UNION all_or_distinct tsql_output_simple_select + { + $$ = makeSetOp(SETOP_UNION, $3, $1, $4); + } + | tsql_output_simple_select INTERSECT all_or_distinct tsql_output_simple_select + { + $$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4); + } + | tsql_output_simple_select EXCEPT all_or_distinct tsql_output_simple_select + { + $$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4); + } + + ; + +tsql_values_clause: + TSQL_VALUES '(' expr_list ')' + { + SelectStmt *n = makeNode(SelectStmt); + n->valuesLists = list_make1($3); + $$ = (Node *) n; + } + | tsql_values_clause ',' '(' expr_list ')' + { + SelectStmt *n = (SelectStmt *) $1; + n->valuesLists = lappend(n->valuesLists, $4); + $$ = (Node *) n; + } + ; + +tsql_output_clause: + TSQL_OUTPUT target_list { $$ = $2; } + ; + +tsql_output_into_target_columns: + '(' insert_column_list ')' { $$ = $2; } + ; + +tsql_output_ExecStmt: + TSQL_EXEC tsql_opt_return tsql_func_name tsql_actual_args + { + List *name = $3; + List *args = $4; + CallStmt *n; + ListCell *lc; + + foreach(lc, args) + { + Node *node = lfirst(lc); + if (node->type == T_RowExpr) + { + RowExpr *row_expr = (RowExpr *) node; + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Row Expression argument not supported"), + parser_errposition(row_expr->location))); + } + } + + n = makeNode(CallStmt); + n->funccall = makeFuncCall(name, args, @1); + + $$ = (Node *) n; + } + ; + +/* END rules for OUTPUT clause support */ +/* --------------------------------- */ + +tsql_stmt : + AlterEventTrigStmt + | AlterCollationStmt + | AlterDatabaseStmt + | AlterDatabaseSetStmt + | AlterDefaultPrivilegesStmt + | AlterDomainStmt + | AlterEnumStmt + | AlterExtensionStmt + | AlterExtensionContentsStmt + | AlterFdwStmt + | AlterForeignTableStmt + | AlterFunctionStmt + | AlterGroupStmt + | tsql_AlterLoginStmt + | AlterObjectDependsStmt + | AlterObjectSchemaStmt + | AlterOwnerStmt + | AlterOperatorStmt + | AlterPolicyStmt + | AlterSeqStmt + | AlterSystemStmt + | AlterTableStmt + | AlterTblSpcStmt + | AlterCompositeTypeStmt + | AlterPublicationStmt + | AlterRoleSetStmt + | AlterRoleStmt + | AlterSubscriptionStmt + | AlterTSConfigurationStmt + | AlterTSDictionaryStmt + | AlterUserMappingStmt + | AnalyzeStmt + | CallStmt + | CheckPointStmt + | ClosePortalStmt + | ClusterStmt + | CommentStmt + | ConstraintsSetStmt + | CopyStmt + | CreateAmStmt + | CreateAsStmt + | CreateCastStmt + | CreateConversionStmt + | CreateDomainStmt + | CreateExtensionStmt + | CreateFdwStmt + | CreateForeignServerStmt + | CreateForeignTableStmt + | tsql_CreateFunctionStmt + | CreateGroupStmt + | tsql_CreateLoginStmt + | CreateMatViewStmt + | CreateOpClassStmt + | CreateOpFamilyStmt + | CreatePublicationStmt + | AlterOpFamilyStmt + | CreatePolicyStmt + | CreatePLangStmt + | CreateSchemaStmt + | CreateSeqStmt + | CreateStmt + | CreateSubscriptionStmt + | CreateStatsStmt + | CreateTableSpaceStmt + | CreateTransformStmt + | tsql_CreateTrigStmt + | CreateEventTrigStmt + | CreateRoleStmt + | CreateUserStmt + | CreateUserMappingStmt + | CreatedbStmt + | DeallocateStmt + | DeclareCursorStmt + | DefineStmt + | tsql_DeleteStmt + | DiscardStmt + | DoStmt + | DropCastStmt + | tsql_DropLoginStmt + | DropOpClassStmt + | DropOpFamilyStmt + | DropOwnedStmt + | DropPLangStmt + | tsql_DropIndexStmt + | DropStmt + | DropSubscriptionStmt + | DropTableSpaceStmt + | DropTransformStmt + | DropRoleStmt + | DropUserMappingStmt + | DropdbStmt + | tsql_ExecStmt + | ExplainStmt + | FetchStmt + | GrantStmt + | GrantRoleStmt + | ImportForeignSchemaStmt + | tsql_IndexStmt + | tsql_InsertStmt + | ListenStmt + | RefreshMatViewStmt + | LoadStmt + | LockStmt + | NotifyStmt + | PrepareStmt + | ReassignOwnedStmt + | ReindexStmt + | RemoveAggrStmt + | RemoveFuncStmt + | RemoveOperStmt + | RenameStmt + | RevokeStmt + | RevokeRoleStmt + | RuleStmt + | SecLabelStmt + | SelectStmt + | tsql_TransactionStmt + | TruncateStmt + | UnlistenStmt + | tsql_UpdateStmt + | VacuumStmt + | VariableResetStmt + | tsql_VariableSetStmt + | VariableShowStmt + | ViewStmt + | tsql_alter_server_role + | /*EMPTY*/ + { $$ = NULL; } + ; + +tsql_opt_INTO: + INTO + | /* empty */ + ; + +tsql_InsertStmt: + opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr '(' insert_column_list ')' + tsql_output_insert_rest + { + $9->relation = $4; + $9->onConflictClause = NULL; + $9->returningList = NULL; + $9->withClause = $1; + $9->cols = $7; + $$ = (Node *) $9; + } + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr tsql_output_insert_rest + { + $6->relation = $4; + $6->onConflictClause = NULL; + $6->returningList = NULL; + $6->withClause = $1; + $6->cols = NIL; + $$ = (Node *) $6; + } + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr DEFAULT TSQL_VALUES + { + InsertStmt *i = makeNode(InsertStmt); + i->relation = $4; + i->onConflictClause = NULL; + i->returningList = NULL; + i->withClause = $1; + i->cols = NIL; + i->selectStmt = NULL; + i->execStmt = NULL; + $$ = (Node *) i; + } + /* OUTPUT syntax */ + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr '(' insert_column_list ')' + tsql_output_clause tsql_output_insert_rest_no_paren + { + if ($10->execStmt) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The OUTPUT clause cannot be used in an INSERT...EXEC statement."), + parser_errposition(@10))); + $10->relation = $4; + $10->onConflictClause = NULL; + $10->returningList = $9; + $10->withClause = $1; + $10->cols = $7; + $$ = (Node *) $10; + } + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr tsql_output_clause tsql_output_insert_rest_no_paren + { + if ($7->execStmt) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The OUTPUT clause cannot be used in an INSERT...EXEC statement."), + parser_errposition(@7))); + $7->relation = $4; + $7->onConflictClause = NULL; + $7->returningList = $6; + $7->withClause = $1; + $7->cols = NIL; + $$ = (Node *) $7; + } + /* conflict on DEFAULT (DEFAULT is allowed as a_expr in tsql_output_clause + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr tsql_output_clause DEFAULT VALUES + { + InsertStmt *i = makeNode(InsertStmt); + i->relation = $4; + i->onConflictClause = NULL; + i->returningList = $6; + i->withClause = $1; + i->cols = NIL; + i->selectStmt = NULL; + i->execStmt = NULL; + $$ = (Node *) i; + } + */ + /* OUTPUT INTO syntax with OUTPUT target column list */ + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr '(' insert_column_list ')' + tsql_output_clause INTO insert_target tsql_output_into_target_columns tsql_output_insert_rest + { + if ($13->execStmt) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The OUTPUT clause cannot be used in an INSERT...EXEC statement."), + parser_errposition(@13))); + $$ = tsql_insert_output_into_cte_transformation($1, $4, $7, $9, $11, $12, $13, 4); + } + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr tsql_output_clause + INTO insert_target tsql_output_into_target_columns tsql_output_insert_rest + { + if ($10->execStmt) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The OUTPUT clause cannot be used in an INSERT...EXEC statement."), + parser_errposition(@10))); + $$ = tsql_insert_output_into_cte_transformation($1, $4, NULL, $6, $8, $9, $10, 4); + } + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr tsql_output_clause + INTO insert_target tsql_output_into_target_columns DEFAULT VALUES + { + InsertStmt *i = makeNode(InsertStmt); + i->relation = NULL; + i->onConflictClause = NULL; + i->returningList = NULL; + i->withClause = NULL; + i->cols = NIL; + i->selectStmt = NULL; + i->execStmt = NULL; + $$ = tsql_insert_output_into_cte_transformation($1, $4, NULL, $6, $8, $9, i, 4); + } + /* Without OUTPUT target column list */ + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr '(' insert_column_list ')' + tsql_output_clause INTO insert_target tsql_output_insert_rest_no_paren + { + if ($12->execStmt) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The OUTPUT clause cannot be used in an INSERT...EXEC statement."), + parser_errposition(@12))); + $$ = tsql_insert_output_into_cte_transformation($1, $4, $7, $9, $11, NIL, $12, 4); + } + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr tsql_output_clause + INTO insert_target tsql_output_insert_rest_no_paren + { + if ($9->execStmt) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The OUTPUT clause cannot be used in an INSERT...EXEC statement."), + parser_errposition(@9))); + $$ = tsql_insert_output_into_cte_transformation($1, $4, NULL, $6, $8, NIL, $9, 4); + } + /* + | opt_with_clause INSERT tsql_opt_INTO insert_target tsql_opt_table_hint_expr tsql_output_clause + INTO insert_target DEFAULT VALUES + { + InsertStmt *i = makeNode(InsertStmt); + i->relation = NULL; + i->onConflictClause = NULL; + i->returningList = NULL; + i->withClause = NULL; + i->cols = NIL; + i->selectStmt = NULL; + i->execStmt = NULL; + $$ = tsql_insert_output_into_cte_transformation($1, $4, NULL, $6, $8, NIL, i, 4); + } + */ + ; + +tsql_ExecStmt: + TSQL_EXEC tsql_opt_return tsql_func_name tsql_actual_args + { + List *name = $3; + List *args = $4; + CallStmt *n; + ListCell *lc; + + foreach(lc, args) + { + Node *node = lfirst(lc); + if (node->type == T_RowExpr) + { + RowExpr *row_expr = (RowExpr *) node; + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Row Expression argument not supported"), + parser_errposition(row_expr->location))); + } + } + + n = makeNode(CallStmt); + n->funccall = makeFuncCall(name, args, @1); + + $$ = (Node *) n; + } + | EXECUTE tsql_opt_return tsql_func_name tsql_actual_args + { + List *name = $3; + List *args = $4; + CallStmt *n; + ListCell *lc; + + foreach(lc, args) + { + Node *node = lfirst(lc); + if (node->type == T_RowExpr) + { + RowExpr *row_expr = (RowExpr *) node; + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Row Expression argument not supported"), + parser_errposition(row_expr->location))); + } + } + + n = makeNode(CallStmt); + n->funccall = makeFuncCall(name, args, @1); + + $$ = (Node *) n; + } + | TSQL_EXEC '(' Sconst ')' + { + DoStmt *n = makeNode(DoStmt); + n->args = list_make1(makeDefElem("as", + (Node *)makeString($3), + @3)); + $$ = (Node *) n; + } + | EXECUTE '(' Sconst ')' + { + DoStmt *n = makeNode(DoStmt); + n->args = list_make1(makeDefElem("as", + (Node *)makeString($3), + @3)); + $$ = (Node *) n; + } + ; + +tsql_opt_return: + PARAM '=' + | /* EMPTY */ + ; + +tsql_actual_args: tsql_actual_arg + { + $$ = list_make1($1); + } + | tsql_actual_args ',' tsql_actual_arg + { + $$ = lappend($1, $3); + } + | /* EMPTY */ + { + $$ = NIL; + } + ; + +tsql_opt_output: + TSQL_OUTPUT { $$ = true; } + | TSQL_OUT { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + +tsql_opt_readonly: TSQL_READONLY { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + +tsql_actual_arg: ColId '=' a_expr tsql_opt_output + { + NamedArgExpr *na = makeNode(NamedArgExpr); + + na->name = $1; /* FIXME: record $4 somewhere - probably need a new Node type */ + na->arg = (Expr *) $3; + na->argnumber = -1; /* until determined */ + na->location = @1; + $$ = (Node *) na; + } + | a_expr tsql_opt_output + { + $$ = $1; /* FIXME: record $2 somewhere - probably need a new Node type */ + } + ; + +tsql_without_login: WITHOUT TSQL_LOGIN { $$ = true; } + ; + +tsql_constraint_check: + CHECK + | TSQL_NOCHECK + ; + +tsql_opt_constraint_name: + CONSTRAINT name + | /* EMPTY */ + ; + +/* + * Computed columns uses b_expr not a_expr to avoid conflict with general NOT + * (used in constraints). Besides, it seems TSQL doesn't allow AND, NOT, IS + * IN clauses in the computed column expression. So, there shouldn't be + * any issues. + */ +TSQL_computed_column: + AS b_expr + { + Constraint *n = makeNode(Constraint); + + n->contype = CONSTR_GENERATED; + n->generated_when = ATTRIBUTE_IDENTITY_ALWAYS; + n->raw_expr = $2; + n->cooked_expr = NULL; + n->location = @1; + + $$ = (Node *)n; + } + | AS b_expr TSQL_PERSISTED + { + Constraint *n = makeNode(Constraint); + + n->contype = CONSTR_GENERATED; + n->generated_when = ATTRIBUTE_IDENTITY_ALWAYS; + n->raw_expr = $2; + n->cooked_expr = NULL; + n->location = @1; + + $$ = (Node *)n; + } + ; + +columnElemWithOptAscDesc: + columnElem ASC + { + IndexElem * n = makeNode(IndexElem); + + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), parser_errposition(@1))); + + n->name = strVal($1); + n->expr = NULL; + n->indexcolname = NULL; + n->collation = NULL; + n->opclass = NULL; + n->ordering = SORTBY_ASC; + n->nulls_ordering = SORTBY_NULLS_DEFAULT; + + $$ = (Node *)n; + } + | columnElem DESC + { + IndexElem * n = makeNode(IndexElem); + + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), parser_errposition(@1))); + + n->name = strVal($1); + n->expr = NULL; + n->indexcolname = NULL; + n->collation = NULL; + n->opclass = NULL; + n->ordering = SORTBY_DESC; + n->nulls_ordering = SORTBY_NULLS_DEFAULT; + + $$ = (Node *)n; + } + ; + +columnListWithOptAscDesc: + columnElemWithOptAscDesc + { + $$ = list_make1($1); + } + | columnListWithOptAscDesc ',' columnElem + { + $$ = lappend($1, $3); + } + | columnListWithOptAscDesc ',' columnElemWithOptAscDesc + { + $$ = lappend($1, $3); + } + | columnList ',' columnElemWithOptAscDesc + { + $$ = lappend($1, $3); + } + ; + +/* + * NOTE: the OptFileGroup production doesn't really belong here. We accept OptFileGroup + * for TSQL compatibility, but that syntax is used to place a table on + * a filegroup (analogous to a tablespace). For now, we just accept the + * filegroup specification and ignore it. This makes it impossible to + * write an ON COMMIT option and an ON filegroup clause in the same + * statement, but that would be illegal syntax anyway. + */ + +OptFileGroup: ON name {} + | TSQL_TEXTIMAGE_ON name + { + TSQLInstrumentation(INSTR_UNSUPPORTED_TSQL_TEXTIMAGE_ON); + } + ; + +tsql_OptParenthesizedIdentList: + '(' tsql_IdentList ')' { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +tsql_IdentList: + NumericOnly ',' opt_plus Iconst + { + $$ = list_make3(makeDefElem("start", (Node *)$1, @1), + makeDefElem("increment", (Node *)makeInteger($4), @1), + makeDefElem("minvalue", (Node *)$1, @1)); + } + | NumericOnly ',' '-' Iconst + { + $$ = list_make3(makeDefElem("start", (Node *)$1, @1), + makeDefElem("increment", (Node *)makeInteger(- $4), @1), + makeDefElem("maxvalue", (Node *)$1, @1)); + } + ; + +opt_plus: + '+' {} + | /* empty */ {} + ; + +/* + * FOR XML clause can have 4 modes: RAW, AUTO, PATH and EXPLICIT. + * Map the mode to the corresponding ENUM. + */ +tsql_for_clause: + TSQL_FOR XML_P TSQL_RAW '(' Sconst ')' tsql_xml_common_directives + { + TSQL_ForClause *n = (TSQL_ForClause *) palloc(sizeof(TSQL_ForClause)); + n->location = @1; + n->mode = TSQL_FORXML_RAW; + n->elementName = $5; + n->commonDirectives = $7; + $$ = (Node *) n; + } + | TSQL_FOR XML_P TSQL_RAW tsql_xml_common_directives + { + TSQL_ForClause *n = (TSQL_ForClause *) palloc(sizeof(TSQL_ForClause)); + n->mode = TSQL_FORXML_RAW; + n->elementName = NULL; + n->commonDirectives = $4; + n->location = @1; + $$ = (Node *) n; + } + | TSQL_FOR XML_P TSQL_AUTO tsql_xml_common_directives + { + TSQL_ForClause *n = (TSQL_ForClause *) palloc(sizeof(TSQL_ForClause)); + TSQLInstrumentation(INSTR_UNSUPPORTED_TSQL_XML_OPTION_AUTO); + n->mode = TSQL_FORXML_AUTO; + n->elementName = NULL; + n->commonDirectives = $4; + n->location = @1; + $$ = (Node *) n; + } + | TSQL_FOR XML_P TSQL_PATH '(' Sconst ')' tsql_xml_common_directives + { + TSQL_ForClause *n = (TSQL_ForClause *) palloc(sizeof(TSQL_ForClause)); + n->mode = TSQL_FORXML_PATH; + n->elementName = $5; + n->commonDirectives = $7; + n->location = @1; + $$ = (Node *) n; + } + | TSQL_FOR XML_P TSQL_PATH tsql_xml_common_directives + { + TSQL_ForClause *n = (TSQL_ForClause *) palloc(sizeof(TSQL_ForClause)); + n->mode = TSQL_FORXML_PATH; + n->elementName = NULL; + n->commonDirectives = $4; + n->location = @1; + $$ = (Node *) n; + } + | TSQL_FOR XML_P TSQL_EXPLICIT tsql_xml_common_directives + { + TSQL_ForClause *n = (TSQL_ForClause *) palloc(sizeof(TSQL_ForClause)); + TSQLInstrumentation(INSTR_UNSUPPORTED_TSQL_XML_OPTION_EXPLICIT); + n->mode = TSQL_FORXML_EXPLICIT; + n->elementName = NULL; + n->commonDirectives = $4; + n->location = @1; + $$ = (Node *) n; + } + ; + +tsql_alter_server_role: + ALTER TSQL_SERVER ROLE ColId ADD_P TSQL_MEMBER RoleSpec + { + GrantRoleStmt *n = makeNode(GrantRoleStmt); + AccessPriv *ap = makeNode(AccessPriv); + + if (0 != strcmp($4, "sysadmin")) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only sysadmin role is supported in ALTER SERVER ROLE statement"), + parser_errposition(@4))); + + ap->priv_name = $4; + n->is_grant = true; + n->granted_roles = list_make1(ap); + n->grantee_roles = list_make1($7); + n->admin_opt = false; + n->grantor = NULL; + $$ = (Node *) n; + } + | ALTER TSQL_SERVER ROLE ColId DROP TSQL_MEMBER RoleSpec + { + GrantRoleStmt *n = makeNode(GrantRoleStmt); + AccessPriv *ap = makeNode(AccessPriv); + + if (0 != strcmp($4, "sysadmin")) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only sysadmin role is supported in ALTER SERVER ROLE statement"), + parser_errposition(@4))); + + ap->priv_name = $4; + n->is_grant = false; + n->granted_roles = list_make1(ap); + n->grantee_roles = list_make1($7); + n->admin_opt = false; + n->grantor = NULL; + $$ = (Node *) n; + } + +tsql_xml_common_directives: + tsql_xml_common_directives ',' tsql_xml_common_directive + { + $$ = lappend($1, $3); + } + | /*EMPTY*/ { $$ = NIL; } + ; + +/* + * FOR XML clause can have 3 directives: BINARY BASE64, TYPE and ROOT. + * Map them to ENUM TSQLXMLDirective and String of the ROOT name respectively. + */ +tsql_xml_common_directive: + BINARY TSQL_BASE64 { $$ = makeIntConst(TSQL_XML_DIRECTIVE_BINARY_BASE64, -1); } + | TYPE_P { $$ = makeIntConst(TSQL_XML_DIRECTIVE_TYPE, -1); } + | TSQL_ROOT { $$ = makeStringConst("root", -1); } + | TSQL_ROOT '(' Sconst ')' { $$ = makeStringConst($3, -1); } + ; + +/* Create Function and Create Trigger in one statement */ +tsql_CreateTrigStmt: + CREATE TRIGGER name ON qualified_name + tsql_TriggerActionTime tsql_TriggerEvents tsql_opt_not_for_replication + AS tokens_remaining + { + CreateTrigStmt *n1 = makeNode(CreateTrigStmt); + CreateFunctionStmt *n2 = makeNode(CreateFunctionStmt); + TriggerTransition *nt_inserted = NULL; + TriggerTransition *nt_deleted = NULL; + DefElem *lang = makeDefElem("language", (Node *) makeString("pltsql"), @1); + DefElem *body = makeDefElem("as", (Node *) list_make1(makeString($10)), @10); + DefElem *trigStmt = makeDefElem("trigStmt", (Node *) n1, @1); + + n1->trigname = $3; + n1->relation = $5; + /* + * Function with the same name as the + * trigger will be created as part of + * this create trigger command. + */ + n1->funcname = list_make1(makeString(n1->trigname)); + n1->args = NIL; + /* TSQL only support statement level triggers as part of the + * syntax, n1->row is false for AFTER, BEFORE and INSTEAD OF + * triggers. + */ + n1->row = false; + n1->timing = $6; + n1->events = intVal(linitial($7)); + n1->columns = NIL; + n1->whenClause = NULL; + n1->isconstraint = false; + n1->deferrable = false; + n1->initdeferred = false; + n1->constrrel = NULL; + n1->transitionRels = NIL; + + /* TODO: Postgres doesn't allow multi-event triggers + * ("INSERT OR UPDATE") with transition tables atm: Triggers.c:497 + * We'll need to create both "inserted" and "deleted" transition + * table if that feature is added in the future. + * + * For now, Create transition table "inserted" or "deleted" for TSQL + */ + if(((n1->events & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT && + (n1->events & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE) + || + ((n1->events & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT && + (n1->events & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) + || + ((n1->events & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE && + (n1->events & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE)) + { + ereport(NOTICE, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Multi-event trigger with transition tables not supported"), + errhint("Use single event trigger with transition table"), + parser_errposition(@1))); + } + else + { + if((n1->events & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT || + (n1->events & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) + { + nt_inserted = makeNode(TriggerTransition); + nt_inserted->name = "inserted"; + nt_inserted->isNew = true; + nt_inserted->isTable = true; + n1->transitionRels = lappend(n1->transitionRels, nt_inserted); + } + if((n1->events & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE || + (n1->events & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) + { + nt_deleted = makeNode(TriggerTransition); + nt_deleted->name = "deleted"; + nt_deleted->isNew = false; + nt_deleted->isTable = true; + n1->transitionRels = lappend(n1->transitionRels, nt_deleted); + } + } + + n2->is_procedure = false; + n2->replace = true; + n2->funcname = list_make1(makeString(n1->trigname));; + n2->parameters = NIL; + n2->returnType = makeTypeName("trigger"); + n2->options = list_make3(lang, body, trigStmt); + + $$ = (Node *) n2; + } + ; + +tsql_TriggerActionTime: + TriggerActionTime + | FOR { $$ = TRIGGER_TYPE_AFTER; } + ; + +/* + * Support ',' separator in tsql_TriggerEvents + */ +tsql_TriggerEvents: + tsql_TriggerOneEvent + { $$ = $1; } + | tsql_TriggerEvents ',' tsql_TriggerOneEvent + { + int events1 = intVal(linitial($1)); + int events2 = intVal(linitial($3)); + List *columns1 = (List *) lsecond($1); + List *columns2 = (List *) lsecond($3); + + if (events1 & events2) + parser_yyerror("duplicate trigger events specified"); + /* + * concat'ing the columns lists loses information about + * which columns went with which event, but so long as + * only UPDATE carries columns and we disallow multiple + * UPDATE items, it doesn't matter. Command execution + * should just ignore the columns for non-UPDATE events. + */ + $$ = list_make2(makeInteger(events1 | events2), + list_concat(columns1, columns2)); + } + ; + +tsql_TriggerOneEvent: + INSERT + { $$ = list_make2(makeInteger(TRIGGER_TYPE_INSERT), NIL); } + | DELETE_P + { $$ = list_make2(makeInteger(TRIGGER_TYPE_DELETE), NIL); } + | UPDATE + { $$ = list_make2(makeInteger(TRIGGER_TYPE_UPDATE), NIL); } + | TRUNCATE + { $$ = list_make2(makeInteger(TRIGGER_TYPE_TRUNCATE), NIL); } + ; + +/* + * NOTE: Only supporting the syntax for now + */ +tsql_opt_not_for_replication: + NOT FOR TSQL_REPLICATION {} + | /*EMPTY*/ {} + ; + +opt_from: FROM {} + | /* EMPTY */ {} + ; + +tsql_IndexStmt: + CREATE opt_unique tsql_opt_cluster tsql_opt_columnstore + INDEX opt_concurrently opt_index_name + ON relation_expr access_method_clause '(' index_params ')' + opt_include where_clause opt_reloptions + tsql_opt_on_filegroup + { + IndexStmt *n = makeNode(IndexStmt); + n->unique = $2; + n->concurrent = $6; + n->idxname = $7; + n->relation = $9; + n->accessMethod = $10; + n->indexParams = $12; + n->indexIncludingParams = $14; + n->whereClause = $15; + n->options = $16; + n->excludeOpNames = NIL; + n->idxcomment = NULL; + n->indexOid = InvalidOid; + n->oldNode = InvalidOid; + n->primary = false; + n->isconstraint = false; + n->deferrable = false; + n->initdeferred = false; + n->transformed = false; + n->if_not_exists = false; + $$ = (Node *)n; + } + ; + +tsql_cluster: + TSQL_CLUSTERED + { + TSQLInstrumentation(INSTR_TSQL_OPTION_CLUSTERED); + $$ = true; + } + | TSQL_NONCLUSTERED + { + TSQLInstrumentation(INSTR_TSQL_OPTION_NON_CLUSTERED); + $$ = false; + } + ; + +tsql_opt_cluster: + tsql_cluster { $$ = $1; } + | /*EMPTY*/ { $$ = false; } + ; + +tsql_opt_columnstore: + TSQL_COLUMNSTORE + { + ereport(NOTICE, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("The COLUMNSTORE option is currently ignored"))); + } + | /*EMPTY*/ + ; + +/* + * NOTE: Only supporting the syntax for now + */ +tsql_on_filegroup: ON name {} + ; + +tsql_opt_on_filegroup: + tsql_on_filegroup {} + | /*EMPTY*/ {} + ; + +/* + * TSQL support for DATA_COMPRESSION in and : + * DATA_COMPRESSION = {NONE | ROW | PAGE} [ON PARTITIONS ( [,...n])] + * eg. ON PARTITIONS (2), ON PARTITIONS (1, 5), ON PARTITIONS (2, 4, 6 TO 8) + */ +tsql_on_ident_partitions_list: + ON IDENT '(' tsql_on_partitions_list ')' + { + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@1))); + } + ; + +tsql_opt_on_partitions_list: + tsql_on_ident_partitions_list {} + | /*EMPTY*/ {} + ; + +tsql_on_partitions_list: + tsql_on_partitions {} + | tsql_on_partitions_list ',' tsql_on_partitions {} + ; + +tsql_on_partitions: + Iconst {} + | Iconst TO Iconst {} + ; + +/* + * TSQL support for options in : + * SYSTEM_VERSIONING, REMOTE_DATA_ARCHIVE, DATA_DELETION + */ +tsql_paren_extra_relopt_list: + '(' tsql_extra_relopt_list ')' + { + if (sql_dialect != SQL_DIALECT_TSQL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("This syntax is only valid when babelfishpg_tsql.sql_dialect is TSQL"), + parser_errposition(@1))); + } + ; + +tsql_extra_relopt_list: + tsql_extra_relopt {} + | tsql_extra_relopt_list ',' tsql_extra_relopt {} + ; + +tsql_extra_relopt: + IDENT '=' tsql_extra_def_arg {} + ; + +tsql_extra_def_arg: + ColId {} + | ON {} + | NULL_P {} + | NumericOnly datepart_arg {} + | IDENT '.' IDENT {} + ; + +/* + * TSQL support for MAX_DURATION option in : + * MAX_DURATION = t2_a1t2_a22t2_a1t2_a23t2_a1t2_a2 +~~END~~ + + +-- Test 2 levels of for xml nesting, without TYPE option, expect special character escaping +select id, (select a from t2 for xml path) as col from t1 for xml path; +go +~~START~~ +ntext +1<row><a>t2_a1</a></row><row><a>t2_a2</a></row><row/>2<row><a>t2_a1</a></row><row><a>t2_a2</a></row><row/>3<row><a>t2_a1</a></row><row><a>t2_a2</a></row><row/> +~~END~~ + + +-- Test 3 levels of for xml nesting with TYPE option +select id, (select id, (select a from t2 for xml path, type) as col1 from t1 for xml path, type) as col2 from t1 for xml path, type; +go +~~START~~ +xml +11t2_a1t2_a22t2_a1t2_a23t2_a1t2_a221t2_a1t2_a22t2_a1t2_a23t2_a1t2_a231t2_a1t2_a22t2_a1t2_a23t2_a1t2_a2 +~~END~~ + + +-- Test simple for xml path in procedure +create table employees( +pers_id int, +fname nvarchar(20), +lname nvarchar(20), +sal money); +insert into employees values (1, 'John', 'Johnson', 123.1234); +insert into employees values (2, 'Max', 'Welch', 200.1234); +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +create procedure p_employee_select as +begin + select * from employees for xml path; +end; +go + +execute p_employee_select; +go +~~START~~ +ntext +1JohnJohnson123.12342MaxWelch200.1234 +~~END~~ + +drop procedure p_employee_select; +go + +-- Test for xml in procedure with parameters +create procedure p_employee_select @minsal MONEY, @maxsal MONEY as +begin + select * from employees where sal > @minsal and sal < @maxsal + for xml path('Employee'); +end; +go + +execute p_employee_select 150, 300; +go +~~START~~ +ntext +2MaxWelch200.1234 +~~END~~ + + +-- Test for xml in create view +create view v1 (col1) as select * from t1 for xml raw, type; +go + +select * from v1; +go +~~START~~ +xml + +~~END~~ + + +-- Test for xml on view with xml column +select * from v1 for xml path; +go +~~START~~ +ntext + +~~END~~ + + +-- Test for xml on pure relational view +create view v2 (col1, col2) as select * from t1; +go + +select * from v2 for xml path; +go +~~START~~ +ntext +1t1_a12t1_a23 +~~END~~ + + +drop view v1, v2; +go + +-- Test for xml and union all +select a from t1 UNION ALL select a from t2 for xml raw ('myrow'); +go +~~START~~ +ntext + +~~END~~ + + +-- Test invalid syntax when FOR XML is on both sides of UNION ALL, this is SQL Server behavior +select a from t1 for xml raw ('t1') UNION ALL select a from t2 for xml raw ('t2'); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near 'UNION' at line 2 and character position 36)~~ + + +-- For xml can access CTE from same query block +with cte as (select a from t1) +select * from cte for xml raw; +go +~~START~~ +ntext + +~~END~~ + + +-- BABEL-1202: For xml subquery can't access CTE from outer query block +with cte as (select a from t1) +select * from t2, (select * from cte for xml raw) as t3(colxml); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "cte" does not exist)~~ + + +with cte as (select a from t1) +select (select * from cte for xml raw) as colxml, * from t2; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "cte" does not exist)~~ + + +with +cte1 as (select * from t1), +cte2 as (select a from cte1 for xml raw) +select * from cte2; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "cte1" does not exist)~~ + + + +-- Test for xml and recursive CTE +CREATE TABLE employees2 ( + id serial, + name varchar(255), + manager_id int +); +INSERT INTO employees2 VALUES (1, 'Mark', null); +INSERT INTO employees2 VALUES (2, 'John', 1); +INSERT INTO employees2 VALUES (3, 'Dan', 2); +INSERT INTO employees2 VALUES (4, 'Clark', 1); +INSERT INTO employees2 VALUES (5, 'Linda', 2); +INSERT INTO employees2 VALUES (6, 'Willy', 2); +INSERT INTO employees2 VALUES (7, 'Barack', 2); +INSERT INTO employees2 VALUES (8, 'Elen', 2); +INSERT INTO employees2 VALUES (9, 'Kate', 3); +INSERT INTO employees2 VALUES (10, 'Terry', 4); +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +WITH managertree AS ( + SELECT id, name, manager_id + FROM employees2 + WHERE id = 2 + UNION ALL + SELECT e.id, e.name, e.manager_id + FROM employees2 e + INNER JOIN managertree mtree ON mtree.id = e.manager_id +) +SELECT * +FROM managertree mt; +GO +~~START~~ +int#!#varchar#!#int +2#!#John#!#1 +3#!#Dan#!#2 +5#!#Linda#!#2 +6#!#Willy#!#2 +7#!#Barack#!#2 +8#!#Elen#!#2 +9#!#Kate#!#3 +~~END~~ + + +WITH managertree AS ( + SELECT id, name, manager_id + FROM employees2 + WHERE id = 2 + UNION ALL + SELECT e.id, e.name, e.manager_id + FROM employees2 e + INNER JOIN managertree mtree ON mtree.id = e.manager_id +) +SELECT * +FROM managertree mt WHERE mt.name = 'Linda' FOR XML RAW ('managertree'); +GO +~~START~~ +ntext + +~~END~~ + + +-- BABEL-1178, data type of variable is lost during variable binding in FORMAT +-- function +create procedure test_forxml @pid int as +declare @a int, @b smallint; +set @a = 1; +set @b = 1; +select a, datalength(@a), datalength(@b) from t1 where id = @pid for xml raw; +go + +-- datalength(@b) should be 2. But the data type of @b is lost during the +-- query transformation for FOR XML, so we end up getting a wrong length. +exec test_forxml 1; +go +~~START~~ +ntext + +~~END~~ + + +-- test string variable can be binded with for xml query +create procedure test_forxml_strvar @pid int, @str varchar(10) as +select * from t1 where id = @pid and a = @str for xml raw; +go +exec test_forxml_strvar 1, 't1_a1'; +go +~~START~~ +ntext + +~~END~~ + +-- test NULL parameter +exec test_forxml_strvar 1, NULL; +go +~~START~~ +ntext + +~~END~~ + + +-- clean up +drop table t1; +drop table t2; +drop table MyTable; +drop table employees; +drop procedure p_employee_select; +drop table employees2; +drop procedure test_forxml; +drop procedure test_forxml_strvar; +go diff --git a/contrib/test/JDBC/expected/TestInt.out b/contrib/test/JDBC/expected/TestInt.out new file mode 100644 index 00000000000..8a0c9319a57 --- /dev/null +++ b/contrib/test/JDBC/expected/TestInt.out @@ -0,0 +1,111 @@ +CREATE TABLE INT_dt(a INT); +prepst#!#INSERT INTO INT_dt(a) values(?) #!#INT|-|a|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|-10 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|10 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|-12234 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|22 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|003 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|9864 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|-01625 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|-2147483648 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-|2147483647 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#INT|-|a|-| +~~ROW COUNT: 1~~ + + +SELECT * FROM INT_dt; +~~START~~ +int +0 +-10 +10 +-12234 +22 +3 +9864 +-1625 +-2147483648 +2147483647 + +~~END~~ + + +INSERT INTO INT_dt(a) values(0) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(-10) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(10) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(-12345) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(22) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(004) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(-01645) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(-2147483648) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(2147483647) +~~ROW COUNT: 1~~ + +INSERT INTO INT_dt(a) values(NULL) +~~ROW COUNT: 1~~ + + +SELECT * FROM INT_dt; +~~START~~ +int +0 +-10 +10 +-12234 +22 +3 +9864 +-1625 +-2147483648 +2147483647 + +0 +-10 +10 +-12345 +22 +4 +-1645 +-2147483648 +2147483647 + +~~END~~ + + +DROP TABLE INT_dt; diff --git a/contrib/test/JDBC/expected/TestInteropMultiUser.out b/contrib/test/JDBC/expected/TestInteropMultiUser.out new file mode 100644 index 00000000000..7ec1f8fad03 --- /dev/null +++ b/contrib/test/JDBC/expected/TestInteropMultiUser.out @@ -0,0 +1,30 @@ +-- psql +CREATE USER normal_user WITH PASSWORD 'normal_password'; +GO +CREATE USER createdb_user WITH CREATEDB PASSWORD 'createdb_password'; +GO +GRANT sysadmin TO createdb_user; +GO +-- tsql user=normal_user password=normal_password +CREATE DATABASE normal_db +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: permission denied to create database)~~ + +DROP DATABASE normal_db +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: database "normal_db" does not exist)~~ + +-- tsql user=createdb_user password=createdb_password +CREATE DATABASE createdb_db +GO +DROP DATABASE createdb_db +GO +-- psql +DROP OWNED BY createdb_user; +GO +DROP USER normal_user, createdb_user; +GO diff --git a/contrib/test/JDBC/expected/TestInteropProcedures.out b/contrib/test/JDBC/expected/TestInteropProcedures.out new file mode 100644 index 00000000000..1bfa54f1806 --- /dev/null +++ b/contrib/test/JDBC/expected/TestInteropProcedures.out @@ -0,0 +1,220 @@ +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +UPDATE procTab1 SET c1=10 where c2='b'; +INSERT INTO procTab1 values(1,'a'); +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + CREATE TABLE procTab1(c1 int); + INSERT INTO procTab1 values (5); + ALTER TABLE procTab1 ADD c2 char; + CALL tsql_interop_proc1(); +END +$$ LANGUAGE PLPGSQL; +GO + +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + INSERT INTO procTab1(c1,c2) values (3,'c'); + UPDATE procTab1 SET c1=10 where c2='b'; + INSERT INTO procTab1(c1,c2) values (4,'d'); + DELETE FROM procTab1 where c2='a'; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM procTab1 order by c1 +GO +~~START~~ +int#!#char +3#!#c +4#!#d +5#!# +10#!#b +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc1() +GO +SELECT DISTINCT c1 FROM procTab1 +GO +~~START~~ +int4 +3 +5 +4 +10 +~~END~~ + + +-- tsql +DROP TABLE procTab1 +GO +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +DECLARE @SQLString NVARCHAR(100); +SET @SQLString = N'ALTER TABLE procTab1 ALTER COLUMN c2 varchar(10);'; +EXEC sp_executesql @SQLString; +INSERT INTO procTab1(c1,c2) values(11,'abc'); +UPDATE procTab1 SET c1=c1+1 WHERE c2 IS NULL OR c2='a'; +EXEC psql_interop_proc3; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc3() +AS +$$ +DECLARE myvar varchar(10) = 'def'; +BEGIN +UPDATE procTab1 SET c2=myvar WHERE c1 BETWEEN 2 AND 10; +INSERT INTO procTab1(c1,c2) values(12,'xyz'); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM procTab1 WHERE c2 LIKE 'a%' +GO +~~START~~ +int#!#varchar +11#!#abc +~~END~~ + +DROP TABLE procTab1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc() +AS +$$ +BEGIN +CALL tsql_interop_proc(); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc; +GO +~~START~~ +varchar +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE cursorTab(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO cursorTab values(0, 0, 0, 0, 0); +INSERT INTO cursorTab values(1, 2, 3, 4, 1); +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select a,b,c,d from cursorTab', 2, 1; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 2; +EXEC sp_cursoroption @cursor_handle, 1, 2; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +EXEC sp_cursorclose @cursor_handle; +GO + +EXEC psql_interop_proc; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#smallint#!#bigint#!#tinyint +0#!#0#!#0#!#0 +1#!#2#!#3#!#4 +~~END~~ + +~~START~~ +int#!#smallint#!#bigint#!#tinyint +0#!#0#!#0#!#0 +1#!#2#!#3#!#4 +~~END~~ + +DROP TABLE cursorTab +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + SELECT 1/0; +END TRY +BEGIN CATCH + SELECT 1; +END CATCH +GO + +-- tsql +EXEC psql_interop_proc +GO +~~START~~ +int +1 +~~END~~ + +DROP PROCEDURE tsql_interop_proc +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc1 +GO +DROP PROCEDURE psql_interop_proc2 +GO +DROP PROCEDURE psql_interop_proc3 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc +GO diff --git a/contrib/test/JDBC/expected/TestIsolationLevels.out b/contrib/test/JDBC/expected/TestIsolationLevels.out new file mode 100644 index 00000000000..0d5e21740cb --- /dev/null +++ b/contrib/test/JDBC/expected/TestIsolationLevels.out @@ -0,0 +1,104 @@ +set transaction isolation level read uncommitted; +go + +set transaction isolation level read committed; +go + +set transaction isolation level repeatable read; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: REPEATABLE READ isolation level is not supported)~~ + + +set transaction isolation level snapshot; +go + +set transaction isolation level serializable; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: SERIALIZABLE isolation level is not supported)~~ + + +select set_config('default_transaction_isolation', 'read uncommitted', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option default_transaction_isolation)~~ + +select set_config('default_transaction_isolation', 'read committed', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option default_transaction_isolation)~~ + +select set_config('default_transaction_isolation', 'repeatable read', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option default_transaction_isolation)~~ + +select set_config('default_transaction_isolation', 'snapshot', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option default_transaction_isolation)~~ + +select set_config('default_transaction_isolation', 'serializable', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option default_transaction_isolation)~~ + + +select set_config('transaction_isolation', 'read uncommitted', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option transaction_isolation)~~ + +select set_config('transaction_isolation', 'read committed', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option transaction_isolation)~~ + +select set_config('transaction_isolation', 'repeatable read', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option transaction_isolation)~~ + +select set_config('transaction_isolation', 'snapshot', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option transaction_isolation)~~ + +select set_config('transaction_isolation', 'serializable', false); +go +~~START~~ +text +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: set_config not allowed for option transaction_isolation)~~ + diff --git a/contrib/test/JDBC/expected/TestMoney.out b/contrib/test/JDBC/expected/TestMoney.out new file mode 100644 index 00000000000..39e3cb83337 --- /dev/null +++ b/contrib/test/JDBC/expected/TestMoney.out @@ -0,0 +1,111 @@ +CREATE TABLE money_dt(a smallmoney, b money); + +prepst#!#INSERT INTO money_dt(a, b) VALUES (?, ?) #!#smallmoney|-|a|-|100.5#!#money|-|b|-|10.05 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|10#!#money|-|b|-|10 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|-10.05 #!#money|-|b|-|-10.0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|-214748.3648#!#money|-|b|-|-922337203685477.5808 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|214748.3647#!#money|-|b|-|22337203685477.5807 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|214748.3647#!#money|-|b|-|22337203685477.5807 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|-214,748.3648#!#money|-|b|-|-922,337,203,685,477.5808 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|214,748.3647#!#money|-|b|-|922,337,203,685,477.5807 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|214,748.3647#!#money|-|b|-|922,337,203,685,477.5807 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#smallmoney|-|a|-|#!#money|-|b|-| +~~ROW COUNT: 1~~ + + +SELECT * FROM money_dt; +~~START~~ +smallmoney#!#money +100.5000#!#10.0500 +10.0000#!#10.0000 +-10.0500#!#-10.0000 +-214748.3648#!#-922337203685477.5808 +214748.3647#!#22337203685477.5807 +214748.3647#!#22337203685477.5807 +-214748.3648#!#-922337203685477.5808 +214748.3647#!#922337203685477.5807 +214748.3647#!#922337203685477.5807 +#!# +~~END~~ + + +INSERT INTO money_dt(a,b) values(100.5,10.05); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values('$10','$10'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values('-10.05','-10.0'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values('10.05','10.0'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values(-214748.3648,'-10.0'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values(14748.3647,-922337203685477.5808); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values('$214748.3647','$22337203685477.5807'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values('-214,748.3648','-922,337,203,685,477.5808'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values('214,748.3647','922,337,203,685,477.5807'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values('$214,748.3647','$922,337,203,685,477.5807'); +~~ROW COUNT: 1~~ + +INSERT INTO money_dt(a,b) values(NULL,NULL); +~~ROW COUNT: 1~~ + + + +SELECT * FROM money_dt; +~~START~~ +smallmoney#!#money +100.5000#!#10.0500 +10.0000#!#10.0000 +-10.0500#!#-10.0000 +-214748.3648#!#-922337203685477.5808 +214748.3647#!#22337203685477.5807 +214748.3647#!#22337203685477.5807 +-214748.3648#!#-922337203685477.5808 +214748.3647#!#922337203685477.5807 +214748.3647#!#922337203685477.5807 +#!# +100.5000#!#10.0500 +10.0000#!#10.0000 +-10.0500#!#-10.0000 +10.0500#!#10.0000 +-214748.3648#!#-10.0000 +14748.3647#!#-922337203685477.5808 +214748.3647#!#22337203685477.5807 +-214748.3648#!#-922337203685477.5808 +214748.3647#!#922337203685477.5807 +214748.3647#!#922337203685477.5807 +#!# +~~END~~ + +DROP TABLE money_dt; diff --git a/contrib/test/JDBC/expected/TestNumeric.out b/contrib/test/JDBC/expected/TestNumeric.out new file mode 100644 index 00000000000..2627f4272ef --- /dev/null +++ b/contrib/test/JDBC/expected/TestNumeric.out @@ -0,0 +1,734 @@ +CREATE TABLE numeric_table(num numeric(5, 2)); + +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.00 +123.46 +123.40 +123.00 +123.45 + +-123.46 +-123.40 +-123.00 +-1.00 +-123.00 +~~END~~ + + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 3)); + +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a1|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a1|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + + +prepst#!#exec#!#numeric|-|a1|-|2147483647|-|10|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|-2147483647|-|10|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|2147483646|-|10|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|-2147483646|-|10|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|2147483648|-|10|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a1|-|-2147483648|-|10|-|0 +~~ROW COUNT: 1~~ + + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.000 +123.456 +123.400 +123.000 +123.450 + +-123.456 +-123.400 +-123.000 +-1.000 +-123.000 +2147483647.000 +-2147483647.000 +2147483646.000 +-2147483646.000 +2147483648.000 +-2147483648.000 +~~END~~ + + +DROP TABLE numeric_table; + +# JIRA 507, WORKING FOR BABEL +#CREATE TABLE numeric_table(num numeric(39, 20)); + +CREATE TABLE numeric_table(num numeric(38, 20)); + +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a2|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a2|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a2|-|2147483647|-|10|-|0 +~~ROW COUNT: 1~~ + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.00000000000000000000 +123.45600000000000000000 +123.40000000000000000000 +123.00000000000000000000 +123.45000000000000000000 + +-123.45600000000000000000 +-123.40000000000000000000 +-123.00000000000000000000 +-1.00000000000000000000 +-123.00000000000000000000 +2147483647.00000000000000000000 +~~END~~ + + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 20)); +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a3|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a3|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a3|-|2147483647|-|10|-|0 +~~ROW COUNT: 1~~ + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.00000000000000000000 +123.45600000000000000000 +123.40000000000000000000 +123.00000000000000000000 +123.45000000000000000000 + +-123.45600000000000000000 +-123.40000000000000000000 +-123.00000000000000000000 +-1.00000000000000000000 +-123.00000000000000000000 +2147483647.00000000000000000000 +~~END~~ + + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 21)); +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a4|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a4|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a4|-|2147483647|-|10|-|0 +~~ROW COUNT: 1~~ + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.000000000000000000000 +123.456000000000000000000 +123.400000000000000000000 +123.000000000000000000000 +123.450000000000000000000 + +-123.456000000000000000000 +-123.400000000000000000000 +-123.000000000000000000000 +-1.000000000000000000000 +-123.000000000000000000000 +2147483647.000000000000000000000 +~~END~~ + + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 22)); +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a5|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a5|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a5|-|2147483647|-|10|-|0 +~~ROW COUNT: 1~~ + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.0000000000000000000000 +123.4560000000000000000000 +123.4000000000000000000000 +123.0000000000000000000000 +123.4500000000000000000000 + +-123.4560000000000000000000 +-123.4000000000000000000000 +-123.0000000000000000000000 +-1.0000000000000000000000 +-123.0000000000000000000000 +2147483647.0000000000000000000000 +~~END~~ + + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 23)); +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a6|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a6|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a6|-|2147483647|-|10|-|0 +~~ROW COUNT: 1~~ + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.00000000000000000000000 +123.45600000000000000000000 +123.40000000000000000000000 +123.00000000000000000000000 +123.45000000000000000000000 + +-123.45600000000000000000000 +-123.40000000000000000000000 +-123.00000000000000000000000 +-1.00000000000000000000000 +-123.00000000000000000000000 +2147483647.00000000000000000000000 +~~END~~ + + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 25)); +prepst#!#INSERT INTO numeric_table(num) VALUES(?) #!#numeric|-|a7|-|3|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|123|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|123.45|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-||-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|-123.456|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|-123.4|-|5|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|-123|-|5|-|2 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#numeric|-|a7|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|-1|-|3|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|-123|-|9|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|247483647|-|10|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#numeric|-|a7|-|-247483647|-|10|-|0 +~~ROW COUNT: 1~~ + +SELECT * FROM numeric_table; +~~START~~ +numeric +3.0000000000000000000000000 +123.4560000000000000000000000 +123.4000000000000000000000000 +123.0000000000000000000000000 +123.4500000000000000000000000 + +-123.4560000000000000000000000 +-123.4000000000000000000000000 +-123.0000000000000000000000000 +-1.0000000000000000000000000 +-123.0000000000000000000000000 +247483647.0000000000000000000000000 +-247483647.0000000000000000000000000 +~~END~~ + + +DROP TABLE numeric_table; + + +CREATE TABLE numeric_table(num numeric(38, 25)); + +insert into numeric_table values (2147483647) +~~ROW COUNT: 1~~ + +insert into numeric_table values (-2147483647) +~~ROW COUNT: 1~~ + + +insert into numeric_table values (2147483646) +~~ROW COUNT: 1~~ + +insert into numeric_table values (-2147483646) +~~ROW COUNT: 1~~ + + +insert into numeric_table values (2147483648) +~~ROW COUNT: 1~~ + +insert into numeric_table values (-2147483648) +~~ROW COUNT: 1~~ + + +#insert into numeric_table values(123456789123456789.1234567891234567891234567); +#insert into numeric_table values(1234567891234567891.1234567891234567891234567); +#insert into numeric_table values(123456789123456789.12345678912345678912345678); +insert into numeric_table values(0.0); +~~ROW COUNT: 1~~ + +insert into numeric_table values(0.0000000000000000000000000); +~~ROW COUNT: 1~~ + +insert into numeric_table values(0.00000000000000000000000000); +~~ROW COUNT: 1~~ + + +SELECT * FROM numeric_table; +~~START~~ +numeric +2147483647.0000000000000000000000000 +-2147483647.0000000000000000000000000 +2147483646.0000000000000000000000000 +-2147483646.0000000000000000000000000 +2147483648.0000000000000000000000000 +-2147483648.0000000000000000000000000 +0E-25 +0E-25 +0E-25 +~~END~~ + + +DROP TABLE numeric_table; + +# BABEL-1459 +declare @numvar numeric(5,4); set @numvar=-0.010; SELECT @numvar as N'@var'; +~~START~~ +numeric +-0.0100 +~~END~~ + +declare @numvar numeric(4,4); set @numvar=-0.010; SELECT @numvar as N'@var'; +~~START~~ +numeric +-0.0100 +~~END~~ + +declare @numvar numeric(5,4); set @numvar=1.01; SELECT @numvar as N'@var'; +~~START~~ +numeric +1.0100 +~~END~~ + +declare @numvar numeric(4,4); set @numvar=0.01; SELECT @numvar as N'@var'; +~~START~~ +numeric +0.0100 +~~END~~ + +declare @numvar numeric(4,4); set @numvar=0; SELECT @numvar as N'@var'; +~~START~~ +numeric +0.0000 +~~END~~ + + +# BABEL-2048 +CREATE TABLE babel_2048_t1(a int, b NUMERIC(19,4), c NUMERIC(20, 9), d FLOAT); +INSERT INTO babel_2048_t1 VALUES (1, 2.3, 3.123456789, 4.123456789); +~~ROW COUNT: 1~~ + +select b - 1 from babel_2048_t1; +~~START~~ +numeric +1.3000 +~~END~~ + +select b - a from babel_2048_t1; +~~START~~ +numeric +1.3000 +~~END~~ + +select a - b from babel_2048_t1; +~~START~~ +numeric +-1.3000 +~~END~~ + +select a + b from babel_2048_t1; +~~START~~ +numeric +3.3000 +~~END~~ + +select a * b from babel_2048_t1; +~~START~~ +numeric +2.3000000 +~~END~~ + +select a / b from babel_2048_t1; +~~START~~ +numeric +0.4347826086956521739 +~~END~~ + +select b / a from babel_2048_t1; +~~START~~ +numeric +2.3000000000000000000 +~~END~~ + +select b * NULL from babel_2048_t1; +~~START~~ +numeric + +~~END~~ + +select b / NULL from babel_2048_t1; +~~START~~ +numeric + +~~END~~ + +select b - NULL from babel_2048_t1; +~~START~~ +numeric + +~~END~~ + +select b + NULL from babel_2048_t1; +~~START~~ +numeric + +~~END~~ + +select SQRT(a / b) from babel_2048_t1; +~~START~~ +numeric +0.65938047 +~~END~~ + +select ROUND(a / b, 3) from babel_2048_t1; +~~START~~ +numeric +0.43500000 +~~END~~ + +select SQRT(7); +~~START~~ +float +2.6457513110645907 +~~END~~ + +select ROUND(10.1234567, 5); +~~START~~ +numeric +10.12346 +~~END~~ + + +# test operation between int and numeric(20, 9) +select a+c, a-c, a*c, a/c, a%c, a%NULL from babel_2048_t1; +~~START~~ +numeric#!#numeric#!#numeric#!#numeric#!#numeric#!#int +4.123456789#!#-2.123456789#!#3.123456789000000#!#0.320158102881954100#!#1.000000000#!# +~~END~~ + +# test operation between numeric and numeric +select b+c, b-c, b*c, b/c, b%c, b%NULL from babel_2048_t1; +~~START~~ +numeric#!#numeric#!#numeric#!#numeric#!#numeric#!#numeric +5.423456789#!#-0.823456789#!#7.18395061470#!#0.73636363662849#!#2.300000000#!# +~~END~~ + +# test operation between numeric and float +select c+d, c-d, c*d, c/d, c%d, c%NULL from babel_2048_t1; +~~START~~ +float#!#float#!#float#!#float#!#numeric#!#numeric +7.246913577999999#!#-0.9999999999999996#!#12.879439101750188#!#0.7574850298740454#!#3.123456789#!# +~~END~~ + + +# test NULLIF with numeric args +select nullif(c, b) from babel_2048_t1; +~~START~~ +numeric +3.123456789 +~~END~~ + + +# test Coalesce with numeric args +select coalesce(NULL, NULL, b) from babel_2048_t1; +~~START~~ +numeric +2.3000 +~~END~~ + + +# test Case expression with numeric args +INSERT INTO babel_2048_t1 VALUES (2, 3.7, 4.123456789, 5.123456789); +~~ROW COUNT: 1~~ + +select case when a <= 1 then b - a when a > 1 then c - a end from babel_2048_t1; +~~START~~ +numeric +1.300000000 +2.123456789 +~~END~~ + + +# test Max() and Min() with numeric args +select Max(c-b), Min(c-b) from babel_2048_t1; +~~START~~ +numeric#!#numeric +0.823456789#!#0.423456789 +~~END~~ + + +drop table babel_2048_t1; diff --git a/contrib/test/JDBC/expected/TestPrepareExec.out b/contrib/test/JDBC/expected/TestPrepareExec.out new file mode 100644 index 00000000000..ebeeddbea21 --- /dev/null +++ b/contrib/test/JDBC/expected/TestPrepareExec.out @@ -0,0 +1,55 @@ +#single parameter bind in target list +prepst#!#SELECT ?#!#INT|-|a|-|1 +~~START~~ +int +1 +~~END~~ + +prepst#!#exec#!#INT|-|a|-|1 +~~START~~ +int +1 +~~END~~ + +prepst#!#exec#!#INT|-|a|-|1 +~~START~~ +int +1 +~~END~~ + +prepst#!#exec#!#INT|-|a|-|1 +~~START~~ +int +1 +~~END~~ + +#no parameter bind +prepst#!#SELECT 1#!# +~~START~~ +int +1 +~~END~~ + +prepst#!#exec#!# +~~START~~ +int +1 +~~END~~ + +prepst#!#exec#!# +~~START~~ +int +1 +~~END~~ + +prepst#!#exec#!# +~~START~~ +int +1 +~~END~~ + +#empty query +prepst#!# #!# +prepst#!#exec#!# +prepst#!#exec#!# +prepst#!#exec#!# diff --git a/contrib/test/JDBC/expected/TestProcedureWithErrorHandling.out b/contrib/test/JDBC/expected/TestProcedureWithErrorHandling.out new file mode 100644 index 00000000000..45a896019a1 --- /dev/null +++ b/contrib/test/JDBC/expected/TestProcedureWithErrorHandling.out @@ -0,0 +1,499 @@ +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE procTab1(c1 money, c2 char); +BEGIN TRY + SELECT xact_state(); + INSERT INTO procTab1 values($1,'a'); + INSERT INTO procTab1 values('i','a'); +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; +END CATCH +GO + +-- psql +CREATE PROCEDURE psql_interop_proc() +AS +$$ +BEGIN + CALL master_dbo.tsql_interop_proc(); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC [public].psql_interop_proc +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + +EXEC tsql_interop_proc +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +SELECT * FROM procTab1 +GO +~~START~~ +money#!#char +1.0000#!#a +~~END~~ + +DROP TABLE procTab1 +GO +BEGIN TRANSACTION +GO +EXEC [public].psql_interop_proc +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +SELECT * FROM procTab1 +GO +~~START~~ +money#!#char +1.0000#!#a +~~END~~ + +COMMIT +GO +DROP TABLE procTab1 +GO + + +-- tsql +/* TODO: BABEL-2377 */ +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +/* SELECT * FROM procTab1; */ +/* GO */ +SET IMPLICIT_TRANSACTIONS ON +GO +EXEC [public].psql_interop_proc +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + +IF @@TRANCOUNT > 0 ROLLBACK; +GO +SET IMPLICIT_TRANSACTIONS OFF +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE procTab1(c1 money, c2 char); +BEGIN TRY + SELECT xact_state(); + INSERT INTO procTab1 values($1,'a'); + EXEC psql_interop_proc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@TRANCOUNT; +END CATCH +GO + +-- psql +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + INSERT INTO procTab1(c1,c2) values('i','a'); +END +$$ LANGUAGE PLPGSQL; +GO + + +-- tsql +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +/* SELECT * FROM procTab1; */ +/* GO */ +EXEC [public].psql_interop_proc; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + +DROP PROCEDURE tsql_interop_proc +GO + +-- psql +DROP PROCEDURE psql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + RAISERROR ('raiserror 16', 16, 1); +END TRY +BEGIN CATCH + SELECT 'CATCH in Procedure 1'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + THROW; +END CATCH +GO +EXEC [public].psql_interop_proc; +GO +~~START~~ +varchar +CATCH in Procedure 1 +~~END~~ + +~~START~~ +int#!#nvarchar#!#int#!#nvarchar#!#int#!#int +2#!#raiserror 16#!#50000#!#tsql_interop_proc#!#16#!#1 +~~END~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + + + +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql + +CREATE PROCEDURE tsql_interop_proc2 +AS +BEGIN TRY + RAISERROR ('raiserror 16', 16, 1); +END TRY +BEGIN CATCH + SELECT 'CATCH in Procedure 1'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + THROW; +END CATCH +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + EXEC tsql_interop_proc2; +END TRY +BEGIN CATCH + SELECT 'CATCH in Procedure 2'; + SELECT + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + THROW; +END CATCH +GO + +EXEC [public].psql_interop_proc; +GO +~~START~~ +varchar +CATCH in Procedure 1 +~~END~~ + +~~START~~ +int#!#nvarchar#!#int#!#nvarchar#!#int#!#int +2#!#raiserror 16#!#50000#!#tsql_interop_proc2#!#16#!#1 +~~END~~ + +~~START~~ +varchar +CATCH in Procedure 2 +~~END~~ + +~~START~~ +int#!#nvarchar#!#int#!#int +50000#!#tsql_interop_proc2#!#16#!#1 +~~END~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + + + +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +DROP PROCEDURE tsql_interop_proc2 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc2 +AS + EXEC [public].psql_interop_proc2; +GO + +-- psql +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + SELECT 100/0; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC [public].psql_interop_proc; +GO +~~START~~ +varchar +CATCH in Procedure 2 +~~END~~ + +~~START~~ +int#!#nvarchar#!#int#!#int +8134#!##!#16#!#1 +~~END~~ + +~~ERROR (Code: 8134)~~ + +~~ERROR (Message: division by zero)~~ + + + +-- psql +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc +GO +DROP PROCEDURE tsql_interop_proc2 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + SELECT 100/0; + RAISERROR('Hello %s', 16, 1, 'World'); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO + +-- tsql +EXEC [public].psql_interop_proc; +GO +~~START~~ +smallint +0 +~~END~~ + + + +-- tsql +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +SET XACT_ABORT OFF; +GO +EXEC [public].psql_interop_proc +GO +~~START~~ +smallint +0 +~~END~~ + +SET XACT_ABORT ON; +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS + RAISERROR('Hello %s', 16, 1, 'World'); + SELECT xact_state(); + SELECT @@trancount; +GO + +-- tsql +EXEC [public].psql_interop_proc +GO +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Hello World)~~ + + +-- psql +CALL psql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: Hello World + Where: PL/tsql function master_dbo.tsql_interop_proc() line 1 at RAISERROR +SQL statement "CALL master_dbo.tsql_interop_proc()" +PL/pgSQL function psql_interop_proc() line 3 at CALL + Server SQLState: YY000)~~ + + +-- tsql +SET XACT_ABORT OFF; +GO +SET IMPLICIT_TRANSACTIONS ON +GO +EXEC [public].psql_interop_proc +GO +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Hello World)~~ + +EXEC tsql_interop_proc +GO +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Hello World)~~ + +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- psql +CALL psql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: Hello World + Where: PL/tsql function master_dbo.tsql_interop_proc() line 1 at RAISERROR +SQL statement "CALL master_dbo.tsql_interop_proc()" +PL/pgSQL function psql_interop_proc() line 3 at CALL + Server SQLState: YY000)~~ + + +-- tsql +SET IMPLICIT_TRANSACTIONS OFF +GO +SET XACT_ABORT ON; +GO + +-- psql +DROP PROCEDURE psql_interop_proc +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc +GO diff --git a/contrib/test/JDBC/expected/TestProcedureWithTransactions.out b/contrib/test/JDBC/expected/TestProcedureWithTransactions.out new file mode 100644 index 00000000000..48ae9ee9865 --- /dev/null +++ b/contrib/test/JDBC/expected/TestProcedureWithTransactions.out @@ -0,0 +1,748 @@ +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +UPDATE procTab1 SET c1=10 where c2='b'; +INSERT INTO procTab1 values(1,'a'); +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + CREATE TABLE procTab1(c1 int); + ALTER TABLE procTab1 OWNER TO master_dbo; + INSERT INTO procTab1 values (5); + ALTER TABLE procTab1 ADD c2 char; + CALL tsql_interop_proc1(); +END +$$ LANGUAGE PLPGSQL; +GO + +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + INSERT INTO procTab1(c1,c2) values (3,'c'); + UPDATE procTab1 SET c1=10 where c2='b'; + INSERT INTO procTab1(c1,c2) values (4,'d'); + DELETE FROM procTab1 where c2='a'; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +COMMIT +GO +SELECT * FROM procTab1 order by c1 +GO +~~START~~ +int#!#char +3#!#c +4#!#d +5#!# +10#!#b +~~END~~ + + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL tsql_interop_proc1() +GO +COMMIT +GO +SELECT * FROM procTab1 +GO +~~START~~ +int4#!#bpchar +5#!# +3#!#c +4#!#d +3#!#c +10#!#b +10#!#b +4#!#d +~~END~~ + + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL tsql_interop_proc1() +GO +ROLLBACK +GO +SELECT * FROM procTab1 +GO +~~START~~ +int4#!#bpchar +5#!# +3#!#c +4#!#d +3#!#c +10#!#b +10#!#b +4#!#d +~~END~~ + + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1 +GO +EXEC tsql_interop_proc1 +GO +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +INSERT INTO procTab1(c1,c2) values(11,'e') +GO +~~ROW COUNT: 1~~ + +ROLLBACK TRANSACTION sp1 +GO +SELECT * FROM procTab1 +GO +~~START~~ +int#!#char +5#!# +3#!#c +4#!#d +3#!#c +10#!#b +10#!#b +4#!#d +~~END~~ + +ROLLBACK +GO +SELECT * FROM procTab1 +GO +~~START~~ +int#!#char +5#!# +3#!#c +4#!#d +3#!#c +10#!#b +10#!#b +4#!#d +~~END~~ + + +-- tsql +DROP TABLE procTab1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +DECLARE myvar varchar(1) = 'f'; +BEGIN + UPDATE procTab1 SET c2=myvar WHERE c1 BETWEEN 2 AND 10; + INSERT INTO procTab1(c1,c2) values(12,'x'); + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction termination)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + SAVEPOINT sp1; + INSERT INTO procTab1(c1,c2) values(13,'w'); + EXECUTE "ROLLBACK TO $1" USING sp1; + ROLLBACK; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: unsupported transaction command in PL/pgSQL)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + INSERT INTO procTab1(c1,c2) values(13,'w'); + ROLLBACK; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction termination)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1; +GO +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction termination)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction termination)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +BEGIN TRANSACTION; +UPDATE procTab1 SET c1=10 where c2='b'; +INSERT INTO procTab1 values(1,'a'); +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + SAVEPOINT sp1; + INSERT INTO procTab1(c1,c2) values(13,'w'); + EXECUTE "ROLLBACK TO $1" USING sp1; + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction command)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +BEGIN TRANSACTION; +UPDATE procTab1 SET c1=10 where c2='b'; +SAVE TRANSACTION sp1; +INSERT INTO procTab1 values(1,'a'); +ROLLBACK TRANSACTION sp1; +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + SAVEPOINT sp2; + INSERT INTO procTab1(c1,c2) values(13,'w'); + EXECUTE "ROLLBACK TO $1" USING sp2; + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction command)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + INSERT INTO procTab1(c1,c2) values(13,'w'); + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction command)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +DECLARE @dt DATETIME = '2019-12-31 14:43:35.863'; +CREATE TABLE procTab1(c1 int, c2 char); +SELECT CONVERT(varchar(30), @dt, 1); +INSERT INTO procTab1 values(1,'a'); +ROLLBACK TRANSACTION sp1; +INSERT INTO procTab1 values(2,'b'); +SELECT CONVERT(varchar(30), CAST('2017-08-25' AS date), 102); +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +SAVEPOINT sp1 +GO +CALL tsql_interop_proc1() +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc1() line 5 at SQL statement + Server SQLState: 2D000)~~ + +COMMIT +GO +SELECT * FROM procTab1 +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: relation "proctab1" does not exist + Position: 15 + Server SQLState: 42P01)~~ + + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1 +GO +EXEC tsql_interop_proc1 +GO +~~START~~ +varchar +12/31/19 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + +ROLLBACK +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +SELECT * FROM procTab1 +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "proctab1" does not exist)~~ + + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle int; +EXEC SP_PREPEXEC @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +EXEC ('select datetimefromparts(2016, 12, 26, 23, 30, 5, 32);') +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc() +AS +$$ +BEGIN +CALL tsql_interop_proc(); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc; +GO +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +datetime +2016-12-26 23:30:05.033 +~~END~~ + +COMMIT +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle INT; +DECLARE @batch VARCHAR(500); +SET @batch = 'begin transaction; insert into t1 values(1); commit;' +EXEC sp_prepare @handle OUT, NULL, @batch +EXEC sp_execute @handle +BEGIN TRANSACTION; +EXEC sp_execute @handle +ROLLBACK; +EXEC SP_UNPREPARE @handle; +GO + +-- tsql +CREATE TABLE t1 (a INT); +GO +EXEC psql_interop_proc; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction command)~~ + +SELECT count(*) FROM t1; +GO +~~START~~ +int +0 +~~END~~ + +DROP PROCEDURE tsql_interop_proc +GO +DROP TABLE t1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle int; +DECLARE @inner_handle int; +DECLARE @batch VARCHAR(500); +SET @batch = 'EXEC SP_PREPARE @inner_handle OUT, NULL, ''SELECT 1'' ' +EXEC SP_PREPARE @handle out, '@inner_handle int OUT', @batch +EXEC SP_EXECUTE @handle, @inner_handle OUT +BEGIN TRANSACTION; +EXEC SP_EXECUTE @inner_handle +ROLLBACK; +EXEC SP_UNPREPARE @inner_handle -- unprepare inner first +EXEC SP_UNPREPARE @handle +GO + +-- tsql +EXEC psql_interop_proc; +GO +~~START~~ +int +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction command)~~ + +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE cursorTab(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO cursorTab values(0, 0, 0, 0, 0); +INSERT INTO cursorTab values(1, 2, 3, 4, 1); +INSERT INTO cursorTab values(4, 5, 6, 7, 4); +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select a,b,c,d from cursorTab', 2, 1; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 2; +EXEC sp_cursoroption @cursor_handle, 1, 2; +BEGIN TRANSACTION; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +COMMIT; +EXEC sp_cursorclose @cursor_handle; +GO + +EXEC psql_interop_proc; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#smallint#!#bigint#!#tinyint +0#!#0#!#0#!#0 +1#!#2#!#3#!#4 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction command)~~ + +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +SELECT DATEPART(weekday, CAST( '2021-12-31' as DATE)); +SELECT DATENAME(year, cast('2002-05-23 23:41:29.998' as smalldatetime)); +SELECT @@DATEFIRST; +SELECT dateadd(year, 2, CAST('20060830' AS datetime)); +SELECT + lower(cast('A ' as char(5))), + left(cast('a ' as char(5)), 2), + charindex(cast('a ' as char(5)), 'a '), + ascii(cast('a ' as char(5))), + datalength(cast('123 ' as char(5))), + length(cast('123 ' as char(5))), + len(cast('123 ' as char(5))); +BEGIN TRANSACTION; +SELECT cast(cast('2021-08-15 ' as char(11)) as sys.smalldatetime); +COMMIT; +GO + +EXEC tsql_interop_proc; +GO +~~START~~ +int +6 +~~END~~ + +~~START~~ +text +2002 +~~END~~ + +~~START~~ +int +7 +~~END~~ + +~~START~~ +datetime +2008-08-30 00:00:00.0 +~~END~~ + +~~START~~ +text#!#text#!#int#!#int#!#int#!#int#!#int +a #!#a #!#0#!#97#!#5#!#5#!#3 +~~END~~ + +~~START~~ +smalldatetime +2021-08-15 00:00:00.0 +~~END~~ + +EXEC psql_interop_proc; +GO +~~START~~ +int +6 +~~END~~ + +~~START~~ +text +2002 +~~END~~ + +~~START~~ +int +7 +~~END~~ + +~~START~~ +datetime +2008-08-30 00:00:00.0 +~~END~~ + +~~START~~ +text#!#text#!#int#!#int#!#int#!#int#!#int +a #!#a #!#0#!#97#!#5#!#5#!#3 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid transaction command)~~ + +DROP PROCEDURE tsql_interop_proc +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc1 +GO +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc +GO diff --git a/contrib/test/JDBC/expected/TestProcedureWithTriggers.out b/contrib/test/JDBC/expected/TestProcedureWithTriggers.out new file mode 100644 index 00000000000..c3f0a8ab8cd --- /dev/null +++ b/contrib/test/JDBC/expected/TestProcedureWithTriggers.out @@ -0,0 +1,1402 @@ +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + CREATE TABLE triggerTab1(c1 int, c2 varchar(30)); + CREATE TABLE triggerTab2(c1 int); + INSERT INTO triggerTab1 VALUES(1, 'first'); + INSERT INTO triggerTab2 VALUES(1); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +INSERT INTO triggerTab2 VALUES(4); +UPDATE triggerTab2 set c1 = c1+2; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +INSERT INTO triggerTab2 VALUES(2); +UPDATE triggerTab1 set c1 = c1+2; +DELETE FROM triggerTab2; +INSERT INTO triggerTab2 values(2); +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + INSERT INTO triggerTab1 VALUES(2, 'second'); + INSERT INTO triggerTab1 VALUES(3, 'third'); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +INSERT INTO triggerTab1 VALUES(2, 'second'); +INSERT INTO triggerTab1 VALUES(3, 'third'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int#!#varchar +5#!#first +5#!#third +6#!#second +~~END~~ + + +EXEC psql_interop_proc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 4~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 5~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#varchar +5#!#third +6#!#second +9#!#third +10#!#second +13#!#first +13#!#third +14#!#second +~~END~~ + + +CALL psql_interop_proc2(); +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int4 +2 +~~END~~ + + +-- tsql +BEGIN TRANSACTION +GO +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 10~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 11~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int#!#varchar +5#!#third +6#!#second +9#!#third +10#!#second +13#!#third +14#!#second +17#!#third +18#!#second +21#!#first +21#!#third +22#!#second +~~END~~ + +COMMIT +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#varchar +5#!#third +6#!#second +9#!#third +10#!#second +13#!#third +14#!#second +17#!#third +18#!#second +21#!#third +22#!#second +25#!#first +25#!#third +26#!#second +~~END~~ + +ROLLBACK +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 12~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 13~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +~~END~~ + +ROLLBACK +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL psql_interop_proc2(); +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int4 +2 +~~END~~ + +COMMIT +GO + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1; +GO +EXEC psql_interop_proc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 14~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 15~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +ROLLBACK TRANSACTION sp1; +GO +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int#!#varchar +5#!#third +6#!#second +9#!#third +10#!#second +13#!#third +14#!#second +17#!#third +18#!#second +21#!#third +22#!#second +25#!#first +25#!#third +26#!#second +~~END~~ + +COMMIT +GO + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1; +GO +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 14~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 15~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +ROLLBACK TRANSACTION sp1; +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +~~END~~ + +COMMIT +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +SAVEPOINT sp1; +GO +CALL tsql_interop_proc(); +GO +ROLLBACK TO sp1; +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int4 +2 +~~END~~ + +COMMIT +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc; +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(2, 'second'); +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 14~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int#!#varchar +4#!#second +7#!#third +8#!#second +11#!#third +12#!#second +15#!#third +16#!#second +19#!#third +20#!#second +23#!#third +24#!#second +27#!#first +27#!#third +28#!#second +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int4 +2 +~~END~~ + + +-- tsql +DROP PROCEDURE tsql_interop_proc; +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(2, 'second'); +ROLLBACK; +INSERT INTO triggerTab1 VALUES(3, 'third'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 15~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 15~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int#!#varchar +5#!#third +6#!#second +9#!#third +10#!#second +13#!#third +14#!#second +17#!#third +18#!#second +21#!#third +22#!#second +25#!#third +26#!#second +29#!#first +29#!#third +30#!#second +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int4 +2 +~~END~~ + + +-- tsql +DROP PROCEDURE tsql_interop_proc; +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(2, 'second'); +SAVE TRANSACTION sp1; +INSERT INTO triggerTab1 VALUES(3, 'third'); +ROLLBACK TRANSACTION sp1; +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 16~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 17~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM triggerTab1 ORDER BY c1; +GO +~~START~~ +int#!#varchar +4#!#second +7#!#third +8#!#second +11#!#third +12#!#second +15#!#third +16#!#second +19#!#third +20#!#second +23#!#third +24#!#second +27#!#third +28#!#second +31#!#first +31#!#third +32#!#second +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * FROM triggerTab2 ORDER BY c1; +GO +~~START~~ +int4 +2 +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO +DROP TABLE triggerTab1; +GO +DROP TABLE triggerTab2; +GO +CREATE PROCEDURE tsql_create_table +AS + CREATE TABLE triggerTab1(c1 int, c2 varchar(30)); + CREATE TABLE triggerTab2(c1 int); + INSERT INTO triggerTab1 VALUES(1, 'first'); + INSERT INTO triggerTab2 VALUES(1); +GO +EXEC tsql_create_table; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +-- psql currentSchema=master_dbo,public +CREATE FUNCTION trigger_txnTrig3() + RETURNS TRIGGER + LANGUAGE PLPGSQL +AS $$ +BEGIN + UPDATE triggerTab1 set c1 = c1/2; + DELETE FROM triggerTab2; + RETURN NEW; +END; +$$ +GO + +CREATE TRIGGER txnTrig3 +AFTER DELETE ON triggerTab1 +FOR EACH ROW +EXECUTE PROCEDURE trigger_txnTrig3(); +GO + +-- tsql +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +INSERT INTO triggerTab2 VALUES(4); +UPDATE triggerTab2 set c1 = c1+2; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +INSERT INTO triggerTab2 VALUES(2); +UPDATE triggerTab1 set c1 = c1+2; +DELETE FROM triggerTab2; +INSERT INTO triggerTab2 values(2); +GO + +-- psql currentSchema=master_dbo,public +CALL psql_interop_proc2(); +GO +SELECT * FROM triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#"sys"."varchar" +5#!#first +5#!#third +6#!#second +~~END~~ + + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc2; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 4~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 5~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +SELECT * FROM triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +~~END~~ + +COMMIT +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO +DROP TRIGGER txnTrig3; +GO +DROP FUNCTION trigger_txnTrig3; +GO + +-- tsql +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +UPDATE triggerTab2 set c1 = c1+2; +COMMIT; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +INSERT INTO triggerTab2 VALUES(2); +SELECT * FROM deleted; +DELETE FROM triggerTab1; +GO + +DROP PROCEDURE tsql_interop_proc +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~START~~ +int#!#varchar +3#!#third +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +~~ROW COUNT: 6~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int#!#varchar +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#"sys"."varchar" +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +BEGIN TRANSACTION; +UPDATE triggerTab2 set c1 = c1+2; +COMMIT; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +INSERT INTO triggerTab1 VALUES(2, 'second'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~START~~ +int +4 +2 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +4 +6 +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function txntrig1() line 1 at SQL statement +PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int4 +2 +4 +6 +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +BEGIN TRANSACTION; +SELECT * FROM inserted; +UPDATE triggerTab2 set c1 = c1+2; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +INSERT INTO triggerTab1 VALUES(3, 'third'); +SAVE TRANSACTION sp1; +INSERT INTO triggerTab2 VALUES(3); +ROLLBACK TRANSACTION sp1; +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~START~~ +int#!#varchar +3#!#third +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +6 +4 +2 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 3~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +4 +6 +8 +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function txntrig1() line 1 at SQL statement +PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#"sys"."varchar" +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SAVE TRANSACTION sp1; +SELECT * FROM inserted; +UPDATE triggerTab2 set c1 = c1+2; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +ROLLBACK TRANSACTION sp1; +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~START~~ +int#!#varchar +3#!#third +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +8 +6 +4 +2 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 4~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +4 +6 +8 +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#"sys"."varchar" +3#!#third +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +ROLLBACK TRANSACTION sp1; +UPDATE triggerTab2 set c1 = c1+2; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +SAVE TRANSACTION sp1; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~ROW COUNT: 1~~ + +~~START~~ +int +8 +6 +4 +2 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 4~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +3 +4 +6 +8 +10 +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#"sys"."varchar" +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +SAVE TRANSACTION sp1; +UPDATE triggerTab2 set c1 = c1+2; +ROLLBACK TRANSACTION sp1; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~START~~ +int#!#varchar +3#!#third +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +10 +8 +6 +4 +2 +3 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 6~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +3 +3 +4 +6 +8 +10 +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#"sys"."varchar" +3#!#third +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +SAVE TRANSACTION sp1; +UPDATE triggerTab2 set c1 = c1+2; +COMMIT; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~START~~ +int#!#varchar +3#!#third +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +10 +8 +6 +4 +2 +3 +3 +~~END~~ + +~~ROW COUNT: 2~~ + +~~ROW COUNT: 7~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +4 +5 +5 +6 +8 +10 +12 +~~END~~ + + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: invalid transaction command + Where: PL/tsql function tsql_interop_proc() line 1 at SQL statement + Server SQLState: 2D000)~~ + +SELECT * from triggerTab1 ORDER BY c1; +GO +~~START~~ +int4#!#"sys"."varchar" +~~END~~ + + +-- tsql +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +SAVE TRANSACTION sp1; +SELECT dateadd(year, 2, CAST('20060830' AS datetime)); +UPDATE triggerTab2 set c1 = c1+2; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +SELECT * FROM inserted; +INSERT INTO triggerTab2 VALUES(2); +SELECT * FROM deleted; +EXEC tsql_interop_proc1; +SELECT * FROM table_interop('tsql_interop','psql_interop'); +ROLLBACK TRANSACTION sp1; +DELETE FROM triggerTab1; +GO + +CREATE PROCEDURE tsql_interop_proc1 +AS +SELECT datepart(week, CAST('2007-04-21' AS date)), datepart(weekday, CAST('2007-04-21' AS date)); +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +COMMIT; +GO + +CREATE FUNCTION table_interop (@arg1 varchar(5), @arg2 varchar(10)) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a, @arg2 as b) +GO + +-- tsql +EXEC tsql_interop_proc +GO +~~START~~ +int#!#varchar +3#!#third +~~END~~ + +~~START~~ +datetime +2008-08-30 00:00:00.0 +~~END~~ + +~~START~~ +int +14 +12 +10 +8 +6 +7 +7 +4 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +12 +10 +8 +6 +4 +5 +5 +2 +~~END~~ + +~~START~~ +int#!#int +16#!#7 +~~END~~ + +~~START~~ +varchar#!#varchar +tsql_#!#psql_inter +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 8~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * from triggerTab2 ORDER BY c1; +GO +~~START~~ +int +2 +3 +4 +5 +5 +6 +8 +10 +12 +~~END~~ + +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc1; +GO +DROP PROCEDURE psql_interop_proc2; +GO + +-- tsql +DROP FUNCTION table_interop; +GO +DROP PROCEDURE tsql_create_table; +GO +DROP PROCEDURE tsql_interop_proc; +GO +DROP PROCEDURE tsql_interop_proc1; +GO +DROP TABLE triggerTab1; +GO +DROP TABLE triggerTab2; +GO diff --git a/contrib/test/JDBC/expected/TestRaiserror.out b/contrib/test/JDBC/expected/TestRaiserror.out new file mode 100644 index 00000000000..855c9d2bfe3 --- /dev/null +++ b/contrib/test/JDBC/expected/TestRaiserror.out @@ -0,0 +1,754 @@ +CREATE TABLE raiserrorTable (a INT); +go + +CREATE PROC raiserrorProc1 AS +BEGIN + INSERT INTO raiserrorTable VALUES (1); + RAISERROR ('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (2); +END +go + +CREATE PROC raiserrorProc2 AS +BEGIN + INSERT INTO raiserrorTable VALUES (111); + EXEC raiserrorProc1; + INSERT INTO raiserrorTable VALUES (222); +END +go + +CREATE PROC raiserrorProc3 AS +BEGIN + BEGIN TRY + RAISERROR ('raiserror 16', 16, 1); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (11); + RAISERROR ('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (12); + END CATCH +END +go + + + + + +/* XACT_ABORT OFF */ +/* 1. Not in TRY...CATCH block, terminates nothing */ +/* Not in TRY...CATCH block */ +RAISERROR('raiserror 0', 0, 1); +DECLARE @m1 NVARCHAR(10); +DECLARE @m2 VARCHAR(10); +DECLARE @m3 NCHAR(10); +DECLARE @m4 CHAR(10); +SET @m1 = 'raiserror 0'; +SET @m2 = 'raiserror 0'; +SET @m3 = 'raiserror 0'; +SET @m4 = 'raiserror 0'; +RAISERROR(@m1, 0, 1); +RAISERROR(@m2, 0, 2); +RAISERROR(@m3, 0, 3); +RAISERROR(@m4, 0, 4); +go + +DECLARE @msg_id INT; +DECLARE @severity INT; +DECLARE @state INT; +SET @msg_id = 51000; +SET @severity = 0; +SET @state = 1; +RAISERROR(@msg_id, @severity, @state) WITH LOG, NOWAIT, SETERROR; +go + +BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); +go +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~ROW COUNT: 1~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +3 +4 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Nested procedure call */ +BEGIN TRANSACTION + EXEC raiserrorProc2; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +111 +1 +2 +222 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* 2. In TRY...CATCH block, catchable */ +/* RAISERROR in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + RAISERROR('raiserror 10', 10, 1); + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + COMMIT TRANSACTION +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +3 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + SELECT xact_state(); + EXEC raiserrorProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Error in both TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES(1); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES(3); +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (7); + SELECT * FROM raiserrorTable + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +5 +7 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Error in nested CATCH */ +BEGIN TRY + BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES(1); + EXEC raiserrorProc3; + INSERT INTO raiserrorTable VALUES(3); +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (7); + SELECT * FROM raiserrorTable + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +11 +5 +7 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Error in CATCH chain */ +BEGIN TRY + BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES(1); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES(3); +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + EXEC raiserrorProc3; + INSERT INTO raiserrorTable VALUES (7); + SELECT * FROM raiserrorTable + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +5 +11 +12 +7 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + + +/* Nested TRY..CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (6); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM raiserrorTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM raiserrorTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM raiserrorTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* XACT_ABORT ON, RAISERROR does not honor XACT_ABORT */ +SET XACT_ABORT ON; +go + +/* 1. Not in TRY...CATCH block, terminates nothing */ +/* Not in TRY...CATCH block */ +BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); +go +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~ROW COUNT: 1~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +3 +4 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Nested procedure call */ +BEGIN TRANSACTION + EXEC raiserrorProc2; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +111 +1 +2 +222 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* 2. In TRY...CATCH block, catchable */ +/* RAISERROR in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + RAISERROR('raiserror 10', 10, 1); + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + COMMIT TRANSACTION +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +3 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + SELECT xact_state(); + EXEC raiserrorProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY..CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (6); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM raiserrorTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM raiserrorTable +TRUNCATE TABLE raiserrorTable +go +~~START~~ +int +3 +5 +~~END~~ + + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM raiserrorTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE raiserrorTable +go + +/* + * SETERROR option + * 1. Outside TRY...CATCH, SETERROR would set @@ERROR to specified msg_id + * or 50000, regardless of the severity level + * 2. Inside TRY...CATCH, @@ERROR is always set to the captured error number + * with or without SETERROR + * TODO: After full support, @@ERROR should return user defined error number + */ +DECLARE @err INT; +RAISERROR(51000, 10, 1); +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +RAISERROR(51000, 10, 2) WITH SETERROR; +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +RAISERROR('raiserror 16', 16, 1); +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +RAISERROR('raiserror 16', 16, 2) WITH SETERROR; +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +go +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~START~~ +int +1 +~~END~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: raiserror 16)~~ + +~~START~~ +int +1 +~~END~~ + + +DECLARE @err INT; +BEGIN TRY + BEGIN TRY + RAISERROR(51000, 16, 1); + END TRY + BEGIN CATCH + SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; + RAISERROR(51000, 16, 2) WITH SETERROR; + END CATCH +END TRY +BEGIN CATCH + SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +END CATCH +go +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +/* Clean up */ +SET XACT_ABORT OFF; +go +DROP PROC raiserrorProc1; +go +DROP PROC raiserrorProc2; +go +DROP PROC raiserrorProc3; +go +DROP TABLE raiserrorTable; +go diff --git a/contrib/test/JDBC/expected/TestRaiserrorFormat.out b/contrib/test/JDBC/expected/TestRaiserrorFormat.out new file mode 100644 index 00000000000..e380245cb74 --- /dev/null +++ b/contrib/test/JDBC/expected/TestRaiserrorFormat.out @@ -0,0 +1,113 @@ +RAISERROR('%s', 16, 1, 'Hi'); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Hi)~~ + + +RAISERROR('Hello %s', 16, 1, 'World'); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Hello World)~~ + + +DECLARE @str VARCHAR(20) = 'Multiple variable inputs'; +DECLARE @p1 TINYINT = 1; +DECLARE @p2 SMALLINT = 2; +DECLARE @p3 INT = 3; +DECLARE @p4 CHAR(5) = 'four'; +DECLARE @p5 VARCHAR(5) = 'five'; +DECLARE @p6 NCHAR(5) = 'six'; +DECLARE @p7 NVARCHAR(5) = 'seven'; +RAISERROR('%s: %d%d%d%s%s%s%s', 16, 1, @str, @p1, @p2, @p3, @p4, @p5, @p6, @p7); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Multiple variable in: 123four fivesix seven)~~ + + +RAISERROR('More than 20 args', 16, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21); +go +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + + +RAISERROR('Signed integer i: %i, %i', 16, 1, 5, -5); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Signed integer i: 5, -5)~~ + + +RAISERROR('Unsigned integer u: %u, %u', 16, 1, 5, -5); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Unsigned integer u: 5, 4294967291)~~ + + +RAISERROR('Unsigned octal o: %o, %o', 16, 1, 5, -5); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Unsigned octal o: 5, 37777777773)~~ + + +RAISERROR('Unsigned hexadecimal x: %x, %X, %X, %X, %x', 16, 1, 11, 11, -11, 50, -50); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Unsigned hexadecimal x: b, B, FFFFFFF5, 32, ffffffce)~~ + + +RAISERROR('Not enough args: %d, %d', 16, 1, 1, 2, 3, 4); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Not enough args: 1, 2)~~ + + +RAISERROR('No arg for placeholder: %s', 16, 1); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: No arg for placeholder: (null))~~ + + +RAISERROR('Invalid placeholder: %m', 16, 1, 0); +go +~~ERROR (Code: 2787)~~ + +~~ERROR (Message: Invalid format specification: %m)~~ + + +RAISERROR('Null arg for placeholder: %s', 16, 1, NULL); +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Null arg for placeholder: (null))~~ + + +-- Datatype mismatch +RAISERROR('Mismatch datatype: %d', 16, 1, 'string'); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Param 1 expected format type %d but received type "varchar")~~ + + +RAISERROR('Mismatch datatype: %o', 16, 1, N'string'); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Param 1 expected format type %o but received type character varying)~~ + + +RAISERROR('Mismatch datatype: %s', 16, 1, 123); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Param 1 expected format type %s but received type integer)~~ + diff --git a/contrib/test/JDBC/expected/TestReal.out b/contrib/test/JDBC/expected/TestReal.out new file mode 100644 index 00000000000..57aa13fc6a6 --- /dev/null +++ b/contrib/test/JDBC/expected/TestReal.out @@ -0,0 +1,102 @@ +CREATE TABLE REAL_dt (a REAL) +prepst#!# INSERT INTO REAL_dt(a) values(?) #!#REAL|-|a|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|1.050 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|01.05 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|0001202 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|-024112329 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|-02.5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|0000000000000000086 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|-3.40E+38 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-|3.40E+38 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#REAL|-|a|-| +~~ROW COUNT: 1~~ + +SELECT * FROM REAL_dt; +~~START~~ +real +0.0 +1.05 +1.05 +1202.0 +-2.4112328E7 +-2.5 +86.0 +-3.4E38 +3.4E38 + +~~END~~ + +INSERT INTO REAL_dt(a) values(0) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(1.050) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(01.05) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(-004) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(-0122455324.5) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(-000002) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(0000000000000000086) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(-3.40E+38) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(3.40E+38) +~~ROW COUNT: 1~~ + +INSERT INTO REAL_dt(a) values(NULL) +~~ROW COUNT: 1~~ + +SELECT * FROM REAL_dt; +~~START~~ +real +0.0 +1.05 +1.05 +1202.0 +-2.4112328E7 +-2.5 +86.0 +-3.4E38 +3.4E38 + +0.0 +1.05 +1.05 +-4.0 +-1.22455328E8 +-2.0 +86.0 +-3.4E38 +3.4E38 + +~~END~~ + +DROP TABLE REAL_dt; diff --git a/contrib/test/JDBC/expected/TestRunTimeErrorWithDefaultBehave.out b/contrib/test/JDBC/expected/TestRunTimeErrorWithDefaultBehave.out new file mode 100644 index 00000000000..6b2248cc3d1 --- /dev/null +++ b/contrib/test/JDBC/expected/TestRunTimeErrorWithDefaultBehave.out @@ -0,0 +1,295 @@ +-- This file contains test cases to test un-mapped runtime error against Babelfish server. +GO + +CREATE SCHEMA error_mapping; +GO + +-- Example 1 +GO + +CREATE TABLE t257(c1 binary(10)); +GO +DECLARE @a binary(10); +SET @a = CAST('21' AS char(10)); +SELECT @a +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Implicit conversion from data type char to binary is not allowed. Use the CONVERT function to run this query.)~~ + +DROP TABLE t257; +GO + +begin transaction +GO +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +DECLARE @a binary(10); +SET @a = CAST('21' AS char(10)); +SELECT @a +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Implicit conversion from data type char to binary is not allowed. Use the CONVERT function to run this query.)~~ + + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO +~~START~~ +text +txn is rolledback +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t257; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: table "t257" does not exist)~~ + + +SET XACT_ABORT ON; +GO + +begin transaction +GO +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +DECLARE @a binary(10); +SET @a = CAST('21' AS char(10)); +SELECT @a +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Implicit conversion from data type char to binary is not allowed. Use the CONVERT function to run this query.)~~ + + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO +~~START~~ +text +txn is rolledback +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t257; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: table "t257" does not exist)~~ + + +SET XACT_ABORT OFF; +GO + + +CREATE TABLE t257(c1 binary(10)); +GO + +create procedure error_mapping.ErrorHandling1 as +begin + begin try + select 1 + DECLARE @a binary(10); + SET @a = CAST('21' AS char(10)); + SELECT @a + end try + begin catch + select xact_state(); + end catch + select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +GO + +begin transaction; +GO + +exec error_mapping.ErrorHandling1; +GO +~~START~~ +int +1 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Implicit conversion from data type char to binary is not allowed. Use the CONVERT function to run this query.)~~ + + +if @@trancount > 0 select cast('Txn is not rolledback') +GO + +if @@trancount > 0 rollback transaction; +GO + + +set xact_abort ON; +GO + +exec error_mapping.ErrorHandling1; +GO +~~START~~ +int +1 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Implicit conversion from data type char to binary is not allowed. Use the CONVERT function to run this query.)~~ + + +if @@trancount > 0 select cast('Txn is not rolledback') +GO + +if @@trancount > 0 rollback transaction; +GO + +set xact_abort OFF; +GO + +drop procedure error_mapping.ErrorHandling1; +GO + +DROP TABLE t257; +GO + +-- Example 2 +GO + +SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'xyz' is not a recognized datepart option)~~ + + +begin transaction +GO + +SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'xyz' is not a recognized datepart option)~~ + + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO +~~START~~ +text +txn is rolledback +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO + +SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'xyz' is not a recognized datepart option)~~ + + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO +~~START~~ +text +txn is rolledback +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +SET XACT_ABORT OFF; +GO + +create procedure error_mapping.ErrorHandling1 as +begin + begin try + select 1 + SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); + end try + begin catch + select xact_state(); + end catch + select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text) +GO + +begin transaction; +GO + +exec error_mapping.ErrorHandling1; +GO +~~START~~ +int +1 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'xyz' is not a recognized datepart option)~~ + + +if @@trancount > 0 select cast('txn is not rolledback' as text) +GO + +if @@trancount > 0 rollback tran; +GO + +set xact_abort ON; +GO + +begin transaction; +GO + +exec error_mapping.ErrorHandling1; +GO +~~START~~ +int +1 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'xyz' is not a recognized datepart option)~~ + + +if @@trancount > 0 select cast('txn is not rolledback' as text) +GO + +if @@trancount > 0 rollback tran; +GO + +set xact_abort OFF; +GO + +drop procedure error_mapping.ErrorHandling1; +GO + +DROP SCHEMA error_mapping; +GO diff --git a/contrib/test/JDBC/expected/TestSPExecutesql.out b/contrib/test/JDBC/expected/TestSPExecutesql.out new file mode 100644 index 00000000000..f8b7190ab57 --- /dev/null +++ b/contrib/test/JDBC/expected/TestSPExecutesql.out @@ -0,0 +1,285 @@ +CREATE TABLE spExecutesqlTable1 (a INT, b VARCHAR(10)); +CREATE TABLE spExecutesqlTable2 (a INT, b INT, c INT, d INT); +go + +/* 1. Execute a string of statement */ +DECLARE @SQLString NVARCHAR(100); +SET @SQLString = N'SELECT N''hello world'';'; +EXEC sp_executesql @SQLString; +go +~~START~~ +varchar +hello world +~~END~~ + + + +/* 2. Execute a statement string with parameters */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable1 VALUES (@a, @b);'; +SET @ParamDef = N'@a INT, @b VARCHAR(10)'; +DECLARE @p INT = 1; +EXEC sp_executesql @SQLString, @ParamDef, @p, @b = N'hello'; +SELECT * FROM spExecutesqlTable1; +go +~~ROW COUNT: 1~~ + +~~START~~ +int#!#varchar +1#!#hello +~~END~~ + + +TRUNCATE TABLE spExecutesqlTable1; +go + + +/* 3. Execute a statement string with both unnamed params and named params */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, 1, 2, @d = 4, @c = 3; +SELECT * FROM spExecutesqlTable2; +go +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int#!#int#!#int +1#!#2#!#3#!#4 +~~END~~ + + +TRUNCATE TABLE spExecutesqlTable2; +go + +/* 4. Nested sp_executesql */ +DECLARE @SQLString1 NVARCHAR(100); +DECLARE @SQLString2 NVARCHAR(max); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString1 = N'INSERT INTO spExecutesqlTable1 VALUES (@a, @b);' +SET @SQLString2 = @SQLString1 + N'EXEC sp_executesql N''INSERT INTO spExecutesqlTable1 VALUES (@c + @c, @d + @d);'', N''@c INT, @d VARCHAR(10)'', @a, @b;' +SET @ParamDef = N'@a INT, @b VARCHAR(10)'; +EXEC sp_executesql @SQLString2, @ParamDef, @a = 10, @b = N'str'; +SELECT * FROM spExecutesqlTable1; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#varchar +10#!#str +20#!#strstr +~~END~~ + + +TRUNCATE TABLE spExecutesqlTable1; +go + +-- Nested with param name collision +DECLARE @SQLString1 NVARCHAR(100); +DECLARE @SQLString2 NVARCHAR(max); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString1 = N'INSERT INTO spExecutesqlTable1 VALUES (@a, @b);' +SET @SQLString2 = @SQLString1 + N'EXEC sp_executesql N''INSERT INTO spExecutesqlTable1 VALUES (@a + @a, @b + @b);'', N''@a INT, @b VARCHAR(10)'', @a, @b;' +SET @ParamDef = N'@a INT, @b VARCHAR(10)'; +EXEC sp_executesql @SQLString2, @ParamDef, @a = 11, @b = N'rts'; +SELECT * FROM spExecutesqlTable1; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#varchar +11#!#rts +22#!#rtsrts +~~END~~ + + +TRUNCATE TABLE spExecutesqlTable1; +go + + +/* 5. Execute a statement string with OUT/INOUT param */ +DECLARE @SQLString1 NVARCHAR(100); +DECLARE @SQLString2 NVARCHAR(max); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString1 = N'SET @a = @b + @b'; +SET @SQLString2 = N'EXEC sp_executesql N''SET @a = @b + @b'', N''@a INT OUT, @b INT'', @a OUT, @b;'; +SET @ParamDef = N'@a INT OUTPUT, @b INT'; +DECLARE @p INT; +DECLARE @a INT; +-- OUT param +EXEC sp_executesql @SQLString1, @ParamDef, @a = @p OUT, @b = 10; +-- Nested with OUT param name collision +EXEC sp_executesql @SQLString2, @ParamDef, @a = @a OUT, @b = 11; +SELECT @p, @a; +go +~~START~~ +int#!#int +20#!#22 +~~END~~ + + + +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = @a + 1;'; +SET @ParamDef = N'@a INT OUT'; +DECLARE @p1 INT = 1; +DECLARE @p2 INT = 1; +-- Declared as INOUT and called as OUT +EXEC sp_executesql @SQLString, @ParamDef, @a = @p1 OUT; +-- Declared as INOUT and called as IN +EXEC sp_executesql @SQLString, @ParamDef, @a = @p2; +SELECT @p1, @p2; +go +~~START~~ +int#!#int +2#!#1 +~~END~~ + + + +/* 6. Implicit cast for IN param */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @b = @a;'; +SET @ParamDef = N'@a FLOAT, @b FLOAT OUT'; +DECLARE @p FLOAT; +DECLARE @a MONEY = 3.14; +EXEC sp_executesql @SQLString, @ParamDef, @a = @a, @b = @p OUTPUT; +SELECT @p; +go +~~START~~ +float +3.14 +~~END~~ + + + +/* 7. Implicit cast for OUT param */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = CAST(''2020-01-01'' AS DATE); SET @b = @a;'; +SET @ParamDef = N'@a DATE OUT, @b DATETIME OUT'; +DECLARE @p1 DATETIME; +DECLARE @p2 DATETIME; +EXEC sp_executesql @SQLString, @ParamDef, @a = @p1 OUT, @b = @p2 OUT; +SELECT @p1, @p2; +go +~~START~~ +datetime#!#datetime +2020-01-01 00:00:00.0#!#2020-01-01 00:00:00.0 +~~END~~ + + +/* Exceptions */ +/* 1. Wrong order of named/unnamed params */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, @b = 2, @c = 3, 1, @d = 4; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near '1' at line 7 and character position 58)~~ + + +/* 2. Number mismatch of param defs and given params */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, 1, 2, 3; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: param definition mismatches with inputs)~~ + + +/* 3. Invalid param name */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, @a = 1, @b = 2, @c = 3, @e = 4; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: param "@e" not defined)~~ + + +/* 4. Invalid param def */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, 1, 2, 3, 4; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error at or near ",")~~ + + +/* 5. Unsupported implicit cast */ +-- IN param +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, 1, 3.14, 'hello', 4; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid input syntax for type integer: "hello")~~ + + + +-- OUT param +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = CAST(''abc'' AS VARCHAR(3));'; +SET @ParamDef = N'@a FLOAT OUT'; +DECLARE @p FLOAT; +EXEC sp_executesql @SQLString, @ParamDef, @a = @p OUT; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid input syntax for type double precision: "abc")~~ + + +/* 6. Invalid OUT param */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = 20'; +SET @ParamDef = N'@a INT OUT'; +EXEC sp_executesql @SQLString, @ParamDef, @a = 10 OUT; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near ';' at line 6 and character position 53)~~ + + + +/* 7. Param declared as IN but called as OUT */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = 20'; +SET @ParamDef = N'@a INT'; +DECLARE @p INT; +EXEC sp_executesql @SQLString, @ParamDef, @a = @p OUT; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: param @a defined as mode i but received mode b)~~ + + +/* Clean up */ +DROP TABLE spExecutesqlTable1; +DROP TABLE spExecutesqlTable2; +go diff --git a/contrib/test/JDBC/expected/TestSPPrepare.out b/contrib/test/JDBC/expected/TestSPPrepare.out new file mode 100644 index 00000000000..f4d1215a142 --- /dev/null +++ b/contrib/test/JDBC/expected/TestSPPrepare.out @@ -0,0 +1,720 @@ +--- Simple SP_PREPARE +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO +~~START~~ +varchar +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + + +--- Simple SP_PREPEXEC +DECLARE @handle int; +EXEC SP_PREPEXEC @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + +~~START~~ +varchar +OK +~~END~~ + + +--- Basic SP_PREPARE with args +DECLARE @handle int; +EXEC SP_PREPARE @handle out, N'@a int, @b int', N'select @a, @b', 10; +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_UNPREPARE @handle; +GO +~~START~~ +int#!#int +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + + +--- Basic SP_PREPARE with args +DECLARE @handle int; +EXEC SP_PREPEXEC @handle out, N'@a int, @b int', N'select @a, @b', 1, 2; +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_UNPREPARE @handle; +GO +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + + +--- SP_PREPARE Batch Support +DECLARE @handle int; +DECLARE @batch nvarchar(500); +DECLARE @paramdef nvarchar(500); +DECLARE @var int; +SET @batch = 'IF (@cond > 0) SELECT @o = @a ELSE SELECT @o = @b' +SET @paramdef = '@cond int, @a int, @b int, @o int OUTPUT' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, -1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_EXECUTE @handle, 1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_UNPREPARE @handle; +GO +~~START~~ +int +20 +~~END~~ + +~~START~~ +int +10 +~~END~~ + + +--- SP_PREPEXEC Batch Support +DECLARE @handle int; +DECLARE @batch nvarchar(500); +DECLARE @paramdef nvarchar(500); +DECLARE @var int; +SET @batch = 'IF (@cond > 0) SELECT @o = @a ELSE SELECT @o = @b' +SET @paramdef = '@cond int, @a int, @b int, @o int OUTPUT' +EXEC SP_PREPEXEC @handle out, @paramdef, @batch, 1, 30, 40, @var OUTPUT +SELECT @var +EXEC SP_EXECUTE @handle, -1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_EXECUTE @handle, 1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_UNPREPARE @handle; +GO +~~START~~ +int +30 +~~END~~ + +~~START~~ +int +20 +~~END~~ + +~~START~~ +int +10 +~~END~~ + + +--- Parsing specific +DECLARE @handle int; +EXEC SP_PREPEXEC @handle + 1 OUTPUT, NULL, 'SELECT 1' +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near '+' at line 3 and character position 25)~~ + + +DECLARE @handle VARCHAR(20) +EXEC SP_PREPEXEC @handle OUTPUT, NULL, 'SELECT 1' +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid prepared_handle param datatype)~~ + + +DECLARE @handle int; +EXEC SP_PREPEXEC @handle, NULL, 'SELECT 1' +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: prepared_handle param is not specified as OUTPUT)~~ + + +--- Corner case 1: empty batch +DECLARE @handle int; +EXEC SP_PREPARE @handle out, NULL, NULL +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: query argument of sp_prepare is null)~~ + + +DECLARE @handle int; +EXEC SP_PREPEXEC @handle out, NULL, NULL +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: batch string argument of sp_prepexec is null)~~ + + +--- Corner case 2: nested prepare +DECLARE @handle int; +DECLARE @inner_handle int; +DECLARE @batch VARCHAR(500); +SET @batch = 'EXEC SP_PREPARE @inner_handle OUT, NULL, ''SELECT 1'' ' +EXEC SP_PREPARE @handle out, '@inner_handle int OUT', @batch +EXEC SP_EXECUTE @handle, @inner_handle OUT +EXEC SP_EXECUTE @inner_handle +EXEC SP_UNPREPARE @inner_handle -- unprepare inner first +EXEC SP_UNPREPARE @handle +GO +~~START~~ +int +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +DECLARE @handle int; +DECLARE @inner_handle int; +DECLARE @batch VARCHAR(500); +SET @batch = 'EXEC SP_PREPARE @inner_handle OUT, NULL, ''SELECT 1'' ' +EXEC SP_PREPARE @handle out, '@inner_handle int OUT', @batch +EXEC SP_EXECUTE @handle, @inner_handle OUT +EXEC SP_EXECUTE @inner_handle +EXEC SP_UNPREPARE @handle --unprepare outer first +EXEC SP_EXECUTE @inner_handle +EXEC SP_UNPREPARE @inner_handle +GO +~~START~~ +int +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +--- Corner case 3: mismatch paramdef and params +DECLARE @handle int; +DECLARE @var int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = 'SELECT @a'; +SET @paramdef = '@a int'; +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 100 +EXEC SP_EXECUTE @handle, @var OUT +EXEC SP_UNPREPARE @handle +GO +~~START~~ +int +~~END~~ + +~~START~~ +int +100 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: param 1 defined as mode i but received mode b)~~ + + +--- Prepare DML statement +CREATE TABLE t1 (a int, b int); +GO + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +INSERT INTO t1 VALUES (@v1, @v2) +INSERT INTO t1 VALUES (@v3, @v4) +' +SET @paramdef = '@v1 int, @v2 int, @v3 int, @v4 int' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 1, 2, 2, 3 +EXEC SP_EXECUTE @handle, 3, 4, 4, 5 +SELECT * FROM t1 ORDER BY 1, 2; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int +1#!#2 +2#!#3 +3#!#4 +4#!#5 +~~END~~ + + + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +UPDATE t1 SET a = a * 10, b = b *10 where a = @var1; +UPDATE t1 SET a = a * 10, b = b *10 where a = @var2; +' +SET @paramdef = '@var1 int, @var2 int' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 3, 4 +SELECT * FROM t1 ORDER BY 1, 2; +EXEC SP_UNPREPARE @handle +DROP TABLE t1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int +10#!#20 +20#!#30 +30#!#40 +40#!#50 +~~END~~ + + +--- Transaction with SP_EXECUTE +CREATE TABLE t1 (a int, b int); +GO + + + + + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +INSERT INTO t1 VALUES (@v1, @v2); +INSERT INTO t1 VALUES (@v3, @v4); +' +SET @paramdef = '@v1 int, @v2 int, @v3 int, @v4 int' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +BEGIN TRANSACTION; +EXEC SP_EXECUTE @handle, 1, 2, 2, 3 +SELECT * FROM t1 ORDER BY 1, 2; +COMMIT; +SELECT * FROM t1 ORDER BY 1, 2; +BEGIN TRANSACTION; +EXEC SP_EXECUTE @handle, 3, 4, 4, 5 +SELECT * FROM t1 ORDER BY 1, 2; +ROLLBACK; +SELECT * FROM t1 ORDER BY 1, 2; +EXEC SP_UNPREPARE @handle +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int +1#!#2 +2#!#3 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +2#!#3 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int +1#!#2 +2#!#3 +3#!#4 +4#!#5 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +2#!#3 +~~END~~ + + +DROP TABLE t1; +GO + +--- PREPARE Batch with Transaction +CREATE TABLE t1 (a int, b int); +GO + + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +BEGIN TRANSACTION +INSERT INTO t1 VALUES (@v1, @v2); +INSERT INTO t1 VALUES (@v3, @v4); +SELECT * FROM t1 ORDER BY 1, 2; +IF (@v1 = 10) + COMMIT; +ELSE + ROLLBACK; +' +SET @paramdef = '@v1 int, @v2 int, @v3 int, @v4 int' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 10, 20, 30, 40 +SELECT * FROM t1 ORDER BY 1, 2; +EXEC SP_EXECUTE @handle, 50, 60, 70, 80 +SELECT * FROM t1 ORDER BY 1, 2; +EXEC SP_UNPREPARE @handle +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int +10#!#20 +30#!#40 +~~END~~ + +~~START~~ +int#!#int +10#!#20 +30#!#40 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int +10#!#20 +30#!#40 +50#!#60 +70#!#80 +~~END~~ + +~~START~~ +int#!#int +10#!#20 +30#!#40 +~~END~~ + + +DROP TABLE t1; +GO + +-- Test Save Point +CREATE TABLE t1 ( a int, b int); +GO + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +SET @batch = ' +DECLARE @handle int; +BEGIN TRANSACTION; +INSERT INTO t1 VALUES (1, 2); +SAVE TRANSACTION my_savept; +EXEC SP_PREPEXEC @handle OUT, NULL, +''INSERT INTO t1 VALUES (3, 4); + SELECT * FROM t1 ORDER BY 1, 2; + ROLLBACK TRANSACTION my_savept; + SELECT * FROM t1 ORDER BY 1, 2; +''; +SELECT * FROM t1 ORDER BY 1, 2; +ROLLBACK; +SELECT * FROM t1 ORDER BY 1, 2; +EXEC SP_UNPREPARE @handle; +' +EXEC SP_PREPARE @handle OUT, NULL, @batch; +PRINT @handle +EXEC SP_EXECUTE @handle; +EXEC SP_UNPREPARE @handle; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int#!#int +1#!#2 +3#!#4 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int#!#int +~~END~~ + + +DROP TABLE t1; +GO + +--- Test string type +CREATE TABLE t1 ( a VARCHAR(10), b VARCHAR(10)); +GO + +DECLARE @handle int; +EXEC SP_PREPEXEC @handle OUT, '@v1 VARCHAR(10), @v2 VARCHAR(10)', 'INSERT INTO t1 VALUES (@v1,@v2)', 'abc', 'efg' +EXEC SP_EXECUTE @handle, 'lmn', 'opq' +EXEC SP_UNPREPARE @handle +SELECT * FROM t1 ORDER BY 1, 2; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +varchar#!#varchar +abc#!#efg +lmn#!#opq +~~END~~ + + +DROP TABLE t1; +GO + +-- Test transaction begins outside the batch and commited/rollbacked inside the batch +CREATE TABLE t1 (a INT); +GO + +BEGIN TRAN; +GO +DECLARE @handle INT; +DECLARE @batch VARCHAR(500); +SET @batch = 'insert into t1 values(1); commit; begin tran;' +EXEC sp_prepare @handle OUT, NULL, @batch +EXEC sp_execute @handle +EXEC sp_execute @handle +EXEC SP_UNPREPARE @handle; +COMMIT; +SELECT COUNT(*) FROM t1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + + +BEGIN TRAN; +GO +DECLARE @handle INT; +DECLARE @batch VARCHAR(500); +SET @batch = 'insert into t1 values(1); rollback tran; begin tran;' +EXEC sp_prepare @handle OUT, NULL, @batch +EXEC sp_execute @handle +EXEC sp_execute @handle +EXEC SP_UNPREPARE @handle; +COMMIT; +SELECT COUNT(*) FROM t1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + + +DROP TABLE t1; +GO + +-- prepare time error 1 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT * FROM t1' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "t1" does not exist)~~ + + +-- prepare time error 1-2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'DECLARE @var int; SELECT * FROM t1 WHERE c = @var' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "t1" does not exist)~~ + + +-- prepare time error 2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'EXEC my_proc' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: procedure my_proc() does not exist)~~ + + +-- prepare time error 2-2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'DECLARE @var int; EXEC my_proc @var' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: procedure my_proc(integer) does not exist)~~ + + +-- runtime error 1 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT * FROM t1; SELECT * FROM t1;' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO +~~START~~ +text +true +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "t1" does not exist)~~ + + +-- runtime error 2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'EXEC my_proc; EXEC my_proc;' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO +~~START~~ +text +true +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: procedure my_proc() does not exist)~~ + + +-- runtime error 3 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'IF (1=1) SELECT * FROM t1;' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO +~~START~~ +text +true +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "t1" does not exist)~~ + + +-- runtime error 4 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'DECLARE @var int; SET @var = 1; select * from t1 where c = @var' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO +~~START~~ +text +true +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation "t1" does not exist)~~ + diff --git a/contrib/test/JDBC/expected/TestSQLQueries.out b/contrib/test/JDBC/expected/TestSQLQueries.out new file mode 100644 index 00000000000..fe4649c3f0a --- /dev/null +++ b/contrib/test/JDBC/expected/TestSQLQueries.out @@ -0,0 +1,839 @@ +#1 CREATE TABLE with primary and unique keyword +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +CREATE TABLE tmp(a int PRIMARY KEY, b int UNIQUE); +INSERT INTO tmp(a,b) values(1,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) values(2,2); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +~~END~~ + +DROP TABLE tmp; + +#2 2 table with foreign key relation +#CREATE TABLE tmp1(b int PRIMARY KEY); +#CREATE TABLE tmp2(a int PRIMARY KEY, b int FOREIGN KEY REFERENCES tmp1(b)); +#INSERT INTO tmp1(b) VALUES (1); +#INSERT INTO tmp2(a,b) values(1,1); +#SELECT * FROM tmp1; +#SELECT * FROM tmp2; +#DROP TABLE tmp2; +#DROP TABLE tmp1; + +#3 Repeated #2 with CONSTRAINT keyword +#CREATE TABLE tmp1(b int PRIMARY KEY); +#CREATE TABLE tmp2(a int, b int, PRIMARY KEY(a), CONSTRAINT FK_tmp FOREIGN KEY (b) REFERENCES tmp1(b)); +#INSERT INTO tmp1(b) VALUES (1); +#INSERT INTO tmp2(a,b) values(1,1); +#SELECT * FROM tmp1; +#SELECT * FROM tmp2; +#DROP TABLE tmp2; +#DROP TABLE tmp1; + +#4 CREATE TABLE with NOT NULL column +CREATE TABLE tmp(a int PRIMARY KEY,b int NOT NULL); +INSERT INTO tmp(a,b) values(1,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) values(2,1); +~~ROW COUNT: 1~~ + +#INSERT INTO tmp(a,b) values(2,NULL); +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#1 +~~END~~ + +DROP TABLE tmp; + +#5 INSERTION and DELETION +CREATE TABLE tmp(a int PRIMARY KEY); +INSERT INTO tmp(a) VALUES(1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a) VALUES(2); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a) VALUES(3); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a) VALUES(4); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a) VALUES(5); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int +1 +2 +3 +4 +5 +~~END~~ + +DELETE FROM tmp WHERE a>2; +~~ROW COUNT: 3~~ + +SELECT * FROM tmp; +~~START~~ +int +1 +2 +~~END~~ + +DROP TABLE tmp; + +#6 IS NOT NULL condition in WHERE clause +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) values(1,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) values(2,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) values(3,NULL); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) values(4,NULL); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp WHERE b IS NOT NULL; +~~START~~ +int#!#int +1#!#1 +2#!#1 +~~END~~ + +DROP TABLE tmp; + +#7 Add new column using ALTER TABLE +CREATE TABLE tmp(a int PRIMARY KEY); +INSERT INTO tmp(a) VALUES(1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a) VALUES(2); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int +1 +2 +~~END~~ + +ALTER TABLE tmp ADD b VARCHAR(20) NULL; +SELECT * FROM tmp; +~~START~~ +int#!#varchar +1#!# +2#!# +~~END~~ + +INSERT INTO tmp(a,b) VALUES(4,'Dipesh'); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(5,' Dipesh'); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#varchar +1#!# +2#!# +4#!#Dipesh +5#!# Dipesh +~~END~~ + +DROP TABLE tmp; + +#8 Repeated #8 with default value for newly added col +CREATE TABLE tmp(a int PRIMARY KEY); +INSERT INTO tmp(a) VALUES(1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a) VALUES(2); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int +1 +2 +~~END~~ + +ALTER TABLE tmp ADD b VARCHAR(20) DEFAULT 'Dipesj'; +SELECT * FROM tmp; +~~START~~ +int#!#varchar +1#!#Dipesj +2#!#Dipesj +~~END~~ + +INSERT INTO tmp(a,b) VALUES(4,'Dipesh'); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(5,' Dipesh'); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#varchar +1#!#Dipesj +2#!#Dipesj +4#!#Dipesh +5#!# Dipesh +~~END~~ + +DROP TABLE tmp; + +#9 Change datatype of existing column using ALTER TABLE +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(2,2); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +~~END~~ + +ALTER TABLE tmp ALTER COLUMN b VARCHAR(10); +SELECT * FROM tmp; +~~START~~ +int#!#varchar +1#!#1 +2#!#2 +~~END~~ + +INSERT INTO tmp(a,b) VALUES(4,'Dipesh'); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(5,' Dipesh'); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#varchar +1#!#1 +2#!#2 +4#!#Dipesh +5#!# Dipesh +~~END~~ + +DROP TABLE tmp; + +#10 UPDATE TABLE with fancy condition +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(2,2); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(3,3); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(4,4); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(5,5); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +3#!#3 +4#!#4 +5#!#5 +~~END~~ + +UPDATE tmp SET b=b+1 WHERE b>2; +~~ROW COUNT: 3~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +3#!#4 +4#!#5 +5#!#6 +~~END~~ + +DROP TABLE tmp; + +#11 DROP some column using ALTER TABLE +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(2,2); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(3,3); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(4,4); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(5,5); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +3#!#3 +4#!#4 +5#!#5 +~~END~~ + +#ALTER TABLE tmp DROP COLUMN b; +#INSERT INTO tmp(a) values (6); +#SELECT * FROM tmp; +DROP TABLE tmp; + +#12 CREATE TABLE with fancy constraint +CREATE TABLE tmp(a int PRIMARY KEY CHECK (a>10),b int); +INSERT INTO tmp(a,b) VALUES(11,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(12,2); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(13,3); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(14,4); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(15,5); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +11#!#1 +12#!#2 +13#!#3 +14#!#4 +15#!#5 +~~END~~ + +DROP TABLE tmp; + +#CREATE PROCEDURE tmp AS +#BEGIN +# CREATE TABLE dip(a int PRIMARY KEY CHECK (a>10),b int); +# INSERT INTO dip(a,b) VALUES(11,1); +# INSERT INTO dip(a,b) VALUES(12,2); +# INSERT INTO dip(a,b) VALUES(13,3); +# INSERT INTO dip(a,b) VALUES(14,4); +# INSERT INTO dip(a,b) VALUES(15,5); +# SELECT * FROM dip; +# DROP TABLE dip; +#END; + +#13 invoke simple stored procedure using EXECUTE +EXECUTE tmp; +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: procedure tmp() does not exist)~~ + + +#14 invoke simple stored procedure using EXEC +EXEC tmp; +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: procedure tmp() does not exist)~~ + + +#DROP PROCEDURE tmp; + +#15 UPDATE rows in existing table +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1),(2,2),(3,3),(4,4),(5,5); +~~ROW COUNT: 5~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +3#!#3 +4#!#4 +5#!#5 +~~END~~ + +UPDATE tmp SET b=b*2+1 WHERE (a>2); +~~ROW COUNT: 3~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +3#!#7 +4#!#9 +5#!#11 +~~END~~ + +DROP TABLE tmp; + +#16 TRUNCATE table +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1),(2,2),(3,3),(4,4),(5,5); +~~ROW COUNT: 5~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +3#!#3 +4#!#4 +5#!#5 +~~END~~ + +TRUNCATE TABLE tmp; +SELECT * FROM tmp; +~~START~~ +int#!#int +~~END~~ + +DROP TABLE tmp; + +CREATE TABLE temp1 (i INT,j INT,t TEXT); + +CREATE TABLE temp2 ( i INT,k INT); + +INSERT INTO temp1 VALUES (1, 4, 'one'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (2, 3, 'two'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (3, 2, 'three'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (4, 1, 'four'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (5, 0, 'five'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (6, 6, 'six'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (7, 7, 'seven'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (8, 8, 'eight'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (0, NULL, 'zero'); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (NULL, NULL, NULL); +~~ROW COUNT: 1~~ + +INSERT INTO temp1 VALUES (NULL, 0, 'zero'); +~~ROW COUNT: 1~~ + + +INSERT INTO temp2 VALUES (1, -1); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (2, 2); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (3, -3); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (2, 4); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (5, -5); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (5, -5); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (0, NULL); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (NULL, NULL); +~~ROW COUNT: 1~~ + +INSERT INTO temp2 VALUES (NULL, 0); +~~ROW COUNT: 1~~ + + +#17 CROSS JOIN +SELECT * FROM temp1 CROSS JOIN temp2; +~~START~~ +int#!#int#!#text#!#int#!#int +1#!#4#!#one#!#1#!#-1 +2#!#3#!#two#!#1#!#-1 +3#!#2#!#three#!#1#!#-1 +4#!#1#!#four#!#1#!#-1 +5#!#0#!#five#!#1#!#-1 +6#!#6#!#six#!#1#!#-1 +7#!#7#!#seven#!#1#!#-1 +8#!#8#!#eight#!#1#!#-1 +0#!##!#zero#!#1#!#-1 +#!##!##!#1#!#-1 +#!#0#!#zero#!#1#!#-1 +1#!#4#!#one#!#2#!#2 +2#!#3#!#two#!#2#!#2 +3#!#2#!#three#!#2#!#2 +4#!#1#!#four#!#2#!#2 +5#!#0#!#five#!#2#!#2 +6#!#6#!#six#!#2#!#2 +7#!#7#!#seven#!#2#!#2 +8#!#8#!#eight#!#2#!#2 +0#!##!#zero#!#2#!#2 +#!##!##!#2#!#2 +#!#0#!#zero#!#2#!#2 +1#!#4#!#one#!#3#!#-3 +2#!#3#!#two#!#3#!#-3 +3#!#2#!#three#!#3#!#-3 +4#!#1#!#four#!#3#!#-3 +5#!#0#!#five#!#3#!#-3 +6#!#6#!#six#!#3#!#-3 +7#!#7#!#seven#!#3#!#-3 +8#!#8#!#eight#!#3#!#-3 +0#!##!#zero#!#3#!#-3 +#!##!##!#3#!#-3 +#!#0#!#zero#!#3#!#-3 +1#!#4#!#one#!#2#!#4 +2#!#3#!#two#!#2#!#4 +3#!#2#!#three#!#2#!#4 +4#!#1#!#four#!#2#!#4 +5#!#0#!#five#!#2#!#4 +6#!#6#!#six#!#2#!#4 +7#!#7#!#seven#!#2#!#4 +8#!#8#!#eight#!#2#!#4 +0#!##!#zero#!#2#!#4 +#!##!##!#2#!#4 +#!#0#!#zero#!#2#!#4 +1#!#4#!#one#!#5#!#-5 +2#!#3#!#two#!#5#!#-5 +3#!#2#!#three#!#5#!#-5 +4#!#1#!#four#!#5#!#-5 +5#!#0#!#five#!#5#!#-5 +6#!#6#!#six#!#5#!#-5 +7#!#7#!#seven#!#5#!#-5 +8#!#8#!#eight#!#5#!#-5 +0#!##!#zero#!#5#!#-5 +#!##!##!#5#!#-5 +#!#0#!#zero#!#5#!#-5 +1#!#4#!#one#!#5#!#-5 +2#!#3#!#two#!#5#!#-5 +3#!#2#!#three#!#5#!#-5 +4#!#1#!#four#!#5#!#-5 +5#!#0#!#five#!#5#!#-5 +6#!#6#!#six#!#5#!#-5 +7#!#7#!#seven#!#5#!#-5 +8#!#8#!#eight#!#5#!#-5 +0#!##!#zero#!#5#!#-5 +#!##!##!#5#!#-5 +#!#0#!#zero#!#5#!#-5 +1#!#4#!#one#!#0#!# +2#!#3#!#two#!#0#!# +3#!#2#!#three#!#0#!# +4#!#1#!#four#!#0#!# +5#!#0#!#five#!#0#!# +6#!#6#!#six#!#0#!# +7#!#7#!#seven#!#0#!# +8#!#8#!#eight#!#0#!# +0#!##!#zero#!#0#!# +#!##!##!#0#!# +#!#0#!#zero#!#0#!# +1#!#4#!#one#!##!# +2#!#3#!#two#!##!# +3#!#2#!#three#!##!# +4#!#1#!#four#!##!# +5#!#0#!#five#!##!# +6#!#6#!#six#!##!# +7#!#7#!#seven#!##!# +8#!#8#!#eight#!##!# +0#!##!#zero#!##!# +#!##!##!##!# +#!#0#!#zero#!##!# +1#!#4#!#one#!##!#0 +2#!#3#!#two#!##!#0 +3#!#2#!#three#!##!#0 +4#!#1#!#four#!##!#0 +5#!#0#!#five#!##!#0 +6#!#6#!#six#!##!#0 +7#!#7#!#seven#!##!#0 +8#!#8#!#eight#!##!#0 +0#!##!#zero#!##!#0 +#!##!##!##!#0 +#!#0#!#zero#!##!#0 +~~END~~ + + +#18 INNER JOIN +SELECT temp1.i, temp1.j, temp1.t, temp2.k FROM temp1 INNER JOIN temp2 ON temp1.i=temp2.i; +~~START~~ +int#!#int#!#text#!#int +0#!##!#zero#!# +1#!#4#!#one#!#-1 +2#!#3#!#two#!#2 +2#!#3#!#two#!#4 +3#!#2#!#three#!#-3 +5#!#0#!#five#!#-5 +5#!#0#!#five#!#-5 +~~END~~ + +SELECT temp1.i, temp1.j, temp1.t, temp2.k FROM temp1 JOIN temp2 ON temp1.i=temp2.i; +~~START~~ +int#!#int#!#text#!#int +0#!##!#zero#!# +1#!#4#!#one#!#-1 +2#!#3#!#two#!#2 +2#!#3#!#two#!#4 +3#!#2#!#three#!#-3 +5#!#0#!#five#!#-5 +5#!#0#!#five#!#-5 +~~END~~ + + +#19 LEFT JOIN +SELECT * FROM temp1 LEFT OUTER JOIN temp2 ON temp1.i=temp2.k; +~~START~~ +int#!#int#!#text#!#int#!#int +0#!##!#zero#!##!#0 +1#!#4#!#one#!##!# +2#!#3#!#two#!#2#!#2 +3#!#2#!#three#!##!# +4#!#1#!#four#!#2#!#4 +5#!#0#!#five#!##!# +6#!#6#!#six#!##!# +7#!#7#!#seven#!##!# +8#!#8#!#eight#!##!# +#!##!##!##!# +#!#0#!#zero#!##!# +~~END~~ + + +#20 RIGHT JOIN +SELECT * FROM temp1 RIGHT OUTER JOIN temp2 ON temp1.i=temp2.i; +~~START~~ +int#!#int#!#text#!#int#!#int +0#!##!#zero#!#0#!# +1#!#4#!#one#!#1#!#-1 +2#!#3#!#two#!#2#!#2 +2#!#3#!#two#!#2#!#4 +3#!#2#!#three#!#3#!#-3 +5#!#0#!#five#!#5#!#-5 +5#!#0#!#five#!#5#!#-5 +#!##!##!##!# +#!##!##!##!#0 +~~END~~ + + +#21 FULL OUTER JOIN +SELECT * FROM temp1,temp2; +~~START~~ +int#!#int#!#text#!#int#!#int +1#!#4#!#one#!#1#!#-1 +2#!#3#!#two#!#1#!#-1 +3#!#2#!#three#!#1#!#-1 +4#!#1#!#four#!#1#!#-1 +5#!#0#!#five#!#1#!#-1 +6#!#6#!#six#!#1#!#-1 +7#!#7#!#seven#!#1#!#-1 +8#!#8#!#eight#!#1#!#-1 +0#!##!#zero#!#1#!#-1 +#!##!##!#1#!#-1 +#!#0#!#zero#!#1#!#-1 +1#!#4#!#one#!#2#!#2 +2#!#3#!#two#!#2#!#2 +3#!#2#!#three#!#2#!#2 +4#!#1#!#four#!#2#!#2 +5#!#0#!#five#!#2#!#2 +6#!#6#!#six#!#2#!#2 +7#!#7#!#seven#!#2#!#2 +8#!#8#!#eight#!#2#!#2 +0#!##!#zero#!#2#!#2 +#!##!##!#2#!#2 +#!#0#!#zero#!#2#!#2 +1#!#4#!#one#!#3#!#-3 +2#!#3#!#two#!#3#!#-3 +3#!#2#!#three#!#3#!#-3 +4#!#1#!#four#!#3#!#-3 +5#!#0#!#five#!#3#!#-3 +6#!#6#!#six#!#3#!#-3 +7#!#7#!#seven#!#3#!#-3 +8#!#8#!#eight#!#3#!#-3 +0#!##!#zero#!#3#!#-3 +#!##!##!#3#!#-3 +#!#0#!#zero#!#3#!#-3 +1#!#4#!#one#!#2#!#4 +2#!#3#!#two#!#2#!#4 +3#!#2#!#three#!#2#!#4 +4#!#1#!#four#!#2#!#4 +5#!#0#!#five#!#2#!#4 +6#!#6#!#six#!#2#!#4 +7#!#7#!#seven#!#2#!#4 +8#!#8#!#eight#!#2#!#4 +0#!##!#zero#!#2#!#4 +#!##!##!#2#!#4 +#!#0#!#zero#!#2#!#4 +1#!#4#!#one#!#5#!#-5 +2#!#3#!#two#!#5#!#-5 +3#!#2#!#three#!#5#!#-5 +4#!#1#!#four#!#5#!#-5 +5#!#0#!#five#!#5#!#-5 +6#!#6#!#six#!#5#!#-5 +7#!#7#!#seven#!#5#!#-5 +8#!#8#!#eight#!#5#!#-5 +0#!##!#zero#!#5#!#-5 +#!##!##!#5#!#-5 +#!#0#!#zero#!#5#!#-5 +1#!#4#!#one#!#5#!#-5 +2#!#3#!#two#!#5#!#-5 +3#!#2#!#three#!#5#!#-5 +4#!#1#!#four#!#5#!#-5 +5#!#0#!#five#!#5#!#-5 +6#!#6#!#six#!#5#!#-5 +7#!#7#!#seven#!#5#!#-5 +8#!#8#!#eight#!#5#!#-5 +0#!##!#zero#!#5#!#-5 +#!##!##!#5#!#-5 +#!#0#!#zero#!#5#!#-5 +1#!#4#!#one#!#0#!# +2#!#3#!#two#!#0#!# +3#!#2#!#three#!#0#!# +4#!#1#!#four#!#0#!# +5#!#0#!#five#!#0#!# +6#!#6#!#six#!#0#!# +7#!#7#!#seven#!#0#!# +8#!#8#!#eight#!#0#!# +0#!##!#zero#!#0#!# +#!##!##!#0#!# +#!#0#!#zero#!#0#!# +1#!#4#!#one#!##!# +2#!#3#!#two#!##!# +3#!#2#!#three#!##!# +4#!#1#!#four#!##!# +5#!#0#!#five#!##!# +6#!#6#!#six#!##!# +7#!#7#!#seven#!##!# +8#!#8#!#eight#!##!# +0#!##!#zero#!##!# +#!##!##!##!# +#!#0#!#zero#!##!# +1#!#4#!#one#!##!#0 +2#!#3#!#two#!##!#0 +3#!#2#!#three#!##!#0 +4#!#1#!#four#!##!#0 +5#!#0#!#five#!##!#0 +6#!#6#!#six#!##!#0 +7#!#7#!#seven#!##!#0 +8#!#8#!#eight#!##!#0 +0#!##!#zero#!##!#0 +#!##!##!##!#0 +#!#0#!#zero#!##!#0 +~~END~~ + + +DROP TABLE temp1; +DROP TABLE temp2; + +#22 dropping all columns +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(2,2); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(3,3); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(4,4); +~~ROW COUNT: 1~~ + +INSERT INTO tmp(a,b) VALUES(5,5); +~~ROW COUNT: 1~~ + +SELECT * FROM tmp; +~~START~~ +int#!#int +1#!#1 +2#!#2 +3#!#3 +4#!#4 +5#!#5 +~~END~~ + +#ALTER TABLE tmp DROP COLUMN b; +#ALTER TABLE tmp DROP COLUMN a; +#SELECT * FROM tmp; +DROP TABLE tmp; + +#23 +CREATE TABLE DATE_dt (a DATE); +INSERT INTO DATE_dt(a) values('2000-12-13'); +~~ROW COUNT: 1~~ + +INSERT INTO DATE_dt(a) values('1900-02-28'); +~~ROW COUNT: 1~~ + +INSERT INTO DATE_dt(a) values('0001-01-01'); +~~ROW COUNT: 1~~ + +INSERT INTO DATE_dt(a) values('9999-12-31'); +~~ROW COUNT: 1~~ + +INSERT INTO DATE_dt(a) values(NULL); +~~ROW COUNT: 1~~ + +SELECT * FROM DATE_dt; +~~START~~ +date +2000-12-13 +1900-02-28 +0001-01-01 +9999-12-31 + +~~END~~ + +ALTER TABLE DATE_dt ALTER COLUMN a DATETIME; +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +SELECT * FROM DATE_dt; +~~START~~ +date +2000-12-13 +1900-02-28 +0001-01-01 +9999-12-31 + +~~END~~ + +DROP TABLE DATE_dt; +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; diff --git a/contrib/test/JDBC/expected/TestSmallDatetime.out b/contrib/test/JDBC/expected/TestSmallDatetime.out new file mode 100644 index 00000000000..b29b9fa7ec3 --- /dev/null +++ b/contrib/test/JDBC/expected/TestSmallDatetime.out @@ -0,0 +1,148 @@ +CREATE TABLE SMALLDATETIME_dt (a SMALLDATETIME) +prepst#!# INSERT INTO SMALLDATETIME_dt(a) values(?) #!#SMALLDATETIME|-|a|-|2000-12-13 12:58:23 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2007-05-08 12:35:29 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2007-05-08 12:35:30 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2007-05-08 12:59:59.998 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:59:59.999 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-02-28 23:59:59.999 +~~ROW COUNT: 1~~ + +#prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:29.999 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:30 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:29 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-02-28 23:45:30 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-02-28 23:45:29 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-12-13 12:58:30 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:29.998 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-01-01 00:00:00 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-|2079-06-06 23:59:29 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLDATETIME|-|a|-| +~~ROW COUNT: 1~~ + +SELECT * FROM SMALLDATETIME_dt; +~~START~~ +smalldatetime +2000-12-13 12:58:00.0 +2007-05-08 12:35:00.0 +2007-05-08 12:36:00.0 +2007-05-08 13:00:00.0 +2000-02-29 00:00:00.0 +1900-03-01 00:00:00.0 +2000-02-28 23:46:00.0 +2000-02-28 23:45:00.0 +1900-02-28 23:46:00.0 +1900-02-28 23:45:00.0 +2000-12-13 12:59:00.0 +2000-02-28 23:45:00.0 +1900-01-01 00:00:00.0 +2079-06-06 23:59:00.0 + +~~END~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2000-12-13 12:58:23'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2007-05-08 12:35:29'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2007-05-08 12:35:30'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2007-05-08 12:59:59.998'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:59:59.999'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('1900-02-28 23:59:59.999'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:29.999'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:30'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:29'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('1900-02-28 23:45:29'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('1900-12-13 12:58:30'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:29.998'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('1900-01-01 00:00:00'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values('2079-06-06 23:59:29'); +~~ROW COUNT: 1~~ + +INSERT INTO SMALLDATETIME_dt(a) values(NULL); +~~ROW COUNT: 1~~ + +SELECT * FROM SMALLDATETIME_dt; +~~START~~ +smalldatetime +2000-12-13 12:58:00.0 +2007-05-08 12:35:00.0 +2007-05-08 12:36:00.0 +2007-05-08 13:00:00.0 +2000-02-29 00:00:00.0 +1900-03-01 00:00:00.0 +2000-02-28 23:46:00.0 +2000-02-28 23:45:00.0 +1900-02-28 23:46:00.0 +1900-02-28 23:45:00.0 +2000-12-13 12:59:00.0 +2000-02-28 23:45:00.0 +1900-01-01 00:00:00.0 +2079-06-06 23:59:00.0 + +2000-12-13 12:58:00.0 +2007-05-08 12:35:00.0 +2007-05-08 12:36:00.0 +2007-05-08 13:00:00.0 +2000-02-29 00:00:00.0 +1900-03-01 00:00:00.0 +2000-02-28 23:46:00.0 +2000-02-28 23:46:00.0 +2000-02-28 23:45:00.0 +1900-02-28 23:45:00.0 +1900-12-13 12:59:00.0 +2000-02-28 23:45:00.0 +1900-01-01 00:00:00.0 +2079-06-06 23:59:00.0 + +~~END~~ + +DROP TABLE SMALLDATETIME_dt; diff --git a/contrib/test/JDBC/expected/TestSmallInt.out b/contrib/test/JDBC/expected/TestSmallInt.out new file mode 100644 index 00000000000..41e35be86e1 --- /dev/null +++ b/contrib/test/JDBC/expected/TestSmallInt.out @@ -0,0 +1,102 @@ +CREATE TABLE SMALLINT_dt (a SMALLINT) +prepst#!# INSERT INTO SMALLINT_dt(a) values(?) #!#SMALLINT|-|a|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|-10 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|100 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|002 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|-029 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|-1234 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|876 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|-32768 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-|32767 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#SMALLINT|-|a|-| +~~ROW COUNT: 1~~ + +SELECT * FROM SMALLINT_dt; +~~START~~ +smallint +0 +-10 +100 +2 +-29 +-1234 +876 +-32768 +32767 + +~~END~~ + +INSERT INTO SMALLINT_dt(a) values(0) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(-10) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(100) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(002) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(-029) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(-1234) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(876) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(-32768) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(32767) +~~ROW COUNT: 1~~ + +INSERT INTO SMALLINT_dt(a) values(NULL) +~~ROW COUNT: 1~~ + +SELECT * FROM SMALLINT_dt; +~~START~~ +smallint +0 +-10 +100 +2 +-29 +-1234 +876 +-32768 +32767 + +0 +-10 +100 +2 +-29 +-1234 +876 +-32768 +32767 + +~~END~~ + +DROP TABLE SMALLINT_dt; diff --git a/contrib/test/JDBC/expected/TestStoredProcedures.out b/contrib/test/JDBC/expected/TestStoredProcedures.out new file mode 100644 index 00000000000..976dea8e415 --- /dev/null +++ b/contrib/test/JDBC/expected/TestStoredProcedures.out @@ -0,0 +1,241 @@ +# PROCEDURE WITH NO BODY +#create procedure sp_test AS BEGIN END; +# PROCEDURE WITH NO PARAMS +create table temp_sp2(a int); +CREATE PROCEDURE sp_test AS BEGIN insert into temp_sp2 values(1); END; +storedproc#!#prep#!#sp_test#!# +~~ROW COUNT: 1~~ + +SELECT * FROM temp_sp2; +~~START~~ +int +1 +~~END~~ + +drop table temp_sp2; +drop Procedure sp_test; +# PROCEDURE WITH INPUT PARAMETER +#drop table temp_sp; +create table temp_sp(a int); +Create Procedure stored_proc1 (@a int) As Begin insert into temp_sp values(@a) End; +# NOT WORKING FOR BABEL,RAISED JIRA 444 +storedproc#!#prep#!#stored_proc1#!#int|-|a|-|-100|-|input +~~ROW COUNT: 1~~ + +# MISMATCH IN RETURN VALUES +exec stored_proc1 -200 +~~ROW COUNT: 1~~ + +exec stored_proc1 0 +~~ROW COUNT: 1~~ + +exec stored_proc1 -1 +~~ROW COUNT: 1~~ + +exec stored_proc1 2 +~~ROW COUNT: 1~~ + +# filed Jira on this, doesnt work for babel +#exec stored_proc1 2.2 +SELECT * FROM temp_sp; +~~START~~ +int +-100 +-200 +0 +-1 +2 +~~END~~ + +DROP table temp_sp; +DROP Procedure stored_proc1 +# input parameter +CREATE PROCEDURE sp_test1 (@a INT) AS BEGIN SET @a=100; Select @a as a; END; +exec sp_test1 2 +~~START~~ +int +100 +~~END~~ + +Declare @a int;Set @a=1; exec sp_test1 @a;select @a as a; +~~START~~ +int +100 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +storedproc#!#prep#!#sp_test1#!#int|-|a|-|1|-|input +~~START~~ +int +100 +~~END~~ + +storedproc#!#prep#!#sp_test1#!#int|-|a|-|10|-|input +~~START~~ +int +100 +~~END~~ + +DROP PROCEDURE sp_test1 +# TESTING OUT PARAMETERS FOR ALL THE DATATYPES +# int +CREATE PROCEDURE sp_test1 (@a INT OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +#Declare @a int;Set @a=1; exec sp_test1 @a;select @a as a; +storedproc#!#prep#!#sp_test1#!#int|-|a|-|1|-|output +~~START~~ +int +100 +~~END~~ + +storedproc#!#prep#!#sp_test1#!#int|-|a|-|1|-|inputoutput +~~START~~ +int +100 +~~END~~ + +DROP PROCEDURE sp_test1 +# smallint +CREATE PROCEDURE sp_test2 (@a SMALLINT OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +#Declare @a smallint;Set @a=1; exec sp_test2 @a;select @a as a; +storedproc#!#prep#!#sp_test2#!#smallint|-|a|-|10|-|output +~~START~~ +smallint +100 +~~END~~ + +storedproc#!#prep#!#sp_test2#!#smallint|-|a|-|10|-|inputoutput +~~START~~ +smallint +100 +~~END~~ + +DROP PROCEDURE sp_test2 +# bigint +CREATE PROCEDURE sp_test3 (@a BIGINT OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +storedproc#!#prep#!#sp_test3#!#bigint|-|a|-|10|-|output +~~START~~ +bigint +100 +~~END~~ + +storedproc#!#prep#!#sp_test3#!#bigint|-|a|-|10|-|inputoutput +~~START~~ +bigint +100 +~~END~~ + +DROP PROCEDURE sp_test3 +# tinyint +#CREATE PROCEDURE sp_test4 (@a tinyint OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +#storedproc#!#prep#!#sp_test4#!#tinyint|-|a|-|10|-|output +#storedproc#!#prep#!#sp_test4#!#tinyint|-|a|-|10|-|inputoutput +#DROP PROCEDURE sp_test4 +# float +CREATE PROCEDURE sp_test5 (@a float OUTPUT) AS BEGIN SET @a=100.12; Select @a as a; END; +#Declare @a float;Set @a=1.1; exec sp_test5 @a;select @a as a; +storedproc#!#prep#!#sp_test5#!#float|-|a|-|10.1|-|output +~~START~~ +float +100.12 +~~END~~ + +storedproc#!#prep#!#sp_test5#!#float|-|a|-|10.1|-|inputoutput +~~START~~ +float +100.12 +~~END~~ + +DROP PROCEDURE sp_test5 +# varchar +#CREATE PROCEDURE sp_test6 (@a varchar OUTPUT) AS BEGIN SET @a='helloworld'; Select @a as a; END; +#storedproc#!#prep#!#sp_test6#!#varchar|-|a|-|hello|-|output +#storedproc#!#prep#!#sp_test6#!#varchar|-|a|-|hello|-|inputoutput +#DROP PROCEDURE sp_test6 +# char BABEL-705 +#CREATE PROCEDURE sp_test7 (@a char OUTPUT) AS BEGIN SET @a='b'; Select @a as a; END; +#Declare @a varchar;Set @a='h'; exec sp_test7 @a;select @a as a; +#storedproc#!#prep#!#sp_test7#!#char|-|a|-|t|-|output +#storedproc#!#prep#!#sp_test7#!#char|-|a|-|t|-|inputoutput +#DROP PROCEDURE sp_test7 +# nvarchar +#CREATE PROCEDURE sp_test9 (@a nvarchar OUTPUT) AS BEGIN SET @a='helloworld'; Select @a as a; END; +#Declare @a nvarchar;Set @a='hello'; exec sp_test9 @a;select @a as a; +#storedproc#!#prep#!#sp_test9#!#varchar|-|a|-|hello|-|output +#storedproc#!#prep#!#sp_test9#!#varchar|-|a|-|hello|-|inputoutput +#DROP PROCEDURE sp_test9 +# numeric +CREATE PROCEDURE sp_test10 (@a numeric(10,4) OUTPUT) AS BEGIN SET @a=100.41; Select @a as a; END; +#Declare @a numeric(10,4);Set @a=10.04; exec sp_test10 @a;select @a as a; +#storedproc#!#prep#!#sp_test10#!#numeric|-|a|-|123|-|10|-|3|-|output +#storedproc#!#prep#!#sp_test10#!#numeric|-|a|-|123|-|10|-|2|-|inputoutput +DROP PROCEDURE sp_test10 +# decimal +CREATE PROCEDURE sp_test11 (@a decimal(10,4) OUTPUT) AS BEGIN SET @a=100.41; Select @a as a; END; +#Declare @a decimal(10,4);Set @a=10.04; exec sp_test11 @a;select @a as a; +#storedproc#!#prep#!#sp_test11#!#decimal|-|a|-|123|-|10|-|3|-|output +#storedproc#!#prep#!#sp_test11#!#decimal|-|a|-|123|-|10|-|2|-|inputoutput +DROP PROCEDURE sp_test11 +# binary +#CREATE PROCEDURE sp_test12 (@a binary OUTPUT) AS BEGIN SET @a=0x121; Select @a as a; END; +#exec sp_test12 0x122 +#Declare @a binary;Set @a=0x121; exec sp_test12 @a;select @a as a; +#storedproc#!#prep#!#sp_test12#!#binary|-|a|-|0x122|-|output +#storedproc#!#prep#!#sp_test12#!#binary|-|a|-|0x122|-|inputoutput +#DROP PROCEDURE sp_test12 +# varbinary BABEL-701 +#CREATE PROCEDURE sp_test13 (@a varbinary OUTPUT) AS BEGIN SET @a=0x121; Select @a as a; END; +#exec sp_test13 0x122 +#Declare @a varbinary;Set @a=0x122; exec sp_test13 @a;select @a as a; +#storedproc#!#prep#!#sp_test13#!#varbinary|-|a|-|0x122|-|output +#storedproc#!#prep#!#sp_test13#!#varbinary|-|a|-|0x122|-|inputoutput +#DROP PROCEDURE sp_test13 +# date +CREATE PROCEDURE sp_test14 (@a date output) AS BEGIN SET @a='1999-1-3'; Select @a as a; END; +#Declare @a DATE;Set @a='9999-12-31'; exec sp_test14 @a;select @a as a; +storedproc#!#prep#!#sp_test14#!#DATE|-|a|-|9999-12-31|-|output +~~START~~ +date +1999-01-03 +~~END~~ + +storedproc#!#prep#!#sp_test14#!#DATE|-|a|-|9999-12-31|-|inputoutput +~~START~~ +date +1999-01-03 +~~END~~ + +DROP PROCEDURE sp_test14 +# time +#CREATE PROCEDURE sp_test15 (@a time(4) OUTPUT) AS BEGIN SET @a='11:25:07.123'; Select @a as a; END; +#Declare @a Time;Set @a='12:45:37.123'; exec sp_test15 @a;select @a as a; +#storedproc#!#prep#!#sp_test15#!#Time|-|a|-|12:45:37.123|-|3|-|output +#storedproc#!#prep#!#sp_test15#!#Time|-|a|-|12:45:37.123|-|3|-|inputoutput +#DROP PROCEDURE sp_test15 +# dateime BABEL-694 +#CREATE PROCEDURE sp_test16 (@a datetime output) AS BEGIN SET @a='2004-05-18 13:59:59.995'; Select @a as a; END; +#Declare @a DATETIME;Set @a='2000-02-28 23:59:59.995'; exec sp_test16 @a;select @a as a; +#storedproc#!#prep#!#sp_test16#!#DATETIME|-|a|-|2000-02-28 23:59:59.995|-|output +#storedproc#!#prep#!#sp_test16#!#DATETIME|-|a|-|2000-02-28 23:59:59.995|-|inputoutput +#DROP PROCEDURE sp_test16 +# datetime2 +#CREATE PROCEDURE sp_test17 (@a datetime2(5) OUTPUT) AS BEGIN SET @a='2014-10-2 1:45:37.123456'; Select @a as a; END; +#Declare @a Datetime2;Set @a='2016-10-23 12:45:37.123456'; exec sp_test17 @a;select @a as a; +#storedproc#!#prep#!#sp_test17#!#Datetime2|-|a|-|2016-10-23 12:45:37.123456|-|5|-|output +#storedproc#!#prep#!#sp_test17#!#Datetime2|-|a|-|2016-10-23 12:45:37.123456|-|5|-|inputoutput +#DROP PROCEDURE sp_test17 +# smalldatetime BABEL-694 +#CREATE PROCEDURE sp_test18 (@a smalldatetime output) AS BEGIN SET @a='2010-02-03 12:58:23'; Select @a as a; END; +#Declare @a SMALLDATETIME;Set @a='2000-12-13 12:58:23'; exec sp_test18 @a;select @a as a; +#storedproc#!#prep#!#sp_test18#!#SMALLDATETIME|-|a|-|2000-12-13 12:58:23|-|output +#storedproc#!#prep#!#sp_test18#!#SMALLDATETIME|-|a|-|2000-12-13 12:58:23|-|inputoutput +#DROP PROCEDURE sp_test18 +# UID +#CREATE PROCEDURE sp_test19 (@a uniqueidentifier OUTPUT) AS BEGIN SET @a='ce8af10a-2709-43b0-9e4e-a02753929d17'; Select @a as a; END; +#Declare @a uniqueidentifier;Set @a='5b7c2e8d-6d90-411d-8e19-9a81067e6f6c'; exec sp_test19 @a;select @a as a; +#storedproc#!#prep#!#sp_test19#!#uniqueidentifier|-|a|-|5b7c2e8d-6d90-411d-8e19-9a81067e6f6c|-|output +#storedproc#!#prep#!#sp_test19#!#uniqueidentifier|-|a|-|5b7c2e8d-6d90-411d-8e19-9a81067e6f6c|-|inputoutput +#DROP PROCEDURE sp_test19 diff --git a/contrib/test/JDBC/expected/TestText.out b/contrib/test/JDBC/expected/TestText.out new file mode 100644 index 00000000000..2fa7afe5f33 --- /dev/null +++ b/contrib/test/JDBC/expected/TestText.out @@ -0,0 +1,32 @@ +CREATE TABLE TEXT_dt (a text, b ntext) +#path to file should be with respect to root of test suite +prepst#!# INSERT INTO TEXT_dt(a, b) values(?, ?) #!#TEXT|-|a|-|utils/sample.txt#!#NTEXT|-|b|-|utils/sample.txt +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TEXT|-|a|-|utils/blank.txt#!#NTEXT|-|b|-|utils/blank.txt +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-| +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-|utils/utf16.txt +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-|utils/emojisText.txt +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-|utils/devanagari.txt +~~ROW COUNT: 1~~ + +SELECT * FROM TEXT_dt; +~~START~~ +text#!#ntext +AAAAAAAAAAAAAAAAAAAABBBBBBBBBBCCCCCbadksjvbajsdcbvjadssejvhsdbfjhcgvasdhgcvsj21639812365091264#!#AAAAAAAAAAAAAAAAAAAABBBBBBBBBBCCCCCbadksjvbajsdcbvjadssejvhsdbfjhcgvasdhgcvsj21639812365091264 +#!# +#!# +#!# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡ͰͱͲͳʹ͵Ͷͷͺͻͼͽ;΄΅Ά·ΈΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖՙ՚՛՜՝՞՟աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև։֊֏ְֱֲֳִֵֶַָֹֺֻּֽ֑֖֛֢֣֤֥֦֧֪֚֭֮֒֓֔֕֗֘֙֜֝֞֟֠֡֨֩֫֬֯־ֿ׀ׁׂ׃ׅׄ׆ׇאבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ׳״؀؁؂؃؄؆؇؈؉؊؋،؍؎؏ؘؙؚؐؑؒؓؔؕؖؗ؛؞؟ؠءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّْٕٖٜٟٓٔٗ٘ٙٚٛٝٞ٠١٢٣٤٥٦٧٨٩٪٫٬٭ٮٯٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿڀځڂڃڄڅچڇڈډڊڋڌڍڎڏڐڑڒړڔڕږڗژڙښڛڜڝڞڟڠڡڢڣڤڥڦڧڨکڪګڬڭڮگڰڱڲڳڴڵڶڷڸڹںڻڼڽھڿۀہۂۃۄۅۆۇۈۉۊۋیۍێۏېۑےۓ۔ەۖۗۘۙۚۛۜ۝۞ۣ۟۠ۡۢۤۥۦۧۨ۩۪ۭ۫۬ۮۯ۰۱۲۳۴۵۶۷۸۹ۺۻۼ۽۾ۿ܀܁܂܃܄܅܆܇܈܉܊܋܌܍܏ܐܑܒܓܔܕܖܗܘܙܚܛܜܝܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯܱܴܷܸܹܻܼܾ݂݄݆݈ܰܲܳܵܶܺܽܿ݀݁݃݅݇݉݊ݍݎݏݐݑݒݓݔݕݖݗݘݙݚݛݜݝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿހށނރބޅކއވމފދތލގޏސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަާިީުޫެޭޮޯްޱ߀߁߂߃߄߅߆߇߈߉ߊߋߌߍߎߏߐߑߒߓߔߕߖߗߘߙߚߛߜߝߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ߶߷߸߹ߺࠀࠁࠂࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌࠍࠎࠏࠐࠑࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜࠝࠞࠟࠠࠡࠢࠣࠤࠥࠦࠧࠨࠩࠪࠫࠬ࠭࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾ࡀࡁࡂࡃࡄࡅࡆࡇࡈࡉࡊࡋࡌࡍࡎࡏࡐࡑࡒࡓࡔࡕࡖࡗࡘ࡙࡚࡛࡞ࢠࢢࢣࢤࢥࢦࢧࢨࢩࢪࢫࢬࣰࣱࣲࣦࣩ࣭࣮࣯ࣶࣹࣺࣤࣥࣧࣨ࣪࣫࣬ࣳࣴࣵࣷࣸࣻࣼࣽࣾऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसहऺऻ़ऽािीुूृॄॅॆेैॉॊोौ्ॎॏॐ॒॑॓॔ॕॖॗक़ख़ग़ज़ड़ढ़फ़य़ॠॡॢॣ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॹॺॻॼॽॾॿঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻ਁਂਃਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਲ਼ਵਸ਼ਸਹ਼ਾਿੀੁੂੇੈੋੌ੍ੑਖ਼ਗ਼ਜ਼ੜਫ਼੦੧੨੩੪੫੬੭੮੯ੰੱੲੳੴੵઁંઃઅઆઇઈઉઊઋઌઍએઐઑઓઔકખગઘઙચછજઝઞટઠડઢણતથદધનપફબભમયરલળવશષસહ઼ઽાિીુૂૃૄૅેૈૉોૌ્ૐૠૡૢૣ૦૧૨૩૪૫૬૭૮૯૰૱ଁଂଃଅଆଇଈଉଊଋଌଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଳଵଶଷସହ଼ଽାିୀୁୂୃୄେୈୋୌ୍ୖୗଡ଼ଢ଼ୟୠୡୢୣ୦୧୨୩୪୫୬୭୮୯୰ୱ୲୳୴୵୶୷ஂஃஅஆஇஈஉஊஎஏஐஒஓஔகஙசஜஞடணதநனபமயரறலளழவஶஷஸஹாிீுூெேைொோௌ்ௐௗ௦௧௨௩௪௫௬௭௮௯௰௱௲௳௴௵௶௷௸௹௺ఁంఃఅఆఇఈఉఊఋఌఎఏఐఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధనపఫబభమయరఱలళవశషసహఽాిీుూృౄెేైొోౌ్ౕౖౘౙౠౡౢౣ౦౧౨౩౪౫౬౭౮౯౸౹౺౻౼౽౾౿ಂಃಅಆಇಈಉಊಋಌಎಏಐಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನಪಫಬಭಮಯರಱಲಳವಶಷಸಹ಼ಽಾಿೀುೂೃೄೆೇೈೊೋೌ್ೕೖೞೠೡೢೣ೦೧೨೩೪೫೬೭೮೯ೱೲംഃഅആഇഈഉഊഋഌഎഏഐഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനഩപഫബഭമയരറലളഴവശഷസഹഺഽാിീുൂൃൄെേൈൊോൌ്ൎൗൠൡൢൣ൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵൹ൺൻർൽൾൿංඃඅආඇඈඉඊඋඌඍඎඏඐඑඒඓඔඕඖකඛගඝඞඟචඡජඣඤඥඦටඨඩඪණඬතථදධනඳපඵබභමඹයරලවශෂසහළෆ්ාැෑිීුූෘෙේෛොෝෞෟෲෳ෴กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛ກຂຄງຈຊຍດຕຖທນບປຜຝພຟມຢຣລວສຫອຮຯະັາຳິີຶືຸູົຼຽເແໂໃໄໆ່້໊໋໌ໍ໐໑໒໓໔໕໖໗໘໙ໜໝໞໟༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအဢဣဤဥဦဧဨဩဪါာိီုူေဲဳဴဵံ့း္်ျြွှဿ၀၁၂၃၄၅၆၇၈၉၊။၌၍၎၏ၐၑၒၓၔၕၖၗၘၙၚၛၜၝၞၟၠၡၢၣၤၥၦၧၨၩၪၫၬၭၮၯၰၱၲၳၴၵၶၷၸၹၺၻၼၽၾၿႀႁႂႃႄႅႆႇႈႉႊႋႌႍႎႏ႐႑႒႓႔႕႖႗႘႙ႚႛႜႝ႞႟ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅჇჍაბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ჻ჼჽჾჿᄀᄁᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜᄝᄞᄟᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿᅀᅁᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌᅍᅎᅏᅐᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜᅝᅞᅟᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀᆁᆂᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌᆍᆎᆏᆐᆑᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜᆝᆞᆟᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀᇁᇂᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌᇍᇎᇏᇐᇑᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜᇝᇞᇟᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿሀሁሂሃሄህሆሇለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿቀቁቂቃቄቅቆቇቈቊቋቌቍቐቑቒቓቔቕቖቘቚቛቜቝበቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿኀኁኂኃኄኅኆኇኈኊኋኌኍነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኯኰኲኳኴኵኸኹኺኻኼኽኾዀዂዃዄዅወዉዊዋዌውዎዏዐዑዒዓዔዕዖዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮዯደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿጀጁጂጃጄጅጆጇገጉጊጋጌግጎጏጐጒጓጔጕጘጙጚጛጜጝጞጟጠጡጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿፀፁፂፃፄፅፆፇፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፝፞፟፠፡።፣፤፥፦፧፨፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼ᎀᎁᎂᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌᎍᎎᎏ᎐᎑᎒᎓᎔᎕᎖᎗᎘᎙ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿᏀᏁᏂᏃᏄᏅᏆᏇᏈᏉᏊᏋᏌᏍᏎᏏᏐᏑᏒᏓᏔᏕᏖᏗᏘᏙᏚᏛᏜᏝᏞᏟᏠᏡᏢᏣᏤᏥᏦᏧᏨᏩᏪᏫᏬᏭᏮᏯᏰᏱᏲᏳᏴ᐀ᐁᐂᐃᐄᐅᐆᐇᐈᐉᐊᐋᐌᐍᐎᐏᐐᐑᐒᐓᐔᐕᐖᐗᐘᐙᐚᐛᐜᐝᐞᐟᐠᐡᐢᐣᐤᐥᐦᐧᐨᐩᐪᐫᐬᐭᐮᐯᐰᐱᐲᐳᐴᐵᐶᐷᐸᐹᐺᐻᐼᐽᐾᐿᑀᑁᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌᑍᑎᑏᑐᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜᑝᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿᒀᒁᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌᒍᒎᒏᒐᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜᒝᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿᓀᓁᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌᓍᓎᓏᓐᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜᓝᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿᔀᔁᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌᔍᔎᔏᔐᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜᔝᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿᕀᕁᕂᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌᕍᕎᕏᕐᕑᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜᕝᕞᕟᕠᕡᕢᕣᕤᕥᕦᕧᕨᕩᕪᕫᕬᕭᕮᕯᕰᕱᕲᕳᕴᕵᕶᕷᕸᕹᕺᕻᕼᕽᕾᕿᖀᖁᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌᖍᖎᖏᖐᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜᖝᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿᗀᗁᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌᗍᗎᗏᗐᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜᗝᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿᘀᘁᘂᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌᘍᘎᘏᘐᘑᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜᘝᘞᘟᘠᘡᘢᘣᘤᘥᘦᘧᘨᘩᘪᘫᘬᘭᘮᘯᘰᘱᘲᘳᘴᘵᘶᘷᘸᘹᘺᘻᘼᘽᘾᘿᙀᙁᙂᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌᙍᙎᙏᙐᙑᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜᙝᙞᙟᙠᙡᙢᙣᙤᙥᙦᙧᙨᙩᙪᙫᙬ᙭᙮ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔᚕᚖᚗᚘᚙᚚ᚛᚜ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰᜀᜁᜂᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜎᜏᜐᜑᜒᜓ᜔ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱᜲᜳ᜴᜵᜶ᝀᝁᝂᝃᝄᝅᝆᝇᝈᝉᝊᝋᝌᝍᝎᝏᝐᝑᝒᝓᝠᝡᝢᝣᝤᝥᝦᝧᝨᝩᝪᝫᝬᝮᝯᝰᝲᝳកខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអឣឤឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ឴឵ាិីឹឺុូួើឿៀេែៃោៅំះៈ៉៊់៌៍៎៏័៑្៓។៕៖ៗ៘៙៚៛ៜ៝០១២៣៤៥៦៧៨៩៰៱៲៳៴៵៶៷៸៹᠀᠁᠂᠃᠄᠅᠆᠇᠈᠉᠊᠋᠌᠍᠎᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ᠠᠡᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿᡀᡁᡂᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜᡝᡞᡟᡠᡡᡢᡣᡤᡥᡦᡧᡨᡩᡪᡫᡬᡭᡮᡯᡰᡱᡲᡳᡴᡵᡶᡷᢀᢁᢂᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌᢍᢎᢏᢐᢑᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜᢝᢞᢟᢠᢡᢢᢣᢤᢥᢦᢧᢨᢩᢪᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀᣁᣂᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌᣍᣎᣏᣐᣑᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜᣝᣞᣟᣠᣡᣢᣣᣤᣥᣦᣧᣨᣩᣪᣫᣬᣭᣮᣯᣰᣱᣲᣳᣴᣵᤀᤁᤂᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌᤍᤎᤏᤐᤑᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤠᤡᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺᥀᥄᥅᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ᥐᥑᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜᥝᥞᥟᥠᥡᥢᥣᥤᥥᥦᥧᥨᥩᥪᥫᥬᥭᥰᥱᥲᥳᥴᦀᦁᦂᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌᦍᦎᦏᦐᦑᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜᦝᦞᦟᦠᦡᦢᦣᦤᦥᦦᦧᦨᦩᦪᦫᦰᦱᦲᦳᦴᦵᦶᦷᦸᦹᦺᦻᦼᦽᦾᦿᧀᧁᧂᧃᧄᧅᧆᧇᧈᧉ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᧚᧞᧟᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿ᨀᨁᨂᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌᨍᨎᨏᨐᨑᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ᨞᨟ᨠᨡᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿᩀᩁᩂᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌᩍᩎᩏᩐᩑᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜᩝᩞ᩠ᩡᩢᩣᩤᩥᩦᩧᩨᩩᩪᩫᩬᩭᩮᩯᩰᩱᩲᩳᩴ᩿᩵᩶᩷᩸᩹᩺᩻᩼᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᪠᪡᪢᪣᪤᪥᪦ᪧ᪨᪩᪪᪫᪬᪭ᬀᬁᬂᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌᬍᬎᬏᬐᬑᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜᬝᬞᬟᬠᬡᬢᬣᬤᬥᬦᬧᬨᬩᬪᬫᬬᬭᬮᬯᬰᬱᬲᬳ᬴ᬵᬶᬷᬸᬹᬺᬻᬼᬽᬾᬿᭀᭁᭂᭃ᭄ᭅᭆᭇᭈᭉᭊᭋ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᭚᭛᭜᭝᭞᭟᭠᭡᭢᭣᭤᭥᭦᭧᭨᭩᭪᭬᭫᭭᭮᭯᭰᭱᭲᭳᭴᭵᭶᭷᭸᭹᭺᭻᭼ᮀᮁᮂᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌᮍᮎᮏᮐᮑᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜᮝᮞᮟᮠᮡᮢᮣᮤᮥᮦᮧᮨᮩ᮪᮫ᮬᮭᮮᮯ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹ᮺᮻᮼᮽᮾᮿᯀᯁᯂᯃᯄᯅᯆᯇᯈᯉᯊᯋᯌᯍᯎᯏᯐᯑᯒᯓᯔᯕᯖᯗᯘᯙᯚᯛᯜᯝᯞᯟᯠᯡᯢᯣᯤᯥ᯦ᯧᯨᯩᯪᯫᯬᯭᯮᯯᯰᯱ᯲᯳᯼᯽᯾᯿ᰀᰁᰂᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌᰍᰎᰏᰐᰑᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜᰝᰞᰟᰠᰡᰢᰣᰤᰥᰦᰧᰨᰩᰪᰫᰬᰭᰮᰯᰰᰱᰲᰳᰴᰵᰶ᰷᰻᰼᰽᰾᰿᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉ᱍᱎᱏ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜᱝᱞᱟᱠᱡᱢᱣᱤᱥᱦᱧᱨᱩᱪᱫᱬᱭᱮᱯᱰᱱᱲᱳᱴᱵᱶᱷᱸᱹᱺᱻᱼᱽ᱾᱿᳀᳁᳂᳃᳄᳅᳆᳇᳐᳑᳒᳓᳔᳕᳖᳗᳘᳙᳜᳝᳞᳟᳚᳛᳠᳡᳢᳣᳤᳥᳦᳧᳨ᳩᳪᳫᳬ᳭ᳮᳯᳰᳱᳲᳳ᳴ᳵᳶᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩᴪᴫᴬᴭᴮᴯᴰᴱᴲᴳᴴᴵᴶᴷᴸᴹᴺᴻᴼᴽᴾᴿᵀᵁᵂᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌᵍᵎᵏᵐᵑᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵸᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜᶝᶞᶟᶠᶡᶢᶣᶤᶥᶦᶧᶨᶩᶪᶫᶬᶭᶮᶯᶰᶱᶲᶳᶴᶵᶶᶷᶸᶹᶺᶻᶼᶽᶾᶿ᷐᷎᷂᷊᷏᷽᷿᷀᷁᷃᷄᷅᷆᷇᷈᷉᷋᷌᷑᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜᷝᷞᷟᷠᷡᷢᷣᷤᷥᷦ᷾᷼᷍ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẜẝẞẟẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹỺỻỼỽỾỿἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆᾼ᾽ι᾿῀῁ῂῃῄῆῇῈΈῊΉῌ῍῎῏ῐῑῒΐῖῗῘῙῚΊ῝῞῟ῠῡῢΰῤῥῦῧῨῩῪΎῬ῭΅`ῲῳῴῶῷῸΌῺΏῼ´῾           ​‌‍‎‏‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧

‪‭‮ ‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞ ⁠⁡⁢⁣⁤⁰ⁱ⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₒₓₔₕₖₗₘₙₚₛₜ₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃒⃓⃘⃙⃚⃐⃑⃔⃕⃖⃗⃛⃜⃝⃞⃟⃠⃡⃢⃣⃤⃥⃦⃪⃫⃨⃬⃭⃮⃯⃧⃩⃰℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠℡™℣ℤ℥Ω℧ℨ℩KÅℬℭ℮ℯℰℱℲℳℴℵℶℷℸℹ℺℻ℼℽℾℿ⅀⅁⅂⅃⅄ⅅⅆⅇⅈⅉ⅊⅋⅌⅍ⅎ⅏⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃↄↅↆↇↈ↉←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡⌢⌣⌤⌥⌦⌧⌨〈〉⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍼⍽⍾⍿⎀⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⎮⎯⎰⎱⎲⎳⎴⎵⎶⎷⎸⎹⎺⎻⎼⎽⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌⏍⏎⏏⏐⏑⏒⏓⏔⏕⏖⏗⏘⏙⏚⏛⏜⏝⏞⏟⏠⏡⏢⏣⏤⏥⏦⏧⏨⏩⏪⏫⏬⏭⏮⏯⏰⏱⏲⏳␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␢␣␤␥␦⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛎⛏⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟⛠⛡⛢⛣⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞❟❠❡❢❣❤❥❦❧❨❩❪❫❬❭❮❯❰❱❲❳❴❵❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔➕➖➗➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➰➱➲➳➴➵➶➷➸➹➺➻➼➽➾➿⟀⟁⟂⟃⟄⟅⟆⟇⟈⟉⟊⟋⟌⟍⟎⟏⟐⟑⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪⟫⟬⟭⟮⟯⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿⦀⦁⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜⦝⦞⦟⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜⧝⧞⧟⧠⧡⧢⧣⧤⧥⧦⧧⧨⧩⧪⧫⧬⧭⧮⧯⧰⧱⧲⧳⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻⨼⨽⨾⨿⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿⪀⪁⪂⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽⫾⫿⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜⬝⬞⬟⬠⬡⬢⬣⬤⬥⬦⬧⬨⬩⬪⬫⬬⬭⬮⬯⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿⭀⭁⭂⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌⭐⭑⭒⭓⭔⭕⭖⭗⭘⭙ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞⱠⱡⱢⱣⱤⱥⱦⱧⱨⱩⱪⱫⱬⱭⱮⱯⱰⱱⱲⱳⱴⱵⱶⱷⱸⱹⱺⱻⱼⱽⱾⱿⲀⲁⲂⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌⲍⲎⲏⲐⲑⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜⲝⲞⲟⲠⲡⲢⲣⲤⲥⲦⲧⲨⲩⲪⲫⲬⲭⲮⲯⲰⲱⲲⲳⲴⲵⲶⲷⲸⲹⲺⲻⲼⲽⲾⲿⳀⳁⳂⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌⳍⳎⳏⳐⳑⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜⳝⳞⳟⳠⳡⳢⳣⳤ⳥⳦⳧⳨⳩⳪ⳫⳬⳭⳮ⳯⳰⳱Ⳳⳳ⳹⳺⳻⳼⳽⳾⳿ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥⴧⴭⴰⴱⴲⴳⴴⴵⴶⴷⴸⴹⴺⴻⴼⴽⴾⴿⵀⵁⵂⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌⵍⵎⵏⵐⵑⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜⵝⵞⵟⵠⵡⵢⵣⵤⵥⵦⵧⵯ⵰⵿ⶀⶁⶂⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌⶍⶎⶏⶐⶑⶒⶓⶔⶕⶖⶠⶡⶢⶣⶤⶥⶦⶨⶩⶪⶫⶬⶭⶮⶰⶱⶲⶳⶴⶵⶶⶸⶹⶺⶻⶼⶽⶾⷀⷁⷂⷃⷄⷅⷆⷈⷉⷊⷋⷌⷍⷎⷐⷑⷒⷓⷔⷕⷖⷘⷙⷚⷛⷜⷝⷞⷠⷡⷢⷣⷤⷥⷦⷧⷨⷩⷪⷫⷬⷭⷮⷯⷰⷱⷲⷳⷴⷵⷶⷷⷸⷹⷺⷻⷼⷽⷾⷿ⸀⸁⸂⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌⸍⸎⸏⸐⸑⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜⸝⸞⸟⸠⸡⸢⸣⸤⸥⸦⸧⸨⸩⸪⸫⸬⸭⸮ⸯ⸰⸱⸲⸳⸴⸵⸶⸷⸸⸹⸺⸻⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻ 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟〠〡〢〣〤〥〦〧〨〩〪〭〮〯〫〬〰〱〲〳〴〵〶〷〸〹〺〻〼〽〾〿ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゙゚゛゜ゝゞゟ゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌㆍㆎ㆐㆑㆒㆓㆔㆕㆖㆗㆘㆙㆚㆛㆜㆝㆞㆟ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷㆸㆹㆺ㇀㇁㇂㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌㇍㇎㇏㇐㇑㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜㇝㇞㇟㇠㇡㇢㇣ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜㈝㈞㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌㉍㉎㉏㉐㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌㋍㋎㋏㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㋿㌀㌁㌂㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌㌍㌎㌏㌐㌑㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜㌝㌞㌟㌠㌡㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿㍀㍁㍂㍃㍄㍅㍆㍇㍈㍉㍊㍋㍌㍍㍎㍏㍐㍑㍒㍓㍔㍕㍖㍗㍘㍙㍚㍛㍜㍝㍞㍟㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍱㍲㍳㍴㍵㍶㍷㍸㍹㍺㍻㍼㍽㍾㍿㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏋㏌㏍㏎㏏㏐㏑㏒㏓㏔㏕㏖㏗㏘㏙㏚㏛㏜㏝㏞㏟㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㏿㐀㐁㐂㐃㐄㐅㐆㐇㐈㐉㐊㐋㐌㐍㐎㐏㐐㐑㐒㐓㐔㐕㐖㐗㐘㐙㐚㐛㐜㐝㐞㐟㐠㐡㐢㐣㐤㐥㐦㐧㐨㐩㐪㐫㐬㐭㐮㐯㐰㐱㐲㐳㐴㐵㐶㐷㐸㐹㐺㐻㐼㐽㐾㐿㑀㑁㑂㑃㑄㑅㑆㑇㑈㑉㑊㑋㑌㑍㑎㑏㑐㑑㑒㑓㑔㑕㑖㑗㑘㑙㑚㑛㑜㑝㑞㑟㑠㑡㑢㑣㑤㑥㑦㑧㑨㑩㑪㑫㑬㑭㑮㑯㑰㑱㑲㑳㑴㑵㑶㑷㑸㑹㑺㑻㑼㑽㑾㑿㒀㒁㒂㒃㒄㒅㒆㒇㒈㒉㒊㒋㒌㒍㒎㒏㒐㒑㒒㒓㒔㒕㒖㒗㒘㒙㒚㒛㒜㒝㒞㒟㒠㒡㒢㒣㒤㒥㒦㒧㒨㒩㒪㒫㒬㒭㒮㒯㒰㒱㒲㒳㒴㒵㒶㒷㒸㒹㒺㒻㒼㒽㒾㒿㓀㓁㓂㓃㓄㓅㓆㓇㓈㓉㓊㓋㓌㓍㓎㓏㓐㓑㓒㓓㓔㓕㓖㓗㓘㓙㓚㓛㓜㓝㓞㓟㓠㓡㓢㓣㓤㓥㓦㓧㓨㓩㓪㓫㓬㓭㓮㓯㓰㓱㓲㓳㓴㓵㓶㓷㓸㓹㓺㓻㓼㓽㓾㓿㔀㔁㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌㔍㔎㔏㔐㔑㔒㔓㔔㔕㔖㔗㔘㔙㔚㔛㔜㔝㔞㔟㔠㔡㔢㔣㔤㔥㔦㔧㔨㔩㔪㔫㔬㔭㔮㔯㔰㔱㔲㔳㔴㔵㔶㔷㔸㔹㔺㔻㔼㔽㔾㔿㕀㕁㕂㕃㕄㕅㕆㕇㕈㕉㕊㕋㕌㕍㕎㕏㕐㕑㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜㕝㕞㕟㕠㕡㕢㕣㕤㕥㕦㕧㕨㕩㕪㕫㕬㕭㕮㕯㕰㕱㕲㕳㕴㕵㕶㕷㕸㕹㕺㕻㕼㕽㕾㕿㖀㖁㖂㖃㖄㖅㖆㖇㖈㖉㖊㖋㖌㖍㖎㖏㖐㖑㖒㖓㖔㖕㖖㖗㖘㖙㖚㖛㖜㖝㖞㖟㖠㖡㖢㖣㖤㖥㖦㖧㖨㖩㖪㖫㖬㖭㖮㖯㖰㖱㖲㖳㖴㖵㖶㖷㖸㖹㖺㖻㖼㖽㖾㖿㗀㗁㗂㗃㗄㗅㗆㗇㗈㗉㗊㗋㗌㗍㗎㗏㗐㗑㗒㗓㗔㗕㗖㗗㗘㗙㗚㗛㗜㗝㗞㗟㗠㗡㗢㗣㗤㗥㗦㗧㗨㗩㗪㗫㗬㗭㗮㗯㗰㗱㗲㗳㗴㗵㗶㗷㗸㗹㗺㗻㗼㗽㗾㗿㘀㘁㘂㘃㘄㘅㘆㘇㘈㘉㘊㘋㘌㘍㘎㘏㘐㘑㘒㘓㘔㘕㘖㘗㘘㘙㘚㘛㘜㘝㘞㘟㘠㘡㘢㘣㘤㘥㘦㘧㘨㘩㘪㘫㘬㘭㘮㘯㘰㘱㘲㘳㘴㘵㘶㘷㘸㘹㘺㘻㘼㘽㘾㘿㙀㙁㙂㙃㙄㙅㙆㙇㙈㙉㙊㙋㙌㙍㙎㙏㙐㙑㙒㙓㙔㙕㙖㙗㙘㙙㙚㙛㙜㙝㙞㙟㙠㙡㙢㙣㙤㙥㙦㙧㙨㙩㙪㙫㙬㙭㙮㙯㙰㙱㙲㙳㙴㙵㙶㙷㙸㙹㙺㙻㙼㙽㙾㙿㚀㚁㚂㚃㚄㚅㚆㚇㚈㚉㚊㚋㚌㚍㚎㚏㚐㚑㚒㚓㚔㚕㚖㚗㚘㚙㚚㚛㚜㚝㚞㚟㚠㚡㚢㚣㚤㚥㚦㚧㚨㚩㚪㚫㚬㚭㚮㚯㚰㚱㚲㚳㚴㚵㚶㚷㚸㚹㚺㚻㚼㚽㚾㚿㛀㛁㛂㛃㛄㛅㛆㛇㛈㛉㛊㛋㛌㛍㛎㛏㛐㛑㛒㛓㛔㛕㛖㛗㛘㛙㛚㛛㛜㛝㛞㛟㛠㛡㛢㛣㛤㛥㛦㛧㛨㛩㛪㛫㛬㛭㛮㛯㛰㛱㛲㛳㛴㛵㛶㛷㛸㛹㛺㛻㛼㛽㛾㛿㜀㜁㜂㜃㜄㜅㜆㜇㜈㜉㜊㜋㜌㜍㜎㜏㜐㜑㜒㜓㜔㜕㜖㜗㜘㜙㜚㜛㜜㜝㜞㜟㜠㜡㜢㜣㜤㜥㜦㜧㜨㜩㜪㜫㜬㜭㜮㜯㜰㜱㜲㜳㜴㜵㜶㜷㜸㜹㜺㜻㜼㜽㜾㜿㝀㝁㝂㝃㝄㝅㝆㝇㝈㝉㝊㝋㝌㝍㝎㝏㝐㝑㝒㝓㝔㝕㝖㝗㝘㝙㝚㝛㝜㝝㝞㝟㝠㝡㝢㝣㝤㝥㝦㝧㝨㝩㝪㝫㝬㝭㝮㝯㝰㝱㝲㝳㝴㝵㝶㝷㝸㝹㝺㝻㝼㝽㝾㝿㞀㞁㞂㞃㞄㞅㞆㞇㞈㞉㞊㞋㞌㞍㞎㞏㞐㞑㞒㞓㞔㞕㞖㞗㞘㞙㞚㞛㞜㞝㞞㞟㞠㞡㞢㞣㞤㞥㞦㞧㞨㞩㞪㞫㞬㞭㞮㞯㞰㞱㞲㞳㞴㞵㞶㞷㞸㞹㞺㞻㞼㞽㞾㞿㟀㟁㟂㟃㟄㟅㟆㟇㟈㟉㟊㟋㟌㟍㟎㟏㟐㟑㟒㟓㟔㟕㟖㟗㟘㟙㟚㟛㟜㟝㟞㟟㟠㟡㟢㟣㟤㟥㟦㟧㟨㟩㟪㟫㟬㟭㟮㟯㟰㟱㟲㟳㟴㟵㟶㟷㟸㟹㟺㟻㟼㟽㟾㟿㠀㠁㠂㠃㠄㠅㠆㠇㠈㠉㠊㠋㠌㠍㠎㠏㠐㠑㠒㠓㠔㠕㠖㠗㠘㠙㠚㠛㠜㠝㠞㠟㠠㠡㠢㠣㠤㠥㠦㠧㠨㠩㠪㠫㠬㠭㠮㠯㠰㠱㠲㠳㠴㠵㠶㠷㠸㠹㠺㠻㠼㠽㠾㠿㡀㡁㡂㡃㡄㡅㡆㡇㡈㡉㡊㡋㡌㡍㡎㡏㡐㡑㡒㡓㡔㡕㡖㡗㡘㡙㡚㡛㡜㡝㡞㡟㡠㡡㡢㡣㡤㡥㡦㡧㡨㡩㡪㡫㡬㡭㡮㡯㡰㡱㡲㡳㡴㡵㡶㡷㡸㡹㡺㡻㡼㡽㡾㡿㢀㢁㢂㢃㢄㢅㢆㢇㢈㢉㢊㢋㢌㢍㢎㢏㢐㢑㢒㢓㢔㢕㢖㢗㢘㢙㢚㢛㢜㢝㢞㢟㢠㢡㢢㢣㢤㢥㢦㢧㢨㢩㢪㢫㢬㢭㢮㢯㢰㢱㢲㢳㢴㢵㢶㢷㢸㢹㢺㢻㢼㢽㢾㢿㣀㣁㣂㣃㣄㣅㣆㣇㣈㣉㣊㣋㣌㣍㣎㣏㣐㣑㣒㣓㣔㣕㣖㣗㣘㣙㣚㣛㣜㣝㣞㣟㣠㣡㣢㣣㣤㣥㣦㣧㣨㣩㣪㣫㣬㣭㣮㣯㣰㣱㣲㣳㣴㣵㣶㣷㣸㣹㣺㣻㣼㣽㣾㣿㤀㤁㤂㤃㤄㤅㤆㤇㤈㤉㤊㤋㤌㤍㤎㤏㤐㤑㤒㤓㤔㤕㤖㤗㤘㤙㤚㤛㤜㤝㤞㤟㤠㤡㤢㤣㤤㤥㤦㤧㤨㤩㤪㤫㤬㤭㤮㤯㤰㤱㤲㤳㤴㤵㤶㤷㤸㤹㤺㤻㤼㤽㤾㤿㥀㥁㥂㥃㥄㥅㥆㥇㥈㥉㥊㥋㥌㥍㥎㥏㥐㥑㥒㥓㥔㥕㥖㥗㥘㥙㥚㥛㥜㥝㥞㥟㥠㥡㥢㥣㥤㥥㥦㥧㥨㥩㥪㥫㥬㥭㥮㥯㥰㥱㥲㥳㥴㥵㥶㥷㥸㥹㥺㥻㥼㥽㥾㥿㦀㦁㦂㦃㦄㦅㦆㦇㦈㦉㦊㦋㦌㦍㦎㦏㦐㦑㦒㦓㦔㦕㦖㦗㦘㦙㦚㦛㦜㦝㦞㦟㦠㦡㦢㦣㦤㦥㦦㦧㦨㦩㦪㦫㦬㦭㦮㦯㦰㦱㦲㦳㦴㦵㦶㦷㦸㦹㦺㦻㦼㦽㦾㦿㧀㧁㧂㧃㧄㧅㧆㧇㧈㧉㧊㧋㧌㧍㧎㧏㧐㧑㧒㧓㧔㧕㧖㧗㧘㧙㧚㧛㧜㧝㧞㧟㧠㧡㧢㧣㧤㧥㧦㧧㧨㧩㧪㧫㧬㧭㧮㧯㧰㧱㧲㧳㧴㧵㧶㧷㧸㧹㧺㧻㧼㧽㧾㧿㨀㨁㨂㨃㨄㨅㨆㨇㨈㨉㨊㨋㨌㨍㨎㨏㨐㨑㨒㨓㨔㨕㨖㨗㨘㨙㨚㨛㨜㨝㨞㨟㨠㨡㨢㨣㨤㨥㨦㨧㨨㨩㨪㨫㨬㨭㨮㨯㨰㨱㨲㨳㨴㨵㨶㨷㨸㨹㨺㨻㨼㨽㨾㨿㩀㩁㩂㩃㩄㩅㩆㩇㩈㩉㩊㩋㩌㩍㩎㩏㩐㩑㩒㩓㩔㩕㩖㩗㩘㩙㩚㩛㩜㩝㩞㩟㩠㩡㩢㩣㩤㩥㩦㩧㩨㩩㩪㩫㩬㩭㩮㩯㩰㩱㩲㩳㩴㩵㩶㩷㩸㩹㩺㩻㩼㩽㩾㩿㪀㪁㪂㪃㪄㪅㪆㪇㪈㪉㪊㪋㪌㪍㪎㪏㪐㪑㪒㪓㪔㪕㪖㪗㪘㪙㪚㪛㪜㪝㪞㪟㪠㪡㪢㪣㪤㪥㪦㪧㪨㪩㪪㪫㪬㪭㪮㪯㪰㪱㪲㪳㪴㪵㪶㪷㪸㪹㪺㪻㪼㪽㪾㪿㫀㫁㫂㫃㫄㫅㫆㫇㫈㫉㫊㫋㫌㫍㫎㫏㫐㫑㫒㫓㫔㫕㫖㫗㫘㫙㫚㫛㫜㫝㫞㫟㫠㫡㫢㫣㫤㫥㫦㫧㫨㫩㫪㫫㫬㫭㫮㫯㫰㫱㫲㫳㫴㫵㫶㫷㫸㫹㫺㫻㫼㫽㫾㫿㬀㬁㬂㬃㬄㬅㬆㬇㬈㬉㬊㬋㬌㬍㬎㬏㬐㬑㬒㬓㬔㬕㬖㬗㬘㬙㬚㬛㬜㬝㬞㬟㬠㬡㬢㬣㬤㬥㬦㬧㬨㬩㬪㬫㬬㬭㬮㬯㬰㬱㬲㬳㬴㬵㬶㬷㬸㬹㬺㬻㬼㬽㬾㬿㭀㭁㭂㭃㭄㭅㭆㭇㭈㭉㭊㭋㭌㭍㭎㭏㭐㭑㭒㭓㭔㭕㭖㭗㭘㭙㭚㭛㭜㭝㭞㭟㭠㭡㭢㭣㭤㭥㭦㭧㭨㭩㭪㭫㭬㭭㭮㭯㭰㭱㭲㭳㭴㭵㭶㭷㭸㭹㭺㭻㭼㭽㭾㭿㮀㮁㮂㮃㮄㮅㮆㮇㮈㮉㮊㮋㮌㮍㮎㮏㮐㮑㮒㮓㮔㮕㮖㮗㮘㮙㮚㮛㮜㮝㮞㮟㮠㮡㮢㮣㮤㮥㮦㮧㮨㮩㮪㮫㮬㮭㮮㮯㮰㮱㮲㮳㮴㮵㮶㮷㮸㮹㮺㮻㮼㮽㮾㮿㯀㯁㯂㯃㯄㯅㯆㯇㯈㯉㯊㯋㯌㯍㯎㯏㯐㯑㯒㯓㯔㯕㯖㯗㯘㯙㯚㯛㯜㯝㯞㯟㯠㯡㯢㯣㯤㯥㯦㯧㯨㯩㯪㯫㯬㯭㯮㯯㯰㯱㯲㯳㯴㯵㯶㯷㯸㯹㯺㯻㯼㯽㯾㯿㰀㰁㰂㰃㰄㰅㰆㰇㰈㰉㰊㰋㰌㰍㰎㰏㰐㰑㰒㰓㰔㰕㰖㰗㰘㰙㰚㰛㰜㰝㰞㰟㰠㰡㰢㰣㰤㰥㰦㰧㰨㰩㰪㰫㰬㰭㰮㰯㰰㰱㰲㰳㰴㰵㰶㰷㰸㰹㰺㰻㰼㰽㰾㰿㱀㱁㱂㱃㱄㱅㱆㱇㱈㱉㱊㱋㱌㱍㱎㱏㱐㱑㱒㱓㱔㱕㱖㱗㱘㱙㱚㱛㱜㱝㱞㱟㱠㱡㱢㱣㱤㱥㱦㱧㱨㱩㱪㱫㱬㱭㱮㱯㱰㱱㱲㱳㱴㱵㱶㱷㱸㱹㱺㱻㱼㱽㱾㱿㲀㲁㲂㲃㲄㲅㲆㲇㲈㲉㲊㲋㲌㲍㲎㲏㲐㲑㲒㲓㲔㲕㲖㲗㲘㲙㲚㲛㲜㲝㲞㲟㲠㲡㲢㲣㲤㲥㲦㲧㲨㲩㲪㲫㲬㲭㲮㲯㲰㲱㲲㲳㲴㲵㲶㲷㲸㲹㲺㲻㲼㲽㲾㲿㳀㳁㳂㳃㳄㳅㳆㳇㳈㳉㳊㳋㳌㳍㳎㳏㳐㳑㳒㳓㳔㳕㳖㳗㳘㳙㳚㳛㳜㳝㳞㳟㳠㳡㳢㳣㳤㳥㳦㳧㳨㳩㳪㳫㳬㳭㳮㳯㳰㳱㳲㳳㳴㳵㳶㳷㳸㳹㳺㳻㳼㳽㳾㳿㴀㴁㴂㴃㴄㴅㴆㴇㴈㴉㴊㴋㴌㴍㴎㴏㴐㴑㴒㴓㴔㴕㴖㴗㴘㴙㴚㴛㴜㴝㴞㴟㴠㴡㴢㴣㴤㴥㴦㴧㴨㴩㴪㴫㴬㴭㴮㴯㴰㴱㴲㴳㴴㴵㴶㴷㴸㴹㴺㴻㴼㴽㴾㴿㵀㵁㵂㵃㵄㵅㵆㵇㵈㵉㵊㵋㵌㵍㵎㵏㵐㵑㵒㵓㵔㵕㵖㵗㵘㵙㵚㵛㵜㵝㵞㵟㵠㵡㵢㵣㵤㵥㵦㵧㵨㵩㵪㵫㵬㵭㵮㵯㵰㵱㵲㵳㵴㵵㵶㵷㵸㵹㵺㵻㵼㵽㵾㵿㶀㶁㶂㶃㶄㶅㶆㶇㶈㶉㶊㶋㶌㶍㶎㶏㶐㶑㶒㶓㶔㶕㶖㶗㶘㶙㶚㶛㶜㶝㶞㶟㶠㶡㶢㶣㶤㶥㶦㶧㶨㶩㶪㶫㶬㶭㶮㶯㶰㶱㶲㶳㶴㶵㶶㶷㶸㶹㶺㶻㶼㶽㶾㶿㷀㷁㷂㷃㷄㷅㷆㷇㷈㷉㷊㷋㷌㷍㷎㷏㷐㷑㷒㷓㷔㷕㷖㷗㷘㷙㷚㷛㷜㷝㷞㷟㷠㷡㷢㷣㷤㷥㷦㷧㷨㷩㷪㷫㷬㷭㷮㷯㷰㷱㷲㷳㷴㷵㷶㷷㷸㷹㷺㷻㷼㷽㷾㷿㸀㸁㸂㸃㸄㸅㸆㸇㸈㸉㸊㸋㸌㸍㸎㸏㸐㸑㸒㸓㸔㸕㸖㸗㸘㸙㸚㸛㸜㸝㸞㸟㸠㸡㸢㸣㸤㸥㸦㸧㸨㸩㸪㸫㸬㸭㸮㸯㸰㸱㸲㸳㸴㸵㸶㸷㸸㸹㸺㸻㸼㸽㸾㸿㹀㹁㹂㹃㹄㹅㹆㹇㹈㹉㹊㹋㹌㹍㹎㹏㹐㹑㹒㹓㹔㹕㹖㹗㹘㹙㹚㹛㹜㹝㹞㹟㹠㹡㹢㹣㹤㹥㹦㹧㹨㹩㹪㹫㹬㹭㹮㹯㹰㹱㹲㹳㹴㹵㹶㹷㹸㹹㹺㹻㹼㹽㹾㹿㺀㺁㺂㺃㺄㺅㺆㺇㺈㺉㺊㺋㺌㺍㺎㺏㺐㺑㺒㺓㺔㺕㺖㺗㺘㺙㺚㺛㺜㺝㺞㺟㺠㺡㺢㺣㺤㺥㺦㺧㺨㺩㺪㺫㺬㺭㺮㺯㺰㺱㺲㺳㺴㺵㺶㺷㺸㺹㺺㺻㺼㺽㺾㺿㻀㻁㻂㻃㻄㻅㻆㻇㻈㻉㻊㻋㻌㻍㻎㻏㻐㻑㻒㻓㻔㻕㻖㻗㻘㻙㻚㻛㻜㻝㻞㻟㻠㻡㻢㻣㻤㻥㻦㻧㻨㻩㻪㻫㻬㻭㻮㻯㻰㻱㻲㻳㻴㻵㻶㻷㻸㻹㻺㻻㻼㻽㻾㻿㼀㼁㼂㼃㼄㼅㼆㼇㼈㼉㼊㼋㼌㼍㼎㼏㼐㼑㼒㼓㼔㼕㼖㼗㼘㼙㼚㼛㼜㼝㼞㼟㼠㼡㼢㼣㼤㼥㼦㼧㼨㼩㼪㼫㼬㼭㼮㼯㼰㼱㼲㼳㼴㼵㼶㼷㼸㼹㼺㼻㼼㼽㼾㼿㽀㽁㽂㽃㽄㽅㽆㽇㽈㽉㽊㽋㽌㽍㽎㽏㽐㽑㽒㽓㽔㽕㽖㽗㽘㽙㽚㽛㽜㽝㽞㽟㽠㽡㽢㽣㽤㽥㽦㽧㽨㽩㽪㽫㽬㽭㽮㽯㽰㽱㽲㽳㽴㽵㽶㽷㽸㽹㽺㽻㽼㽽㽾㽿㾀㾁㾂㾃㾄㾅㾆㾇㾈㾉㾊㾋㾌㾍㾎㾏㾐㾑㾒㾓㾔㾕㾖㾗㾘㾙㾚㾛㾜㾝㾞㾟㾠㾡㾢㾣㾤㾥㾦㾧㾨㾩㾪㾫㾬㾭㾮㾯㾰㾱㾲㾳㾴㾵㾶㾷㾸㾹㾺㾻㾼㾽㾾㾿㿀㿁㿂㿃㿄㿅㿆㿇㿈㿉㿊㿋㿌㿍㿎㿏㿐㿑㿒㿓㿔㿕㿖㿗㿘㿙㿚㿛㿜㿝㿞㿟㿠㿡㿢㿣㿤㿥㿦㿧㿨㿩㿪㿫㿬㿭㿮㿯㿰㿱㿲㿳㿴㿵㿶㿷㿸㿹㿺㿻㿼㿽㿾㿿䀀䀁䀂䀃䀄䀅䀆䀇䀈䀉䀊䀋䀌䀍䀎䀏䀐䀑䀒䀓䀔䀕䀖䀗䀘䀙䀚䀛䀜䀝䀞䀟䀠䀡䀢䀣䀤䀥䀦䀧䀨䀩䀪䀫䀬䀭䀮䀯䀰䀱䀲䀳䀴䀵䀶䀷䀸䀹䀺䀻䀼䀽䀾䀿䁀䁁䁂䁃䁄䁅䁆䁇䁈䁉䁊䁋䁌䁍䁎䁏䁐䁑䁒䁓䁔䁕䁖䁗䁘䁙䁚䁛䁜䁝䁞䁟䁠䁡䁢䁣䁤䁥䁦䁧䁨䁩䁪䁫䁬䁭䁮䁯䁰䁱䁲䁳䁴䁵䁶䁷䁸䁹䁺䁻䁼䁽䁾䁿䂀䂁䂂䂃䂄䂅䂆䂇䂈䂉䂊䂋䂌䂍䂎䂏䂐䂑䂒䂓䂔䂕䂖䂗䂘䂙䂚䂛䂜䂝䂞䂟䂠䂡䂢䂣䂤䂥䂦䂧䂨䂩䂪䂫䂬䂭䂮䂯䂰䂱䂲䂳䂴䂵䂶䂷䂸䂹䂺䂻䂼䂽䂾䂿䃀䃁䃂䃃䃄䃅䃆䃇䃈䃉䃊䃋䃌䃍䃎䃏䃐䃑䃒䃓䃔䃕䃖䃗䃘䃙䃚䃛䃜䃝䃞䃟䃠䃡䃢䃣䃤䃥䃦䃧䃨䃩䃪䃫䃬䃭䃮䃯䃰䃱䃲䃳䃴䃵䃶䃷䃸䃹䃺䃻䃼䃽䃾䃿䄀䄁䄂䄃䄄䄅䄆䄇䄈䄉䄊䄋䄌䄍䄎䄏䄐䄑䄒䄓䄔䄕䄖䄗䄘䄙䄚䄛䄜䄝䄞䄟䄠䄡䄢䄣䄤䄥䄦䄧䄨䄩䄪䄫䄬䄭䄮䄯䄰䄱䄲䄳䄴䄵䄶䄷䄸䄹䄺䄻䄼䄽䄾䄿䅀䅁䅂䅃䅄䅅䅆䅇䅈䅉䅊䅋䅌䅍䅎䅏䅐䅑䅒䅓䅔䅕䅖䅗䅘䅙䅚䅛䅜䅝䅞䅟䅠䅡䅢䅣䅤䅥䅦䅧䅨䅩䅪䅫䅬䅭䅮䅯䅰䅱䅲䅳䅴䅵䅶䅷䅸䅹䅺䅻䅼䅽䅾䅿䆀䆁䆂䆃䆄䆅䆆䆇䆈䆉䆊䆋䆌䆍䆎䆏䆐䆑䆒䆓䆔䆕䆖䆗䆘䆙䆚䆛䆜䆝䆞䆟䆠䆡䆢䆣䆤䆥䆦䆧䆨䆩䆪䆫䆬䆭䆮䆯䆰䆱䆲䆳䆴䆵䆶䆷䆸䆹䆺䆻䆼䆽䆾䆿䇀䇁䇂䇃䇄䇅䇆䇇䇈䇉䇊䇋䇌䇍䇎䇏䇐䇑䇒䇓䇔䇕䇖䇗䇘䇙䇚䇛䇜䇝䇞䇟䇠䇡䇢䇣䇤䇥䇦䇧䇨䇩䇪䇫䇬䇭䇮䇯䇰䇱䇲䇳䇴䇵䇶䇷䇸䇹䇺䇻䇼䇽䇾䇿䈀䈁䈂䈃䈄䈅䈆䈇䈈䈉䈊䈋䈌䈍䈎䈏䈐䈑䈒䈓䈔䈕䈖䈗䈘䈙䈚䈛䈜䈝䈞䈟䈠䈡䈢䈣䈤䈥䈦䈧䈨䈩䈪䈫䈬䈭䈮䈯䈰䈱䈲䈳䈴䈵䈶䈷䈸䈹䈺䈻䈼䈽䈾䈿䉀䉁䉂䉃䉄䉅䉆䉇䉈䉉䉊䉋䉌䉍䉎䉏䉐䉑䉒䉓䉔䉕䉖䉗䉘䉙䉚䉛䉜䉝䉞䉟䉠䉡䉢䉣䉤䉥䉦䉧䉨䉩䉪䉫䉬䉭䉮䉯䉰䉱䉲䉳䉴䉵䉶䉷䉸䉹䉺䉻䉼䉽䉾䉿䊀䊁䊂䊃䊄䊅䊆䊇䊈䊉䊊䊋䊌䊍䊎䊏䊐䊑䊒䊓䊔䊕䊖䊗䊘䊙䊚䊛䊜䊝䊞䊟䊠䊡䊢䊣䊤䊥䊦䊧䊨䊩䊪䊫䊬䊭䊮䊯䊰䊱䊲䊳䊴䊵䊶䊷䊸䊹䊺䊻䊼䊽䊾䊿䋀䋁䋂䋃䋄䋅䋆䋇䋈䋉䋊䋋䋌䋍䋎䋏䋐䋑䋒䋓䋔䋕䋖䋗䋘䋙䋚䋛䋜䋝䋞䋟䋠䋡䋢䋣䋤䋥䋦䋧䋨䋩䋪䋫䋬䋭䋮䋯䋰䋱䋲䋳䋴䋵䋶䋷䋸䋹䋺䋻䋼䋽䋾䋿䌀䌁䌂䌃䌄䌅䌆䌇䌈䌉䌊䌋䌌䌍䌎䌏䌐䌑䌒䌓䌔䌕䌖䌗䌘䌙䌚䌛䌜䌝䌞䌟䌠䌡䌢䌣䌤䌥䌦䌧䌨䌩䌪䌫䌬䌭䌮䌯䌰䌱䌲䌳䌴䌵䌶䌷䌸䌹䌺䌻䌼䌽䌾䌿䍀䍁䍂䍃䍄䍅䍆䍇䍈䍉䍊䍋䍌䍍䍎䍏䍐䍑䍒䍓䍔䍕䍖䍗䍘䍙䍚䍛䍜䍝䍞䍟䍠䍡䍢䍣䍤䍥䍦䍧䍨䍩䍪䍫䍬䍭䍮䍯䍰䍱䍲䍳䍴䍵䍶䍷䍸䍹䍺䍻䍼䍽䍾䍿䎀䎁䎂䎃䎄䎅䎆䎇䎈䎉䎊䎋䎌䎍䎎䎏䎐䎑䎒䎓䎔䎕䎖䎗䎘䎙䎚䎛䎜䎝䎞䎟䎠䎡䎢䎣䎤䎥䎦䎧䎨䎩䎪䎫䎬䎭䎮䎯䎰䎱䎲䎳䎴䎵䎶䎷䎸䎹䎺䎻䎼䎽䎾䎿䏀䏁䏂䏃䏄䏅䏆䏇䏈䏉䏊䏋䏌䏍䏎䏏䏐䏑䏒䏓䏔䏕䏖䏗䏘䏙䏚䏛䏜䏝䏞䏟䏠䏡䏢䏣䏤䏥䏦䏧䏨䏩䏪䏫䏬䏭䏮䏯䏰䏱䏲䏳䏴䏵䏶䏷䏸䏹䏺䏻䏼䏽䏾䏿䐀䐁䐂䐃䐄䐅䐆䐇䐈䐉䐊䐋䐌䐍䐎䐏䐐䐑䐒䐓䐔䐕䐖䐗䐘䐙䐚䐛䐜䐝䐞䐟䐠䐡䐢䐣䐤䐥䐦䐧䐨䐩䐪䐫䐬䐭䐮䐯䐰䐱䐲䐳䐴䐵䐶䐷䐸䐹䐺䐻䐼䐽䐾䐿䑀䑁䑂䑃䑄䑅䑆䑇䑈䑉䑊䑋䑌䑍䑎䑏䑐䑑䑒䑓䑔䑕䑖䑗䑘䑙䑚䑛䑜䑝䑞䑟䑠䑡䑢䑣䑤䑥䑦䑧䑨䑩䑪䑫䑬䑭䑮䑯䑰䑱䑲䑳䑴䑵䑶䑷䑸䑹䑺䑻䑼䑽䑾䑿䒀䒁䒂䒃䒄䒅䒆䒇䒈䒉䒊䒋䒌䒍䒎䒏䒐䒑䒒䒓䒔䒕䒖䒗䒘䒙䒚䒛䒜䒝䒞䒟䒠䒡䒢䒣䒤䒥䒦䒧䒨䒩䒪䒫䒬䒭䒮䒯䒰䒱䒲䒳䒴䒵䒶䒷䒸䒹䒺䒻䒼䒽䒾䒿䓀䓁䓂䓃䓄䓅䓆䓇䓈䓉䓊䓋䓌䓍䓎䓏䓐䓑䓒䓓䓔䓕䓖䓗䓘䓙䓚䓛䓜䓝䓞䓟䓠䓡䓢䓣䓤䓥䓦䓧䓨䓩䓪䓫䓬䓭䓮䓯䓰䓱䓲䓳䓴䓵䓶䓷䓸䓹䓺䓻䓼䓽䓾䓿䔀䔁䔂䔃䔄䔅䔆䔇䔈䔉䔊䔋䔌䔍䔎䔏䔐䔑䔒䔓䔔䔕䔖䔗䔘䔙䔚䔛䔜䔝䔞䔟䔠䔡䔢䔣䔤䔥䔦䔧䔨䔩䔪䔫䔬䔭䔮䔯䔰䔱䔲䔳䔴䔵䔶䔷䔸䔹䔺䔻䔼䔽䔾䔿䕀䕁䕂䕃䕄䕅䕆䕇䕈䕉䕊䕋䕌䕍䕎䕏䕐䕑䕒䕓䕔䕕䕖䕗䕘䕙䕚䕛䕜䕝䕞䕟䕠䕡䕢䕣䕤䕥䕦䕧䕨䕩䕪䕫䕬䕭䕮䕯䕰䕱䕲䕳䕴䕵䕶䕷䕸䕹䕺䕻䕼䕽䕾䕿䖀䖁䖂䖃䖄䖅䖆䖇䖈䖉䖊䖋䖌䖍䖎䖏䖐䖑䖒䖓䖔䖕䖖䖗䖘䖙䖚䖛䖜䖝䖞䖟䖠䖡䖢䖣䖤䖥䖦䖧䖨䖩䖪䖫䖬䖭䖮䖯䖰䖱䖲䖳䖴䖵䖶䖷䖸䖹䖺䖻䖼䖽䖾䖿䗀䗁䗂䗃䗄䗅䗆䗇䗈䗉䗊䗋䗌䗍䗎䗏䗐䗑䗒䗓䗔䗕䗖䗗䗘䗙䗚䗛䗜䗝䗞䗟䗠䗡䗢䗣䗤䗥䗦䗧䗨䗩䗪䗫䗬䗭䗮䗯䗰䗱䗲䗳䗴䗵䗶䗷䗸䗹䗺䗻䗼䗽䗾䗿䘀䘁䘂䘃䘄䘅䘆䘇䘈䘉䘊䘋䘌䘍䘎䘏䘐䘑䘒䘓䘔䘕䘖䘗䘘䘙䘚䘛䘜䘝䘞䘟䘠䘡䘢䘣䘤䘥䘦䘧䘨䘩䘪䘫䘬䘭䘮䘯䘰䘱䘲䘳䘴䘵䘶䘷䘸䘹䘺䘻䘼䘽䘾䘿䙀䙁䙂䙃䙄䙅䙆䙇䙈䙉䙊䙋䙌䙍䙎䙏䙐䙑䙒䙓䙔䙕䙖䙗䙘䙙䙚䙛䙜䙝䙞䙟䙠䙡䙢䙣䙤䙥䙦䙧䙨䙩䙪䙫䙬䙭䙮䙯䙰䙱䙲䙳䙴䙵䙶䙷䙸䙹䙺䙻䙼䙽䙾䙿䚀䚁䚂䚃䚄䚅䚆䚇䚈䚉䚊䚋䚌䚍䚎䚏䚐䚑䚒䚓䚔䚕䚖䚗䚘䚙䚚䚛䚜䚝䚞䚟䚠䚡䚢䚣䚤䚥䚦䚧䚨䚩䚪䚫䚬䚭䚮䚯䚰䚱䚲䚳䚴䚵䚶䚷䚸䚹䚺䚻䚼䚽䚾䚿䛀䛁䛂䛃䛄䛅䛆䛇䛈䛉䛊䛋䛌䛍䛎䛏䛐䛑䛒䛓䛔䛕䛖䛗䛘䛙䛚䛛䛜䛝䛞䛟䛠䛡䛢䛣䛤䛥䛦䛧䛨䛩䛪䛫䛬䛭䛮䛯䛰䛱䛲䛳䛴䛵䛶䛷䛸䛹䛺䛻䛼䛽䛾䛿䜀䜁䜂䜃䜄䜅䜆䜇䜈䜉䜊䜋䜌䜍䜎䜏䜐䜑䜒䜓䜔䜕䜖䜗䜘䜙䜚䜛䜜䜝䜞䜟䜠䜡䜢䜣䜤䜥䜦䜧䜨䜩䜪䜫䜬䜭䜮䜯䜰䜱䜲䜳䜴䜵䜶䜷䜸䜹䜺䜻䜼䜽䜾䜿䝀䝁䝂䝃䝄䝅䝆䝇䝈䝉䝊䝋䝌䝍䝎䝏䝐䝑䝒䝓䝔䝕䝖䝗䝘䝙䝚䝛䝜䝝䝞䝟䝠䝡䝢䝣䝤䝥䝦䝧䝨䝩䝪䝫䝬䝭䝮䝯䝰䝱䝲䝳䝴䝵䝶䝷䝸䝹䝺䝻䝼䝽䝾䝿䞀䞁䞂䞃䞄䞅䞆䞇䞈䞉䞊䞋䞌䞍䞎䞏䞐䞑䞒䞓䞔䞕䞖䞗䞘䞙䞚䞛䞜䞝䞞䞟䞠䞡䞢䞣䞤䞥䞦䞧䞨䞩䞪䞫䞬䞭䞮䞯䞰䞱䞲䞳䞴䞵䞶䞷䞸䞹䞺䞻䞼䞽䞾䞿䟀䟁䟂䟃䟄䟅䟆䟇䟈䟉䟊䟋䟌䟍䟎䟏䟐䟑䟒䟓䟔䟕䟖䟗䟘䟙䟚䟛䟜䟝䟞䟟䟠䟡䟢䟣䟤䟥䟦䟧䟨䟩䟪䟫䟬䟭䟮䟯䟰䟱䟲䟳䟴䟵䟶䟷䟸䟹䟺䟻䟼䟽䟾䟿䠀䠁䠂䠃䠄䠅䠆䠇䠈䠉䠊䠋䠌䠍䠎䠏䠐䠑䠒䠓䠔䠕䠖䠗䠘䠙䠚䠛䠜䠝䠞䠟䠠䠡䠢䠣䠤䠥䠦䠧䠨䠩䠪䠫䠬䠭䠮䠯䠰䠱䠲䠳䠴䠵䠶䠷䠸䠹䠺䠻䠼䠽䠾䠿䡀䡁䡂䡃䡄䡅䡆䡇䡈䡉䡊䡋䡌䡍䡎䡏䡐䡑䡒䡓䡔䡕䡖䡗䡘䡙䡚䡛䡜䡝䡞䡟䡠䡡䡢䡣䡤䡥䡦䡧䡨䡩䡪䡫䡬䡭䡮䡯䡰䡱䡲䡳䡴䡵䡶䡷䡸䡹䡺䡻䡼䡽䡾䡿䢀䢁䢂䢃䢄䢅䢆䢇䢈䢉䢊䢋䢌䢍䢎䢏䢐䢑䢒䢓䢔䢕䢖䢗䢘䢙䢚䢛䢜䢝䢞䢟䢠䢡䢢䢣䢤䢥䢦䢧䢨䢩䢪䢫䢬䢭䢮䢯䢰䢱䢲䢳䢴䢵䢶䢷䢸䢹䢺䢻䢼䢽䢾䢿䣀䣁䣂䣃䣄䣅䣆䣇䣈䣉䣊䣋䣌䣍䣎䣏䣐䣑䣒䣓䣔䣕䣖䣗䣘䣙䣚䣛䣜䣝䣞䣟䣠䣡䣢䣣䣤䣥䣦䣧䣨䣩䣪䣫䣬䣭䣮䣯䣰䣱䣲䣳䣴䣵䣶䣷䣸䣹䣺䣻䣼䣽䣾䣿䤀䤁䤂䤃䤄䤅䤆䤇䤈䤉䤊䤋䤌䤍䤎䤏䤐䤑䤒䤓䤔䤕䤖䤗䤘䤙䤚䤛䤜䤝䤞䤟䤠䤡䤢䤣䤤䤥䤦䤧䤨䤩䤪䤫䤬䤭䤮䤯䤰䤱䤲䤳䤴䤵䤶䤷䤸䤹䤺䤻䤼䤽䤾䤿䥀䥁䥂䥃䥄䥅䥆䥇䥈䥉䥊䥋䥌䥍䥎䥏䥐䥑䥒䥓䥔䥕䥖䥗䥘䥙䥚䥛䥜䥝䥞䥟䥠䥡䥢䥣䥤䥥䥦䥧䥨䥩䥪䥫䥬䥭䥮䥯䥰䥱䥲䥳䥴䥵䥶䥷䥸䥹䥺䥻䥼䥽䥾䥿䦀䦁䦂䦃䦄䦅䦆䦇䦈䦉䦊䦋䦌䦍䦎䦏䦐䦑䦒䦓䦔䦕䦖䦗䦘䦙䦚䦛䦜䦝䦞䦟䦠䦡䦢䦣䦤䦥䦦䦧䦨䦩䦪䦫䦬䦭䦮䦯䦰䦱䦲䦳䦴䦵䦶䦷䦸䦹䦺䦻䦼䦽䦾䦿䧀䧁䧂䧃䧄䧅䧆䧇䧈䧉䧊䧋䧌䧍䧎䧏䧐䧑䧒䧓䧔䧕䧖䧗䧘䧙䧚䧛䧜䧝䧞䧟䧠䧡䧢䧣䧤䧥䧦䧧䧨䧩䧪䧫䧬䧭䧮䧯䧰䧱䧲䧳䧴䧵䧶䧷䧸䧹䧺䧻䧼䧽䧾䧿䨀䨁䨂䨃䨄䨅䨆䨇䨈䨉䨊䨋䨌䨍䨎䨏䨐䨑䨒䨓䨔䨕䨖䨗䨘䨙䨚䨛䨜䨝䨞䨟䨠䨡䨢䨣䨤䨥䨦䨧䨨䨩䨪䨫䨬䨭䨮䨯䨰䨱䨲䨳䨴䨵䨶䨷䨸䨹䨺䨻䨼䨽䨾䨿䩀䩁䩂䩃䩄䩅䩆䩇䩈䩉䩊䩋䩌䩍䩎䩏䩐䩑䩒䩓䩔䩕䩖䩗䩘䩙䩚䩛䩜䩝䩞䩟䩠䩡䩢䩣䩤䩥䩦䩧䩨䩩䩪䩫䩬䩭䩮䩯䩰䩱䩲䩳䩴䩵䩶䩷䩸䩹䩺䩻䩼䩽䩾䩿䪀䪁䪂䪃䪄䪅䪆䪇䪈䪉䪊䪋䪌䪍䪎䪏䪐䪑䪒䪓䪔䪕䪖䪗䪘䪙䪚䪛䪜䪝䪞䪟䪠䪡䪢䪣䪤䪥䪦䪧䪨䪩䪪䪫䪬䪭䪮䪯䪰䪱䪲䪳䪴䪵䪶䪷䪸䪹䪺䪻䪼䪽䪾䪿䫀䫁䫂䫃䫄䫅䫆䫇䫈䫉䫊䫋䫌䫍䫎䫏䫐䫑䫒䫓䫔䫕䫖䫗䫘䫙䫚䫛䫜䫝䫞䫟䫠䫡䫢䫣䫤䫥䫦䫧䫨䫩䫪䫫䫬䫭䫮䫯䫰䫱䫲䫳䫴䫵䫶䫷䫸䫹䫺䫻䫼䫽䫾䫿䬀䬁䬂䬃䬄䬅䬆䬇䬈䬉䬊䬋䬌䬍䬎䬏䬐䬑䬒䬓䬔䬕䬖䬗䬘䬙䬚䬛䬜䬝䬞䬟䬠䬡䬢䬣䬤䬥䬦䬧䬨䬩䬪䬫䬬䬭䬮䬯䬰䬱䬲䬳䬴䬵䬶䬷䬸䬹䬺䬻䬼䬽䬾䬿䭀䭁䭂䭃䭄䭅䭆䭇䭈䭉䭊䭋䭌䭍䭎䭏䭐䭑䭒䭓䭔䭕䭖䭗䭘䭙䭚䭛䭜䭝䭞䭟䭠䭡䭢䭣䭤䭥䭦䭧䭨䭩䭪䭫䭬䭭䭮䭯䭰䭱䭲䭳䭴䭵䭶䭷䭸䭹䭺䭻䭼䭽䭾䭿䮀䮁䮂䮃䮄䮅䮆䮇䮈䮉䮊䮋䮌䮍䮎䮏䮐䮑䮒䮓䮔䮕䮖䮗䮘䮙䮚䮛䮜䮝䮞䮟䮠䮡䮢䮣䮤䮥䮦䮧䮨䮩䮪䮫䮬䮭䮮䮯䮰䮱䮲䮳䮴䮵䮶䮷䮸䮹䮺䮻䮼䮽䮾䮿䯀䯁䯂䯃䯄䯅䯆䯇䯈䯉䯊䯋䯌䯍䯎䯏䯐䯑䯒䯓䯔䯕䯖䯗䯘䯙䯚䯛䯜䯝䯞䯟䯠䯡䯢䯣䯤䯥䯦䯧䯨䯩䯪䯫䯬䯭䯮䯯䯰䯱䯲䯳䯴䯵䯶䯷䯸䯹䯺䯻䯼䯽䯾䯿䰀䰁䰂䰃䰄䰅䰆䰇䰈䰉䰊䰋䰌䰍䰎䰏䰐䰑䰒䰓䰔䰕䰖䰗䰘䰙䰚䰛䰜䰝䰞䰟䰠䰡䰢䰣䰤䰥䰦䰧䰨䰩䰪䰫䰬䰭䰮䰯䰰䰱䰲䰳䰴䰵䰶䰷䰸䰹䰺䰻䰼䰽䰾䰿䱀䱁䱂䱃䱄䱅䱆䱇䱈䱉䱊䱋䱌䱍䱎䱏䱐䱑䱒䱓䱔䱕䱖䱗䱘䱙䱚䱛䱜䱝䱞䱟䱠䱡䱢䱣䱤䱥䱦䱧䱨䱩䱪䱫䱬䱭䱮䱯䱰䱱䱲䱳䱴䱵䱶䱷䱸䱹䱺䱻䱼䱽䱾䱿䲀䲁䲂䲃䲄䲅䲆䲇䲈䲉䲊䲋䲌䲍䲎䲏䲐䲑䲒䲓䲔䲕䲖䲗䲘䲙䲚䲛䲜䲝䲞䲟䲠䲡䲢䲣䲤䲥䲦䲧䲨䲩䲪䲫䲬䲭䲮䲯䲰䲱䲲䲳䲴䲵䲶䲷䲸䲹䲺䲻䲼䲽䲾䲿䳀䳁䳂䳃䳄䳅䳆䳇䳈䳉䳊䳋䳌䳍䳎䳏䳐䳑䳒䳓䳔䳕䳖䳗䳘䳙䳚䳛䳜䳝䳞䳟䳠䳡䳢䳣䳤䳥䳦䳧䳨䳩䳪䳫䳬䳭䳮䳯䳰䳱䳲䳳䳴䳵䳶䳷䳸䳹䳺䳻䳼䳽䳾䳿䴀䴁䴂䴃䴄䴅䴆䴇䴈䴉䴊䴋䴌䴍䴎䴏䴐䴑䴒䴓䴔䴕䴖䴗䴘䴙䴚䴛䴜䴝䴞䴟䴠䴡䴢䴣䴤䴥䴦䴧䴨䴩䴪䴫䴬䴭䴮䴯䴰䴱䴲䴳䴴䴵䴶䴷䴸䴹䴺䴻䴼䴽䴾䴿䵀䵁䵂䵃䵄䵅䵆䵇䵈䵉䵊䵋䵌䵍䵎䵏䵐䵑䵒䵓䵔䵕䵖䵗䵘䵙䵚䵛䵜䵝䵞䵟䵠䵡䵢䵣䵤䵥䵦䵧䵨䵩䵪䵫䵬䵭䵮䵯䵰䵱䵲䵳䵴䵵䵶䵷䵸䵹䵺䵻䵼䵽䵾䵿䶀䶁䶂䶃䶄䶅䶆䶇䶈䶉䶊䶋䶌䶍䶎䶏䶐䶑䶒䶓䶔䶕䶖䶗䶘䶙䶚䶛䶜䶝䶞䶟䶠䶡䶢䶣䶤䶥䶦䶧䶨䶩䶪䶫䶬䶭䶮䶯䶰䶱䶲䶳䶴䶵䷀䷁䷂䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌䷍䷎䷏䷐䷑䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜䷝䷞䷟䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿一丁丂七丄丅丆万丈三上下丌不与丏丐丑丒专且丕世丗丘丙业丛东丝丞丟丠両丢丣两严並丧丨丩个丫丬中丮丯丰丱串丳临丵丶丷丸丹为主丼丽举丿乀乁乂乃乄久乆乇么义乊之乌乍乎乏乐乑乒乓乔乕乖乗乘乙乚乛乜九乞也习乡乢乣乤乥书乧乨乩乪乫乬乭乮乯买乱乲乳乴乵乶乷乸乹乺乻乼乽乾乿亀亁亂亃亄亅了亇予争亊事二亍于亏亐云互亓五井亖亗亘亙亚些亜亝亞亟亠亡亢亣交亥亦产亨亩亪享京亭亮亯亰亱亲亳亴亵亶亷亸亹人亻亼亽亾亿什仁仂仃仄仅仆仇仈仉今介仌仍从仏仐仑仒仓仔仕他仗付仙仚仛仜仝仞仟仠仡仢代令以仦仧仨仩仪仫们仭仮仯仰仱仲仳仴仵件价仸仹仺任仼份仾仿伀企伂伃伄伅伆伇伈伉伊伋伌伍伎伏伐休伒伓伔伕伖众优伙会伛伜伝伞伟传伡伢伣伤伥伦伧伨伩伪伫伬伭伮伯估伱伲伳伴伵伶伷伸伹伺伻似伽伾伿佀佁佂佃佄佅但佇佈佉佊佋佌位低住佐佑佒体佔何佖佗佘余佚佛作佝佞佟你佡佢佣佤佥佦佧佨佩佪佫佬佭佮佯佰佱佲佳佴併佶佷佸佹佺佻佼佽佾使侀侁侂侃侄侅來侇侈侉侊例侌侍侎侏侐侑侒侓侔侕侖侗侘侙侚供侜依侞侟侠価侢侣侤侥侦侧侨侩侪侫侬侭侮侯侰侱侲侳侴侵侶侷侸侹侺侻侼侽侾便俀俁係促俄俅俆俇俈俉俊俋俌俍俎俏俐俑俒俓俔俕俖俗俘俙俚俛俜保俞俟俠信俢俣俤俥俦俧俨俩俪俫俬俭修俯俰俱俲俳俴俵俶俷俸俹俺俻俼俽俾俿倀倁倂倃倄倅倆倇倈倉倊個倌倍倎倏倐們倒倓倔倕倖倗倘候倚倛倜倝倞借倠倡倢倣値倥倦倧倨倩倪倫倬倭倮倯倰倱倲倳倴倵倶倷倸倹债倻值倽倾倿偀偁偂偃偄偅偆假偈偉偊偋偌偍偎偏偐偑偒偓偔偕偖偗偘偙做偛停偝偞偟偠偡偢偣偤健偦偧偨偩偪偫偬偭偮偯偰偱偲偳側偵偶偷偸偹偺偻偼偽偾偿傀傁傂傃傄傅傆傇傈傉傊傋傌傍傎傏傐傑傒傓傔傕傖傗傘備傚傛傜傝傞傟傠傡傢傣傤傥傦傧储傩傪傫催傭傮傯傰傱傲傳傴債傶傷傸傹傺傻傼傽傾傿僀僁僂僃僄僅僆僇僈僉僊僋僌働僎像僐僑僒僓僔僕僖僗僘僙僚僛僜僝僞僟僠僡僢僣僤僥僦僧僨僩僪僫僬僭僮僯僰僱僲僳僴僵僶僷僸價僺僻僼僽僾僿儀儁儂儃億儅儆儇儈儉儊儋儌儍儎儏儐儑儒儓儔儕儖儗儘儙儚儛儜儝儞償儠儡儢儣儤儥儦儧儨儩優儫儬儭儮儯儰儱儲儳儴儵儶儷儸儹儺儻儼儽儾儿兀允兂元兄充兆兇先光兊克兌免兎兏児兑兒兓兔兕兖兗兘兙党兛兜兝兞兟兠兡兢兣兤入兦內全兩兪八公六兮兯兰共兲关兴兵其具典兹兺养兼兽兾兿冀冁冂冃冄内円冇冈冉冊冋册再冎冏冐冑冒冓冔冕冖冗冘写冚军农冝冞冟冠冡冢冣冤冥冦冧冨冩冪冫冬冭冮冯冰冱冲决冴况冶冷冸冹冺冻冼冽冾冿净凁凂凃凄凅准凇凈凉凊凋凌凍凎减凐凑凒凓凔凕凖凗凘凙凚凛凜凝凞凟几凡凢凣凤凥処凧凨凩凪凫凬凭凮凯凰凱凲凳凴凵凶凷凸凹出击凼函凾凿刀刁刂刃刄刅分切刈刉刊刋刌刍刎刏刐刑划刓刔刕刖列刘则刚创刜初刞刟删刡刢刣判別刦刧刨利刪别刬刭刮刯到刱刲刳刴刵制刷券刹刺刻刼刽刾刿剀剁剂剃剄剅剆則剈剉削剋剌前剎剏剐剑剒剓剔剕剖剗剘剙剚剛剜剝剞剟剠剡剢剣剤剥剦剧剨剩剪剫剬剭剮副剰剱割剳剴創剶剷剸剹剺剻剼剽剾剿劀劁劂劃劄劅劆劇劈劉劊劋劌劍劎劏劐劑劒劓劔劕劖劗劘劙劚力劜劝办功加务劢劣劤劥劦劧动助努劫劬劭劮劯劰励劲劳労劵劶劷劸効劺劻劼劽劾势勀勁勂勃勄勅勆勇勈勉勊勋勌勍勎勏勐勑勒勓勔動勖勗勘務勚勛勜勝勞募勠勡勢勣勤勥勦勧勨勩勪勫勬勭勮勯勰勱勲勳勴勵勶勷勸勹勺勻勼勽勾勿匀匁匂匃匄包匆匇匈匉匊匋匌匍匎匏匐匑匒匓匔匕化北匘匙匚匛匜匝匞匟匠匡匢匣匤匥匦匧匨匩匪匫匬匭匮匯匰匱匲匳匴匵匶匷匸匹区医匼匽匾匿區十卂千卄卅卆升午卉半卋卌卍华协卐卑卒卓協单卖南単卙博卛卜卝卞卟占卡卢卣卤卥卦卧卨卩卪卫卬卭卮卯印危卲即却卵卶卷卸卹卺卻卼卽卾卿厀厁厂厃厄厅历厇厈厉厊压厌厍厎厏厐厑厒厓厔厕厖厗厘厙厚厛厜厝厞原厠厡厢厣厤厥厦厧厨厩厪厫厬厭厮厯厰厱厲厳厴厵厶厷厸厹厺去厼厽厾县叀叁参參叄叅叆叇又叉及友双反収叏叐发叒叓叔叕取受变叙叚叛叜叝叞叟叠叡叢口古句另叧叨叩只叫召叭叮可台叱史右叴叵叶号司叹叺叻叼叽叾叿吀吁吂吃各吅吆吇合吉吊吋同名后吏吐向吒吓吔吕吖吗吘吙吚君吜吝吞吟吠吡吢吣吤吥否吧吨吩吪含听吭吮启吰吱吲吳吴吵吶吷吸吹吺吻吼吽吾吿呀呁呂呃呄呅呆呇呈呉告呋呌呍呎呏呐呑呒呓呔呕呖呗员呙呚呛呜呝呞呟呠呡呢呣呤呥呦呧周呩呪呫呬呭呮呯呰呱呲味呴呵呶呷呸呹呺呻呼命呾呿咀咁咂咃咄咅咆咇咈咉咊咋和咍咎咏咐咑咒咓咔咕咖咗咘咙咚咛咜咝咞咟咠咡咢咣咤咥咦咧咨咩咪咫咬咭咮咯咰咱咲咳咴咵咶咷咸咹咺咻咼咽咾咿哀品哂哃哄哅哆哇哈哉哊哋哌响哎哏哐哑哒哓哔哕哖哗哘哙哚哛哜哝哞哟哠員哢哣哤哥哦哧哨哩哪哫哬哭哮哯哰哱哲哳哴哵哶哷哸哹哺哻哼哽哾哿唀唁唂唃唄唅唆唇唈唉唊唋唌唍唎唏唐唑唒唓唔唕唖唗唘唙唚唛唜唝唞唟唠唡唢唣唤唥唦唧唨唩唪唫唬唭售唯唰唱唲唳唴唵唶唷唸唹唺唻唼唽唾唿啀啁啂啃啄啅商啇啈啉啊啋啌啍啎問啐啑啒啓啔啕啖啗啘啙啚啛啜啝啞啟啠啡啢啣啤啥啦啧啨啩啪啫啬啭啮啯啰啱啲啳啴啵啶啷啸啹啺啻啼啽啾啿喀喁喂喃善喅喆喇喈喉喊喋喌喍喎喏喐喑喒喓喔喕喖喗喘喙喚喛喜喝喞喟喠喡喢喣喤喥喦喧喨喩喪喫喬喭單喯喰喱喲喳喴喵営喷喸喹喺喻喼喽喾喿嗀嗁嗂嗃嗄嗅嗆嗇嗈嗉嗊嗋嗌嗍嗎嗏嗐嗑嗒嗓嗔嗕嗖嗗嗘嗙嗚嗛嗜嗝嗞嗟嗠嗡嗢嗣嗤嗥嗦嗧嗨嗩嗪嗫嗬嗭嗮嗯嗰嗱嗲嗳嗴嗵嗶嗷嗸嗹嗺嗻嗼嗽嗾嗿嘀嘁嘂嘃嘄嘅嘆嘇嘈嘉嘊嘋嘌嘍嘎嘏嘐嘑嘒嘓嘔嘕嘖嘗嘘嘙嘚嘛嘜嘝嘞嘟嘠嘡嘢嘣嘤嘥嘦嘧嘨嘩嘪嘫嘬嘭嘮嘯嘰嘱嘲嘳嘴嘵嘶嘷嘸嘹嘺嘻嘼嘽嘾嘿噀噁噂噃噄噅噆噇噈噉噊噋噌噍噎噏噐噑噒噓噔噕噖噗噘噙噚噛噜噝噞噟噠噡噢噣噤噥噦噧器噩噪噫噬噭噮噯噰噱噲噳噴噵噶噷噸噹噺噻噼噽噾噿嚀嚁嚂嚃嚄嚅嚆嚇嚈嚉嚊嚋嚌嚍嚎嚏嚐嚑嚒嚓嚔嚕嚖嚗嚘嚙嚚嚛嚜嚝嚞嚟嚠嚡嚢嚣嚤嚥嚦嚧嚨嚩嚪嚫嚬嚭嚮嚯嚰嚱嚲嚳嚴嚵嚶嚷嚸嚹嚺嚻嚼嚽嚾嚿囀囁囂囃囄囅囆囇囈囉囊囋囌囍囎囏囐囑囒囓囔囕囖囗囘囙囚四囜囝回囟因囡团団囤囥囦囧囨囩囪囫囬园囮囯困囱囲図围囵囶囷囸囹固囻囼国图囿圀圁圂圃圄圅圆圇圈圉圊國圌圍圎圏圐圑園圓圔圕圖圗團圙圚圛圜圝圞土圠圡圢圣圤圥圦圧在圩圪圫圬圭圮圯地圱圲圳圴圵圶圷圸圹场圻圼圽圾圿址坁坂坃坄坅坆均坈坉坊坋坌坍坎坏坐坑坒坓坔坕坖块坘坙坚坛坜坝坞坟坠坡坢坣坤坥坦坧坨坩坪坫坬坭坮坯坰坱坲坳坴坵坶坷坸坹坺坻坼坽坾坿垀垁垂垃垄垅垆垇垈垉垊型垌垍垎垏垐垑垒垓垔垕垖垗垘垙垚垛垜垝垞垟垠垡垢垣垤垥垦垧垨垩垪垫垬垭垮垯垰垱垲垳垴垵垶垷垸垹垺垻垼垽垾垿埀埁埂埃埄埅埆埇埈埉埊埋埌埍城埏埐埑埒埓埔埕埖埗埘埙埚埛埜埝埞域埠埡埢埣埤埥埦埧埨埩埪埫埬埭埮埯埰埱埲埳埴埵埶執埸培基埻埼埽埾埿堀堁堂堃堄堅堆堇堈堉堊堋堌堍堎堏堐堑堒堓堔堕堖堗堘堙堚堛堜堝堞堟堠堡堢堣堤堥堦堧堨堩堪堫堬堭堮堯堰報堲堳場堵堶堷堸堹堺堻堼堽堾堿塀塁塂塃塄塅塆塇塈塉塊塋塌塍塎塏塐塑塒塓塔塕塖塗塘塙塚塛塜塝塞塟塠塡塢塣塤塥塦塧塨塩塪填塬塭塮塯塰塱塲塳塴塵塶塷塸塹塺塻塼塽塾塿墀墁墂境墄墅墆墇墈墉墊墋墌墍墎墏墐墑墒墓墔墕墖増墘墙墚墛墜墝增墟墠墡墢墣墤墥墦墧墨墩墪墫墬墭墮墯墰墱墲墳墴墵墶墷墸墹墺墻墼墽墾墿壀壁壂壃壄壅壆壇壈壉壊壋壌壍壎壏壐壑壒壓壔壕壖壗壘壙壚壛壜壝壞壟壠壡壢壣壤壥壦壧壨壩壪士壬壭壮壯声壱売壳壴壵壶壷壸壹壺壻壼壽壾壿夀夁夂夃处夅夆备夈変夊夋夌复夎夏夐夑夒夓夔夕外夗夘夙多夛夜夝夞够夠夡夢夣夤夥夦大夨天太夫夬夭央夯夰失夲夳头夵夶夷夸夹夺夻夼夽夾夿奀奁奂奃奄奅奆奇奈奉奊奋奌奍奎奏奐契奒奓奔奕奖套奘奙奚奛奜奝奞奟奠奡奢奣奤奥奦奧奨奩奪奫奬奭奮奯奰奱奲女奴奵奶奷奸她奺奻奼好奾奿妀妁如妃妄妅妆妇妈妉妊妋妌妍妎妏妐妑妒妓妔妕妖妗妘妙妚妛妜妝妞妟妠妡妢妣妤妥妦妧妨妩妪妫妬妭妮妯妰妱妲妳妴妵妶妷妸妹妺妻妼妽妾妿姀姁姂姃姄姅姆姇姈姉姊始姌姍姎姏姐姑姒姓委姕姖姗姘姙姚姛姜姝姞姟姠姡姢姣姤姥姦姧姨姩姪姫姬姭姮姯姰姱姲姳姴姵姶姷姸姹姺姻姼姽姾姿娀威娂娃娄娅娆娇娈娉娊娋娌娍娎娏娐娑娒娓娔娕娖娗娘娙娚娛娜娝娞娟娠娡娢娣娤娥娦娧娨娩娪娫娬娭娮娯娰娱娲娳娴娵娶娷娸娹娺娻娼娽娾娿婀婁婂婃婄婅婆婇婈婉婊婋婌婍婎婏婐婑婒婓婔婕婖婗婘婙婚婛婜婝婞婟婠婡婢婣婤婥婦婧婨婩婪婫婬婭婮婯婰婱婲婳婴婵婶婷婸婹婺婻婼婽婾婿媀媁媂媃媄媅媆媇媈媉媊媋媌媍媎媏媐媑媒媓媔媕媖媗媘媙媚媛媜媝媞媟媠媡媢媣媤媥媦媧媨媩媪媫媬媭媮媯媰媱媲媳媴媵媶媷媸媹媺媻媼媽媾媿嫀嫁嫂嫃嫄嫅嫆嫇嫈嫉嫊嫋嫌嫍嫎嫏嫐嫑嫒嫓嫔嫕嫖嫗嫘嫙嫚嫛嫜嫝嫞嫟嫠嫡嫢嫣嫤嫥嫦嫧嫨嫩嫪嫫嫬嫭嫮嫯嫰嫱嫲嫳嫴嫵嫶嫷嫸嫹嫺嫻嫼嫽嫾嫿嬀嬁嬂嬃嬄嬅嬆嬇嬈嬉嬊嬋嬌嬍嬎嬏嬐嬑嬒嬓嬔嬕嬖嬗嬘嬙嬚嬛嬜嬝嬞嬟嬠嬡嬢嬣嬤嬥嬦嬧嬨嬩嬪嬫嬬嬭嬮嬯嬰嬱嬲嬳嬴嬵嬶嬷嬸嬹嬺嬻嬼嬽嬾嬿孀孁孂孃孄孅孆孇孈孉孊孋孌孍孎孏子孑孒孓孔孕孖字存孙孚孛孜孝孞孟孠孡孢季孤孥学孧孨孩孪孫孬孭孮孯孰孱孲孳孴孵孶孷學孹孺孻孼孽孾孿宀宁宂它宄宅宆宇守安宊宋完宍宎宏宐宑宒宓宔宕宖宗官宙定宛宜宝实実宠审客宣室宥宦宧宨宩宪宫宬宭宮宯宰宱宲害宴宵家宷宸容宺宻宼宽宾宿寀寁寂寃寄寅密寇寈寉寊寋富寍寎寏寐寑寒寓寔寕寖寗寘寙寚寛寜寝寞察寠寡寢寣寤寥實寧寨審寪寫寬寭寮寯寰寱寲寳寴寵寶寷寸对寺寻导寽対寿尀封専尃射尅将將專尉尊尋尌對導小尐少尒尓尔尕尖尗尘尙尚尛尜尝尞尟尠尡尢尣尤尥尦尧尨尩尪尫尬尭尮尯尰就尲尳尴尵尶尷尸尹尺尻尼尽尾尿局屁层屃屄居屆屇屈屉届屋屌屍屎屏屐屑屒屓屔展屖屗屘屙屚屛屜屝属屟屠屡屢屣層履屦屧屨屩屪屫屬屭屮屯屰山屲屳屴屵屶屷屸屹屺屻屼屽屾屿岀岁岂岃岄岅岆岇岈岉岊岋岌岍岎岏岐岑岒岓岔岕岖岗岘岙岚岛岜岝岞岟岠岡岢岣岤岥岦岧岨岩岪岫岬岭岮岯岰岱岲岳岴岵岶岷岸岹岺岻岼岽岾岿峀峁峂峃峄峅峆峇峈峉峊峋峌峍峎峏峐峑峒峓峔峕峖峗峘峙峚峛峜峝峞峟峠峡峢峣峤峥峦峧峨峩峪峫峬峭峮峯峰峱峲峳峴峵島峷峸峹峺峻峼峽峾峿崀崁崂崃崄崅崆崇崈崉崊崋崌崍崎崏崐崑崒崓崔崕崖崗崘崙崚崛崜崝崞崟崠崡崢崣崤崥崦崧崨崩崪崫崬崭崮崯崰崱崲崳崴崵崶崷崸崹崺崻崼崽崾崿嵀嵁嵂嵃嵄嵅嵆嵇嵈嵉嵊嵋嵌嵍嵎嵏嵐嵑嵒嵓嵔嵕嵖嵗嵘嵙嵚嵛嵜嵝嵞嵟嵠嵡嵢嵣嵤嵥嵦嵧嵨嵩嵪嵫嵬嵭嵮嵯嵰嵱嵲嵳嵴嵵嵶嵷嵸嵹嵺嵻嵼嵽嵾嵿嶀嶁嶂嶃嶄嶅嶆嶇嶈嶉嶊嶋嶌嶍嶎嶏嶐嶑嶒嶓嶔嶕嶖嶗嶘嶙嶚嶛嶜嶝嶞嶟嶠嶡嶢嶣嶤嶥嶦嶧嶨嶩嶪嶫嶬嶭嶮嶯嶰嶱嶲嶳嶴嶵嶶嶷嶸嶹嶺嶻嶼嶽嶾嶿巀巁巂巃巄巅巆巇巈巉巊巋巌巍巎巏巐巑巒巓巔巕巖巗巘巙巚巛巜川州巟巠巡巢巣巤工左巧巨巩巪巫巬巭差巯巰己已巳巴巵巶巷巸巹巺巻巼巽巾巿帀币市布帄帅帆帇师帉帊帋希帍帎帏帐帑帒帓帔帕帖帗帘帙帚帛帜帝帞帟帠帡帢帣帤帥带帧帨帩帪師帬席帮帯帰帱帲帳帴帵帶帷常帹帺帻帼帽帾帿幀幁幂幃幄幅幆幇幈幉幊幋幌幍幎幏幐幑幒幓幔幕幖幗幘幙幚幛幜幝幞幟幠幡幢幣幤幥幦幧幨幩幪幫幬幭幮幯幰幱干平年幵并幷幸幹幺幻幼幽幾广庀庁庂広庄庅庆庇庈庉床庋庌庍庎序庐庑庒库应底庖店庘庙庚庛府庝庞废庠庡庢庣庤庥度座庨庩庪庫庬庭庮庯庰庱庲庳庴庵庶康庸庹庺庻庼庽庾庿廀廁廂廃廄廅廆廇廈廉廊廋廌廍廎廏廐廑廒廓廔廕廖廗廘廙廚廛廜廝廞廟廠廡廢廣廤廥廦廧廨廩廪廫廬廭廮廯廰廱廲廳廴廵延廷廸廹建廻廼廽廾廿开弁异弃弄弅弆弇弈弉弊弋弌弍弎式弐弑弒弓弔引弖弗弘弙弚弛弜弝弞弟张弡弢弣弤弥弦弧弨弩弪弫弬弭弮弯弰弱弲弳弴張弶強弸弹强弻弼弽弾弿彀彁彂彃彄彅彆彇彈彉彊彋彌彍彎彏彐彑归当彔录彖彗彘彙彚彛彜彝彞彟彠彡形彣彤彥彦彧彨彩彪彫彬彭彮彯彰影彲彳彴彵彶彷彸役彺彻彼彽彾彿往征徂徃径待徆徇很徉徊律後徍徎徏徐徑徒従徔徕徖得徘徙徚徛徜徝從徟徠御徢徣徤徥徦徧徨復循徫徬徭微徯徰徱徲徳徴徵徶德徸徹徺徻徼徽徾徿忀忁忂心忄必忆忇忈忉忊忋忌忍忎忏忐忑忒忓忔忕忖志忘忙忚忛応忝忞忟忠忡忢忣忤忥忦忧忨忩忪快忬忭忮忯忰忱忲忳忴念忶忷忸忹忺忻忼忽忾忿怀态怂怃怄怅怆怇怈怉怊怋怌怍怎怏怐怑怒怓怔怕怖怗怘怙怚怛怜思怞怟怠怡怢怣怤急怦性怨怩怪怫怬怭怮怯怰怱怲怳怴怵怶怷怸怹怺总怼怽怾怿恀恁恂恃恄恅恆恇恈恉恊恋恌恍恎恏恐恑恒恓恔恕恖恗恘恙恚恛恜恝恞恟恠恡恢恣恤恥恦恧恨恩恪恫恬恭恮息恰恱恲恳恴恵恶恷恸恹恺恻恼恽恾恿悀悁悂悃悄悅悆悇悈悉悊悋悌悍悎悏悐悑悒悓悔悕悖悗悘悙悚悛悜悝悞悟悠悡悢患悤悥悦悧您悩悪悫悬悭悮悯悰悱悲悳悴悵悶悷悸悹悺悻悼悽悾悿惀惁惂惃惄情惆惇惈惉惊惋惌惍惎惏惐惑惒惓惔惕惖惗惘惙惚惛惜惝惞惟惠惡惢惣惤惥惦惧惨惩惪惫惬惭惮惯惰惱惲想惴惵惶惷惸惹惺惻惼惽惾惿愀愁愂愃愄愅愆愇愈愉愊愋愌愍愎意愐愑愒愓愔愕愖愗愘愙愚愛愜愝愞感愠愡愢愣愤愥愦愧愨愩愪愫愬愭愮愯愰愱愲愳愴愵愶愷愸愹愺愻愼愽愾愿慀慁慂慃慄慅慆慇慈慉慊態慌慍慎慏慐慑慒慓慔慕慖慗慘慙慚慛慜慝慞慟慠慡慢慣慤慥慦慧慨慩慪慫慬慭慮慯慰慱慲慳慴慵慶慷慸慹慺慻慼慽慾慿憀憁憂憃憄憅憆憇憈憉憊憋憌憍憎憏憐憑憒憓憔憕憖憗憘憙憚憛憜憝憞憟憠憡憢憣憤憥憦憧憨憩憪憫憬憭憮憯憰憱憲憳憴憵憶憷憸憹憺憻憼憽憾憿懀懁懂懃懄懅懆懇懈應懊懋懌懍懎懏懐懑懒懓懔懕懖懗懘懙懚懛懜懝懞懟懠懡懢懣懤懥懦懧懨懩懪懫懬懭懮懯懰懱懲懳懴懵懶懷懸懹懺懻懼懽懾懿戀戁戂戃戄戅戆戇戈戉戊戋戌戍戎戏成我戒戓戔戕或戗战戙戚戛戜戝戞戟戠戡戢戣戤戥戦戧戨戩截戫戬戭戮戯戰戱戲戳戴戵戶户戸戹戺戻戼戽戾房所扁扂扃扄扅扆扇扈扉扊手扌才扎扏扐扑扒打扔払扖扗托扙扚扛扜扝扞扟扠扡扢扣扤扥扦执扨扩扪扫扬扭扮扯扰扱扲扳扴扵扶扷扸批扺扻扼扽找承技抁抂抃抄抅抆抇抈抉把抋抌抍抎抏抐抑抒抓抔投抖抗折抙抚抛抜抝択抟抠抡抢抣护报抦抧抨抩抪披抬抭抮抯抰抱抲抳抴抵抶抷抸抹抺抻押抽抾抿拀拁拂拃拄担拆拇拈拉拊拋拌拍拎拏拐拑拒拓拔拕拖拗拘拙拚招拜拝拞拟拠拡拢拣拤拥拦拧拨择拪拫括拭拮拯拰拱拲拳拴拵拶拷拸拹拺拻拼拽拾拿挀持挂挃挄挅挆指挈按挊挋挌挍挎挏挐挑挒挓挔挕挖挗挘挙挚挛挜挝挞挟挠挡挢挣挤挥挦挧挨挩挪挫挬挭挮振挰挱挲挳挴挵挶挷挸挹挺挻挼挽挾挿捀捁捂捃捄捅捆捇捈捉捊捋捌捍捎捏捐捑捒捓捔捕捖捗捘捙捚捛捜捝捞损捠捡换捣捤捥捦捧捨捩捪捫捬捭据捯捰捱捲捳捴捵捶捷捸捹捺捻捼捽捾捿掀掁掂掃掄掅掆掇授掉掊掋掌掍掎掏掐掑排掓掔掕掖掗掘掙掚掛掜掝掞掟掠採探掣掤接掦控推掩措掫掬掭掮掯掰掱掲掳掴掵掶掷掸掹掺掻掼掽掾掿揀揁揂揃揄揅揆揇揈揉揊揋揌揍揎描提揑插揓揔揕揖揗揘揙揚換揜揝揞揟揠握揢揣揤揥揦揧揨揩揪揫揬揭揮揯揰揱揲揳援揵揶揷揸揹揺揻揼揽揾揿搀搁搂搃搄搅搆搇搈搉搊搋搌損搎搏搐搑搒搓搔搕搖搗搘搙搚搛搜搝搞搟搠搡搢搣搤搥搦搧搨搩搪搫搬搭搮搯搰搱搲搳搴搵搶搷搸搹携搻搼搽搾搿摀摁摂摃摄摅摆摇摈摉摊摋摌摍摎摏摐摑摒摓摔摕摖摗摘摙摚摛摜摝摞摟摠摡摢摣摤摥摦摧摨摩摪摫摬摭摮摯摰摱摲摳摴摵摶摷摸摹摺摻摼摽摾摿撀撁撂撃撄撅撆撇撈撉撊撋撌撍撎撏撐撑撒撓撔撕撖撗撘撙撚撛撜撝撞撟撠撡撢撣撤撥撦撧撨撩撪撫撬播撮撯撰撱撲撳撴撵撶撷撸撹撺撻撼撽撾撿擀擁擂擃擄擅擆擇擈擉擊擋擌操擎擏擐擑擒擓擔擕擖擗擘擙據擛擜擝擞擟擠擡擢擣擤擥擦擧擨擩擪擫擬擭擮擯擰擱擲擳擴擵擶擷擸擹擺擻擼擽擾擿攀攁攂攃攄攅攆攇攈攉攊攋攌攍攎攏攐攑攒攓攔攕攖攗攘攙攚攛攜攝攞攟攠攡攢攣攤攥攦攧攨攩攪攫攬攭攮支攰攱攲攳攴攵收攷攸改攺攻攼攽放政敀敁敂敃敄故敆敇效敉敊敋敌敍敎敏敐救敒敓敔敕敖敗敘教敚敛敜敝敞敟敠敡敢散敤敥敦敧敨敩敪敫敬敭敮敯数敱敲敳整敵敶敷數敹敺敻敼敽敾敿斀斁斂斃斄斅斆文斈斉斊斋斌斍斎斏斐斑斒斓斔斕斖斗斘料斚斛斜斝斞斟斠斡斢斣斤斥斦斧斨斩斪斫斬断斮斯新斱斲斳斴斵斶斷斸方斺斻於施斾斿旀旁旂旃旄旅旆旇旈旉旊旋旌旍旎族旐旑旒旓旔旕旖旗旘旙旚旛旜旝旞旟无旡既旣旤日旦旧旨早旪旫旬旭旮旯旰旱旲旳旴旵时旷旸旹旺旻旼旽旾旿昀昁昂昃昄昅昆昇昈昉昊昋昌昍明昏昐昑昒易昔昕昖昗昘昙昚昛昜昝昞星映昡昢昣昤春昦昧昨昩昪昫昬昭昮是昰昱昲昳昴昵昶昷昸昹昺昻昼昽显昿晀晁時晃晄晅晆晇晈晉晊晋晌晍晎晏晐晑晒晓晔晕晖晗晘晙晚晛晜晝晞晟晠晡晢晣晤晥晦晧晨晩晪晫晬晭普景晰晱晲晳晴晵晶晷晸晹智晻晼晽晾晿暀暁暂暃暄暅暆暇暈暉暊暋暌暍暎暏暐暑暒暓暔暕暖暗暘暙暚暛暜暝暞暟暠暡暢暣暤暥暦暧暨暩暪暫暬暭暮暯暰暱暲暳暴暵暶暷暸暹暺暻暼暽暾暿曀曁曂曃曄曅曆曇曈曉曊曋曌曍曎曏曐曑曒曓曔曕曖曗曘曙曚曛曜曝曞曟曠曡曢曣曤曥曦曧曨曩曪曫曬曭曮曯曰曱曲曳更曵曶曷書曹曺曻曼曽曾替最朁朂會朄朅朆朇月有朊朋朌服朎朏朐朑朒朓朔朕朖朗朘朙朚望朜朝朞期朠朡朢朣朤朥朦朧木朩未末本札朮术朰朱朲朳朴朵朶朷朸朹机朻朼朽朾朿杀杁杂权杄杅杆杇杈杉杊杋杌杍李杏材村杒杓杔杕杖杗杘杙杚杛杜杝杞束杠条杢杣杤来杦杧杨杩杪杫杬杭杮杯杰東杲杳杴杵杶杷杸杹杺杻杼杽松板枀极枂枃构枅枆枇枈枉枊枋枌枍枎枏析枑枒枓枔枕枖林枘枙枚枛果枝枞枟枠枡枢枣枤枥枦枧枨枩枪枫枬枭枮枯枰枱枲枳枴枵架枷枸枹枺枻枼枽枾枿柀柁柂柃柄柅柆柇柈柉柊柋柌柍柎柏某柑柒染柔柕柖柗柘柙柚柛柜柝柞柟柠柡柢柣柤查柦柧柨柩柪柫柬柭柮柯柰柱柲柳柴柵柶柷柸柹柺査柼柽柾柿栀栁栂栃栄栅栆标栈栉栊栋栌栍栎栏栐树栒栓栔栕栖栗栘栙栚栛栜栝栞栟栠校栢栣栤栥栦栧栨栩株栫栬栭栮栯栰栱栲栳栴栵栶样核根栺栻格栽栾栿桀桁桂桃桄桅框桇案桉桊桋桌桍桎桏桐桑桒桓桔桕桖桗桘桙桚桛桜桝桞桟桠桡桢档桤桥桦桧桨桩桪桫桬桭桮桯桰桱桲桳桴桵桶桷桸桹桺桻桼桽桾桿梀梁梂梃梄梅梆梇梈梉梊梋梌梍梎梏梐梑梒梓梔梕梖梗梘梙梚梛梜條梞梟梠梡梢梣梤梥梦梧梨梩梪梫梬梭梮梯械梱梲梳梴梵梶梷梸梹梺梻梼梽梾梿检棁棂棃棄棅棆棇棈棉棊棋棌棍棎棏棐棑棒棓棔棕棖棗棘棙棚棛棜棝棞棟棠棡棢棣棤棥棦棧棨棩棪棫棬棭森棯棰棱棲棳棴棵棶棷棸棹棺棻棼棽棾棿椀椁椂椃椄椅椆椇椈椉椊椋椌植椎椏椐椑椒椓椔椕椖椗椘椙椚椛検椝椞椟椠椡椢椣椤椥椦椧椨椩椪椫椬椭椮椯椰椱椲椳椴椵椶椷椸椹椺椻椼椽椾椿楀楁楂楃楄楅楆楇楈楉楊楋楌楍楎楏楐楑楒楓楔楕楖楗楘楙楚楛楜楝楞楟楠楡楢楣楤楥楦楧楨楩楪楫楬業楮楯楰楱楲楳楴極楶楷楸楹楺楻楼楽楾楿榀榁概榃榄榅榆榇榈榉榊榋榌榍榎榏榐榑榒榓榔榕榖榗榘榙榚榛榜榝榞榟榠榡榢榣榤榥榦榧榨榩榪榫榬榭榮榯榰榱榲榳榴榵榶榷榸榹榺榻榼榽榾榿槀槁槂槃槄槅槆槇槈槉槊構槌槍槎槏槐槑槒槓槔槕槖槗様槙槚槛槜槝槞槟槠槡槢槣槤槥槦槧槨槩槪槫槬槭槮槯槰槱槲槳槴槵槶槷槸槹槺槻槼槽槾槿樀樁樂樃樄樅樆樇樈樉樊樋樌樍樎樏樐樑樒樓樔樕樖樗樘標樚樛樜樝樞樟樠模樢樣樤樥樦樧樨権横樫樬樭樮樯樰樱樲樳樴樵樶樷樸樹樺樻樼樽樾樿橀橁橂橃橄橅橆橇橈橉橊橋橌橍橎橏橐橑橒橓橔橕橖橗橘橙橚橛橜橝橞機橠橡橢橣橤橥橦橧橨橩橪橫橬橭橮橯橰橱橲橳橴橵橶橷橸橹橺橻橼橽橾橿檀檁檂檃檄檅檆檇檈檉檊檋檌檍檎檏檐檑檒檓檔檕檖檗檘檙檚檛檜檝檞檟檠檡檢檣檤檥檦檧檨檩檪檫檬檭檮檯檰檱檲檳檴檵檶檷檸檹檺檻檼檽檾檿櫀櫁櫂櫃櫄櫅櫆櫇櫈櫉櫊櫋櫌櫍櫎櫏櫐櫑櫒櫓櫔櫕櫖櫗櫘櫙櫚櫛櫜櫝櫞櫟櫠櫡櫢櫣櫤櫥櫦櫧櫨櫩櫪櫫櫬櫭櫮櫯櫰櫱櫲櫳櫴櫵櫶櫷櫸櫹櫺櫻櫼櫽櫾櫿欀欁欂欃欄欅欆欇欈欉權欋欌欍欎欏欐欑欒欓欔欕欖欗欘欙欚欛欜欝欞欟欠次欢欣欤欥欦欧欨欩欪欫欬欭欮欯欰欱欲欳欴欵欶欷欸欹欺欻欼欽款欿歀歁歂歃歄歅歆歇歈歉歊歋歌歍歎歏歐歑歒歓歔歕歖歗歘歙歚歛歜歝歞歟歠歡止正此步武歧歨歩歪歫歬歭歮歯歰歱歲歳歴歵歶歷歸歹歺死歼歽歾歿殀殁殂殃殄殅殆殇殈殉殊残殌殍殎殏殐殑殒殓殔殕殖殗殘殙殚殛殜殝殞殟殠殡殢殣殤殥殦殧殨殩殪殫殬殭殮殯殰殱殲殳殴段殶殷殸殹殺殻殼殽殾殿毀毁毂毃毄毅毆毇毈毉毊毋毌母毎每毐毑毒毓比毕毖毗毘毙毚毛毜毝毞毟毠毡毢毣毤毥毦毧毨毩毪毫毬毭毮毯毰毱毲毳毴毵毶毷毸毹毺毻毼毽毾毿氀氁氂氃氄氅氆氇氈氉氊氋氌氍氎氏氐民氒氓气氕氖気氘氙氚氛氜氝氞氟氠氡氢氣氤氥氦氧氨氩氪氫氬氭氮氯氰氱氲氳水氵氶氷永氹氺氻氼氽氾氿汀汁求汃汄汅汆汇汈汉汊汋汌汍汎汏汐汑汒汓汔汕汖汗汘汙汚汛汜汝汞江池污汢汣汤汥汦汧汨汩汪汫汬汭汮汯汰汱汲汳汴汵汶汷汸汹決汻汼汽汾汿沀沁沂沃沄沅沆沇沈沉沊沋沌沍沎沏沐沑沒沓沔沕沖沗沘沙沚沛沜沝沞沟沠没沢沣沤沥沦沧沨沩沪沫沬沭沮沯沰沱沲河沴沵沶沷沸油沺治沼沽沾沿泀況泂泃泄泅泆泇泈泉泊泋泌泍泎泏泐泑泒泓泔法泖泗泘泙泚泛泜泝泞泟泠泡波泣泤泥泦泧注泩泪泫泬泭泮泯泰泱泲泳泴泵泶泷泸泹泺泻泼泽泾泿洀洁洂洃洄洅洆洇洈洉洊洋洌洍洎洏洐洑洒洓洔洕洖洗洘洙洚洛洜洝洞洟洠洡洢洣洤津洦洧洨洩洪洫洬洭洮洯洰洱洲洳洴洵洶洷洸洹洺活洼洽派洿浀流浂浃浄浅浆浇浈浉浊测浌浍济浏浐浑浒浓浔浕浖浗浘浙浚浛浜浝浞浟浠浡浢浣浤浥浦浧浨浩浪浫浬浭浮浯浰浱浲浳浴浵浶海浸浹浺浻浼浽浾浿涀涁涂涃涄涅涆涇消涉涊涋涌涍涎涏涐涑涒涓涔涕涖涗涘涙涚涛涜涝涞涟涠涡涢涣涤涥润涧涨涩涪涫涬涭涮涯涰涱液涳涴涵涶涷涸涹涺涻涼涽涾涿淀淁淂淃淄淅淆淇淈淉淊淋淌淍淎淏淐淑淒淓淔淕淖淗淘淙淚淛淜淝淞淟淠淡淢淣淤淥淦淧淨淩淪淫淬淭淮淯淰深淲淳淴淵淶混淸淹淺添淼淽淾淿渀渁渂渃渄清渆渇済渉渊渋渌渍渎渏渐渑渒渓渔渕渖渗渘渙渚減渜渝渞渟渠渡渢渣渤渥渦渧渨温渪渫測渭渮港渰渱渲渳渴渵渶渷游渹渺渻渼渽渾渿湀湁湂湃湄湅湆湇湈湉湊湋湌湍湎湏湐湑湒湓湔湕湖湗湘湙湚湛湜湝湞湟湠湡湢湣湤湥湦湧湨湩湪湫湬湭湮湯湰湱湲湳湴湵湶湷湸湹湺湻湼湽湾湿満溁溂溃溄溅溆溇溈溉溊溋溌溍溎溏源溑溒溓溔溕準溗溘溙溚溛溜溝溞溟溠溡溢溣溤溥溦溧溨溩溪溫溬溭溮溯溰溱溲溳溴溵溶溷溸溹溺溻溼溽溾溿滀滁滂滃滄滅滆滇滈滉滊滋滌滍滎滏滐滑滒滓滔滕滖滗滘滙滚滛滜滝滞滟滠满滢滣滤滥滦滧滨滩滪滫滬滭滮滯滰滱滲滳滴滵滶滷滸滹滺滻滼滽滾滿漀漁漂漃漄漅漆漇漈漉漊漋漌漍漎漏漐漑漒漓演漕漖漗漘漙漚漛漜漝漞漟漠漡漢漣漤漥漦漧漨漩漪漫漬漭漮漯漰漱漲漳漴漵漶漷漸漹漺漻漼漽漾漿潀潁潂潃潄潅潆潇潈潉潊潋潌潍潎潏潐潑潒潓潔潕潖潗潘潙潚潛潜潝潞潟潠潡潢潣潤潥潦潧潨潩潪潫潬潭潮潯潰潱潲潳潴潵潶潷潸潹潺潻潼潽潾潿澀澁澂澃澄澅澆澇澈澉澊澋澌澍澎澏澐澑澒澓澔澕澖澗澘澙澚澛澜澝澞澟澠澡澢澣澤澥澦澧澨澩澪澫澬澭澮澯澰澱澲澳澴澵澶澷澸澹澺澻澼澽澾澿激濁濂濃濄濅濆濇濈濉濊濋濌濍濎濏濐濑濒濓濔濕濖濗濘濙濚濛濜濝濞濟濠濡濢濣濤濥濦濧濨濩濪濫濬濭濮濯濰濱濲濳濴濵濶濷濸濹濺濻濼濽濾濿瀀瀁瀂瀃瀄瀅瀆瀇瀈瀉瀊瀋瀌瀍瀎瀏瀐瀑瀒瀓瀔瀕瀖瀗瀘瀙瀚瀛瀜瀝瀞瀟瀠瀡瀢瀣瀤瀥瀦瀧瀨瀩瀪瀫瀬瀭瀮瀯瀰瀱瀲瀳瀴瀵瀶瀷瀸瀹瀺瀻瀼瀽瀾瀿灀灁灂灃灄灅灆灇灈灉灊灋灌灍灎灏灐灑灒灓灔灕灖灗灘灙灚灛灜灝灞灟灠灡灢灣灤灥灦灧灨灩灪火灬灭灮灯灰灱灲灳灴灵灶灷灸灹灺灻灼災灾灿炀炁炂炃炄炅炆炇炈炉炊炋炌炍炎炏炐炑炒炓炔炕炖炗炘炙炚炛炜炝炞炟炠炡炢炣炤炥炦炧炨炩炪炫炬炭炮炯炰炱炲炳炴炵炶炷炸点為炻炼炽炾炿烀烁烂烃烄烅烆烇烈烉烊烋烌烍烎烏烐烑烒烓烔烕烖烗烘烙烚烛烜烝烞烟烠烡烢烣烤烥烦烧烨烩烪烫烬热烮烯烰烱烲烳烴烵烶烷烸烹烺烻烼烽烾烿焀焁焂焃焄焅焆焇焈焉焊焋焌焍焎焏焐焑焒焓焔焕焖焗焘焙焚焛焜焝焞焟焠無焢焣焤焥焦焧焨焩焪焫焬焭焮焯焰焱焲焳焴焵然焷焸焹焺焻焼焽焾焿煀煁煂煃煄煅煆煇煈煉煊煋煌煍煎煏煐煑煒煓煔煕煖煗煘煙煚煛煜煝煞煟煠煡煢煣煤煥煦照煨煩煪煫煬煭煮煯煰煱煲煳煴煵煶煷煸煹煺煻煼煽煾煿熀熁熂熃熄熅熆熇熈熉熊熋熌熍熎熏熐熑熒熓熔熕熖熗熘熙熚熛熜熝熞熟熠熡熢熣熤熥熦熧熨熩熪熫熬熭熮熯熰熱熲熳熴熵熶熷熸熹熺熻熼熽熾熿燀燁燂燃燄燅燆燇燈燉燊燋燌燍燎燏燐燑燒燓燔燕燖燗燘燙燚燛燜燝燞營燠燡燢燣燤燥燦燧燨燩燪燫燬燭燮燯燰燱燲燳燴燵燶燷燸燹燺燻燼燽燾燿爀爁爂爃爄爅爆爇爈爉爊爋爌爍爎爏爐爑爒爓爔爕爖爗爘爙爚爛爜爝爞爟爠爡爢爣爤爥爦爧爨爩爪爫爬爭爮爯爰爱爲爳爴爵父爷爸爹爺爻爼爽爾爿牀牁牂牃牄牅牆片版牉牊牋牌牍牎牏牐牑牒牓牔牕牖牗牘牙牚牛牜牝牞牟牠牡牢牣牤牥牦牧牨物牪牫牬牭牮牯牰牱牲牳牴牵牶牷牸特牺牻牼牽牾牿犀犁犂犃犄犅犆犇犈犉犊犋犌犍犎犏犐犑犒犓犔犕犖犗犘犙犚犛犜犝犞犟犠犡犢犣犤犥犦犧犨犩犪犫犬犭犮犯犰犱犲犳犴犵状犷犸犹犺犻犼犽犾犿狀狁狂狃狄狅狆狇狈狉狊狋狌狍狎狏狐狑狒狓狔狕狖狗狘狙狚狛狜狝狞狟狠狡狢狣狤狥狦狧狨狩狪狫独狭狮狯狰狱狲狳狴狵狶狷狸狹狺狻狼狽狾狿猀猁猂猃猄猅猆猇猈猉猊猋猌猍猎猏猐猑猒猓猔猕猖猗猘猙猚猛猜猝猞猟猠猡猢猣猤猥猦猧猨猩猪猫猬猭献猯猰猱猲猳猴猵猶猷猸猹猺猻猼猽猾猿獀獁獂獃獄獅獆獇獈獉獊獋獌獍獎獏獐獑獒獓獔獕獖獗獘獙獚獛獜獝獞獟獠獡獢獣獤獥獦獧獨獩獪獫獬獭獮獯獰獱獲獳獴獵獶獷獸獹獺獻獼獽獾獿玀玁玂玃玄玅玆率玈玉玊王玌玍玎玏玐玑玒玓玔玕玖玗玘玙玚玛玜玝玞玟玠玡玢玣玤玥玦玧玨玩玪玫玬玭玮环现玱玲玳玴玵玶玷玸玹玺玻玼玽玾玿珀珁珂珃珄珅珆珇珈珉珊珋珌珍珎珏珐珑珒珓珔珕珖珗珘珙珚珛珜珝珞珟珠珡珢珣珤珥珦珧珨珩珪珫珬班珮珯珰珱珲珳珴珵珶珷珸珹珺珻珼珽現珿琀琁琂球琄琅理琇琈琉琊琋琌琍琎琏琐琑琒琓琔琕琖琗琘琙琚琛琜琝琞琟琠琡琢琣琤琥琦琧琨琩琪琫琬琭琮琯琰琱琲琳琴琵琶琷琸琹琺琻琼琽琾琿瑀瑁瑂瑃瑄瑅瑆瑇瑈瑉瑊瑋瑌瑍瑎瑏瑐瑑瑒瑓瑔瑕瑖瑗瑘瑙瑚瑛瑜瑝瑞瑟瑠瑡瑢瑣瑤瑥瑦瑧瑨瑩瑪瑫瑬瑭瑮瑯瑰瑱瑲瑳瑴瑵瑶瑷瑸瑹瑺瑻瑼瑽瑾瑿璀璁璂璃璄璅璆璇璈璉璊璋璌璍璎璏璐璑璒璓璔璕璖璗璘璙璚璛璜璝璞璟璠璡璢璣璤璥璦璧璨璩璪璫璬璭璮璯環璱璲璳璴璵璶璷璸璹璺璻璼璽璾璿瓀瓁瓂瓃瓄瓅瓆瓇瓈瓉瓊瓋瓌瓍瓎瓏瓐瓑瓒瓓瓔瓕瓖瓗瓘瓙瓚瓛瓜瓝瓞瓟瓠瓡瓢瓣瓤瓥瓦瓧瓨瓩瓪瓫瓬瓭瓮瓯瓰瓱瓲瓳瓴瓵瓶瓷瓸瓹瓺瓻瓼瓽瓾瓿甀甁甂甃甄甅甆甇甈甉甊甋甌甍甎甏甐甑甒甓甔甕甖甗甘甙甚甛甜甝甞生甠甡產産甤甥甦甧用甩甪甫甬甭甮甯田由甲申甴电甶男甸甹町画甼甽甾甿畀畁畂畃畄畅畆畇畈畉畊畋界畍畎畏畐畑畒畓畔畕畖畗畘留畚畛畜畝畞畟畠畡畢畣畤略畦畧畨畩番畫畬畭畮畯異畱畲畳畴畵當畷畸畹畺畻畼畽畾畿疀疁疂疃疄疅疆疇疈疉疊疋疌疍疎疏疐疑疒疓疔疕疖疗疘疙疚疛疜疝疞疟疠疡疢疣疤疥疦疧疨疩疪疫疬疭疮疯疰疱疲疳疴疵疶疷疸疹疺疻疼疽疾疿痀痁痂痃痄病痆症痈痉痊痋痌痍痎痏痐痑痒痓痔痕痖痗痘痙痚痛痜痝痞痟痠痡痢痣痤痥痦痧痨痩痪痫痬痭痮痯痰痱痲痳痴痵痶痷痸痹痺痻痼痽痾痿瘀瘁瘂瘃瘄瘅瘆瘇瘈瘉瘊瘋瘌瘍瘎瘏瘐瘑瘒瘓瘔瘕瘖瘗瘘瘙瘚瘛瘜瘝瘞瘟瘠瘡瘢瘣瘤瘥瘦瘧瘨瘩瘪瘫瘬瘭瘮瘯瘰瘱瘲瘳瘴瘵瘶瘷瘸瘹瘺瘻瘼瘽瘾瘿癀癁療癃癄癅癆癇癈癉癊癋癌癍癎癏癐癑癒癓癔癕癖癗癘癙癚癛癜癝癞癟癠癡癢癣癤癥癦癧癨癩癪癫癬癭癮癯癰癱癲癳癴癵癶癷癸癹発登發白百癿皀皁皂皃的皅皆皇皈皉皊皋皌皍皎皏皐皑皒皓皔皕皖皗皘皙皚皛皜皝皞皟皠皡皢皣皤皥皦皧皨皩皪皫皬皭皮皯皰皱皲皳皴皵皶皷皸皹皺皻皼皽皾皿盀盁盂盃盄盅盆盇盈盉益盋盌盍盎盏盐监盒盓盔盕盖盗盘盙盚盛盜盝盞盟盠盡盢監盤盥盦盧盨盩盪盫盬盭目盯盰盱盲盳直盵盶盷相盹盺盻盼盽盾盿眀省眂眃眄眅眆眇眈眉眊看県眍眎眏眐眑眒眓眔眕眖眗眘眙眚眛眜眝眞真眠眡眢眣眤眥眦眧眨眩眪眫眬眭眮眯眰眱眲眳眴眵眶眷眸眹眺眻眼眽眾眿着睁睂睃睄睅睆睇睈睉睊睋睌睍睎睏睐睑睒睓睔睕睖睗睘睙睚睛睜睝睞睟睠睡睢督睤睥睦睧睨睩睪睫睬睭睮睯睰睱睲睳睴睵睶睷睸睹睺睻睼睽睾睿瞀瞁瞂瞃瞄瞅瞆瞇瞈瞉瞊瞋瞌瞍瞎瞏瞐瞑瞒瞓瞔瞕瞖瞗瞘瞙瞚瞛瞜瞝瞞瞟瞠瞡瞢瞣瞤瞥瞦瞧瞨瞩瞪瞫瞬瞭瞮瞯瞰瞱瞲瞳瞴瞵瞶瞷瞸瞹瞺瞻瞼瞽瞾瞿矀矁矂矃矄矅矆矇矈矉矊矋矌矍矎矏矐矑矒矓矔矕矖矗矘矙矚矛矜矝矞矟矠矡矢矣矤知矦矧矨矩矪矫矬短矮矯矰矱矲石矴矵矶矷矸矹矺矻矼矽矾矿砀码砂砃砄砅砆砇砈砉砊砋砌砍砎砏砐砑砒砓研砕砖砗砘砙砚砛砜砝砞砟砠砡砢砣砤砥砦砧砨砩砪砫砬砭砮砯砰砱砲砳破砵砶砷砸砹砺砻砼砽砾砿础硁硂硃硄硅硆硇硈硉硊硋硌硍硎硏硐硑硒硓硔硕硖硗硘硙硚硛硜硝硞硟硠硡硢硣硤硥硦硧硨硩硪硫硬硭确硯硰硱硲硳硴硵硶硷硸硹硺硻硼硽硾硿碀碁碂碃碄碅碆碇碈碉碊碋碌碍碎碏碐碑碒碓碔碕碖碗碘碙碚碛碜碝碞碟碠碡碢碣碤碥碦碧碨碩碪碫碬碭碮碯碰碱碲碳碴碵碶碷碸碹確碻碼碽碾碿磀磁磂磃磄磅磆磇磈磉磊磋磌磍磎磏磐磑磒磓磔磕磖磗磘磙磚磛磜磝磞磟磠磡磢磣磤磥磦磧磨磩磪磫磬磭磮磯磰磱磲磳磴磵磶磷磸磹磺磻磼磽磾磿礀礁礂礃礄礅礆礇礈礉礊礋礌礍礎礏礐礑礒礓礔礕礖礗礘礙礚礛礜礝礞礟礠礡礢礣礤礥礦礧礨礩礪礫礬礭礮礯礰礱礲礳礴礵礶礷礸礹示礻礼礽社礿祀祁祂祃祄祅祆祇祈祉祊祋祌祍祎祏祐祑祒祓祔祕祖祗祘祙祚祛祜祝神祟祠祡祢祣祤祥祦祧票祩祪祫祬祭祮祯祰祱祲祳祴祵祶祷祸祹祺祻祼祽祾祿禀禁禂禃禄禅禆禇禈禉禊禋禌禍禎福禐禑禒禓禔禕禖禗禘禙禚禛禜禝禞禟禠禡禢禣禤禥禦禧禨禩禪禫禬禭禮禯禰禱禲禳禴禵禶禷禸禹禺离禼禽禾禿秀私秂秃秄秅秆秇秈秉秊秋秌种秎秏秐科秒秓秔秕秖秗秘秙秚秛秜秝秞租秠秡秢秣秤秥秦秧秨秩秪秫秬秭秮积称秱秲秳秴秵秶秷秸秹秺移秼秽秾秿稀稁稂稃稄稅稆稇稈稉稊程稌稍税稏稐稑稒稓稔稕稖稗稘稙稚稛稜稝稞稟稠稡稢稣稤稥稦稧稨稩稪稫稬稭種稯稰稱稲稳稴稵稶稷稸稹稺稻稼稽稾稿穀穁穂穃穄穅穆穇穈穉穊穋穌積穎穏穐穑穒穓穔穕穖穗穘穙穚穛穜穝穞穟穠穡穢穣穤穥穦穧穨穩穪穫穬穭穮穯穰穱穲穳穴穵究穷穸穹空穻穼穽穾穿窀突窂窃窄窅窆窇窈窉窊窋窌窍窎窏窐窑窒窓窔窕窖窗窘窙窚窛窜窝窞窟窠窡窢窣窤窥窦窧窨窩窪窫窬窭窮窯窰窱窲窳窴窵窶窷窸窹窺窻窼窽窾窿竀竁竂竃竄竅竆竇竈竉竊立竌竍竎竏竐竑竒竓竔竕竖竗竘站竚竛竜竝竞竟章竡竢竣竤童竦竧竨竩竪竫竬竭竮端竰竱竲竳竴竵競竷竸竹竺竻竼竽竾竿笀笁笂笃笄笅笆笇笈笉笊笋笌笍笎笏笐笑笒笓笔笕笖笗笘笙笚笛笜笝笞笟笠笡笢笣笤笥符笧笨笩笪笫第笭笮笯笰笱笲笳笴笵笶笷笸笹笺笻笼笽笾笿筀筁筂筃筄筅筆筇筈等筊筋筌筍筎筏筐筑筒筓答筕策筗筘筙筚筛筜筝筞筟筠筡筢筣筤筥筦筧筨筩筪筫筬筭筮筯筰筱筲筳筴筵筶筷筸筹筺筻筼筽签筿简箁箂箃箄箅箆箇箈箉箊箋箌箍箎箏箐箑箒箓箔箕箖算箘箙箚箛箜箝箞箟箠管箢箣箤箥箦箧箨箩箪箫箬箭箮箯箰箱箲箳箴箵箶箷箸箹箺箻箼箽箾箿節篁篂篃範篅篆篇篈築篊篋篌篍篎篏篐篑篒篓篔篕篖篗篘篙篚篛篜篝篞篟篠篡篢篣篤篥篦篧篨篩篪篫篬篭篮篯篰篱篲篳篴篵篶篷篸篹篺篻篼篽篾篿簀簁簂簃簄簅簆簇簈簉簊簋簌簍簎簏簐簑簒簓簔簕簖簗簘簙簚簛簜簝簞簟簠簡簢簣簤簥簦簧簨簩簪簫簬簭簮簯簰簱簲簳簴簵簶簷簸簹簺簻簼簽簾簿籀籁籂籃籄籅籆籇籈籉籊籋籌籍籎籏籐籑籒籓籔籕籖籗籘籙籚籛籜籝籞籟籠籡籢籣籤籥籦籧籨籩籪籫籬籭籮籯籰籱籲米籴籵籶籷籸籹籺类籼籽籾籿粀粁粂粃粄粅粆粇粈粉粊粋粌粍粎粏粐粑粒粓粔粕粖粗粘粙粚粛粜粝粞粟粠粡粢粣粤粥粦粧粨粩粪粫粬粭粮粯粰粱粲粳粴粵粶粷粸粹粺粻粼粽精粿糀糁糂糃糄糅糆糇糈糉糊糋糌糍糎糏糐糑糒糓糔糕糖糗糘糙糚糛糜糝糞糟糠糡糢糣糤糥糦糧糨糩糪糫糬糭糮糯糰糱糲糳糴糵糶糷糸糹糺系糼糽糾糿紀紁紂紃約紅紆紇紈紉紊紋紌納紎紏紐紑紒紓純紕紖紗紘紙級紛紜紝紞紟素紡索紣紤紥紦紧紨紩紪紫紬紭紮累細紱紲紳紴紵紶紷紸紹紺紻紼紽紾紿絀絁終絃組絅絆絇絈絉絊絋経絍絎絏結絑絒絓絔絕絖絗絘絙絚絛絜絝絞絟絠絡絢絣絤絥給絧絨絩絪絫絬絭絮絯絰統絲絳絴絵絶絷絸絹絺絻絼絽絾絿綀綁綂綃綄綅綆綇綈綉綊綋綌綍綎綏綐綑綒經綔綕綖綗綘継続綛綜綝綞綟綠綡綢綣綤綥綦綧綨綩綪綫綬維綮綯綰綱網綳綴綵綶綷綸綹綺綻綼綽綾綿緀緁緂緃緄緅緆緇緈緉緊緋緌緍緎総緐緑緒緓緔緕緖緗緘緙線緛緜緝緞緟締緡緢緣緤緥緦緧編緩緪緫緬緭緮緯緰緱緲緳練緵緶緷緸緹緺緻緼緽緾緿縀縁縂縃縄縅縆縇縈縉縊縋縌縍縎縏縐縑縒縓縔縕縖縗縘縙縚縛縜縝縞縟縠縡縢縣縤縥縦縧縨縩縪縫縬縭縮縯縰縱縲縳縴縵縶縷縸縹縺縻縼總績縿繀繁繂繃繄繅繆繇繈繉繊繋繌繍繎繏繐繑繒繓織繕繖繗繘繙繚繛繜繝繞繟繠繡繢繣繤繥繦繧繨繩繪繫繬繭繮繯繰繱繲繳繴繵繶繷繸繹繺繻繼繽繾繿纀纁纂纃纄纅纆纇纈纉纊纋續纍纎纏纐纑纒纓纔纕纖纗纘纙纚纛纜纝纞纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵缶缷缸缹缺缻缼缽缾缿罀罁罂罃罄罅罆罇罈罉罊罋罌罍罎罏罐网罒罓罔罕罖罗罘罙罚罛罜罝罞罟罠罡罢罣罤罥罦罧罨罩罪罫罬罭置罯罰罱署罳罴罵罶罷罸罹罺罻罼罽罾罿羀羁羂羃羄羅羆羇羈羉羊羋羌羍美羏羐羑羒羓羔羕羖羗羘羙羚羛羜羝羞羟羠羡羢羣群羥羦羧羨義羪羫羬羭羮羯羰羱羲羳羴羵羶羷羸羹羺羻羼羽羾羿翀翁翂翃翄翅翆翇翈翉翊翋翌翍翎翏翐翑習翓翔翕翖翗翘翙翚翛翜翝翞翟翠翡翢翣翤翥翦翧翨翩翪翫翬翭翮翯翰翱翲翳翴翵翶翷翸翹翺翻翼翽翾翿耀老耂考耄者耆耇耈耉耊耋而耍耎耏耐耑耒耓耔耕耖耗耘耙耚耛耜耝耞耟耠耡耢耣耤耥耦耧耨耩耪耫耬耭耮耯耰耱耲耳耴耵耶耷耸耹耺耻耼耽耾耿聀聁聂聃聄聅聆聇聈聉聊聋职聍聎聏聐聑聒聓联聕聖聗聘聙聚聛聜聝聞聟聠聡聢聣聤聥聦聧聨聩聪聫聬聭聮聯聰聱聲聳聴聵聶職聸聹聺聻聼聽聾聿肀肁肂肃肄肅肆肇肈肉肊肋肌肍肎肏肐肑肒肓肔肕肖肗肘肙肚肛肜肝肞肟肠股肢肣肤肥肦肧肨肩肪肫肬肭肮肯肰肱育肳肴肵肶肷肸肹肺肻肼肽肾肿胀胁胂胃胄胅胆胇胈胉胊胋背胍胎胏胐胑胒胓胔胕胖胗胘胙胚胛胜胝胞胟胠胡胢胣胤胥胦胧胨胩胪胫胬胭胮胯胰胱胲胳胴胵胶胷胸胹胺胻胼能胾胿脀脁脂脃脄脅脆脇脈脉脊脋脌脍脎脏脐脑脒脓脔脕脖脗脘脙脚脛脜脝脞脟脠脡脢脣脤脥脦脧脨脩脪脫脬脭脮脯脰脱脲脳脴脵脶脷脸脹脺脻脼脽脾脿腀腁腂腃腄腅腆腇腈腉腊腋腌腍腎腏腐腑腒腓腔腕腖腗腘腙腚腛腜腝腞腟腠腡腢腣腤腥腦腧腨腩腪腫腬腭腮腯腰腱腲腳腴腵腶腷腸腹腺腻腼腽腾腿膀膁膂膃膄膅膆膇膈膉膊膋膌膍膎膏膐膑膒膓膔膕膖膗膘膙膚膛膜膝膞膟膠膡膢膣膤膥膦膧膨膩膪膫膬膭膮膯膰膱膲膳膴膵膶膷膸膹膺膻膼膽膾膿臀臁臂臃臄臅臆臇臈臉臊臋臌臍臎臏臐臑臒臓臔臕臖臗臘臙臚臛臜臝臞臟臠臡臢臣臤臥臦臧臨臩自臫臬臭臮臯臰臱臲至致臵臶臷臸臹臺臻臼臽臾臿舀舁舂舃舄舅舆與興舉舊舋舌舍舎舏舐舑舒舓舔舕舖舗舘舙舚舛舜舝舞舟舠舡舢舣舤舥舦舧舨舩航舫般舭舮舯舰舱舲舳舴舵舶舷舸船舺舻舼舽舾舿艀艁艂艃艄艅艆艇艈艉艊艋艌艍艎艏艐艑艒艓艔艕艖艗艘艙艚艛艜艝艞艟艠艡艢艣艤艥艦艧艨艩艪艫艬艭艮良艰艱色艳艴艵艶艷艸艹艺艻艼艽艾艿芀芁节芃芄芅芆芇芈芉芊芋芌芍芎芏芐芑芒芓芔芕芖芗芘芙芚芛芜芝芞芟芠芡芢芣芤芥芦芧芨芩芪芫芬芭芮芯芰花芲芳芴芵芶芷芸芹芺芻芼芽芾芿苀苁苂苃苄苅苆苇苈苉苊苋苌苍苎苏苐苑苒苓苔苕苖苗苘苙苚苛苜苝苞苟苠苡苢苣苤若苦苧苨苩苪苫苬苭苮苯苰英苲苳苴苵苶苷苸苹苺苻苼苽苾苿茀茁茂范茄茅茆茇茈茉茊茋茌茍茎茏茐茑茒茓茔茕茖茗茘茙茚茛茜茝茞茟茠茡茢茣茤茥茦茧茨茩茪茫茬茭茮茯茰茱茲茳茴茵茶茷茸茹茺茻茼茽茾茿荀荁荂荃荄荅荆荇荈草荊荋荌荍荎荏荐荑荒荓荔荕荖荗荘荙荚荛荜荝荞荟荠荡荢荣荤荥荦荧荨荩荪荫荬荭荮药荰荱荲荳荴荵荶荷荸荹荺荻荼荽荾荿莀莁莂莃莄莅莆莇莈莉莊莋莌莍莎莏莐莑莒莓莔莕莖莗莘莙莚莛莜莝莞莟莠莡莢莣莤莥莦莧莨莩莪莫莬莭莮莯莰莱莲莳莴莵莶获莸莹莺莻莼莽莾莿菀菁菂菃菄菅菆菇菈菉菊菋菌菍菎菏菐菑菒菓菔菕菖菗菘菙菚菛菜菝菞菟菠菡菢菣菤菥菦菧菨菩菪菫菬菭菮華菰菱菲菳菴菵菶菷菸菹菺菻菼菽菾菿萀萁萂萃萄萅萆萇萈萉萊萋萌萍萎萏萐萑萒萓萔萕萖萗萘萙萚萛萜萝萞萟萠萡萢萣萤营萦萧萨萩萪萫萬萭萮萯萰萱萲萳萴萵萶萷萸萹萺萻萼落萾萿葀葁葂葃葄葅葆葇葈葉葊葋葌葍葎葏葐葑葒葓葔葕葖著葘葙葚葛葜葝葞葟葠葡葢董葤葥葦葧葨葩葪葫葬葭葮葯葰葱葲葳葴葵葶葷葸葹葺葻葼葽葾葿蒀蒁蒂蒃蒄蒅蒆蒇蒈蒉蒊蒋蒌蒍蒎蒏蒐蒑蒒蒓蒔蒕蒖蒗蒘蒙蒚蒛蒜蒝蒞蒟蒠蒡蒢蒣蒤蒥蒦蒧蒨蒩蒪蒫蒬蒭蒮蒯蒰蒱蒲蒳蒴蒵蒶蒷蒸蒹蒺蒻蒼蒽蒾蒿蓀蓁蓂蓃蓄蓅蓆蓇蓈蓉蓊蓋蓌蓍蓎蓏蓐蓑蓒蓓蓔蓕蓖蓗蓘蓙蓚蓛蓜蓝蓞蓟蓠蓡蓢蓣蓤蓥蓦蓧蓨蓩蓪蓫蓬蓭蓮蓯蓰蓱蓲蓳蓴蓵蓶蓷蓸蓹蓺蓻蓼蓽蓾蓿蔀蔁蔂蔃蔄蔅蔆蔇蔈蔉蔊蔋蔌蔍蔎蔏蔐蔑蔒蔓蔔蔕蔖蔗蔘蔙蔚蔛蔜蔝蔞蔟蔠蔡蔢蔣蔤蔥蔦蔧蔨蔩蔪蔫蔬蔭蔮蔯蔰蔱蔲蔳蔴蔵蔶蔷蔸蔹蔺蔻蔼蔽蔾蔿蕀蕁蕂蕃蕄蕅蕆蕇蕈蕉蕊蕋蕌蕍蕎蕏蕐蕑蕒蕓蕔蕕蕖蕗蕘蕙蕚蕛蕜蕝蕞蕟蕠蕡蕢蕣蕤蕥蕦蕧蕨蕩蕪蕫蕬蕭蕮蕯蕰蕱蕲蕳蕴蕵蕶蕷蕸蕹蕺蕻蕼蕽蕾蕿薀薁薂薃薄薅薆薇薈薉薊薋薌薍薎薏薐薑薒薓薔薕薖薗薘薙薚薛薜薝薞薟薠薡薢薣薤薥薦薧薨薩薪薫薬薭薮薯薰薱薲薳薴薵薶薷薸薹薺薻薼薽薾薿藀藁藂藃藄藅藆藇藈藉藊藋藌藍藎藏藐藑藒藓藔藕藖藗藘藙藚藛藜藝藞藟藠藡藢藣藤藥藦藧藨藩藪藫藬藭藮藯藰藱藲藳藴藵藶藷藸藹藺藻藼藽藾藿蘀蘁蘂蘃蘄蘅蘆蘇蘈蘉蘊蘋蘌蘍蘎蘏蘐蘑蘒蘓蘔蘕蘖蘗蘘蘙蘚蘛蘜蘝蘞蘟蘠蘡蘢蘣蘤蘥蘦蘧蘨蘩蘪蘫蘬蘭蘮蘯蘰蘱蘲蘳蘴蘵蘶蘷蘸蘹蘺蘻蘼蘽蘾蘿虀虁虂虃虄虅虆虇虈虉虊虋虌虍虎虏虐虑虒虓虔處虖虗虘虙虚虛虜虝虞號虠虡虢虣虤虥虦虧虨虩虪虫虬虭虮虯虰虱虲虳虴虵虶虷虸虹虺虻虼虽虾虿蚀蚁蚂蚃蚄蚅蚆蚇蚈蚉蚊蚋蚌蚍蚎蚏蚐蚑蚒蚓蚔蚕蚖蚗蚘蚙蚚蚛蚜蚝蚞蚟蚠蚡蚢蚣蚤蚥蚦蚧蚨蚩蚪蚫蚬蚭蚮蚯蚰蚱蚲蚳蚴蚵蚶蚷蚸蚹蚺蚻蚼蚽蚾蚿蛀蛁蛂蛃蛄蛅蛆蛇蛈蛉蛊蛋蛌蛍蛎蛏蛐蛑蛒蛓蛔蛕蛖蛗蛘蛙蛚蛛蛜蛝蛞蛟蛠蛡蛢蛣蛤蛥蛦蛧蛨蛩蛪蛫蛬蛭蛮蛯蛰蛱蛲蛳蛴蛵蛶蛷蛸蛹蛺蛻蛼蛽蛾蛿蜀蜁蜂蜃蜄蜅蜆蜇蜈蜉蜊蜋蜌蜍蜎蜏蜐蜑蜒蜓蜔蜕蜖蜗蜘蜙蜚蜛蜜蜝蜞蜟蜠蜡蜢蜣蜤蜥蜦蜧蜨蜩蜪蜫蜬蜭蜮蜯蜰蜱蜲蜳蜴蜵蜶蜷蜸蜹蜺蜻蜼蜽蜾蜿蝀蝁蝂蝃蝄蝅蝆蝇蝈蝉蝊蝋蝌蝍蝎蝏蝐蝑蝒蝓蝔蝕蝖蝗蝘蝙蝚蝛蝜蝝蝞蝟蝠蝡蝢蝣蝤蝥蝦蝧蝨蝩蝪蝫蝬蝭蝮蝯蝰蝱蝲蝳蝴蝵蝶蝷蝸蝹蝺蝻蝼蝽蝾蝿螀螁螂螃螄螅螆螇螈螉螊螋螌融螎螏螐螑螒螓螔螕螖螗螘螙螚螛螜螝螞螟螠螡螢螣螤螥螦螧螨螩螪螫螬螭螮螯螰螱螲螳螴螵螶螷螸螹螺螻螼螽螾螿蟀蟁蟂蟃蟄蟅蟆蟇蟈蟉蟊蟋蟌蟍蟎蟏蟐蟑蟒蟓蟔蟕蟖蟗蟘蟙蟚蟛蟜蟝蟞蟟蟠蟡蟢蟣蟤蟥蟦蟧蟨蟩蟪蟫蟬蟭蟮蟯蟰蟱蟲蟳蟴蟵蟶蟷蟸蟹蟺蟻蟼蟽蟾蟿蠀蠁蠂蠃蠄蠅蠆蠇蠈蠉蠊蠋蠌蠍蠎蠏蠐蠑蠒蠓蠔蠕蠖蠗蠘蠙蠚蠛蠜蠝蠞蠟蠠蠡蠢蠣蠤蠥蠦蠧蠨蠩蠪蠫蠬蠭蠮蠯蠰蠱蠲蠳蠴蠵蠶蠷蠸蠹蠺蠻蠼蠽蠾蠿血衁衂衃衄衅衆衇衈衉衊衋行衍衎衏衐衑衒術衔衕衖街衘衙衚衛衜衝衞衟衠衡衢衣衤补衦衧表衩衪衫衬衭衮衯衰衱衲衳衴衵衶衷衸衹衺衻衼衽衾衿袀袁袂袃袄袅袆袇袈袉袊袋袌袍袎袏袐袑袒袓袔袕袖袗袘袙袚袛袜袝袞袟袠袡袢袣袤袥袦袧袨袩袪被袬袭袮袯袰袱袲袳袴袵袶袷袸袹袺袻袼袽袾袿裀裁裂裃裄装裆裇裈裉裊裋裌裍裎裏裐裑裒裓裔裕裖裗裘裙裚裛補裝裞裟裠裡裢裣裤裥裦裧裨裩裪裫裬裭裮裯裰裱裲裳裴裵裶裷裸裹裺裻裼製裾裿褀褁褂褃褄褅褆複褈褉褊褋褌褍褎褏褐褑褒褓褔褕褖褗褘褙褚褛褜褝褞褟褠褡褢褣褤褥褦褧褨褩褪褫褬褭褮褯褰褱褲褳褴褵褶褷褸褹褺褻褼褽褾褿襀襁襂襃襄襅襆襇襈襉襊襋襌襍襎襏襐襑襒襓襔襕襖襗襘襙襚襛襜襝襞襟襠襡襢襣襤襥襦襧襨襩襪襫襬襭襮襯襰襱襲襳襴襵襶襷襸襹襺襻襼襽襾西覀要覂覃覄覅覆覇覈覉覊見覌覍覎規覐覑覒覓覔覕視覗覘覙覚覛覜覝覞覟覠覡覢覣覤覥覦覧覨覩親覫覬覭覮覯覰覱覲観覴覵覶覷覸覹覺覻覼覽覾覿觀见观觃规觅视觇览觉觊觋觌觍觎觏觐觑角觓觔觕觖觗觘觙觚觛觜觝觞觟觠觡觢解觤觥触觧觨觩觪觫觬觭觮觯觰觱觲觳觴觵觶觷觸觹觺觻觼觽觾觿言訁訂訃訄訅訆訇計訉訊訋訌訍討訏訐訑訒訓訔訕訖託記訙訚訛訜訝訞訟訠訡訢訣訤訥訦訧訨訩訪訫訬設訮訯訰許訲訳訴訵訶訷訸訹診註証訽訾訿詀詁詂詃詄詅詆詇詈詉詊詋詌詍詎詏詐詑詒詓詔評詖詗詘詙詚詛詜詝詞詟詠詡詢詣詤詥試詧詨詩詪詫詬詭詮詯詰話該詳詴詵詶詷詸詹詺詻詼詽詾詿誀誁誂誃誄誅誆誇誈誉誊誋誌認誎誏誐誑誒誓誔誕誖誗誘誙誚誛誜誝語誟誠誡誢誣誤誥誦誧誨誩說誫説読誮誯誰誱課誳誴誵誶誷誸誹誺誻誼誽誾調諀諁諂諃諄諅諆談諈諉諊請諌諍諎諏諐諑諒諓諔諕論諗諘諙諚諛諜諝諞諟諠諡諢諣諤諥諦諧諨諩諪諫諬諭諮諯諰諱諲諳諴諵諶諷諸諹諺諻諼諽諾諿謀謁謂謃謄謅謆謇謈謉謊謋謌謍謎謏謐謑謒謓謔謕謖謗謘謙謚講謜謝謞謟謠謡謢謣謤謥謦謧謨謩謪謫謬謭謮謯謰謱謲謳謴謵謶謷謸謹謺謻謼謽謾謿譀譁譂譃譄譅譆譇譈證譊譋譌譍譎譏譐譑譒譓譔譕譖譗識譙譚譛譜譝譞譟譠譡譢譣譤譥警譧譨譩譪譫譬譭譮譯議譱譲譳譴譵譶護譸譹譺譻譼譽譾譿讀讁讂讃讄讅讆讇讈讉變讋讌讍讎讏讐讑讒讓讔讕讖讗讘讙讚讛讜讝讞讟讠计订讣认讥讦讧讨让讪讫讬训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谉谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷谸谹谺谻谼谽谾谿豀豁豂豃豄豅豆豇豈豉豊豋豌豍豎豏豐豑豒豓豔豕豖豗豘豙豚豛豜豝豞豟豠象豢豣豤豥豦豧豨豩豪豫豬豭豮豯豰豱豲豳豴豵豶豷豸豹豺豻豼豽豾豿貀貁貂貃貄貅貆貇貈貉貊貋貌貍貎貏貐貑貒貓貔貕貖貗貘貙貚貛貜貝貞貟負財貢貣貤貥貦貧貨販貪貫責貭貮貯貰貱貲貳貴貵貶買貸貹貺費貼貽貾貿賀賁賂賃賄賅賆資賈賉賊賋賌賍賎賏賐賑賒賓賔賕賖賗賘賙賚賛賜賝賞賟賠賡賢賣賤賥賦賧賨賩質賫賬賭賮賯賰賱賲賳賴賵賶賷賸賹賺賻購賽賾賿贀贁贂贃贄贅贆贇贈贉贊贋贌贍贎贏贐贑贒贓贔贕贖贗贘贙贚贛贜贝贞负贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赤赥赦赧赨赩赪赫赬赭赮赯走赱赲赳赴赵赶起赸赹赺赻赼赽赾赿趀趁趂趃趄超趆趇趈趉越趋趌趍趎趏趐趑趒趓趔趕趖趗趘趙趚趛趜趝趞趟趠趡趢趣趤趥趦趧趨趩趪趫趬趭趮趯趰趱趲足趴趵趶趷趸趹趺趻趼趽趾趿跀跁跂跃跄跅跆跇跈跉跊跋跌跍跎跏跐跑跒跓跔跕跖跗跘跙跚跛跜距跞跟跠跡跢跣跤跥跦跧跨跩跪跫跬跭跮路跰跱跲跳跴践跶跷跸跹跺跻跼跽跾跿踀踁踂踃踄踅踆踇踈踉踊踋踌踍踎踏踐踑踒踓踔踕踖踗踘踙踚踛踜踝踞踟踠踡踢踣踤踥踦踧踨踩踪踫踬踭踮踯踰踱踲踳踴踵踶踷踸踹踺踻踼踽踾踿蹀蹁蹂蹃蹄蹅蹆蹇蹈蹉蹊蹋蹌蹍蹎蹏蹐蹑蹒蹓蹔蹕蹖蹗蹘蹙蹚蹛蹜蹝蹞蹟蹠蹡蹢蹣蹤蹥蹦蹧蹨蹩蹪蹫蹬蹭蹮蹯蹰蹱蹲蹳蹴蹵蹶蹷蹸蹹蹺蹻蹼蹽蹾蹿躀躁躂躃躄躅躆躇躈躉躊躋躌躍躎躏躐躑躒躓躔躕躖躗躘躙躚躛躜躝躞躟躠躡躢躣躤躥躦躧躨躩躪身躬躭躮躯躰躱躲躳躴躵躶躷躸躹躺躻躼躽躾躿軀軁軂軃軄軅軆軇軈軉車軋軌軍軎軏軐軑軒軓軔軕軖軗軘軙軚軛軜軝軞軟軠軡転軣軤軥軦軧軨軩軪軫軬軭軮軯軰軱軲軳軴軵軶軷軸軹軺軻軼軽軾軿輀輁輂較輄輅輆輇輈載輊輋輌輍輎輏輐輑輒輓輔輕輖輗輘輙輚輛輜輝輞輟輠輡輢輣輤輥輦輧輨輩輪輫輬輭輮輯輰輱輲輳輴輵輶輷輸輹輺輻輼輽輾輿轀轁轂轃轄轅轆轇轈轉轊轋轌轍轎轏轐轑轒轓轔轕轖轗轘轙轚轛轜轝轞轟轠轡轢轣轤轥车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辛辜辝辞辟辠辡辢辣辤辥辦辧辨辩辪辫辬辭辮辯辰辱農辳辴辵辶辷辸边辺辻込辽达辿迀迁迂迃迄迅迆过迈迉迊迋迌迍迎迏运近迒迓返迕迖迗还这迚进远违连迟迠迡迢迣迤迥迦迧迨迩迪迫迬迭迮迯述迱迲迳迴迵迶迷迸迹迺迻迼追迾迿退送适逃逄逅逆逇逈选逊逋逌逍逎透逐逑递逓途逕逖逗逘這通逛逜逝逞速造逡逢連逤逥逦逧逨逩逪逫逬逭逮逯逰週進逳逴逵逶逷逸逹逺逻逼逽逾逿遀遁遂遃遄遅遆遇遈遉遊運遌遍過遏遐遑遒道達違遖遗遘遙遚遛遜遝遞遟遠遡遢遣遤遥遦遧遨適遪遫遬遭遮遯遰遱遲遳遴遵遶遷選遹遺遻遼遽遾避邀邁邂邃還邅邆邇邈邉邊邋邌邍邎邏邐邑邒邓邔邕邖邗邘邙邚邛邜邝邞邟邠邡邢那邤邥邦邧邨邩邪邫邬邭邮邯邰邱邲邳邴邵邶邷邸邹邺邻邼邽邾邿郀郁郂郃郄郅郆郇郈郉郊郋郌郍郎郏郐郑郒郓郔郕郖郗郘郙郚郛郜郝郞郟郠郡郢郣郤郥郦郧部郩郪郫郬郭郮郯郰郱郲郳郴郵郶郷郸郹郺郻郼都郾郿鄀鄁鄂鄃鄄鄅鄆鄇鄈鄉鄊鄋鄌鄍鄎鄏鄐鄑鄒鄓鄔鄕鄖鄗鄘鄙鄚鄛鄜鄝鄞鄟鄠鄡鄢鄣鄤鄥鄦鄧鄨鄩鄪鄫鄬鄭鄮鄯鄰鄱鄲鄳鄴鄵鄶鄷鄸鄹鄺鄻鄼鄽鄾鄿酀酁酂酃酄酅酆酇酈酉酊酋酌配酎酏酐酑酒酓酔酕酖酗酘酙酚酛酜酝酞酟酠酡酢酣酤酥酦酧酨酩酪酫酬酭酮酯酰酱酲酳酴酵酶酷酸酹酺酻酼酽酾酿醀醁醂醃醄醅醆醇醈醉醊醋醌醍醎醏醐醑醒醓醔醕醖醗醘醙醚醛醜醝醞醟醠醡醢醣醤醥醦醧醨醩醪醫醬醭醮醯醰醱醲醳醴醵醶醷醸醹醺醻醼醽醾醿釀釁釂釃釄釅釆采釈釉释釋里重野量釐金釒釓釔釕釖釗釘釙釚釛釜針釞釟釠釡釢釣釤釥釦釧釨釩釪釫釬釭釮釯釰釱釲釳釴釵釶釷釸釹釺釻釼釽釾釿鈀鈁鈂鈃鈄鈅鈆鈇鈈鈉鈊鈋鈌鈍鈎鈏鈐鈑鈒鈓鈔鈕鈖鈗鈘鈙鈚鈛鈜鈝鈞鈟鈠鈡鈢鈣鈤鈥鈦鈧鈨鈩鈪鈫鈬鈭鈮鈯鈰鈱鈲鈳鈴鈵鈶鈷鈸鈹鈺鈻鈼鈽鈾鈿鉀鉁鉂鉃鉄鉅鉆鉇鉈鉉鉊鉋鉌鉍鉎鉏鉐鉑鉒鉓鉔鉕鉖鉗鉘鉙鉚鉛鉜鉝鉞鉟鉠鉡鉢鉣鉤鉥鉦鉧鉨鉩鉪鉫鉬鉭鉮鉯鉰鉱鉲鉳鉴鉵鉶鉷鉸鉹鉺鉻鉼鉽鉾鉿銀銁銂銃銄銅銆銇銈銉銊銋銌銍銎銏銐銑銒銓銔銕銖銗銘銙銚銛銜銝銞銟銠銡銢銣銤銥銦銧銨銩銪銫銬銭銮銯銰銱銲銳銴銵銶銷銸銹銺銻銼銽銾銿鋀鋁鋂鋃鋄鋅鋆鋇鋈鋉鋊鋋鋌鋍鋎鋏鋐鋑鋒鋓鋔鋕鋖鋗鋘鋙鋚鋛鋜鋝鋞鋟鋠鋡鋢鋣鋤鋥鋦鋧鋨鋩鋪鋫鋬鋭鋮鋯鋰鋱鋲鋳鋴鋵鋶鋷鋸鋹鋺鋻鋼鋽鋾鋿錀錁錂錃錄錅錆錇錈錉錊錋錌錍錎錏錐錑錒錓錔錕錖錗錘錙錚錛錜錝錞錟錠錡錢錣錤錥錦錧錨錩錪錫錬錭錮錯錰錱録錳錴錵錶錷錸錹錺錻錼錽錾錿鍀鍁鍂鍃鍄鍅鍆鍇鍈鍉鍊鍋鍌鍍鍎鍏鍐鍑鍒鍓鍔鍕鍖鍗鍘鍙鍚鍛鍜鍝鍞鍟鍠鍡鍢鍣鍤鍥鍦鍧鍨鍩鍪鍫鍬鍭鍮鍯鍰鍱鍲鍳鍴鍵鍶鍷鍸鍹鍺鍻鍼鍽鍾鍿鎀鎁鎂鎃鎄鎅鎆鎇鎈鎉鎊鎋鎌鎍鎎鎏鎐鎑鎒鎓鎔鎕鎖鎗鎘鎙鎚鎛鎜鎝鎞鎟鎠鎡鎢鎣鎤鎥鎦鎧鎨鎩鎪鎫鎬鎭鎮鎯鎰鎱鎲鎳鎴鎵鎶鎷鎸鎹鎺鎻鎼鎽鎾鎿鏀鏁鏂鏃鏄鏅鏆鏇鏈鏉鏊鏋鏌鏍鏎鏏鏐鏑鏒鏓鏔鏕鏖鏗鏘鏙鏚鏛鏜鏝鏞鏟鏠鏡鏢鏣鏤鏥鏦鏧鏨鏩鏪鏫鏬鏭鏮鏯鏰鏱鏲鏳鏴鏵鏶鏷鏸鏹鏺鏻鏼鏽鏾鏿鐀鐁鐂鐃鐄鐅鐆鐇鐈鐉鐊鐋鐌鐍鐎鐏鐐鐑鐒鐓鐔鐕鐖鐗鐘鐙鐚鐛鐜鐝鐞鐟鐠鐡鐢鐣鐤鐥鐦鐧鐨鐩鐪鐫鐬鐭鐮鐯鐰鐱鐲鐳鐴鐵鐶鐷鐸鐹鐺鐻鐼鐽鐾鐿鑀鑁鑂鑃鑄鑅鑆鑇鑈鑉鑊鑋鑌鑍鑎鑏鑐鑑鑒鑓鑔鑕鑖鑗鑘鑙鑚鑛鑜鑝鑞鑟鑠鑡鑢鑣鑤鑥鑦鑧鑨鑩鑪鑫鑬鑭鑮鑯鑰鑱鑲鑳鑴鑵鑶鑷鑸鑹鑺鑻鑼鑽鑾鑿钀钁钂钃钄钅钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钜钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铇铈铉铊铋铌铍铎铏铐铑铒铓铔铕铖铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铩铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗锘错锚锛锜锝锞锟锠锡锢锣锤锥锦锧锨锩锪锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镄镅镆镇镈镉镊镋镌镍镎镏镐镑镒镓镔镕镖镗镘镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镵镶長镸镹镺镻镼镽镾长門閁閂閃閄閅閆閇閈閉閊開閌閍閎閏閐閑閒間閔閕閖閗閘閙閚閛閜閝閞閟閠閡関閣閤閥閦閧閨閩閪閫閬閭閮閯閰閱閲閳閴閵閶閷閸閹閺閻閼閽閾閿闀闁闂闃闄闅闆闇闈闉闊闋闌闍闎闏闐闑闒闓闔闕闖闗闘闙闚闛關闝闞闟闠闡闢闣闤闥闦闧门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛阜阝阞队阠阡阢阣阤阥阦阧阨阩阪阫阬阭阮阯阰阱防阳阴阵阶阷阸阹阺阻阼阽阾阿陀陁陂陃附际陆陇陈陉陊陋陌降陎陏限陑陒陓陔陕陖陗陘陙陚陛陜陝陞陟陠陡院陣除陥陦陧陨险陪陫陬陭陮陯陰陱陲陳陴陵陶陷陸陹険陻陼陽陾陿隀隁隂隃隄隅隆隇隈隉隊隋隌隍階随隐隑隒隓隔隕隖隗隘隙隚際障隝隞隟隠隡隢隣隤隥隦隧隨隩險隫隬隭隮隯隰隱隲隳隴隵隶隷隸隹隺隻隼隽难隿雀雁雂雃雄雅集雇雈雉雊雋雌雍雎雏雐雑雒雓雔雕雖雗雘雙雚雛雜雝雞雟雠雡離難雤雥雦雧雨雩雪雫雬雭雮雯雰雱雲雳雴雵零雷雸雹雺電雼雽雾雿需霁霂霃霄霅霆震霈霉霊霋霌霍霎霏霐霑霒霓霔霕霖霗霘霙霚霛霜霝霞霟霠霡霢霣霤霥霦霧霨霩霪霫霬霭霮霯霰霱露霳霴霵霶霷霸霹霺霻霼霽霾霿靀靁靂靃靄靅靆靇靈靉靊靋靌靍靎靏靐靑青靓靔靕靖靗靘静靚靛靜靝非靟靠靡面靣靤靥靦靧靨革靪靫靬靭靮靯靰靱靲靳靴靵靶靷靸靹靺靻靼靽靾靿鞀鞁鞂鞃鞄鞅鞆鞇鞈鞉鞊鞋鞌鞍鞎鞏鞐鞑鞒鞓鞔鞕鞖鞗鞘鞙鞚鞛鞜鞝鞞鞟鞠鞡鞢鞣鞤鞥鞦鞧鞨鞩鞪鞫鞬鞭鞮鞯鞰鞱鞲鞳鞴鞵鞶鞷鞸鞹鞺鞻鞼鞽鞾鞿韀韁韂韃韄韅韆韇韈韉韊韋韌韍韎韏韐韑韒韓韔韕韖韗韘韙韚韛韜韝韞韟韠韡韢韣韤韥韦韧韨韩韪韫韬韭韮韯韰韱韲音韴韵韶韷韸韹韺韻韼韽韾響頀頁頂頃頄項順頇須頉頊頋頌頍頎頏預頑頒頓頔頕頖頗領頙頚頛頜頝頞頟頠頡頢頣頤頥頦頧頨頩頪頫頬頭頮頯頰頱頲頳頴頵頶頷頸頹頺頻頼頽頾頿顀顁顂顃顄顅顆顇顈顉顊顋題額顎顏顐顑顒顓顔顕顖顗願顙顚顛顜顝類顟顠顡顢顣顤顥顦顧顨顩顪顫顬顭顮顯顰顱顲顳顴页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧風颩颪颫颬颭颮颯颰颱颲颳颴颵颶颷颸颹颺颻颼颽颾颿飀飁飂飃飄飅飆飇飈飉飊飋飌飍风飏飐飑飒飓飔飕飖飗飘飙飚飛飜飝飞食飠飡飢飣飤飥飦飧飨飩飪飫飬飭飮飯飰飱飲飳飴飵飶飷飸飹飺飻飼飽飾飿餀餁餂餃餄餅餆餇餈餉養餋餌餍餎餏餐餑餒餓餔餕餖餗餘餙餚餛餜餝餞餟餠餡餢餣餤餥餦餧館餩餪餫餬餭餮餯餰餱餲餳餴餵餶餷餸餹餺餻餼餽餾餿饀饁饂饃饄饅饆饇饈饉饊饋饌饍饎饏饐饑饒饓饔饕饖饗饘饙饚饛饜饝饞饟饠饡饢饣饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕首馗馘香馚馛馜馝馞馟馠馡馢馣馤馥馦馧馨馩馪馫馬馭馮馯馰馱馲馳馴馵馶馷馸馹馺馻馼馽馾馿駀駁駂駃駄駅駆駇駈駉駊駋駌駍駎駏駐駑駒駓駔駕駖駗駘駙駚駛駜駝駞駟駠駡駢駣駤駥駦駧駨駩駪駫駬駭駮駯駰駱駲駳駴駵駶駷駸駹駺駻駼駽駾駿騀騁騂騃騄騅騆騇騈騉騊騋騌騍騎騏騐騑騒験騔騕騖騗騘騙騚騛騜騝騞騟騠騡騢騣騤騥騦騧騨騩騪騫騬騭騮騯騰騱騲騳騴騵騶騷騸騹騺騻騼騽騾騿驀驁驂驃驄驅驆驇驈驉驊驋驌驍驎驏驐驑驒驓驔驕驖驗驘驙驚驛驜驝驞驟驠驡驢驣驤驥驦驧驨驩驪驫马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧骨骩骪骫骬骭骮骯骰骱骲骳骴骵骶骷骸骹骺骻骼骽骾骿髀髁髂髃髄髅髆髇髈髉髊髋髌髍髎髏髐髑髒髓體髕髖髗高髙髚髛髜髝髞髟髠髡髢髣髤髥髦髧髨髩髪髫髬髭髮髯髰髱髲髳髴髵髶髷髸髹髺髻髼髽髾髿鬀鬁鬂鬃鬄鬅鬆鬇鬈鬉鬊鬋鬌鬍鬎鬏鬐鬑鬒鬓鬔鬕鬖鬗鬘鬙鬚鬛鬜鬝鬞鬟鬠鬡鬢鬣鬤鬥鬦鬧鬨鬩鬪鬫鬬鬭鬮鬯鬰鬱鬲鬳鬴鬵鬶鬷鬸鬹鬺鬻鬼鬽鬾鬿魀魁魂魃魄魅魆魇魈魉魊魋魌魍魎魏魐魑魒魓魔魕魖魗魘魙魚魛魜魝魞魟魠魡魢魣魤魥魦魧魨魩魪魫魬魭魮魯魰魱魲魳魴魵魶魷魸魹魺魻魼魽魾魿鮀鮁鮂鮃鮄鮅鮆鮇鮈鮉鮊鮋鮌鮍鮎鮏鮐鮑鮒鮓鮔鮕鮖鮗鮘鮙鮚鮛鮜鮝鮞鮟鮠鮡鮢鮣鮤鮥鮦鮧鮨鮩鮪鮫鮬鮭鮮鮯鮰鮱鮲鮳鮴鮵鮶鮷鮸鮹鮺鮻鮼鮽鮾鮿鯀鯁鯂鯃鯄鯅鯆鯇鯈鯉鯊鯋鯌鯍鯎鯏鯐鯑鯒鯓鯔鯕鯖鯗鯘鯙鯚鯛鯜鯝鯞鯟鯠鯡鯢鯣鯤鯥鯦鯧鯨鯩鯪鯫鯬鯭鯮鯯鯰鯱鯲鯳鯴鯵鯶鯷鯸鯹鯺鯻鯼鯽鯾鯿鰀鰁鰂鰃鰄鰅鰆鰇鰈鰉鰊鰋鰌鰍鰎鰏鰐鰑鰒鰓鰔鰕鰖鰗鰘鰙鰚鰛鰜鰝鰞鰟鰠鰡鰢鰣鰤鰥鰦鰧鰨鰩鰪鰫鰬鰭鰮鰯鰰鰱鰲鰳鰴鰵鰶鰷鰸鰹鰺鰻鰼鰽鰾鰿鱀鱁鱂鱃鱄鱅鱆鱇鱈鱉鱊鱋鱌鱍鱎鱏鱐鱑鱒鱓鱔鱕鱖鱗鱘鱙鱚鱛鱜鱝鱞鱟鱠鱡鱢鱣鱤鱥鱦鱧鱨鱩鱪鱫鱬鱭鱮鱯鱰鱱鱲鱳鱴鱵鱶鱷鱸鱹鱺鱻鱼鱽鱾鱿鲀鲁鲂鲃鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳚鳛鳜鳝鳞鳟鳠鳡鳢鳣鳤鳥鳦鳧鳨鳩鳪鳫鳬鳭鳮鳯鳰鳱鳲鳳鳴鳵鳶鳷鳸鳹鳺鳻鳼鳽鳾鳿鴀鴁鴂鴃鴄鴅鴆鴇鴈鴉鴊鴋鴌鴍鴎鴏鴐鴑鴒鴓鴔鴕鴖鴗鴘鴙鴚鴛鴜鴝鴞鴟鴠鴡鴢鴣鴤鴥鴦鴧鴨鴩鴪鴫鴬鴭鴮鴯鴰鴱鴲鴳鴴鴵鴶鴷鴸鴹鴺鴻鴼鴽鴾鴿鵀鵁鵂鵃鵄鵅鵆鵇鵈鵉鵊鵋鵌鵍鵎鵏鵐鵑鵒鵓鵔鵕鵖鵗鵘鵙鵚鵛鵜鵝鵞鵟鵠鵡鵢鵣鵤鵥鵦鵧鵨鵩鵪鵫鵬鵭鵮鵯鵰鵱鵲鵳鵴鵵鵶鵷鵸鵹鵺鵻鵼鵽鵾鵿鶀鶁鶂鶃鶄鶅鶆鶇鶈鶉鶊鶋鶌鶍鶎鶏鶐鶑鶒鶓鶔鶕鶖鶗鶘鶙鶚鶛鶜鶝鶞鶟鶠鶡鶢鶣鶤鶥鶦鶧鶨鶩鶪鶫鶬鶭鶮鶯鶰鶱鶲鶳鶴鶵鶶鶷鶸鶹鶺鶻鶼鶽鶾鶿鷀鷁鷂鷃鷄鷅鷆鷇鷈鷉鷊鷋鷌鷍鷎鷏鷐鷑鷒鷓鷔鷕鷖鷗鷘鷙鷚鷛鷜鷝鷞鷟鷠鷡鷢鷣鷤鷥鷦鷧鷨鷩鷪鷫鷬鷭鷮鷯鷰鷱鷲鷳鷴鷵鷶鷷鷸鷹鷺鷻鷼鷽鷾鷿鸀鸁鸂鸃鸄鸅鸆鸇鸈鸉鸊鸋鸌鸍鸎鸏鸐鸑鸒鸓鸔鸕鸖鸗鸘鸙鸚鸛鸜鸝鸞鸟鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹙鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹮鹯鹰鹱鹲鹳鹴鹵鹶鹷鹸鹹鹺鹻鹼鹽鹾鹿麀麁麂麃麄麅麆麇麈麉麊麋麌麍麎麏麐麑麒麓麔麕麖麗麘麙麚麛麜麝麞麟麠麡麢麣麤麥麦麧麨麩麪麫麬麭麮麯麰麱麲麳麴麵麶麷麸麹麺麻麼麽麾麿黀黁黂黃黄黅黆黇黈黉黊黋黌黍黎黏黐黑黒黓黔黕黖黗默黙黚黛黜黝點黟黠黡黢黣黤黥黦黧黨黩黪黫黬黭黮黯黰黱黲黳黴黵黶黷黸黹黺黻黼黽黾黿鼀鼁鼂鼃鼄鼅鼆鼇鼈鼉鼊鼋鼌鼍鼎鼏鼐鼑鼒鼓鼔鼕鼖鼗鼘鼙鼚鼛鼜鼝鼞鼟鼠鼡鼢鼣鼤鼥鼦鼧鼨鼩鼪鼫鼬鼭鼮鼯鼰鼱鼲鼳鼴鼵鼶鼷鼸鼹鼺鼻鼼鼽鼾鼿齀齁齂齃齄齅齆齇齈齉齊齋齌齍齎齏齐齑齒齓齔齕齖齗齘齙齚齛齜齝齞齟齠齡齢齣齤齥齦齧齨齩齪齫齬齭齮齯齰齱齲齳齴齵齶齷齸齹齺齻齼齽齾齿龀龁龂龃龄龅龆龇龈龉龊龋龌龍龎龏龐龑龒龓龔龕龖龗龘龙龚龛龜龝龞龟龠龡龢龣龤龥龦龧龨龩龪龫龬龭龮龯龰龱龲龳龴龵龶龷龸龹龺龻龼龽龾龿鿀鿁鿂鿃鿄鿅鿆鿇鿈鿉鿊鿋鿌ꀀꀁꀂꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌꀍꀎꀏꀐꀑꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜꀝꀞꀟꀠꀡꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿꁀꁁꁂꁃꁄꁅꁆꁇꁈꁉꁊꁋꁌꁍꁎꁏꁐꁑꁒꁓꁔꁕꁖꁗꁘꁙꁚꁛꁜꁝꁞꁟꁠꁡꁢꁣꁤꁥꁦꁧꁨꁩꁪꁫꁬꁭꁮꁯꁰꁱꁲꁳꁴꁵꁶꁷꁸꁹꁺꁻꁼꁽꁾꁿꂀꂁꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌꂍꂎꂏꂐꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜꂝꂞꂟꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿꃀꃁꃂꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌꃍꃎꃏꃐꃑꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜꃝꃞꃟꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿꄀꄁꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌꄍꄎꄏꄐꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜꄝꄞꄟꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿꅀꅁꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌꅍꅎꅏꅐꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜꅝꅞꅟꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿꆀꆁꆂꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌꆍꆎꆏꆐꆑꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜꆝꆞꆟꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿꇀꇁꇂꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌꇍꇎꇏꇐꇑꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜꇝꇞꇟꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿꈀꈁꈂꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌꈍꈎꈏꈐꈑꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜꈝꈞꈟꈠꈡꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿꉀꉁꉂꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌꉍꉎꉏꉐꉑꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜꉝꉞꉟꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿꊀꊁꊂꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌꊍꊎꊏꊐꊑꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜꊝꊞꊟꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿꋀꋁꋂꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌꋍꋎꋏꋐꋑꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜꋝꋞꋟꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿꌀꌁꌂꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌꌍꌎꌏꌐꌑꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜꌝꌞꌟꌠꌡꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿꍀꍁꍂꍃꍄꍅꍆꍇꍈꍉꍊꍋꍌꍍꍎꍏꍐꍑꍒꍓꍔꍕꍖꍗꍘꍙꍚꍛꍜꍝꍞꍟꍠꍡꍢꍣꍤꍥꍦꍧꍨꍩꍪꍫꍬꍭꍮꍯꍰꍱꍲꍳꍴꍵꍶꍷꍸꍹꍺꍻꍼꍽꍾꍿꎀꎁꎂꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌꎍꎎꎏꎐꎑꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜꎝꎞꎟꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿꏀꏁꏂꏃꏄꏅꏆꏇꏈꏉꏊꏋꏌꏍꏎꏏꏐꏑꏒꏓꏔꏕꏖꏗꏘꏙꏚꏛꏜꏝꏞꏟꏠꏡꏢꏣꏤꏥꏦꏧꏨꏩꏪꏫꏬꏭꏮꏯꏰꏱꏲꏳꏴꏵꏶꏷꏸꏹꏺꏻꏼꏽꏾꏿꐀꐁꐂꐃꐄꐅꐆꐇꐈꐉꐊꐋꐌꐍꐎꐏꐐꐑꐒꐓꐔꐕꐖꐗꐘꐙꐚꐛꐜꐝꐞꐟꐠꐡꐢꐣꐤꐥꐦꐧꐨꐩꐪꐫꐬꐭꐮꐯꐰꐱꐲꐳꐴꐵꐶꐷꐸꐹꐺꐻꐼꐽꐾꐿꑀꑁꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌꑍꑎꑏꑐꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜꑝꑞꑟꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿꒀꒁꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ꒐꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜꒝꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀꓁꓂꓃꓄꓅꓆ꓐꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜꓝꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ꓾꓿ꔀꔁꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌꔍꔎꔏꔐꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜꔝꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀꕁꕂꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌꕍꕎꕏꕐꕑꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜꕝꕞꕟꕠꕡꕢꕣꕤꕥꕦꕧꕨꕩꕪꕫꕬꕭꕮꕯꕰꕱꕲꕳꕴꕵꕶꕷꕸꕹꕺꕻꕼꕽꕾꕿꖀꖁꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌꖍꖎꖏꖐꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜꖝꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀꗁꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌꗍꗎꗏꗐꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜꗝꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀꘁꘂꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ꘍꘎꘏ꘐꘑꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜꘝꘞꘟ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ꘪꘫꙀꙁꙂꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌꙍꙎꙏꙐꙑꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜꙝꙞꙟꙠꙡꙢꙣꙤꙥꙦꙧꙨꙩꙪꙫꙬꙭꙮ꙯꙰꙱꙲꙳ꙴꙵꙶꙷꙸꙹꙺꙻ꙼꙽꙾ꙿꚀꚁꚂꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌꚍꚎꚏꚐꚑꚒꚓꚔꚕꚖꚗꚟꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀꛁꛂꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌꛍꛎꛏꛐꛑꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜꛝꛞꛟꛠꛡꛢꛣꛤꛥꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ꛰꛱꛲꛳꛴꛵꛶꛷꜀꜁꜂꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌꜍꜎꜏꜐꜑꜒꜓꜔꜕꜖ꜗꜘꜙꜚꜛꜜꜝꜞꜟ꜠꜡ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿꝀꝁꝂꝃꝄꝅꝆꝇꝈꝉꝊꝋꝌꝍꝎꝏꝐꝑꝒꝓꝔꝕꝖꝗꝘꝙꝚꝛꝜꝝꝞꝟꝠꝡꝢꝣꝤꝥꝦꝧꝨꝩꝪꝫꝬꝭꝮꝯꝰꝱꝲꝳꝴꝵꝶꝷꝸꝹꝺꝻꝼꝽꝾꝿꞀꞁꞂꞃꞄꞅꞆꞇꞈ꞉꞊ꞋꞌꞍꞎꞐꞑꞒꞓꞠꞡꞢꞣꞤꞥꞦꞧꞨꞩꞪꟸꟹꟺꟻꟼꟽꟾꟿꠀꠁꠂꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌꠍꠎꠏꠐꠑꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜꠝꠞꠟꠠꠡꠢꠣꠤꠥꠦꠧ꠨꠩꠪꠫꠰꠱꠲꠳꠴꠵꠶꠷꠸꠹ꡀꡁꡂꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌꡍꡎꡏꡐꡑꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜꡝꡞꡟꡠꡡꡢꡣꡤꡥꡦꡧꡨꡩꡪꡫꡬꡭꡮꡯꡰꡱꡲꡳ꡴꡵꡶꡷ꢀꢁꢂꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌꢍꢎꢏꢐꢑꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜꢝꢞꢟꢠꢡꢢꢣꢤꢥꢦꢧꢨꢩꢪꢫꢬꢭꢮꢯꢰꢱꢲꢳꢴꢵꢶꢷꢸꢹꢺꢻꢼꢽꢾꢿꣀꣁꣂꣃ꣄꣎꣏꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ꣸꣹꣺ꣻ꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌꤍꤎꤏꤐꤑꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜꤝꤞꤟꤠꤡꤢꤣꤤꤥꤦꤧꤨꤩꤪ꤫꤬꤭꤮꤯ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀꥁꥂꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌꥍꥎꥏꥐꥑꥒ꥓꥟ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼꦀꦁꦂꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌꦍꦎꦏꦐꦑꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜꦝꦞꦟꦠꦡꦢꦣꦤꦥꦦꦧꦨꦩꦪꦫꦬꦭꦮꦯꦰꦱꦲ꦳ꦴꦵꦶꦷꦸꦹꦺꦻꦼꦽꦾꦿ꧀꧁꧂꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌꧍ꧏ꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧞꧟ꨀꨁꨂꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌꨍꨎꨏꨐꨑꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜꨝꨞꨟꨠꨡꨢꨣꨤꨥꨦꨧꨨꨩꨪꨫꨬꨭꨮꨯꨰꨱꨲꨳꨴꨵꨶꩀꩁꩂꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌꩍ꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꩜꩝꩞꩟ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ꩷꩸꩹ꩺꩻꪀꪁꪂꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌꪍꪎꪏꪐꪑꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜꪝꪞꪟꪠꪡꪢꪣꪤꪥꪦꪧꪨꪩꪪꪫꪬꪭꪮꪯꪰꪱꪴꪲꪳꪵꪶꪷꪸꪹꪺꪻꪼꪽꪾ꪿ꫀ꫁ꫂꫛꫜꫝ꫞꫟ꫠꫡꫢꫣꫤꫥꫦꫧꫨꫩꫪꫫꫬꫭꫮꫯ꫰꫱ꫲꫳꫴꫵ꫶ꬁꬂꬃꬄꬅꬆꬉꬊꬋꬌꬍꬎꬑꬒꬓꬔꬕꬖꬠꬡꬢꬣꬤꬥꬦꬨꬩꬪꬫꬬꬭꬮꯀꯁꯂꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌꯍꯎꯏꯐꯑꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜꯝꯞꯟꯠꯡꯢꯣꯤꯥꯦꯧꯨꯩꯪ꯫꯬꯭꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹가각갂갃간갅갆갇갈갉갊갋갌갍갎갏감갑값갓갔강갖갗갘같갚갛개객갞갟갠갡갢갣갤갥갦갧갨갩갪갫갬갭갮갯갰갱갲갳갴갵갶갷갸갹갺갻갼갽갾갿걀걁걂걃걄걅걆걇걈걉걊걋걌걍걎걏걐걑걒걓걔걕걖걗걘걙걚걛걜걝걞걟걠걡걢걣걤걥걦걧걨걩걪걫걬걭걮걯거걱걲걳건걵걶걷걸걹걺걻걼걽걾걿검겁겂것겄겅겆겇겈겉겊겋게겍겎겏겐겑겒겓겔겕겖겗겘겙겚겛겜겝겞겟겠겡겢겣겤겥겦겧겨격겪겫견겭겮겯결겱겲겳겴겵겶겷겸겹겺겻겼경겾겿곀곁곂곃계곅곆곇곈곉곊곋곌곍곎곏곐곑곒곓곔곕곖곗곘곙곚곛곜곝곞곟고곡곢곣곤곥곦곧골곩곪곫곬곭곮곯곰곱곲곳곴공곶곷곸곹곺곻과곽곾곿관괁괂괃괄괅괆괇괈괉괊괋괌괍괎괏괐광괒괓괔괕괖괗괘괙괚괛괜괝괞괟괠괡괢괣괤괥괦괧괨괩괪괫괬괭괮괯괰괱괲괳괴괵괶괷괸괹괺괻괼괽괾괿굀굁굂굃굄굅굆굇굈굉굊굋굌굍굎굏교굑굒굓굔굕굖굗굘굙굚굛굜굝굞굟굠굡굢굣굤굥굦굧굨굩굪굫구국굮굯군굱굲굳굴굵굶굷굸굹굺굻굼굽굾굿궀궁궂궃궄궅궆궇궈궉궊궋권궍궎궏궐궑궒궓궔궕궖궗궘궙궚궛궜궝궞궟궠궡궢궣궤궥궦궧궨궩궪궫궬궭궮궯궰궱궲궳궴궵궶궷궸궹궺궻궼궽궾궿귀귁귂귃귄귅귆귇귈귉귊귋귌귍귎귏귐귑귒귓귔귕귖귗귘귙귚귛규귝귞귟균귡귢귣귤귥귦귧귨귩귪귫귬귭귮귯귰귱귲귳귴귵귶귷그극귺귻근귽귾귿글긁긂긃긄긅긆긇금급긊긋긌긍긎긏긐긑긒긓긔긕긖긗긘긙긚긛긜긝긞긟긠긡긢긣긤긥긦긧긨긩긪긫긬긭긮긯기긱긲긳긴긵긶긷길긹긺긻긼긽긾긿김깁깂깃깄깅깆깇깈깉깊깋까깍깎깏깐깑깒깓깔깕깖깗깘깙깚깛깜깝깞깟깠깡깢깣깤깥깦깧깨깩깪깫깬깭깮깯깰깱깲깳깴깵깶깷깸깹깺깻깼깽깾깿꺀꺁꺂꺃꺄꺅꺆꺇꺈꺉꺊꺋꺌꺍꺎꺏꺐꺑꺒꺓꺔꺕꺖꺗꺘꺙꺚꺛꺜꺝꺞꺟꺠꺡꺢꺣꺤꺥꺦꺧꺨꺩꺪꺫꺬꺭꺮꺯꺰꺱꺲꺳꺴꺵꺶꺷꺸꺹꺺꺻꺼꺽꺾꺿껀껁껂껃껄껅껆껇껈껉껊껋껌껍껎껏껐껑껒껓껔껕껖껗께껙껚껛껜껝껞껟껠껡껢껣껤껥껦껧껨껩껪껫껬껭껮껯껰껱껲껳껴껵껶껷껸껹껺껻껼껽껾껿꼀꼁꼂꼃꼄꼅꼆꼇꼈꼉꼊꼋꼌꼍꼎꼏꼐꼑꼒꼓꼔꼕꼖꼗꼘꼙꼚꼛꼜꼝꼞꼟꼠꼡꼢꼣꼤꼥꼦꼧꼨꼩꼪꼫꼬꼭꼮꼯꼰꼱꼲꼳꼴꼵꼶꼷꼸꼹꼺꼻꼼꼽꼾꼿꽀꽁꽂꽃꽄꽅꽆꽇꽈꽉꽊꽋꽌꽍꽎꽏꽐꽑꽒꽓꽔꽕꽖꽗꽘꽙꽚꽛꽜꽝꽞꽟꽠꽡꽢꽣꽤꽥꽦꽧꽨꽩꽪꽫꽬꽭꽮꽯꽰꽱꽲꽳꽴꽵꽶꽷꽸꽹꽺꽻꽼꽽꽾꽿꾀꾁꾂꾃꾄꾅꾆꾇꾈꾉꾊꾋꾌꾍꾎꾏꾐꾑꾒꾓꾔꾕꾖꾗꾘꾙꾚꾛꾜꾝꾞꾟꾠꾡꾢꾣꾤꾥꾦꾧꾨꾩꾪꾫꾬꾭꾮꾯꾰꾱꾲꾳꾴꾵꾶꾷꾸꾹꾺꾻꾼꾽꾾꾿꿀꿁꿂꿃꿄꿅꿆꿇꿈꿉꿊꿋꿌꿍꿎꿏꿐꿑꿒꿓꿔꿕꿖꿗꿘꿙꿚꿛꿜꿝꿞꿟꿠꿡꿢꿣꿤꿥꿦꿧꿨꿩꿪꿫꿬꿭꿮꿯꿰꿱꿲꿳꿴꿵꿶꿷꿸꿹꿺꿻꿼꿽꿾꿿뀀뀁뀂뀃뀄뀅뀆뀇뀈뀉뀊뀋뀌뀍뀎뀏뀐뀑뀒뀓뀔뀕뀖뀗뀘뀙뀚뀛뀜뀝뀞뀟뀠뀡뀢뀣뀤뀥뀦뀧뀨뀩뀪뀫뀬뀭뀮뀯뀰뀱뀲뀳뀴뀵뀶뀷뀸뀹뀺뀻뀼뀽뀾뀿끀끁끂끃끄끅끆끇끈끉끊끋끌끍끎끏끐끑끒끓끔끕끖끗끘끙끚끛끜끝끞끟끠끡끢끣끤끥끦끧끨끩끪끫끬끭끮끯끰끱끲끳끴끵끶끷끸끹끺끻끼끽끾끿낀낁낂낃낄낅낆낇낈낉낊낋낌낍낎낏낐낑낒낓낔낕낖낗나낙낚낛난낝낞낟날낡낢낣낤낥낦낧남납낪낫났낭낮낯낰낱낲낳내낵낶낷낸낹낺낻낼낽낾낿냀냁냂냃냄냅냆냇냈냉냊냋냌냍냎냏냐냑냒냓냔냕냖냗냘냙냚냛냜냝냞냟냠냡냢냣냤냥냦냧냨냩냪냫냬냭냮냯냰냱냲냳냴냵냶냷냸냹냺냻냼냽냾냿넀넁넂넃넄넅넆넇너넉넊넋넌넍넎넏널넑넒넓넔넕넖넗넘넙넚넛넜넝넞넟넠넡넢넣네넥넦넧넨넩넪넫넬넭넮넯넰넱넲넳넴넵넶넷넸넹넺넻넼넽넾넿녀녁녂녃년녅녆녇녈녉녊녋녌녍녎녏념녑녒녓녔녕녖녗녘녙녚녛녜녝녞녟녠녡녢녣녤녥녦녧녨녩녪녫녬녭녮녯녰녱녲녳녴녵녶녷노녹녺녻논녽녾녿놀놁놂놃놄놅놆놇놈놉놊놋놌농놎놏놐놑높놓놔놕놖놗놘놙놚놛놜놝놞놟놠놡놢놣놤놥놦놧놨놩놪놫놬놭놮놯놰놱놲놳놴놵놶놷놸놹놺놻놼놽놾놿뇀뇁뇂뇃뇄뇅뇆뇇뇈뇉뇊뇋뇌뇍뇎뇏뇐뇑뇒뇓뇔뇕뇖뇗뇘뇙뇚뇛뇜뇝뇞뇟뇠뇡뇢뇣뇤뇥뇦뇧뇨뇩뇪뇫뇬뇭뇮뇯뇰뇱뇲뇳뇴뇵뇶뇷뇸뇹뇺뇻뇼뇽뇾뇿눀눁눂눃누눅눆눇눈눉눊눋눌눍눎눏눐눑눒눓눔눕눖눗눘눙눚눛눜눝눞눟눠눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄늅늆늇늈늉늊늋늌늍늎늏느늑늒늓는늕늖늗늘늙늚늛늜늝늞늟늠늡늢늣늤능늦늧늨늩늪늫늬늭늮늯늰늱늲늳늴늵늶늷늸늹늺늻늼늽늾늿닀닁닂닃닄닅닆닇니닉닊닋닌닍닎닏닐닑닒닓닔닕닖닗님닙닚닛닜닝닞닟닠닡닢닣다닥닦닧단닩닪닫달닭닮닯닰닱닲닳담답닶닷닸당닺닻닼닽닾닿대댁댂댃댄댅댆댇댈댉댊댋댌댍댎댏댐댑댒댓댔댕댖댗댘댙댚댛댜댝댞댟댠댡댢댣댤댥댦댧댨댩댪댫댬댭댮댯댰댱댲댳댴댵댶댷댸댹댺댻댼댽댾댿덀덁덂덃덄덅덆덇덈덉덊덋덌덍덎덏덐덑덒덓더덕덖덗던덙덚덛덜덝덞덟덠덡덢덣덤덥덦덧덨덩덪덫덬덭덮덯데덱덲덳덴덵덶덷델덹덺덻덼덽덾덿뎀뎁뎂뎃뎄뎅뎆뎇뎈뎉뎊뎋뎌뎍뎎뎏뎐뎑뎒뎓뎔뎕뎖뎗뎘뎙뎚뎛뎜뎝뎞뎟뎠뎡뎢뎣뎤뎥뎦뎧뎨뎩뎪뎫뎬뎭뎮뎯뎰뎱뎲뎳뎴뎵뎶뎷뎸뎹뎺뎻뎼뎽뎾뎿돀돁돂돃도독돆돇돈돉돊돋돌돍돎돏돐돑돒돓돔돕돖돗돘동돚돛돜돝돞돟돠돡돢돣돤돥돦돧돨돩돪돫돬돭돮돯돰돱돲돳돴돵돶돷돸돹돺돻돼돽돾돿됀됁됂됃됄됅됆됇됈됉됊됋됌됍됎됏됐됑됒됓됔됕됖됗되됙됚됛된됝됞됟될됡됢됣됤됥됦됧됨됩됪됫됬됭됮됯됰됱됲됳됴됵됶됷됸됹됺됻됼됽됾됿둀둁둂둃둄둅둆둇둈둉둊둋둌둍둎둏두둑둒둓둔둕둖둗둘둙둚둛둜둝둞둟둠둡둢둣둤둥둦둧둨둩둪둫둬둭둮둯둰둱둲둳둴둵둶둷둸둹둺둻둼둽둾둿뒀뒁뒂뒃뒄뒅뒆뒇뒈뒉뒊뒋뒌뒍뒎뒏뒐뒑뒒뒓뒔뒕뒖뒗뒘뒙뒚뒛뒜뒝뒞뒟뒠뒡뒢뒣뒤뒥뒦뒧뒨뒩뒪뒫뒬뒭뒮뒯뒰뒱뒲뒳뒴뒵뒶뒷뒸뒹뒺뒻뒼뒽뒾뒿듀듁듂듃듄듅듆듇듈듉듊듋듌듍듎듏듐듑듒듓듔듕듖듗듘듙듚듛드득듞듟든듡듢듣들듥듦듧듨듩듪듫듬듭듮듯듰등듲듳듴듵듶듷듸듹듺듻듼듽듾듿딀딁딂딃딄딅딆딇딈딉딊딋딌딍딎딏딐딑딒딓디딕딖딗딘딙딚딛딜딝딞딟딠딡딢딣딤딥딦딧딨딩딪딫딬딭딮딯따딱딲딳딴딵딶딷딸딹딺딻딼딽딾딿땀땁땂땃땄땅땆땇땈땉땊땋때땍땎땏땐땑땒땓땔땕땖땗땘땙땚땛땜땝땞땟땠땡땢땣땤땥땦땧땨땩땪땫땬땭땮땯땰땱땲땳땴땵땶땷땸땹땺땻땼땽땾땿떀떁떂떃떄떅떆떇떈떉떊떋떌떍떎떏떐떑떒떓떔떕떖떗떘떙떚떛떜떝떞떟떠떡떢떣떤떥떦떧떨떩떪떫떬떭떮떯떰떱떲떳떴떵떶떷떸떹떺떻떼떽떾떿뗀뗁뗂뗃뗄뗅뗆뗇뗈뗉뗊뗋뗌뗍뗎뗏뗐뗑뗒뗓뗔뗕뗖뗗뗘뗙뗚뗛뗜뗝뗞뗟뗠뗡뗢뗣뗤뗥뗦뗧뗨뗩뗪뗫뗬뗭뗮뗯뗰뗱뗲뗳뗴뗵뗶뗷뗸뗹뗺뗻뗼뗽뗾뗿똀똁똂똃똄똅똆똇똈똉똊똋똌똍똎똏또똑똒똓똔똕똖똗똘똙똚똛똜똝똞똟똠똡똢똣똤똥똦똧똨똩똪똫똬똭똮똯똰똱똲똳똴똵똶똷똸똹똺똻똼똽똾똿뙀뙁뙂뙃뙄뙅뙆뙇뙈뙉뙊뙋뙌뙍뙎뙏뙐뙑뙒뙓뙔뙕뙖뙗뙘뙙뙚뙛뙜뙝뙞뙟뙠뙡뙢뙣뙤뙥뙦뙧뙨뙩뙪뙫뙬뙭뙮뙯뙰뙱뙲뙳뙴뙵뙶뙷뙸뙹뙺뙻뙼뙽뙾뙿뚀뚁뚂뚃뚄뚅뚆뚇뚈뚉뚊뚋뚌뚍뚎뚏뚐뚑뚒뚓뚔뚕뚖뚗뚘뚙뚚뚛뚜뚝뚞뚟뚠뚡뚢뚣뚤뚥뚦뚧뚨뚩뚪뚫뚬뚭뚮뚯뚰뚱뚲뚳뚴뚵뚶뚷뚸뚹뚺뚻뚼뚽뚾뚿뛀뛁뛂뛃뛄뛅뛆뛇뛈뛉뛊뛋뛌뛍뛎뛏뛐뛑뛒뛓뛔뛕뛖뛗뛘뛙뛚뛛뛜뛝뛞뛟뛠뛡뛢뛣뛤뛥뛦뛧뛨뛩뛪뛫뛬뛭뛮뛯뛰뛱뛲뛳뛴뛵뛶뛷뛸뛹뛺뛻뛼뛽뛾뛿뜀뜁뜂뜃뜄뜅뜆뜇뜈뜉뜊뜋뜌뜍뜎뜏뜐뜑뜒뜓뜔뜕뜖뜗뜘뜙뜚뜛뜜뜝뜞뜟뜠뜡뜢뜣뜤뜥뜦뜧뜨뜩뜪뜫뜬뜭뜮뜯뜰뜱뜲뜳뜴뜵뜶뜷뜸뜹뜺뜻뜼뜽뜾뜿띀띁띂띃띄띅띆띇띈띉띊띋띌띍띎띏띐띑띒띓띔띕띖띗띘띙띚띛띜띝띞띟띠띡띢띣띤띥띦띧띨띩띪띫띬띭띮띯띰띱띲띳띴띵띶띷띸띹띺띻라락띾띿란랁랂랃랄랅랆랇랈랉랊랋람랍랎랏랐랑랒랓랔랕랖랗래랙랚랛랜랝랞랟랠랡랢랣랤랥랦랧램랩랪랫랬랭랮랯랰랱랲랳랴략랶랷랸랹랺랻랼랽랾랿럀럁럂럃럄럅럆럇럈량럊럋럌럍럎럏럐럑럒럓럔럕럖럗럘럙럚럛럜럝럞럟럠럡럢럣럤럥럦럧럨럩럪럫러럭럮럯런럱럲럳럴럵럶럷럸럹럺럻럼럽럾럿렀렁렂렃렄렅렆렇레렉렊렋렌렍렎렏렐렑렒렓렔렕렖렗렘렙렚렛렜렝렞렟렠렡렢렣려력렦렧련렩렪렫렬렭렮렯렰렱렲렳렴렵렶렷렸령렺렻렼렽렾렿례롁롂롃롄롅롆롇롈롉롊롋롌롍롎롏롐롑롒롓롔롕롖롗롘롙롚롛로록롞롟론롡롢롣롤롥롦롧롨롩롪롫롬롭롮롯롰롱롲롳롴롵롶롷롸롹롺롻롼롽롾롿뢀뢁뢂뢃뢄뢅뢆뢇뢈뢉뢊뢋뢌뢍뢎뢏뢐뢑뢒뢓뢔뢕뢖뢗뢘뢙뢚뢛뢜뢝뢞뢟뢠뢡뢢뢣뢤뢥뢦뢧뢨뢩뢪뢫뢬뢭뢮뢯뢰뢱뢲뢳뢴뢵뢶뢷뢸뢹뢺뢻뢼뢽뢾뢿룀룁룂룃룄룅룆룇룈룉룊룋료룍룎룏룐룑룒룓룔룕룖룗룘룙룚룛룜룝룞룟룠룡룢룣룤룥룦룧루룩룪룫룬룭룮룯룰룱룲룳룴룵룶룷룸룹룺룻룼룽룾룿뤀뤁뤂뤃뤄뤅뤆뤇뤈뤉뤊뤋뤌뤍뤎뤏뤐뤑뤒뤓뤔뤕뤖뤗뤘뤙뤚뤛뤜뤝뤞뤟뤠뤡뤢뤣뤤뤥뤦뤧뤨뤩뤪뤫뤬뤭뤮뤯뤰뤱뤲뤳뤴뤵뤶뤷뤸뤹뤺뤻뤼뤽뤾뤿륀륁륂륃륄륅륆륇륈륉륊륋륌륍륎륏륐륑륒륓륔륕륖륗류륙륚륛륜륝륞륟률륡륢륣륤륥륦륧륨륩륪륫륬륭륮륯륰륱륲륳르륵륶륷른륹륺륻를륽륾륿릀릁릂릃름릅릆릇릈릉릊릋릌릍릎릏릐릑릒릓릔릕릖릗릘릙릚릛릜릝릞릟릠릡릢릣릤릥릦릧릨릩릪릫리릭릮릯린릱릲릳릴릵릶릷릸릹릺릻림립릾릿맀링맂맃맄맅맆맇마막맊맋만맍많맏말맑맒맓맔맕맖맗맘맙맚맛맜망맞맟맠맡맢맣매맥맦맧맨맩맪맫맬맭맮맯맰맱맲맳맴맵맶맷맸맹맺맻맼맽맾맿먀먁먂먃먄먅먆먇먈먉먊먋먌먍먎먏먐먑먒먓먔먕먖먗먘먙먚먛먜먝먞먟먠먡먢먣먤먥먦먧먨먩먪먫먬먭먮먯먰먱먲먳먴먵먶먷머먹먺먻먼먽먾먿멀멁멂멃멄멅멆멇멈멉멊멋멌멍멎멏멐멑멒멓메멕멖멗멘멙멚멛멜멝멞멟멠멡멢멣멤멥멦멧멨멩멪멫멬멭멮멯며멱멲멳면멵멶멷멸멹멺멻멼멽멾멿몀몁몂몃몄명몆몇몈몉몊몋몌몍몎몏몐몑몒몓몔몕몖몗몘몙몚몛몜몝몞몟몠몡몢몣몤몥몦몧모목몪몫몬몭몮몯몰몱몲몳몴몵몶몷몸몹몺못몼몽몾몿뫀뫁뫂뫃뫄뫅뫆뫇뫈뫉뫊뫋뫌뫍뫎뫏뫐뫑뫒뫓뫔뫕뫖뫗뫘뫙뫚뫛뫜뫝뫞뫟뫠뫡뫢뫣뫤뫥뫦뫧뫨뫩뫪뫫뫬뫭뫮뫯뫰뫱뫲뫳뫴뫵뫶뫷뫸뫹뫺뫻뫼뫽뫾뫿묀묁묂묃묄묅묆묇묈묉묊묋묌묍묎묏묐묑묒묓묔묕묖묗묘묙묚묛묜묝묞묟묠묡묢묣묤묥묦묧묨묩묪묫묬묭묮묯묰묱묲묳무묵묶묷문묹묺묻물묽묾묿뭀뭁뭂뭃뭄뭅뭆뭇뭈뭉뭊뭋뭌뭍뭎뭏뭐뭑뭒뭓뭔뭕뭖뭗뭘뭙뭚뭛뭜뭝뭞뭟뭠뭡뭢뭣뭤뭥뭦뭧뭨뭩뭪뭫뭬뭭뭮뭯뭰뭱뭲뭳뭴뭵뭶뭷뭸뭹뭺뭻뭼뭽뭾뭿뮀뮁뮂뮃뮄뮅뮆뮇뮈뮉뮊뮋뮌뮍뮎뮏뮐뮑뮒뮓뮔뮕뮖뮗뮘뮙뮚뮛뮜뮝뮞뮟뮠뮡뮢뮣뮤뮥뮦뮧뮨뮩뮪뮫뮬뮭뮮뮯뮰뮱뮲뮳뮴뮵뮶뮷뮸뮹뮺뮻뮼뮽뮾뮿므믁믂믃믄믅믆믇믈믉믊믋믌믍믎믏믐믑믒믓믔믕믖믗믘믙믚믛믜믝믞믟믠믡믢믣믤믥믦믧믨믩믪믫믬믭믮믯믰믱믲믳믴믵믶믷미믹믺믻민믽믾믿밀밁밂밃밄밅밆밇밈밉밊밋밌밍밎및밐밑밒밓바박밖밗반밙밚받발밝밞밟밠밡밢밣밤밥밦밧밨방밪밫밬밭밮밯배백밲밳밴밵밶밷밸밹밺밻밼밽밾밿뱀뱁뱂뱃뱄뱅뱆뱇뱈뱉뱊뱋뱌뱍뱎뱏뱐뱑뱒뱓뱔뱕뱖뱗뱘뱙뱚뱛뱜뱝뱞뱟뱠뱡뱢뱣뱤뱥뱦뱧뱨뱩뱪뱫뱬뱭뱮뱯뱰뱱뱲뱳뱴뱵뱶뱷뱸뱹뱺뱻뱼뱽뱾뱿벀벁벂벃버벅벆벇번벉벊벋벌벍벎벏벐벑벒벓범법벖벗벘벙벚벛벜벝벞벟베벡벢벣벤벥벦벧벨벩벪벫벬벭벮벯벰벱벲벳벴벵벶벷벸벹벺벻벼벽벾벿변볁볂볃별볅볆볇볈볉볊볋볌볍볎볏볐병볒볓볔볕볖볗볘볙볚볛볜볝볞볟볠볡볢볣볤볥볦볧볨볩볪볫볬볭볮볯볰볱볲볳보복볶볷본볹볺볻볼볽볾볿봀봁봂봃봄봅봆봇봈봉봊봋봌봍봎봏봐봑봒봓봔봕봖봗봘봙봚봛봜봝봞봟봠봡봢봣봤봥봦봧봨봩봪봫봬봭봮봯봰봱봲봳봴봵봶봷봸봹봺봻봼봽봾봿뵀뵁뵂뵃뵄뵅뵆뵇뵈뵉뵊뵋뵌뵍뵎뵏뵐뵑뵒뵓뵔뵕뵖뵗뵘뵙뵚뵛뵜뵝뵞뵟뵠뵡뵢뵣뵤뵥뵦뵧뵨뵩뵪뵫뵬뵭뵮뵯뵰뵱뵲뵳뵴뵵뵶뵷뵸뵹뵺뵻뵼뵽뵾뵿부북붂붃분붅붆붇불붉붊붋붌붍붎붏붐붑붒붓붔붕붖붗붘붙붚붛붜붝붞붟붠붡붢붣붤붥붦붧붨붩붪붫붬붭붮붯붰붱붲붳붴붵붶붷붸붹붺붻붼붽붾붿뷀뷁뷂뷃뷄뷅뷆뷇뷈뷉뷊뷋뷌뷍뷎뷏뷐뷑뷒뷓뷔뷕뷖뷗뷘뷙뷚뷛뷜뷝뷞뷟뷠뷡뷢뷣뷤뷥뷦뷧뷨뷩뷪뷫뷬뷭뷮뷯뷰뷱뷲뷳뷴뷵뷶뷷뷸뷹뷺뷻뷼뷽뷾뷿븀븁븂븃븄븅븆븇븈븉븊븋브븍븎븏븐븑븒븓블븕븖븗븘븙븚븛븜븝븞븟븠븡븢븣븤븥븦븧븨븩븪븫븬븭븮븯븰븱븲븳븴븵븶븷븸븹븺븻븼븽븾븿빀빁빂빃비빅빆빇빈빉빊빋빌빍빎빏빐빑빒빓빔빕빖빗빘빙빚빛빜빝빞빟빠빡빢빣빤빥빦빧빨빩빪빫빬빭빮빯빰빱빲빳빴빵빶빷빸빹빺빻빼빽빾빿뺀뺁뺂뺃뺄뺅뺆뺇뺈뺉뺊뺋뺌뺍뺎뺏뺐뺑뺒뺓뺔뺕뺖뺗뺘뺙뺚뺛뺜뺝뺞뺟뺠뺡뺢뺣뺤뺥뺦뺧뺨뺩뺪뺫뺬뺭뺮뺯뺰뺱뺲뺳뺴뺵뺶뺷뺸뺹뺺뺻뺼뺽뺾뺿뻀뻁뻂뻃뻄뻅뻆뻇뻈뻉뻊뻋뻌뻍뻎뻏뻐뻑뻒뻓뻔뻕뻖뻗뻘뻙뻚뻛뻜뻝뻞뻟뻠뻡뻢뻣뻤뻥뻦뻧뻨뻩뻪뻫뻬뻭뻮뻯뻰뻱뻲뻳뻴뻵뻶뻷뻸뻹뻺뻻뻼뻽뻾뻿뼀뼁뼂뼃뼄뼅뼆뼇뼈뼉뼊뼋뼌뼍뼎뼏뼐뼑뼒뼓뼔뼕뼖뼗뼘뼙뼚뼛뼜뼝뼞뼟뼠뼡뼢뼣뼤뼥뼦뼧뼨뼩뼪뼫뼬뼭뼮뼯뼰뼱뼲뼳뼴뼵뼶뼷뼸뼹뼺뼻뼼뼽뼾뼿뽀뽁뽂뽃뽄뽅뽆뽇뽈뽉뽊뽋뽌뽍뽎뽏뽐뽑뽒뽓뽔뽕뽖뽗뽘뽙뽚뽛뽜뽝뽞뽟뽠뽡뽢뽣뽤뽥뽦뽧뽨뽩뽪뽫뽬뽭뽮뽯뽰뽱뽲뽳뽴뽵뽶뽷뽸뽹뽺뽻뽼뽽뽾뽿뾀뾁뾂뾃뾄뾅뾆뾇뾈뾉뾊뾋뾌뾍뾎뾏뾐뾑뾒뾓뾔뾕뾖뾗뾘뾙뾚뾛뾜뾝뾞뾟뾠뾡뾢뾣뾤뾥뾦뾧뾨뾩뾪뾫뾬뾭뾮뾯뾰뾱뾲뾳뾴뾵뾶뾷뾸뾹뾺뾻뾼뾽뾾뾿뿀뿁뿂뿃뿄뿅뿆뿇뿈뿉뿊뿋뿌뿍뿎뿏뿐뿑뿒뿓뿔뿕뿖뿗뿘뿙뿚뿛뿜뿝뿞뿟뿠뿡뿢뿣뿤뿥뿦뿧뿨뿩뿪뿫뿬뿭뿮뿯뿰뿱뿲뿳뿴뿵뿶뿷뿸뿹뿺뿻뿼뿽뿾뿿쀀쀁쀂쀃쀄쀅쀆쀇쀈쀉쀊쀋쀌쀍쀎쀏쀐쀑쀒쀓쀔쀕쀖쀗쀘쀙쀚쀛쀜쀝쀞쀟쀠쀡쀢쀣쀤쀥쀦쀧쀨쀩쀪쀫쀬쀭쀮쀯쀰쀱쀲쀳쀴쀵쀶쀷쀸쀹쀺쀻쀼쀽쀾쀿쁀쁁쁂쁃쁄쁅쁆쁇쁈쁉쁊쁋쁌쁍쁎쁏쁐쁑쁒쁓쁔쁕쁖쁗쁘쁙쁚쁛쁜쁝쁞쁟쁠쁡쁢쁣쁤쁥쁦쁧쁨쁩쁪쁫쁬쁭쁮쁯쁰쁱쁲쁳쁴쁵쁶쁷쁸쁹쁺쁻쁼쁽쁾쁿삀삁삂삃삄삅삆삇삈삉삊삋삌삍삎삏삐삑삒삓삔삕삖삗삘삙삚삛삜삝삞삟삠삡삢삣삤삥삦삧삨삩삪삫사삭삮삯산삱삲삳살삵삶삷삸삹삺삻삼삽삾삿샀상샂샃샄샅샆샇새색샊샋샌샍샎샏샐샑샒샓샔샕샖샗샘샙샚샛샜생샞샟샠샡샢샣샤샥샦샧샨샩샪샫샬샭샮샯샰샱샲샳샴샵샶샷샸샹샺샻샼샽샾샿섀섁섂섃섄섅섆섇섈섉섊섋섌섍섎섏섐섑섒섓섔섕섖섗섘섙섚섛서석섞섟선섡섢섣설섥섦섧섨섩섪섫섬섭섮섯섰성섲섳섴섵섶섷세섹섺섻센섽섾섿셀셁셂셃셄셅셆셇셈셉셊셋셌셍셎셏셐셑셒셓셔셕셖셗션셙셚셛셜셝셞셟셠셡셢셣셤셥셦셧셨셩셪셫셬셭셮셯셰셱셲셳셴셵셶셷셸셹셺셻셼셽셾셿솀솁솂솃솄솅솆솇솈솉솊솋소속솎솏손솑솒솓솔솕솖솗솘솙솚솛솜솝솞솟솠송솢솣솤솥솦솧솨솩솪솫솬솭솮솯솰솱솲솳솴솵솶솷솸솹솺솻솼솽솾솿쇀쇁쇂쇃쇄쇅쇆쇇쇈쇉쇊쇋쇌쇍쇎쇏쇐쇑쇒쇓쇔쇕쇖쇗쇘쇙쇚쇛쇜쇝쇞쇟쇠쇡쇢쇣쇤쇥쇦쇧쇨쇩쇪쇫쇬쇭쇮쇯쇰쇱쇲쇳쇴쇵쇶쇷쇸쇹쇺쇻쇼쇽쇾쇿숀숁숂숃숄숅숆숇숈숉숊숋숌숍숎숏숐숑숒숓숔숕숖숗수숙숚숛순숝숞숟술숡숢숣숤숥숦숧숨숩숪숫숬숭숮숯숰숱숲숳숴숵숶숷숸숹숺숻숼숽숾숿쉀쉁쉂쉃쉄쉅쉆쉇쉈쉉쉊쉋쉌쉍쉎쉏쉐쉑쉒쉓쉔쉕쉖쉗쉘쉙쉚쉛쉜쉝쉞쉟쉠쉡쉢쉣쉤쉥쉦쉧쉨쉩쉪쉫쉬쉭쉮쉯쉰쉱쉲쉳쉴쉵쉶쉷쉸쉹쉺쉻쉼쉽쉾쉿슀슁슂슃슄슅슆슇슈슉슊슋슌슍슎슏슐슑슒슓슔슕슖슗슘슙슚슛슜슝슞슟슠슡슢슣스슥슦슧슨슩슪슫슬슭슮슯슰슱슲슳슴습슶슷슸승슺슻슼슽슾슿싀싁싂싃싄싅싆싇싈싉싊싋싌싍싎싏싐싑싒싓싔싕싖싗싘싙싚싛시식싞싟신싡싢싣실싥싦싧싨싩싪싫심십싮싯싰싱싲싳싴싵싶싷싸싹싺싻싼싽싾싿쌀쌁쌂쌃쌄쌅쌆쌇쌈쌉쌊쌋쌌쌍쌎쌏쌐쌑쌒쌓쌔쌕쌖쌗쌘쌙쌚쌛쌜쌝쌞쌟쌠쌡쌢쌣쌤쌥쌦쌧쌨쌩쌪쌫쌬쌭쌮쌯쌰쌱쌲쌳쌴쌵쌶쌷쌸쌹쌺쌻쌼쌽쌾쌿썀썁썂썃썄썅썆썇썈썉썊썋썌썍썎썏썐썑썒썓썔썕썖썗썘썙썚썛썜썝썞썟썠썡썢썣썤썥썦썧써썩썪썫썬썭썮썯썰썱썲썳썴썵썶썷썸썹썺썻썼썽썾썿쎀쎁쎂쎃쎄쎅쎆쎇쎈쎉쎊쎋쎌쎍쎎쎏쎐쎑쎒쎓쎔쎕쎖쎗쎘쎙쎚쎛쎜쎝쎞쎟쎠쎡쎢쎣쎤쎥쎦쎧쎨쎩쎪쎫쎬쎭쎮쎯쎰쎱쎲쎳쎴쎵쎶쎷쎸쎹쎺쎻쎼쎽쎾쎿쏀쏁쏂쏃쏄쏅쏆쏇쏈쏉쏊쏋쏌쏍쏎쏏쏐쏑쏒쏓쏔쏕쏖쏗쏘쏙쏚쏛쏜쏝쏞쏟쏠쏡쏢쏣쏤쏥쏦쏧쏨쏩쏪쏫쏬쏭쏮쏯쏰쏱쏲쏳쏴쏵쏶쏷쏸쏹쏺쏻쏼쏽쏾쏿쐀쐁쐂쐃쐄쐅쐆쐇쐈쐉쐊쐋쐌쐍쐎쐏쐐쐑쐒쐓쐔쐕쐖쐗쐘쐙쐚쐛쐜쐝쐞쐟쐠쐡쐢쐣쐤쐥쐦쐧쐨쐩쐪쐫쐬쐭쐮쐯쐰쐱쐲쐳쐴쐵쐶쐷쐸쐹쐺쐻쐼쐽쐾쐿쑀쑁쑂쑃쑄쑅쑆쑇쑈쑉쑊쑋쑌쑍쑎쑏쑐쑑쑒쑓쑔쑕쑖쑗쑘쑙쑚쑛쑜쑝쑞쑟쑠쑡쑢쑣쑤쑥쑦쑧쑨쑩쑪쑫쑬쑭쑮쑯쑰쑱쑲쑳쑴쑵쑶쑷쑸쑹쑺쑻쑼쑽쑾쑿쒀쒁쒂쒃쒄쒅쒆쒇쒈쒉쒊쒋쒌쒍쒎쒏쒐쒑쒒쒓쒔쒕쒖쒗쒘쒙쒚쒛쒜쒝쒞쒟쒠쒡쒢쒣쒤쒥쒦쒧쒨쒩쒪쒫쒬쒭쒮쒯쒰쒱쒲쒳쒴쒵쒶쒷쒸쒹쒺쒻쒼쒽쒾쒿쓀쓁쓂쓃쓄쓅쓆쓇쓈쓉쓊쓋쓌쓍쓎쓏쓐쓑쓒쓓쓔쓕쓖쓗쓘쓙쓚쓛쓜쓝쓞쓟쓠쓡쓢쓣쓤쓥쓦쓧쓨쓩쓪쓫쓬쓭쓮쓯쓰쓱쓲쓳쓴쓵쓶쓷쓸쓹쓺쓻쓼쓽쓾쓿씀씁씂씃씄씅씆씇씈씉씊씋씌씍씎씏씐씑씒씓씔씕씖씗씘씙씚씛씜씝씞씟씠씡씢씣씤씥씦씧씨씩씪씫씬씭씮씯씰씱씲씳씴씵씶씷씸씹씺씻씼씽씾씿앀앁앂앃아악앆앇안앉않앋알앍앎앏앐앑앒앓암압앖앗았앙앚앛앜앝앞앟애액앢앣앤앥앦앧앨앩앪앫앬앭앮앯앰앱앲앳앴앵앶앷앸앹앺앻야약앾앿얀얁얂얃얄얅얆얇얈얉얊얋얌얍얎얏얐양얒얓얔얕얖얗얘얙얚얛얜얝얞얟얠얡얢얣얤얥얦얧얨얩얪얫얬얭얮얯얰얱얲얳어억얶얷언얹얺얻얼얽얾얿엀엁엂엃엄업없엇었엉엊엋엌엍엎엏에엑엒엓엔엕엖엗엘엙엚엛엜엝엞엟엠엡엢엣엤엥엦엧엨엩엪엫여역엮엯연엱엲엳열엵엶엷엸엹엺엻염엽엾엿였영옂옃옄옅옆옇예옉옊옋옌옍옎옏옐옑옒옓옔옕옖옗옘옙옚옛옜옝옞옟옠옡옢옣오옥옦옧온옩옪옫올옭옮옯옰옱옲옳옴옵옶옷옸옹옺옻옼옽옾옿와왁왂왃완왅왆왇왈왉왊왋왌왍왎왏왐왑왒왓왔왕왖왗왘왙왚왛왜왝왞왟왠왡왢왣왤왥왦왧왨왩왪왫왬왭왮왯왰왱왲왳왴왵왶왷외왹왺왻왼왽왾왿욀욁욂욃욄욅욆욇욈욉욊욋욌욍욎욏욐욑욒욓요욕욖욗욘욙욚욛욜욝욞욟욠욡욢욣욤욥욦욧욨용욪욫욬욭욮욯우욱욲욳운욵욶욷울욹욺욻욼욽욾욿움웁웂웃웄웅웆웇웈웉웊웋워웍웎웏원웑웒웓월웕웖웗웘웙웚웛웜웝웞웟웠웡웢웣웤웥웦웧웨웩웪웫웬웭웮웯웰웱웲웳웴웵웶웷웸웹웺웻웼웽웾웿윀윁윂윃위윅윆윇윈윉윊윋윌윍윎윏윐윑윒윓윔윕윖윗윘윙윚윛윜윝윞윟유육윢윣윤윥윦윧율윩윪윫윬윭윮윯윰윱윲윳윴융윶윷윸윹윺윻으윽윾윿은읁읂읃을읅읆읇읈읉읊읋음읍읎읏읐응읒읓읔읕읖읗의읙읚읛읜읝읞읟읠읡읢읣읤읥읦읧읨읩읪읫읬읭읮읯읰읱읲읳이익읶읷인읹읺읻일읽읾읿잀잁잂잃임입잆잇있잉잊잋잌잍잎잏자작잒잓잔잕잖잗잘잙잚잛잜잝잞잟잠잡잢잣잤장잦잧잨잩잪잫재잭잮잯잰잱잲잳잴잵잶잷잸잹잺잻잼잽잾잿쟀쟁쟂쟃쟄쟅쟆쟇쟈쟉쟊쟋쟌쟍쟎쟏쟐쟑쟒쟓쟔쟕쟖쟗쟘쟙쟚쟛쟜쟝쟞쟟쟠쟡쟢쟣쟤쟥쟦쟧쟨쟩쟪쟫쟬쟭쟮쟯쟰쟱쟲쟳쟴쟵쟶쟷쟸쟹쟺쟻쟼쟽쟾쟿저적젂젃전젅젆젇절젉젊젋젌젍젎젏점접젒젓젔정젖젗젘젙젚젛제젝젞젟젠젡젢젣젤젥젦젧젨젩젪젫젬젭젮젯젰젱젲젳젴젵젶젷져젹젺젻젼젽젾젿졀졁졂졃졄졅졆졇졈졉졊졋졌졍졎졏졐졑졒졓졔졕졖졗졘졙졚졛졜졝졞졟졠졡졢졣졤졥졦졧졨졩졪졫졬졭졮졯조족졲졳존졵졶졷졸졹졺졻졼졽졾졿좀좁좂좃좄종좆좇좈좉좊좋좌좍좎좏좐좑좒좓좔좕좖좗좘좙좚좛좜좝좞좟좠좡좢좣좤좥좦좧좨좩좪좫좬좭좮좯좰좱좲좳좴좵좶좷좸좹좺좻좼좽좾좿죀죁죂죃죄죅죆죇죈죉죊죋죌죍죎죏죐죑죒죓죔죕죖죗죘죙죚죛죜죝죞죟죠죡죢죣죤죥죦죧죨죩죪죫죬죭죮죯죰죱죲죳죴죵죶죷죸죹죺죻주죽죾죿준줁줂줃줄줅줆줇줈줉줊줋줌줍줎줏줐중줒줓줔줕줖줗줘줙줚줛줜줝줞줟줠줡줢줣줤줥줦줧줨줩줪줫줬줭줮줯줰줱줲줳줴줵줶줷줸줹줺줻줼줽줾줿쥀쥁쥂쥃쥄쥅쥆쥇쥈쥉쥊쥋쥌쥍쥎쥏쥐쥑쥒쥓쥔쥕쥖쥗쥘쥙쥚쥛쥜쥝쥞쥟쥠쥡쥢쥣쥤쥥쥦쥧쥨쥩쥪쥫쥬쥭쥮쥯쥰쥱쥲쥳쥴쥵쥶쥷쥸쥹쥺쥻쥼쥽쥾쥿즀즁즂즃즄즅즆즇즈즉즊즋즌즍즎즏즐즑즒즓즔즕즖즗즘즙즚즛즜증즞즟즠즡즢즣즤즥즦즧즨즩즪즫즬즭즮즯즰즱즲즳즴즵즶즷즸즹즺즻즼즽즾즿지직짂짃진짅짆짇질짉짊짋짌짍짎짏짐집짒짓짔징짖짗짘짙짚짛짜짝짞짟짠짡짢짣짤짥짦짧짨짩짪짫짬짭짮짯짰짱짲짳짴짵짶짷째짹짺짻짼짽짾짿쨀쨁쨂쨃쨄쨅쨆쨇쨈쨉쨊쨋쨌쨍쨎쨏쨐쨑쨒쨓쨔쨕쨖쨗쨘쨙쨚쨛쨜쨝쨞쨟쨠쨡쨢쨣쨤쨥쨦쨧쨨쨩쨪쨫쨬쨭쨮쨯쨰쨱쨲쨳쨴쨵쨶쨷쨸쨹쨺쨻쨼쨽쨾쨿쩀쩁쩂쩃쩄쩅쩆쩇쩈쩉쩊쩋쩌쩍쩎쩏쩐쩑쩒쩓쩔쩕쩖쩗쩘쩙쩚쩛쩜쩝쩞쩟쩠쩡쩢쩣쩤쩥쩦쩧쩨쩩쩪쩫쩬쩭쩮쩯쩰쩱쩲쩳쩴쩵쩶쩷쩸쩹쩺쩻쩼쩽쩾쩿쪀쪁쪂쪃쪄쪅쪆쪇쪈쪉쪊쪋쪌쪍쪎쪏쪐쪑쪒쪓쪔쪕쪖쪗쪘쪙쪚쪛쪜쪝쪞쪟쪠쪡쪢쪣쪤쪥쪦쪧쪨쪩쪪쪫쪬쪭쪮쪯쪰쪱쪲쪳쪴쪵쪶쪷쪸쪹쪺쪻쪼쪽쪾쪿쫀쫁쫂쫃쫄쫅쫆쫇쫈쫉쫊쫋쫌쫍쫎쫏쫐쫑쫒쫓쫔쫕쫖쫗쫘쫙쫚쫛쫜쫝쫞쫟쫠쫡쫢쫣쫤쫥쫦쫧쫨쫩쫪쫫쫬쫭쫮쫯쫰쫱쫲쫳쫴쫵쫶쫷쫸쫹쫺쫻쫼쫽쫾쫿쬀쬁쬂쬃쬄쬅쬆쬇쬈쬉쬊쬋쬌쬍쬎쬏쬐쬑쬒쬓쬔쬕쬖쬗쬘쬙쬚쬛쬜쬝쬞쬟쬠쬡쬢쬣쬤쬥쬦쬧쬨쬩쬪쬫쬬쬭쬮쬯쬰쬱쬲쬳쬴쬵쬶쬷쬸쬹쬺쬻쬼쬽쬾쬿쭀쭁쭂쭃쭄쭅쭆쭇쭈쭉쭊쭋쭌쭍쭎쭏쭐쭑쭒쭓쭔쭕쭖쭗쭘쭙쭚쭛쭜쭝쭞쭟쭠쭡쭢쭣쭤쭥쭦쭧쭨쭩쭪쭫쭬쭭쭮쭯쭰쭱쭲쭳쭴쭵쭶쭷쭸쭹쭺쭻쭼쭽쭾쭿쮀쮁쮂쮃쮄쮅쮆쮇쮈쮉쮊쮋쮌쮍쮎쮏쮐쮑쮒쮓쮔쮕쮖쮗쮘쮙쮚쮛쮜쮝쮞쮟쮠쮡쮢쮣쮤쮥쮦쮧쮨쮩쮪쮫쮬쮭쮮쮯쮰쮱쮲쮳쮴쮵쮶쮷쮸쮹쮺쮻쮼쮽쮾쮿쯀쯁쯂쯃쯄쯅쯆쯇쯈쯉쯊쯋쯌쯍쯎쯏쯐쯑쯒쯓쯔쯕쯖쯗쯘쯙쯚쯛쯜쯝쯞쯟쯠쯡쯢쯣쯤쯥쯦쯧쯨쯩쯪쯫쯬쯭쯮쯯쯰쯱쯲쯳쯴쯵쯶쯷쯸쯹쯺쯻쯼쯽쯾쯿찀찁찂찃찄찅찆찇찈찉찊찋찌찍찎찏찐찑찒찓찔찕찖찗찘찙찚찛찜찝찞찟찠찡찢찣찤찥찦찧차착찪찫찬찭찮찯찰찱찲찳찴찵찶찷참찹찺찻찼창찾찿챀챁챂챃채책챆챇챈챉챊챋챌챍챎챏챐챑챒챓챔챕챖챗챘챙챚챛챜챝챞챟챠챡챢챣챤챥챦챧챨챩챪챫챬챭챮챯챰챱챲챳챴챵챶챷챸챹챺챻챼챽챾챿첀첁첂첃첄첅첆첇첈첉첊첋첌첍첎첏첐첑첒첓첔첕첖첗처척첚첛천첝첞첟철첡첢첣첤첥첦첧첨첩첪첫첬청첮첯첰첱첲첳체첵첶첷첸첹첺첻첼첽첾첿쳀쳁쳂쳃쳄쳅쳆쳇쳈쳉쳊쳋쳌쳍쳎쳏쳐쳑쳒쳓쳔쳕쳖쳗쳘쳙쳚쳛쳜쳝쳞쳟쳠쳡쳢쳣쳤쳥쳦쳧쳨쳩쳪쳫쳬쳭쳮쳯쳰쳱쳲쳳쳴쳵쳶쳷쳸쳹쳺쳻쳼쳽쳾쳿촀촁촂촃촄촅촆촇초촉촊촋촌촍촎촏촐촑촒촓촔촕촖촗촘촙촚촛촜총촞촟촠촡촢촣촤촥촦촧촨촩촪촫촬촭촮촯촰촱촲촳촴촵촶촷촸촹촺촻촼촽촾촿쵀쵁쵂쵃쵄쵅쵆쵇쵈쵉쵊쵋쵌쵍쵎쵏쵐쵑쵒쵓쵔쵕쵖쵗쵘쵙쵚쵛최쵝쵞쵟쵠쵡쵢쵣쵤쵥쵦쵧쵨쵩쵪쵫쵬쵭쵮쵯쵰쵱쵲쵳쵴쵵쵶쵷쵸쵹쵺쵻쵼쵽쵾쵿춀춁춂춃춄춅춆춇춈춉춊춋춌춍춎춏춐춑춒춓추축춖춗춘춙춚춛출춝춞춟춠춡춢춣춤춥춦춧춨충춪춫춬춭춮춯춰춱춲춳춴춵춶춷춸춹춺춻춼춽춾춿췀췁췂췃췄췅췆췇췈췉췊췋췌췍췎췏췐췑췒췓췔췕췖췗췘췙췚췛췜췝췞췟췠췡췢췣췤췥췦췧취췩췪췫췬췭췮췯췰췱췲췳췴췵췶췷췸췹췺췻췼췽췾췿츀츁츂츃츄츅츆츇츈츉츊츋츌츍츎츏츐츑츒츓츔츕츖츗츘츙츚츛츜츝츞츟츠측츢츣츤츥츦츧츨츩츪츫츬츭츮츯츰츱츲츳츴층츶츷츸츹츺츻츼츽츾츿칀칁칂칃칄칅칆칇칈칉칊칋칌칍칎칏칐칑칒칓칔칕칖칗치칙칚칛친칝칞칟칠칡칢칣칤칥칦칧침칩칪칫칬칭칮칯칰칱칲칳카칵칶칷칸칹칺칻칼칽칾칿캀캁캂캃캄캅캆캇캈캉캊캋캌캍캎캏캐캑캒캓캔캕캖캗캘캙캚캛캜캝캞캟캠캡캢캣캤캥캦캧캨캩캪캫캬캭캮캯캰캱캲캳캴캵캶캷캸캹캺캻캼캽캾캿컀컁컂컃컄컅컆컇컈컉컊컋컌컍컎컏컐컑컒컓컔컕컖컗컘컙컚컛컜컝컞컟컠컡컢컣커컥컦컧컨컩컪컫컬컭컮컯컰컱컲컳컴컵컶컷컸컹컺컻컼컽컾컿케켁켂켃켄켅켆켇켈켉켊켋켌켍켎켏켐켑켒켓켔켕켖켗켘켙켚켛켜켝켞켟켠켡켢켣켤켥켦켧켨켩켪켫켬켭켮켯켰켱켲켳켴켵켶켷켸켹켺켻켼켽켾켿콀콁콂콃콄콅콆콇콈콉콊콋콌콍콎콏콐콑콒콓코콕콖콗콘콙콚콛콜콝콞콟콠콡콢콣콤콥콦콧콨콩콪콫콬콭콮콯콰콱콲콳콴콵콶콷콸콹콺콻콼콽콾콿쾀쾁쾂쾃쾄쾅쾆쾇쾈쾉쾊쾋쾌쾍쾎쾏쾐쾑쾒쾓쾔쾕쾖쾗쾘쾙쾚쾛쾜쾝쾞쾟쾠쾡쾢쾣쾤쾥쾦쾧쾨쾩쾪쾫쾬쾭쾮쾯쾰쾱쾲쾳쾴쾵쾶쾷쾸쾹쾺쾻쾼쾽쾾쾿쿀쿁쿂쿃쿄쿅쿆쿇쿈쿉쿊쿋쿌쿍쿎쿏쿐쿑쿒쿓쿔쿕쿖쿗쿘쿙쿚쿛쿜쿝쿞쿟쿠쿡쿢쿣쿤쿥쿦쿧쿨쿩쿪쿫쿬쿭쿮쿯쿰쿱쿲쿳쿴쿵쿶쿷쿸쿹쿺쿻쿼쿽쿾쿿퀀퀁퀂퀃퀄퀅퀆퀇퀈퀉퀊퀋퀌퀍퀎퀏퀐퀑퀒퀓퀔퀕퀖퀗퀘퀙퀚퀛퀜퀝퀞퀟퀠퀡퀢퀣퀤퀥퀦퀧퀨퀩퀪퀫퀬퀭퀮퀯퀰퀱퀲퀳퀴퀵퀶퀷퀸퀹퀺퀻퀼퀽퀾퀿큀큁큂큃큄큅큆큇큈큉큊큋큌큍큎큏큐큑큒큓큔큕큖큗큘큙큚큛큜큝큞큟큠큡큢큣큤큥큦큧큨큩큪큫크큭큮큯큰큱큲큳클큵큶큷큸큹큺큻큼큽큾큿킀킁킂킃킄킅킆킇킈킉킊킋킌킍킎킏킐킑킒킓킔킕킖킗킘킙킚킛킜킝킞킟킠킡킢킣키킥킦킧킨킩킪킫킬킭킮킯킰킱킲킳킴킵킶킷킸킹킺킻킼킽킾킿타탁탂탃탄탅탆탇탈탉탊탋탌탍탎탏탐탑탒탓탔탕탖탗탘탙탚탛태택탞탟탠탡탢탣탤탥탦탧탨탩탪탫탬탭탮탯탰탱탲탳탴탵탶탷탸탹탺탻탼탽탾탿턀턁턂턃턄턅턆턇턈턉턊턋턌턍턎턏턐턑턒턓턔턕턖턗턘턙턚턛턜턝턞턟턠턡턢턣턤턥턦턧턨턩턪턫턬턭턮턯터턱턲턳턴턵턶턷털턹턺턻턼턽턾턿텀텁텂텃텄텅텆텇텈텉텊텋테텍텎텏텐텑텒텓텔텕텖텗텘텙텚텛템텝텞텟텠텡텢텣텤텥텦텧텨텩텪텫텬텭텮텯텰텱텲텳텴텵텶텷텸텹텺텻텼텽텾텿톀톁톂톃톄톅톆톇톈톉톊톋톌톍톎톏톐톑톒톓톔톕톖톗톘톙톚톛톜톝톞톟토톡톢톣톤톥톦톧톨톩톪톫톬톭톮톯톰톱톲톳톴통톶톷톸톹톺톻톼톽톾톿퇀퇁퇂퇃퇄퇅퇆퇇퇈퇉퇊퇋퇌퇍퇎퇏퇐퇑퇒퇓퇔퇕퇖퇗퇘퇙퇚퇛퇜퇝퇞퇟퇠퇡퇢퇣퇤퇥퇦퇧퇨퇩퇪퇫퇬퇭퇮퇯퇰퇱퇲퇳퇴퇵퇶퇷퇸퇹퇺퇻퇼퇽퇾퇿툀툁툂툃툄툅툆툇툈툉툊툋툌툍툎툏툐툑툒툓툔툕툖툗툘툙툚툛툜툝툞툟툠툡툢툣툤툥툦툧툨툩툪툫투툭툮툯툰툱툲툳툴툵툶툷툸툹툺툻툼툽툾툿퉀퉁퉂퉃퉄퉅퉆퉇퉈퉉퉊퉋퉌퉍퉎퉏퉐퉑퉒퉓퉔퉕퉖퉗퉘퉙퉚퉛퉜퉝퉞퉟퉠퉡퉢퉣퉤퉥퉦퉧퉨퉩퉪퉫퉬퉭퉮퉯퉰퉱퉲퉳퉴퉵퉶퉷퉸퉹퉺퉻퉼퉽퉾퉿튀튁튂튃튄튅튆튇튈튉튊튋튌튍튎튏튐튑튒튓튔튕튖튗튘튙튚튛튜튝튞튟튠튡튢튣튤튥튦튧튨튩튪튫튬튭튮튯튰튱튲튳튴튵튶튷트특튺튻튼튽튾튿틀틁틂틃틄틅틆틇틈틉틊틋틌틍틎틏틐틑틒틓틔틕틖틗틘틙틚틛틜틝틞틟틠틡틢틣틤틥틦틧틨틩틪틫틬틭틮틯티틱틲틳틴틵틶틷틸틹틺틻틼틽틾틿팀팁팂팃팄팅팆팇팈팉팊팋파팍팎팏판팑팒팓팔팕팖팗팘팙팚팛팜팝팞팟팠팡팢팣팤팥팦팧패팩팪팫팬팭팮팯팰팱팲팳팴팵팶팷팸팹팺팻팼팽팾팿퍀퍁퍂퍃퍄퍅퍆퍇퍈퍉퍊퍋퍌퍍퍎퍏퍐퍑퍒퍓퍔퍕퍖퍗퍘퍙퍚퍛퍜퍝퍞퍟퍠퍡퍢퍣퍤퍥퍦퍧퍨퍩퍪퍫퍬퍭퍮퍯퍰퍱퍲퍳퍴퍵퍶퍷퍸퍹퍺퍻퍼퍽퍾퍿펀펁펂펃펄펅펆펇펈펉펊펋펌펍펎펏펐펑펒펓펔펕펖펗페펙펚펛펜펝펞펟펠펡펢펣펤펥펦펧펨펩펪펫펬펭펮펯펰펱펲펳펴펵펶펷편펹펺펻펼펽펾펿폀폁폂폃폄폅폆폇폈평폊폋폌폍폎폏폐폑폒폓폔폕폖폗폘폙폚폛폜폝폞폟폠폡폢폣폤폥폦폧폨폩폪폫포폭폮폯폰폱폲폳폴폵폶폷폸폹폺폻폼폽폾폿퐀퐁퐂퐃퐄퐅퐆퐇퐈퐉퐊퐋퐌퐍퐎퐏퐐퐑퐒퐓퐔퐕퐖퐗퐘퐙퐚퐛퐜퐝퐞퐟퐠퐡퐢퐣퐤퐥퐦퐧퐨퐩퐪퐫퐬퐭퐮퐯퐰퐱퐲퐳퐴퐵퐶퐷퐸퐹퐺퐻퐼퐽퐾퐿푀푁푂푃푄푅푆푇푈푉푊푋푌푍푎푏푐푑푒푓푔푕푖푗푘푙푚푛표푝푞푟푠푡푢푣푤푥푦푧푨푩푪푫푬푭푮푯푰푱푲푳푴푵푶푷푸푹푺푻푼푽푾푿풀풁풂풃풄풅풆풇품풉풊풋풌풍풎풏풐풑풒풓풔풕풖풗풘풙풚풛풜풝풞풟풠풡풢풣풤풥풦풧풨풩풪풫풬풭풮풯풰풱풲풳풴풵풶풷풸풹풺풻풼풽풾풿퓀퓁퓂퓃퓄퓅퓆퓇퓈퓉퓊퓋퓌퓍퓎퓏퓐퓑퓒퓓퓔퓕퓖퓗퓘퓙퓚퓛퓜퓝퓞퓟퓠퓡퓢퓣퓤퓥퓦퓧퓨퓩퓪퓫퓬퓭퓮퓯퓰퓱퓲퓳퓴퓵퓶퓷퓸퓹퓺퓻퓼퓽퓾퓿픀픁픂픃프픅픆픇픈픉픊픋플픍픎픏픐픑픒픓픔픕픖픗픘픙픚픛픜픝픞픟픠픡픢픣픤픥픦픧픨픩픪픫픬픭픮픯픰픱픲픳픴픵픶픷픸픹픺픻피픽픾픿핀핁핂핃필핅핆핇핈핉핊핋핌핍핎핏핐핑핒핓핔핕핖핗하학핚핛한핝핞핟할핡핢핣핤핥핦핧함합핪핫핬항핮핯핰핱핲핳해핵핶핷핸핹핺핻핼핽핾핿햀햁햂햃햄햅햆햇했행햊햋햌햍햎햏햐햑햒햓햔햕햖햗햘햙햚햛햜햝햞햟햠햡햢햣햤향햦햧햨햩햪햫햬햭햮햯햰햱햲햳햴햵햶햷햸햹햺햻햼햽햾햿헀헁헂헃헄헅헆헇허헉헊헋헌헍헎헏헐헑헒헓헔헕헖헗험헙헚헛헜헝헞헟헠헡헢헣헤헥헦헧헨헩헪헫헬헭헮헯헰헱헲헳헴헵헶헷헸헹헺헻헼헽헾헿혀혁혂혃현혅혆혇혈혉혊혋혌혍혎혏혐협혒혓혔형혖혗혘혙혚혛혜혝혞혟혠혡혢혣혤혥혦혧혨혩혪혫혬혭혮혯혰혱혲혳혴혵혶혷호혹혺혻혼혽혾혿홀홁홂홃홄홅홆홇홈홉홊홋홌홍홎홏홐홑홒홓화확홖홗환홙홚홛활홝홞홟홠홡홢홣홤홥홦홧홨황홪홫홬홭홮홯홰홱홲홳홴홵홶홷홸홹홺홻홼홽홾홿횀횁횂횃횄횅횆횇횈횉횊횋회획횎횏횐횑횒횓횔횕횖횗횘횙횚횛횜횝횞횟횠횡횢횣횤횥횦횧효횩횪횫횬횭횮횯횰횱횲횳횴횵횶횷횸횹횺횻횼횽횾횿훀훁훂훃후훅훆훇훈훉훊훋훌훍훎훏훐훑훒훓훔훕훖훗훘훙훚훛훜훝훞훟훠훡훢훣훤훥훦훧훨훩훪훫훬훭훮훯훰훱훲훳훴훵훶훷훸훹훺훻훼훽훾훿휀휁휂휃휄휅휆휇휈휉휊휋휌휍휎휏휐휑휒휓휔휕휖휗휘휙휚휛휜휝휞휟휠휡휢휣휤휥휦휧휨휩휪휫휬휭휮휯휰휱휲휳휴휵휶휷휸휹휺휻휼휽휾휿흀흁흂흃흄흅흆흇흈흉흊흋흌흍흎흏흐흑흒흓흔흕흖흗흘흙흚흛흜흝흞흟흠흡흢흣흤흥흦흧흨흩흪흫희흭흮흯흰흱흲흳흴흵흶흷흸흹흺흻흼흽흾흿힀힁힂힃힄힅힆힇히힉힊힋힌힍힎힏힐힑힒힓힔힕힖힗힘힙힚힛힜힝힞힟힠힡힢힣ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀퟁퟂퟃퟄퟅퟆퟋퟌퟍퟎퟏퟐퟑퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜퟝퟞퟟퟠퟡퟢퟣퟤퟥퟦퟧퟨퟩퟪퟫퟬퟭퟮퟯퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????􏰀???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????豈更車賈滑串句龜龜契金喇奈懶癩羅蘿螺裸邏樂洛烙珞落酪駱亂卵欄爛蘭鸞嵐濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄鹿論壟弄籠聾牢磊賂雷壘屢樓淚漏累縷陋勒肋凜凌稜綾菱陵讀拏樂諾丹寧怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵呂女廬旅濾礪閭驪麗黎力曆歷轢年憐戀撚漣煉璉秊練聯輦蓮連鍊列劣咽烈裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂燎療蓼遼龍暈阮劉杻柳流溜琉留硫紐類六戮陸倫崙淪輪律慄栗率隆利吏履易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺切度拓糖宅洞暴輻行降見廓兀嗀﨎﨏塚﨑晴﨓﨔凞猪益礼神祥福靖精羽﨟蘒﨡諸﨣﨤逸都﨧﨨﨩飯飼館鶴郞隷侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎懲敏既暑梅海渚漢煮爫琢碑社祉祈祐祖祝禍禎穀突節練縉繁署者臭艹艹著褐視謁謹賓贈辶逸難響頻恵𤋮舘並况全侀充冀勇勺喝啕喙嗢塚墳奄奔婢嬨廒廙彩徭惘慎愈憎慠懲戴揄搜摒敖晴朗望杖歹殺流滛滋漢瀞煮瞧爵犯猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹變贈輸遲醙鉶陼難靖韛響頋頻鬒龜𢡊𢡄𣏕㮝䀘䀹𥉉𥳐𧻓齃龎fffiflffifflſtstﬓﬔﬕﬖﬗיִﬞײַﬠﬡﬢﬣﬤﬥﬦﬧﬨ﬩שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּטּיּךּכּלּמּנּסּףּפּצּקּרּשּתּוֹבֿכֿפֿﭏﭐﭑﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜﭝﭞﭟﭠﭡﭢﭣﭤﭥﭦﭧﭨﭩﭪﭫﭬﭭﭮﭯﭰﭱﭲﭳﭴﭵﭶﭷﭸﭹﭺﭻﭼﭽﭾﭿﮀﮁﮂﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌﮍﮎﮏﮐﮑﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜﮝﮞﮟﮠﮡﮢﮣﮤﮥﮦﮧﮨﮩﮪﮫﮬﮭﮮﮯﮰﮱ﮲﮳﮴﮵﮶﮷﮸﮹﮺﮻﮼﮽﮾﮿﯀﯁ﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜﯝﯞﯟﯠﯡﯢﯣﯤﯥﯦﯧﯨﯩﯪﯫﯬﯭﯮﯯﯰﯱﯲﯳﯴﯵﯶﯷﯸﯹﯺﯻﯼﯽﯾﯿﰀﰁﰂﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌﰍﰎﰏﰐﰑﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜﰝﰞﰟﰠﰡﰢﰣﰤﰥﰦﰧﰨﰩﰪﰫﰬﰭﰮﰯﰰﰱﰲﰳﰴﰵﰶﰷﰸﰹﰺﰻﰼﰽﰾﰿﱀﱁﱂﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌﱍﱎﱏﱐﱑﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜﱝﱞﱟﱠﱡﱢﱣﱤﱥﱦﱧﱨﱩﱪﱫﱬﱭﱮﱯﱰﱱﱲﱳﱴﱵﱶﱷﱸﱹﱺﱻﱼﱽﱾﱿﲀﲁﲂﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌﲍﲎﲏﲐﲑﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜﲝﲞﲟﲠﲡﲢﲣﲤﲥﲦﲧﲨﲩﲪﲫﲬﲭﲮﲯﲰﲱﲲﲳﲴﲵﲶﲷﲸﲹﲺﲻﲼﲽﲾﲿﳀﳁﳂﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌﳍﳎﳏﳐﳑﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜﳝﳞﳟﳠﳡﳢﳣﳤﳥﳦﳧﳨﳩﳪﳫﳬﳭﳮﳯﳰﳱﳲﳳﳴﳵﳶﳷﳸﳹﳺﳻﳼﳽﳾﳿﴀﴁﴂﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌﴍﴎﴏﴐﴑﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜﴝﴞﴟﴠﴡﴢﴣﴤﴥﴦﴧﴨﴩﴪﴫﴬﴭﴮﴯﴰﴱﴲﴳﴴﴵﴶﴷﴸﴹﴺﴻﴼﴽ﴾﴿ﵐﵑﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜﵝﵞﵟﵠﵡﵢﵣﵤﵥﵦﵧﵨﵩﵪﵫﵬﵭﵮﵯﵰﵱﵲﵳﵴﵵﵶﵷﵸﵹﵺﵻﵼﵽﵾﵿﶀﶁﶂﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌﶍﶎﶏﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜﶝﶞﶟﶠﶡﶢﶣﶤﶥﶦﶧﶨﶩﶪﶫﶬﶭﶮﶯﶰﶱﶲﶳﶴﶵﶶﶷﶸﶹﶺﶻﶼﶽﶾﶿﷀﷁﷂﷃﷄﷅﷆﷇﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ﷼﷽︀︁︂︃︄︅︆︇︈︉︊︋︌︍︎️︐︑︒︓︔︕︖︗︘︙︠︡︢︣︤︥︦︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌﹍﹎﹏﹐﹑﹒﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹨﹩﹪﹫ﹰﹱﹲﹳﹴﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⦅⦆。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑ하ᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ¢£¬ ̄¦¥₩│←↑→↓■○�‬‬‬ +#!#😀😃😁😎😒😞😍🙂😆😊😉 +#!#ऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसहऺऻ़ऽािीुूृॄॅॆेैॉॊोौ्ॎॏॐ॒॑॓॔ॕॖॗक़ख़ग़ज़ड़ढ़फ़य़ॠॡॢॣ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॹॺॻॼॽॾॿ +~~END~~ + +DROP TABLE TEXT_dt; diff --git a/contrib/test/JDBC/expected/TestThrow.out b/contrib/test/JDBC/expected/TestThrow.out new file mode 100644 index 00000000000..a046adc2b5e --- /dev/null +++ b/contrib/test/JDBC/expected/TestThrow.out @@ -0,0 +1,595 @@ +CREATE TABLE throwTable (a INT); +go + +CREATE PROC throwProc1 AS +BEGIN + INSERT INTO throwTable VALUES (1); + THROW 51000, 'Throw error', 1; + INSERT INTO throwTable VALUES (2); +END +go + +CREATE PROC throwProc2 AS +BEGIN + INSERT INTO throwTable VALUES (111); + EXEC throwProc1; + INSERT INTO throwTable VALUES (222); +END +go + +/* Error -- THORW; can only be called in CATCH block */ +THROW; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: THROW without parameters should be executed inside a CATCH block)~~ + + +/* Error -- THROW; can only be called in CATCH block */ +BEGIN TRY + THROW; +END TRY +BEGIN CATCH + THROW; +END CATCH +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: THROW without parameters should be executed inside a CATCH block)~~ + + +/* Re-throw current caught error */ +BEGIN TRY + THROW 50000, 'Throw error', 1; +END TRY +BEGIN CATCH + THROW; +END CATCH +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Throw error)~~ + + +/* Nested TRY...CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + PRINT 100/0; + END TRY + BEGIN CATCH + THROW 50000, 'Throw error', 1; + END CATCH +END TRY +BEGIN CATCH + THROW; +END CATCH +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Throw error)~~ + + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + PRINT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + THROW; + END TRY + BEGIN CATCH + THROW; + END CATCH +END CATCH +go +~~ERROR (Code: 8134)~~ + +~~ERROR (Message: division by zero)~~ + + + + +/* XACT_ABORT OFF */ +/* 1. Not in TRY...CATCH block, throw exception */ +/* Not in TRY...CATCH block */ +DECLARE @err_no INT; +DECLARE @msg VARCHAR(50); +DECLARE @state INT; +SET @err_no = 51000; +SET @msg = N'Throw error'; +SET @state = 1; +THROW @err_no, @msg, @state; +go +~~ERROR (Code: 51000)~~ + +~~ERROR (Message: Throw error)~~ + + +BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); +go +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Throw error)~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRAN; +go +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +3 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested procedure call */ +BEGIN TRAN + EXEC throwProc2; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 51000)~~ + +~~ERROR (Message: Throw error)~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +111 +1 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* 2. In TRY...CATCH block, catchable, abort batch without rollback */ +/* THROW in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + COMMIT TRAN +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +3 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC throwProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM throwTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM throwTable +go +~~START~~ +int +3 +6 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM throwTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* XACT_ABORT ON */ +SET XACT_ABORT ON; +go + +/* 1. Not in TRY...CATCH block, rollback transaction */ +/* Not in TRY...CATCH block */ +BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); +go +~~ROW COUNT: 1~~ + +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Throw error)~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRAN; +go +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested procedure call */ +BEGIN TRAN + EXEC throwProc2; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 51000)~~ + +~~ERROR (Message: Throw error)~~ + +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* 2. In TRY...CATCH block, catchable, abort batch without rollback */ +/* THROW in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + COMMIT TRAN +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +3 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC throwProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM throwTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM throwTable +go +~~START~~ +int +3 +6 +~~END~~ + +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END CATCH +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +SELECT * FROM throwTable +go +~~START~~ +int +3 +5 +~~END~~ + +TRUNCATE TABLE throwTable +go + +-- BABEL-2479 +THROW 50000, 'Throw error', 1; +go +~~ERROR (Code: 50000)~~ + +~~ERROR (Message: Throw error)~~ + + +/* Clean up */ +SET XACT_ABORT OFF; +go +DROP PROC throwProc1; +go +DROP PROC throwProc2; +go +DROP TABLE throwTable; +go diff --git a/contrib/test/JDBC/expected/TestTime.out b/contrib/test/JDBC/expected/TestTime.out new file mode 100644 index 00000000000..b822af5e123 --- /dev/null +++ b/contrib/test/JDBC/expected/TestTime.out @@ -0,0 +1,300 @@ +Create table TestTime(a time(6)) + +prepst#!# Insert into TestTime Values(?) #!#Time|-|a|-|12:45:37.123|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-| +~~ROW COUNT: 1~~ + + +select * from TestTime +~~START~~ +time +12:45:37.000000 +12:45:37.000000 +12:45:37.000000 +12:45:37.000000 +12:45:37.000000 +12:45:37.000000 +12:45:37.000000 + +~~END~~ + + +Drop table TestTime + +Create table TestTime(a time(5)) + +prepst#!# Insert into TestTime Values(?) #!#Time|-|a|-|12:45:37.123|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-| +~~ROW COUNT: 1~~ + + +select * from TestTime +~~START~~ +time +12:45:37.00000 +12:45:37.00000 +12:45:37.00000 +12:45:37.00000 +12:45:37.00000 +12:45:37.00000 +12:45:37.00000 + +~~END~~ + + +Drop table TestTime + +Create table TestTime(a time(4)) + +prepst#!# Insert into TestTime Values(?) #!#Time|-|a|-|12:45:37.123|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-| +~~ROW COUNT: 1~~ + + +select * from TestTime +~~START~~ +time +12:45:37.0000 +12:45:37.0000 +12:45:37.0000 +12:45:37.0000 +12:45:37.0000 +12:45:37.0000 +12:45:37.0000 + +~~END~~ + + +Drop table TestTime + +Create table TestTime(a time(3)) + +prepst#!# Insert into TestTime Values(?) #!#Time|-|a|-|12:45:37.123|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-| +~~ROW COUNT: 1~~ + + +select * from TestTime +~~START~~ +time +12:45:37.000 +12:45:37.000 +12:45:37.000 +12:45:37.000 +12:45:37.000 +12:45:37.000 +12:45:37.000 + +~~END~~ + + +Drop table TestTime + +Create table TestTime(a time(2)) + +prepst#!# Insert into TestTime Values(?) #!#Time|-|a|-|12:45:37.123|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-| +~~ROW COUNT: 1~~ + + +select * from TestTime +~~START~~ +time +12:45:37.00 +12:45:37.00 +12:45:37.00 +12:45:37.00 +12:45:37.00 +12:45:37.00 +12:45:37.00 + +~~END~~ + + +Drop table TestTime + +Create table TestTime(a time(1)) + +prepst#!# Insert into TestTime Values(?) #!#Time|-|a|-|12:45:37.123|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-| +~~ROW COUNT: 1~~ + + +select * from TestTime +~~START~~ +time +12:45:37.0 +12:45:37.0 +12:45:37.0 +12:45:37.0 +12:45:37.0 +12:45:37.0 +12:45:37.0 + +~~END~~ + + +Drop table TestTime + +Create table TestTime(a time(0)) + +prepst#!# Insert into TestTime Values(?) #!#Time|-|a|-|12:45:37.123|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#Time|-|a|-| +~~ROW COUNT: 1~~ + + +select * from TestTime +~~START~~ +time +12:45:37 +12:45:37 +12:45:37 +12:45:37 +12:45:37 +12:45:37 +12:45:37 + +~~END~~ + + +Drop table TestTime diff --git a/contrib/test/JDBC/expected/TestTinyInt.out b/contrib/test/JDBC/expected/TestTinyInt.out new file mode 100644 index 00000000000..73471b08493 --- /dev/null +++ b/contrib/test/JDBC/expected/TestTinyInt.out @@ -0,0 +1,103 @@ +CREATE TABLE TINYINT_dt (a TINYINT) +prepst#!# INSERT INTO TINYINT_dt(a) values(?) #!#TINYINT|-|a|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-|-10 +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +prepst#!#exec#!#TINYINT|-|a|-|100 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-|002 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-|029 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-|004 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-|87 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-|0 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-|255 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#TINYINT|-|a|-| +~~ROW COUNT: 1~~ + +SELECT * FROM TINYINT_dt; +~~START~~ +tinyint +0 +100 +2 +29 +4 +87 +0 +255 + +~~END~~ + +INSERT INTO TINYINT_dt(a) values(0) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(120) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(100) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(004) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(0) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(002) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(86) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(1000) +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +INSERT INTO TINYINT_dt(a) values(255) +~~ROW COUNT: 1~~ + +INSERT INTO TINYINT_dt(a) values(NULL) +~~ROW COUNT: 1~~ + +SELECT * FROM TINYINT_dt; +~~START~~ +tinyint +0 +100 +2 +29 +4 +87 +0 +255 + +0 +120 +100 +4 +0 +2 +86 +255 + +~~END~~ + +DROP TABLE TINYINT_dt; diff --git a/contrib/test/JDBC/expected/TestTransactionName.out b/contrib/test/JDBC/expected/TestTransactionName.out new file mode 100644 index 00000000000..bfead419187 --- /dev/null +++ b/contrib/test/JDBC/expected/TestTransactionName.out @@ -0,0 +1,135 @@ + +-- Tests for transaction name in babel +CREATE TABLE TestTxnName(C1 INT); +GO + +-- Transaction name longer than 32 chars no allowed +BEGIN TRANSACTION longname11111111111111111111111111111111111111; +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: Transaction name length 46 above limit 32)~~ + + +-- Transaction name longer than 32 truncated +DECLARE @txnName varchar(100) = 'a1111111111111111111111111111111truncatethis'; +BEGIN TRAN @txnName; +ROLLBACK TRAN a1111111111111111111111111111111 +GO + +SELECT @@trancount; +GO +~~START~~ +int +0 +~~END~~ + + + +-- Transaction/savepoint names are case sensitive +DECLARE @txnName varchar(100) = 'Abc'; +DECLARE @spName varchar(100) = 'aBc'; +BEGIN TRAN abc; +INSERT INTO TestTxnName VALUES(1); +SAVE TRAN @spName; +INSERT INTO TestTxnName VALUES(2); +SAVE TRAN abC; +INSERT INTO TestTxnName VALUES(3); +SAVE TRAN ABc; +INSERT INTO TestTxnName VALUES(4); +SAVE TRAN AbC; +INSERT INTO TestTxnName VALUES(5); +SAVE TRAN [aBC]; +INSERT INTO TestTxnName VALUES(6); +BEGIN TRAN @txnName; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +SELECT C1 FROM TestTxnName ORDER BY C1; +GO +~~START~~ +int +1 +2 +3 +4 +5 +6 +~~END~~ + + +ROLLBACK TRAN [AbC]; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO +~~START~~ +int +1 +2 +3 +4 +~~END~~ + + +ROLLBACK TRAN ABc; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO +~~START~~ +int +1 +2 +3 +~~END~~ + + +ROLLBACK TRAN aBc; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO +~~START~~ +int +1 +~~END~~ + + +DECLARE @txnName varchar(100) = 'abc'; +ROLLBACK TRAN @txnName; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO +~~START~~ +int +~~END~~ + + +SELECT @@trancount; +GO +~~START~~ +int +0 +~~END~~ + + +DECLARE @txnName varchar(100) = 'abc'; +BEGIN TRAN @txnName; +COMMIT TRAN @txnName; +GO + +SELECT @@trancount; +GO +~~START~~ +int +0 +~~END~~ + + +DROP TABLE TestTxnName; +GO diff --git a/contrib/test/JDBC/expected/TestTransactionSupportForProcedure.out b/contrib/test/JDBC/expected/TestTransactionSupportForProcedure.out new file mode 100644 index 00000000000..9cd7e8958ba --- /dev/null +++ b/contrib/test/JDBC/expected/TestTransactionSupportForProcedure.out @@ -0,0 +1,976 @@ +#setup +create table txnproctable (c1 int not null, c2 varchar(100)) + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +create procedure txnproc1 as begin tran; insert into txnproctable values (1, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; commit tran; +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +~~END~~ + + + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK +drop procedure txnproc1; +create procedure txnproc1 as begin tran; insert into txnproctable values(2, 'xyz'); save tran sp1; delete from txnproctable; rollback tran sp1; rollback tran; +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +~~END~~ + + + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +drop procedure txnproc1 +create procedure txnproc1 as begin tran; insert into txnproctable values(3, 'dbd'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 2~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 0 current count 1)~~ + +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +~~END~~ + +commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +~~END~~ + + + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +drop procedure txnproc1 +create procedure txnproc1 as begin tran; insert into txnproctable values(4, 'sbd'); save tran sp1; update txnproctable set c1 = c1 + 1; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 3~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 0 current count 1)~~ + +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +~~END~~ + +rollback tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +~~END~~ + +commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +~~END~~ + + + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +begin tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(5, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 4~~ + +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +~~END~~ + +commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +~~END~~ + + + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(6, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 5~~ + +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +6#!#abc +~~END~~ + +commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +6#!#abc +~~END~~ + + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +begin tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +6#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(7, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; update txnproctable set c1 = c1 + 1; commit tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +1#!#abc +3#!#dbd +4#!#sbd +5#!#abc +6#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 6~~ + +~~ROW COUNT: 6~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(8, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; update txnproctable set c1 = c1 + 1; rollback tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 7~~ + +~~ROW COUNT: 7~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + + +# BEGIN TRAN +# START SAVEPOINT +# PROC +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +save tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(9, 'abc'); update txnproctable set c1 = c1 + 1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 7~~ + +rollback tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +rollback tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + + +# BEGIN TRAN +# START SAVEPOINT +# PROC +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +save tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(10, 'abc'); update txnproctable set c1 = c1 + 1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 7~~ + +rollback tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + + +# BEGIN TRAN +# START SAVEPOINT +# PROC +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +save tran sp1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(11, 'abc'); rollback tran sp1; update txnproctable set c1 = c1 + 1; +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +2#!#abc +4#!#dbd +5#!#sbd +6#!#abc +7#!#abc +8#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 6~~ + +commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +3#!#abc +5#!#dbd +6#!#sbd +7#!#abc +8#!#abc +9#!#abc +~~END~~ + + +# PROC1 +# BEGIN TRAN +# PROC2 +# PROC3 +# BEGIN TRAN +# START SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +# COMMIT +create procedure txnProc3 as begin tran; insert into txnproctable values (16, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +3#!#abc +5#!#dbd +6#!#sbd +7#!#abc +8#!#abc +9#!#abc +~~END~~ + +create procedure txnProc2 as update txnproctable set c1 = c1 + 1; exec txnProc3; commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +3#!#abc +5#!#dbd +6#!#sbd +7#!#abc +8#!#abc +9#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as delete from txnproctable; begin tran; exec txnProc2; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +3#!#abc +5#!#dbd +6#!#sbd +7#!#abc +8#!#abc +9#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 6~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 2)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 0 current count 1)~~ + +select @@trancount +~~START~~ +int +1 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +16#!#abc +~~END~~ + +commit tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +16#!#abc +~~END~~ + + +# PROC1 +# BEGIN TRAN +# PROC2 +# PROC3 +# BEGIN TRAN +# START SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK TRAN +# COMMIT +drop procedure txnproc3 +create procedure txnProc3 as begin tran; insert into txnproctable values (20, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +16#!#abc +~~END~~ + +drop procedure txnproc2 +create procedure txnProc2 as update txnproctable set c1 = c1 + 1; exec txnProc3; rollback tran; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +16#!#abc +~~END~~ + +drop procedure txnproc1 +create procedure txnproc1 as delete from txnproctable; begin tran; exec txnProc2; +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +16#!#abc +~~END~~ + +exec txnproc1; +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 2)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +select @@trancount +~~START~~ +int +0 +~~END~~ + +select * from txnproctable order by c1; +~~START~~ +int#!#varchar +~~END~~ + + +#cleanup +drop procedure txnproc3 +drop procedure txnproc2 +drop procedure txnproc1 +drop table txnproctable diff --git a/contrib/test/JDBC/expected/TestTransactionsSQLBatch.out b/contrib/test/JDBC/expected/TestTransactionsSQLBatch.out new file mode 100644 index 00000000000..c60f47da53a --- /dev/null +++ b/contrib/test/JDBC/expected/TestTransactionsSQLBatch.out @@ -0,0 +1,494 @@ +create table TxnTable(c1 int); + +# Begin transaction -> commit transaction +begin transaction; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +begin transaction; +select @@trancount; +~~START~~ +int +2 +~~END~~ + +set transaction isolation level read committed; +#show transaction_isolation; +#show default_transaction_isolation; +insert into TxnTable values(1); +~~ROW COUNT: 1~~ + +commit transaction; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +commit transaction; +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select c1 from TxnTable; +~~START~~ +int +1 +~~END~~ + + +# Begin transaction -> rollback transaction +begin transaction; +insert into TxnTable values(2); +~~ROW COUNT: 1~~ + +rollback transaction; +select c1 from TxnTable; +~~START~~ +int +1 +~~END~~ + + +#Begin tran -> commit tran +begin tran; +insert into TxnTable values(2); +~~ROW COUNT: 1~~ + +commit tran; +select c1 from TxnTable; +~~START~~ +int +1 +2 +~~END~~ + + +# Begin tran -> rollback tran +begin tran; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +begin tran; +set transaction isolation level read uncommitted; +#show transaction_isolation; +#show default_transaction_isolation; +insert into TxnTable values(3); +~~ROW COUNT: 1~~ + +select @@trancount; +~~START~~ +int +2 +~~END~~ + +rollback tran; +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select c1 from TxnTable; +~~START~~ +int +1 +2 +~~END~~ + + +set transaction isolation level repeatable read; +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: REPEATABLE READ isolation level is not supported)~~ + +#show transaction_isolation; +#show default_transaction_isolation; + +# Begin transaction -> commit +begin transaction; +insert into TxnTable values(4); +~~ROW COUNT: 1~~ + +commit; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +~~END~~ + + +# Begin transaction -> commit work +begin transaction; +insert into TxnTable values(5); +~~ROW COUNT: 1~~ + +commit work; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +5 +~~END~~ + + +# Begin transaction -> rollback +begin transaction; +insert into TxnTable values(6); +~~ROW COUNT: 1~~ + +rollback; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +5 +~~END~~ + + +# Begin transaction -> rollback work +begin transaction; +insert into TxnTable values(7); +~~ROW COUNT: 1~~ + +rollback work; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +5 +~~END~~ + + +# Begin transaction name -> commit transaction name +begin transaction txn1; +insert into TxnTable values(8); +~~ROW COUNT: 1~~ + +commit transaction txn1; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +5 +8 +~~END~~ + + +# Begin transaction name -> rollback transaction name +begin transaction txn1; +insert into TxnTable values(9); +~~ROW COUNT: 1~~ + +rollback transaction txn1; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +5 +8 +~~END~~ + + +# Begin tran name -> commit tran name +begin tran txn1; +insert into TxnTable values(10); +~~ROW COUNT: 1~~ + +commit tran txn1; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +5 +8 +10 +~~END~~ + + +# Begin tran name -> rollback tran name +begin tran txn1; +insert into TxnTable values(10); +~~ROW COUNT: 1~~ + +rollback tran txn1; +select c1 from TxnTable; +~~START~~ +int +1 +2 +4 +5 +8 +10 +~~END~~ + + +truncate table TxnTable; + +# save tran name -> rollback tran name +set transaction isolation level snapshot; +#show transaction_isolation; +#show default_transaction_isolation; +begin transaction txn1; +insert into TxnTable values(1); +~~ROW COUNT: 1~~ + +save transaction sp1; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +insert into TxnTable values(2); +~~ROW COUNT: 1~~ + +save tran sp2; +insert into TxnTable values(3); +~~ROW COUNT: 1~~ + +save tran sp2; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +insert into TxnTable values(4); +~~ROW COUNT: 1~~ + +select c1 from TxnTable; +~~START~~ +int +1 +2 +3 +4 +~~END~~ + +rollback tran sp2; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +select c1 from TxnTable; +~~START~~ +int +1 +2 +3 +~~END~~ + +rollback tran sp2; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +select c1 from TxnTable; +~~START~~ +int +1 +2 +~~END~~ + +rollback tran sp1; +select @@trancount; +~~START~~ +int +1 +~~END~~ + +select c1 from TxnTable; +~~START~~ +int +1 +~~END~~ + +rollback tran txn1; +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select c1 from TxnTable; +~~START~~ +int +~~END~~ + + +# begin transaction name -> save transaction name -> rollback to first savepoint +begin transaction txn1; +insert into TxnTable values(1); +~~ROW COUNT: 1~~ + +save transaction sp1; +insert into TxnTable values(2); +~~ROW COUNT: 1~~ + +save transaction sp2; +insert into TxnTable values(3); +~~ROW COUNT: 1~~ + +save transaction sp3; +insert into TxnTable values(4); +~~ROW COUNT: 1~~ + +rollback tran sp1; +#rollback tran sp1; -- this will give an error +rollback tran; +select c1 from TxnTable; +~~START~~ +int +~~END~~ + + +# begin transaction name -> save transaction name -> rollback tran name, Rollback whole transaction +set transaction isolation level serializable; +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: SERIALIZABLE isolation level is not supported)~~ + +#show transaction_isolation; +#show default_transaction_isolation; +begin transaction txn1; +insert into TxnTable values(1); +~~ROW COUNT: 1~~ + +save transaction sp1; +begin transaction txn1; +insert into TxnTable values(2); +~~ROW COUNT: 1~~ + +save transaction sp1; +insert into TxnTable values(3); +~~ROW COUNT: 1~~ + +select @@trancount; +~~START~~ +int +2 +~~END~~ + +rollback tran txn1; +select @@trancount; +~~START~~ +int +0 +~~END~~ + +select c1 from TxnTable; +~~START~~ +int +~~END~~ + + +# begin transaction -> save transaction name -> rollback to savepoint, commit transaction +begin transaction txn1; +insert into TxnTable values(1); +~~ROW COUNT: 1~~ + +save transaction sp1; +insert into TxnTable values(2); +~~ROW COUNT: 1~~ + +select c1 from TxnTable; +~~START~~ +int +1 +2 +~~END~~ + +rollback tran sp1; +commit transaction; +select c1 from TxnTable; +~~START~~ +int +1 +~~END~~ + + +# begin transaction -> save transaction name -> rollback to savepoint +# save transaction name -> commit transaction +begin transaction txn1; +insert into TxnTable values(3); +~~ROW COUNT: 1~~ + +save transaction sp1; +insert into TxnTable values(4); +~~ROW COUNT: 1~~ + +select c1 from TxnTable; +~~START~~ +int +1 +3 +4 +~~END~~ + +rollback tran sp1; +save transaction sp2; +insert into TxnTable values(5); +~~ROW COUNT: 1~~ + +commit transaction; +select c1 from TxnTable; +~~START~~ +int +1 +3 +5 +~~END~~ + + +# begin transaction -> save transaction name -> error -> rollback to savepoint +# commit transaction +begin transaction txn1; +insert into TxnTable values(6); +~~ROW COUNT: 1~~ + +save transaction sp1; +insert into TxnTable values(7); +~~ROW COUNT: 1~~ + +#select c1 frm TxnTable; -- error +rollback tran sp1; +commit transaction; +select c1 from TxnTable; +~~START~~ +int +1 +3 +5 +6 +~~END~~ + + +Drop table TxnTable; diff --git a/contrib/test/JDBC/expected/TestUDD.out b/contrib/test/JDBC/expected/TestUDD.out new file mode 100644 index 00000000000..4095d876908 --- /dev/null +++ b/contrib/test/JDBC/expected/TestUDD.out @@ -0,0 +1,139 @@ +CREATE TYPE udd_varchar from varchar(15); +CREATE TYPE udd_nvarchar from nvarchar(15); +CREATE TYPE udd_int from int; +CREATE TYPE udd_char from char(25); +CREATE TYPE udd_nchar from nchar(20) NOT NULL; +CREATE TYPE udd_datetime from datetime; +CREATE TYPE udd_numeric from numeric(4,1); + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +CREATE TABLE udd_dt (a udd_varchar UNIQUE, b udd_nvarchar, c udd_int PRIMARY KEY, d udd_char DEFAULT 'Whoops!', e udd_nchar, f udd_datetime, g udd_numeric CHECK (g >= 103.5)) + +INSERT INTO udd_dt VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +~~ROW COUNT: 1~~ + + +#passing a non unique value in column a +#throws error: duplicate key value violates unique constraint +INSERT INTO udd_dt VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "udd_dt_a_key")~~ + + +#passing a non unique value in column c +#throws error: duplicate key value violates unique constraint +INSERT INTO udd_dt VALUES ('Banana', N'green', 1, 'Bangalore', N'Crying😭', '2007-01-14 23:34:23.749', 908.7); +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "udd_dt_pkey")~~ + + +#passing a NULL value in column c +#throws error: null value in column "c" violates not-null constraint +INSERT INTO udd_dt VALUES ('Guava', N'yellow', NULL, 'Mumbai', N'Smirk😏', '1907-05-09 11:14:13.749', 245.6); +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "udd_dt" violates not-null constraint)~~ + + +#passing no value for column d +INSERT INTO udd_dt(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +~~ROW COUNT: 1~~ + + +#passing a NULL value in column e +#throws error: domain udd_nchar does not allow null values +INSERT INTO udd_dt VALUES ('Kiwi', N'purple', 4, 'Kolkata', NULL, '1907-05-09 11:14:13.749', 874.0); +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: domain udd_nchar does not allow null values)~~ + + +#passing a value less than 103.5 in column g +#throws error: new row for relation "udd_dt" violates check constraint "udd_dt_g_check" +INSERT INTO udd_dt VALUES ('Grape', N'white', 5, 'Pune', N'Angry😠', '2000-02-28 23:59:59.989', 100.1); +~~ERROR (Code: 547)~~ + +~~ERROR (Message: new row for relation "udd_dt" violates check constraint "udd_dt_g_check")~~ + + +SELECT * FROM udd_dt; +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + + +DROP TABLE udd_dt; + +CREATE TABLE udd_dt (a udd_varchar UNIQUE, b udd_nvarchar, c udd_int PRIMARY KEY, d udd_char DEFAULT 'Whoops!', e udd_nchar, f udd_datetime, g udd_numeric CHECK (g >= 103.5)) + +prepst#!#INSERT INTO udd_dt VALUES (?, ?, ?, ?, ?, ?, ?)#!#varchar|-|a|-|Apple#!#nvarchar|-|b|-|red#!#int|-|c|-|1#!#char|-|d|-|Delhi#!#nchar|-|e|-|Sad😞#!#datetime|-|f|-|2000-12-13 12:58:23.123#!#numeric|-|g|-|123.1|-|4|-|1 +~~ROW COUNT: 1~~ + + +#passing a non unique value in column a +#throws error: duplicate key value violates unique constraint +prepst#!#exec#!#varchar|-|a|-|Apple#!#nvarchar|-|b|-|blue#!#int|-|c|-|2#!#char|-|d|-|Chennai#!#nchar|-|e|-|Neutral😐#!#datetime|-|f|-|2006-11-11 22:47:23.128#!#numeric|-|g|-|512.4|-|4|-|1 +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "udd_dt_a_key")~~ + + +#passing a non unique value in column c +#throws error: duplicate key value violates unique constraint +prepst#!#exec#!#varchar|-|a|-|Banana#!#nvarchar|-|b|-|green#!#int|-|c|-|1#!#char|-|d|-|Bangalore#!#nchar|-|e|-|Crying😭#!#datetime|-|f|-|2007-01-14 23:34:23.749#!#numeric|-|g|-|908.7|-|4|-|1 +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "udd_dt_pkey")~~ + + +#passing a NULL value in column c +#throws error: null value in column "c" violates not-null constraint +prepst#!#exec#!#varchar|-|a|-|Guava#!#nvarchar|-|b|-|yellow#!#int|-|c|-|#!#char|-|d|-|Mumbai#!#nchar|-|e|-|Smirk😏'#!#datetime|-|f|-|1907-05-09 11:14:13.749#!#numeric|-|g|-|245.6|-|4|-|1 +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "udd_dt" violates not-null constraint)~~ + + +#passing a NULL value in column e +#throws error: domain udd_nchar does not allow null values +prepst#!#exec#!#varchar|-|a|-|Kiwi#!#nvarchar|-|b|-|purple#!#int|-|c|-|4#!#char|-|d|-|Kolkata#!#nchar|-|e|-|#!#datetime|-|f|-|1907-05-09 11:14:13.749#!#numeric|-|g|-|874.0|-|4|-|1 +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: domain udd_nchar does not allow null values)~~ + + +#passing a value less than 103.5 in column g +#throws error: new row for relation "udd_dt" violates check constraint "udd_dt_g_check" +prepst#!#exec#!#varchar|-|a|-|Grape#!#nvarchar|-|b|-|white#!#int|-|c|-|5#!#char|-|d|-|Pune#!#nchar|-|e|-|Angry😠#!#datetime|-|f|-|2000-02-28 23:59:59.989#!#numeric|-|g|-|100.1|-|4|-|1 +~~ERROR (Code: 547)~~ + +~~ERROR (Message: new row for relation "udd_dt" violates check constraint "udd_dt_g_check")~~ + + +#passing no value for column d +prepst#!#INSERT INTO udd_dt(a, b, c, e, f, g) VALUES (?, ?, ?, ?, ?, ?)#!#varchar|-|a1|-|Orange#!#nvarchar|-|b1|-|#!#int|-|c1|-|5#!#nchar|-|e1|-|Happy😀#!#datetime|-|f1|-|1900-02-28 23:59:59.989#!#numeric|-|g1|-|342.5|-|4|-|1 +~~ROW COUNT: 1~~ + + +SELECT * FROM udd_dt; +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#5#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + + +DROP TABLE udd_dt; + +DROP TYPE udd_varchar +DROP TYPE udd_nvarchar +DROP TYPE udd_int +DROP TYPE udd_char +DROP TYPE udd_nchar +DROP TYPE udd_datetime +DROP TYPE udd_numeric +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; diff --git a/contrib/test/JDBC/expected/TestUniqueIdentifier.out b/contrib/test/JDBC/expected/TestUniqueIdentifier.out new file mode 100644 index 00000000000..0652b81f558 --- /dev/null +++ b/contrib/test/JDBC/expected/TestUniqueIdentifier.out @@ -0,0 +1,151 @@ +CREATE TABLE uniqueidentifier_dt (a uniqueidentifier); + +INSERT INTO uniqueidentifier_dt VALUES ('51f178a6-53c7-472c-9be1-1c08942342d7') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('dd8cb046-461d-411e-be40-d219252ce849') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('b84ebcc9-c927-4cfe-b08e-dc7f25b5087c') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('bab96bc8-60b9-40dd-b0de-c90a80f5739e') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('d424fdef-1404-4bac-8289-c725b540f93d') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('60aeaa5c-e272-4b17-bad0-c25710fd7a60') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('253fb146-7e45-45ef-9d92-bbe14a8ad1b2') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('dba2726c-2131-409f-aefa-5c8079571623') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('b3400fa7-3a60-40ec-b40e-fc85a3eb262d') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES ('851763b5-b068-42ae-88ec-764bfb0e5605') +~~ROW COUNT: 1~~ + +INSERT INTO uniqueidentifier_dt VALUES (NULL) +~~ROW COUNT: 1~~ + +SELECT * FROM uniqueidentifier_dt +~~START~~ +uniqueidentifier +51F178A6-53C7-472C-9BE1-1C08942342D7 +DD8CB046-461D-411E-BE40-D219252CE849 +B84EBCC9-C927-4CFE-B08E-DC7F25B5087C +BAB96BC8-60B9-40DD-B0DE-C90A80F5739E +D424FDEF-1404-4BAC-8289-C725B540F93D +60AEAA5C-E272-4B17-BAD0-C25710FD7A60 +253FB146-7E45-45EF-9D92-BBE14A8AD1B2 +DBA2726C-2131-409F-AEFA-5C8079571623 +B3400FA7-3A60-40EC-B40E-FC85A3EB262D +851763B5-B068-42AE-88EC-764BFB0E5605 + +~~END~~ + + +prepst#!#INSERT INTO uniqueidentifier_dt VALUES (?)#!#uniqueidentifier|-|a|-|51f178a6-53c7-472c-9be1-1c08942342d7 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|767392df-87d0-450d-9b24-85a86c02811d +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|9bcb5632-53c3-4695-b617-d9a7055813ce +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|ac81a140-f686-4259-9b90-dd46f10b355f +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|3d08372d-770c-48b9-9740-a667d036680e +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|518d52c7-4d79-4143-ab33-b3765689fdf4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|bc3fa456-7391-4060-b5d8-430048075cf4 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|3b75b2dd-01b7-4958-9de7-f92410693547 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|ce8af10a-2709-43b0-9e4e-a02753929d17 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|5b7c2e8d-6d90-411d-8e19-9a81067e6f6c +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-| +~~ROW COUNT: 1~~ + +SELECT * FROM uniqueidentifier_dt; +~~START~~ +uniqueidentifier +51F178A6-53C7-472C-9BE1-1C08942342D7 +DD8CB046-461D-411E-BE40-D219252CE849 +B84EBCC9-C927-4CFE-B08E-DC7F25B5087C +BAB96BC8-60B9-40DD-B0DE-C90A80F5739E +D424FDEF-1404-4BAC-8289-C725B540F93D +60AEAA5C-E272-4B17-BAD0-C25710FD7A60 +253FB146-7E45-45EF-9D92-BBE14A8AD1B2 +DBA2726C-2131-409F-AEFA-5C8079571623 +B3400FA7-3A60-40EC-B40E-FC85A3EB262D +851763B5-B068-42AE-88EC-764BFB0E5605 + +51F178A6-53C7-472C-9BE1-1C08942342D7 +767392DF-87D0-450D-9B24-85A86C02811D +9BCB5632-53C3-4695-B617-D9A7055813CE +AC81A140-F686-4259-9B90-DD46F10B355F +3D08372D-770C-48B9-9740-A667D036680E +518D52C7-4D79-4143-AB33-B3765689FDF4 +BC3FA456-7391-4060-B5D8-430048075CF4 +3B75B2DD-01B7-4958-9DE7-F92410693547 +CE8AF10A-2709-43B0-9E4E-A02753929D17 +5B7C2E8D-6D90-411D-8E19-9A81067E6F6C + +~~END~~ + + +# to demonstrate the truncation of data when the value is too long +INSERT INTO uniqueidentifier_dt VALUES ('51f178a6-53c7-472c-9be1-1c08942342d7thisIsTooLong') +~~ROW COUNT: 1~~ + +prepst#!#exec#!#uniqueidentifier|-|a|-|767392df-87d0-450d-9b24-85a86c02811dthisIsTooLong +~~ROW COUNT: 1~~ + +SELECT * FROM uniqueidentifier_dt; +~~START~~ +uniqueidentifier +51F178A6-53C7-472C-9BE1-1C08942342D7 +DD8CB046-461D-411E-BE40-D219252CE849 +B84EBCC9-C927-4CFE-B08E-DC7F25B5087C +BAB96BC8-60B9-40DD-B0DE-C90A80F5739E +D424FDEF-1404-4BAC-8289-C725B540F93D +60AEAA5C-E272-4B17-BAD0-C25710FD7A60 +253FB146-7E45-45EF-9D92-BBE14A8AD1B2 +DBA2726C-2131-409F-AEFA-5C8079571623 +B3400FA7-3A60-40EC-B40E-FC85A3EB262D +851763B5-B068-42AE-88EC-764BFB0E5605 + +51F178A6-53C7-472C-9BE1-1C08942342D7 +767392DF-87D0-450D-9B24-85A86C02811D +9BCB5632-53C3-4695-B617-D9A7055813CE +AC81A140-F686-4259-9B90-DD46F10B355F +3D08372D-770C-48B9-9740-A667D036680E +518D52C7-4D79-4143-AB33-B3765689FDF4 +BC3FA456-7391-4060-B5D8-430048075CF4 +3B75B2DD-01B7-4958-9DE7-F92410693547 +CE8AF10A-2709-43B0-9E4E-A02753929D17 +5B7C2E8D-6D90-411D-8E19-9A81067E6F6C + +51F178A6-53C7-472C-9BE1-1C08942342D7 +767392DF-87D0-450D-9B24-85A86C02811D +~~END~~ + + +DROP TABLE uniqueidentifier_dt; diff --git a/contrib/test/JDBC/expected/TestVarChar.out b/contrib/test/JDBC/expected/TestVarChar.out new file mode 100644 index 00000000000..1e9cbb78fd5 --- /dev/null +++ b/contrib/test/JDBC/expected/TestVarChar.out @@ -0,0 +1,159 @@ +CREATE TABLE VARCHAR_dt (a VARCHAR(20), b NVARCHAR(24)) +prepst#!# INSERT INTO VARCHAR_dt(a, b) values(?, ?) #!#VARCHAR|-|a|-|Dipesh#!#NVARCHAR|-|b|-|Dhameliya +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-| Dipesh #!#NVARCHAR|-|b|-| Dhameliya +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-| D#!#NVARCHAR|-|b|-| 🤣😃 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-| #!#NVARCHAR|-|b|-| +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-|#!#NVARCHAR|-|b|-| +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-|d#!#NVARCHAR|-|b|-|D +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-|abcdefghijklmnopqrst#!#NVARCHAR|-|b|-|abcdefghijklmnopqrstuvwx +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-|abcdefghijklmnopqrstu#!#NVARCHAR|-|b|-|abcdefghijklmnopqrstuvwxy +~~ERROR (Code: 8152)~~ + +~~ERROR (Message: value too long for type character varying(20))~~ + +prepst#!#exec#!#VARCHAR|-|a|-| #!#NVARCHAR|-|b|-|😊😋😎😍😅😆 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#VARCHAR|-|a|-|#!#NVARCHAR|-|b|-| +~~ROW COUNT: 1~~ + +SELECT * FROM VARCHAR_dt; +~~START~~ +varchar#!#nvarchar +Dipesh#!#Dhameliya + Dipesh #!# Dhameliya + D#!# 🤣😃 + #!# +#!# +d#!#D +abcdefghijklmnopqrst#!#abcdefghijklmnopqrstuvwx + #!#😊😋😎😍😅😆 +#!# +~~END~~ + +INSERT INTO VARCHAR_dt(a,b) values('Dipesh','Dhameliya') +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt(a,b) values(' Dipesh',' Dhameliya') +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt(a,b) values(' D',N' 🤣😃') +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt(a,b) values(' ',' ') +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt(a,b) values(' ',N'😊😋😎😍😅😆') +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt(a,b) values('','') +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt(a,b) values('d','D') +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt(a,b) values('abcdefghijklmnopqrst','abcdefghijklmnopqrstuvwx') +~~ROW COUNT: 1~~ + +#INSERT INTO VARCHAR_dt(a,b) values('abcdefghijklmnopqrstu','abcdefghijklmnopqrstuvwxy') -- for this case, BABEL and SQL both will throw an error +INSERT INTO VARCHAR_dt(a,b) values(NULL,NULL) +~~ROW COUNT: 1~~ + +SELECT * FROM VARCHAR_dt; +~~START~~ +varchar#!#nvarchar +Dipesh#!#Dhameliya + Dipesh #!# Dhameliya + D#!# 🤣😃 + #!# +#!# +d#!#D +abcdefghijklmnopqrst#!#abcdefghijklmnopqrstuvwx + #!#😊😋😎😍😅😆 +#!# +Dipesh#!#Dhameliya + Dipesh#!# Dhameliya + D#!# 🤣😃 + #!# + #!#😊😋😎😍😅😆 +#!# +d#!#D +abcdefghijklmnopqrst#!#abcdefghijklmnopqrstuvwx +#!# +~~END~~ + +DROP TABLE VARCHAR_dt; + +CREATE TABLE VARCHAR_dt (a varchar(max), b nvarchar(max)); +INSERT INTO VARCHAR_dt values ('hello', N'hello😃'); +~~ROW COUNT: 1~~ + + +#checking with string of length > 65535 +INSERT INTO VARCHAR_dt values ('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', N'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😃'); +~~ROW COUNT: 1~~ + +INSERT INTO VARCHAR_dt values (NULL, NULL); +~~ROW COUNT: 1~~ + +prepst#!#INSERT INTO VARCHAR_dt values(?, ?)#!#varchar|-|a|-|hello#!#nvarchar|-|b|-|hello😃 +~~ROW COUNT: 1~~ + + +#checking with string of length > 65535 via prep-exec +prepst#!#exec#!#varchar|-|a|-|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#!#nvarchar|-|b|-|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😃 +~~ROW COUNT: 1~~ + +prepst#!#exec#!#varchar|-|a|-|#!#nvarchar|-|b|-| +~~ROW COUNT: 1~~ + +select * from VARCHAR_dt +~~START~~ +varchar#!#nvarchar +hello#!#hello😃 +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#!#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😃 +#!# +hello#!#hello😃 +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#!#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😃 +#!# +~~END~~ + +drop table VARCHAR_dt; + +create table VARCHAR_dt (a varchar(max), b int, c int, d int, e int ,f int, g int, h int, i int); +insert into VARCHAR_dt (a,b,c,d,e,f,g,h,i) values (NULL,1,2,3,4,5,6,7,8); +~~ROW COUNT: 1~~ + +select * from VARCHAR_dt; +~~START~~ +varchar#!#int#!#int#!#int#!#int#!#int#!#int#!#int#!#int +#!#1#!#2#!#3#!#4#!#5#!#6#!#7#!#8 +~~END~~ + +drop table VARCHAR_dt; + +create table VARCHAR_dt (a varchar(10), b int, c int, d int, e int ,f int, g int, h int, i int); +insert into VARCHAR_dt (a,b,c,d,e,f,g,h,i) values (NULL,1,2,3,4,5,6,7,8); +~~ROW COUNT: 1~~ + +select * from VARCHAR_dt; +~~START~~ +varchar#!#int#!#int#!#int#!#int#!#int#!#int#!#int#!#int +#!#1#!#2#!#3#!#4#!#5#!#6#!#7#!#8 +~~END~~ + +drop table VARCHAR_dt; diff --git a/contrib/test/JDBC/expected/TestXML.out b/contrib/test/JDBC/expected/TestXML.out new file mode 100644 index 00000000000..dc7168f2452 --- /dev/null +++ b/contrib/test/JDBC/expected/TestXML.out @@ -0,0 +1,29 @@ +CREATE TABLE XML_dt (a XML) +#prepst#!# INSERT INTO XML_dt values(@a) #!#XML|-|a|-| +#prepst#!# INSERT INTO XML_dt values(@a) #!#XML|-|a|-|Contact Name 2YYY-YYY-YYYY +#prepst#!#exec#!#XML|-|a|-| +SELECT * FROM XML_dt; +~~START~~ +xml +~~END~~ + +INSERT INTO XML_dt values('Contact Name 2YYY-YYY-YYYY') +~~ROW COUNT: 1~~ + +INSERT INTO XML_dt values(NULL) +~~ROW COUNT: 1~~ + +#INSERT INTO XML_dt values('') +INSERT INTO XML_dt values(Contact Name 2YYY-YYY-YYYY) +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near '<' at line 1 and character position 26)~~ + +SELECT * FROM XML_dt; +~~START~~ +xml +Contact Name 2YYY-YYY-YYYY + +~~END~~ + +DROP TABLE XML_dt; diff --git a/contrib/test/JDBC/expected/babel_collation.out b/contrib/test/JDBC/expected/babel_collation.out new file mode 100644 index 00000000000..2a023c395e6 --- /dev/null +++ b/contrib/test/JDBC/expected/babel_collation.out @@ -0,0 +1,201 @@ +create table testing_collation (col varchar(20)); +go +insert into testing_collation values ('JONES'); +insert into testing_collation values ('jones'); +insert into testing_collation values ('Jones'); +insert into testing_collation values ('JoNes'); +insert into testing_collation values ('JoNés'); +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +select * from testing_collation where col collate SQL_Latin1_General_CP1_CS_AS = 'JoNes'; +go +~~START~~ +varchar +JoNes +~~END~~ + + +select * from testing_collation where col collate SQL_Latin1_General_CP1_CI_AS = 'JoNes'; +go +~~START~~ +varchar +JONES +jones +Jones +JoNes +~~END~~ + + +select * from testing_collation where col collate SQL_Latin1_General_CP1_CI_AI = 'JoNes'; +go +~~START~~ +varchar +JONES +jones +Jones +JoNes +JoNés +~~END~~ + + +-- all the currently supported TSQL collations +SELECT * from fn_helpcollations(); +go +~~START~~ +varchar#!#varchar +arabic_cs_as#!#Arabic, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +arabic_ci_ai#!#Arabic, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +arabic_ci_as#!#Arabic, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_bin2#!#Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1250_ci_ai#!#Default locale, code page 1250, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1250_ci_as#!#Default locale, code page 1250, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1250_cs_ai#!#Default locale, code page 1250, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1250_cs_as#!#Default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1250_cs_as#!#Default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1251_ci_ai#!#Default locale, code page 1251, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1251_ci_as#!#Default locale, code page 1251, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1251_cs_ai#!#Default locale, code page 1251, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1251_cs_as#!#Default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1251_cs_as#!#Default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1253_ci_ai#!#Default locale, code page 1253, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1253_ci_as#!#Default locale, code page 1253, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1253_cs_ai#!#Default locale, code page 1253, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1253_cs_as#!#Default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1253_cs_as#!#Default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1254_ci_ai#!#Default locale, code page 1254, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1254_ci_as#!#Default locale, code page 1254, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1254_cs_ai#!#Default locale, code page 1254, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1254_cs_as#!#Default locale, code page 1254, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1254_cs_as#!#Default locale, code page 1254, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1255_ci_ai#!#Default locale, code page 1255, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1255_ci_as#!#Default locale, code page 1255, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1255_cs_ai#!#Default locale, code page 1255, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1255_cs_as#!#Default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1255_cs_as#!#Default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1256_ci_ai#!#Default locale, code page 1256, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1256_ci_as#!#Default locale, code page 1256, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1256_cs_ai#!#Default locale, code page 1256, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1256_cs_as#!#Default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1256_cs_as#!#Default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1257_ci_ai#!#Default locale, code page 1257, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1257_ci_as#!#Default locale, code page 1257, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1257_cs_ai#!#Default locale, code page 1257, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1257_cs_as#!#Default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1257_cs_as#!#Default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1258_ci_ai#!#Default locale, code page 1258, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1258_ci_as#!#Default locale, code page 1258, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1258_cs_ai#!#Default locale, code page 1258, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1258_cs_as#!#Default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1258_cs_as#!#Default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp1_ci_ai#!#Default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1_ci_as#!#Default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1_cs_ai#!#Default locale, code page 1252, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp1_cs_as#!#Default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp1_cs_as#!#Default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_cp847_ci_ai#!#Default locale, code page 847, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp847_ci_as#!#Default locale, code page 847, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp847_cs_ai#!#Default locale, code page 847, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_cp847_cs_as#!#Default locale, code page 847, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_pref_cp847_cs_as#!#Default locale, code page 847, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +bbf_unicode_general_ci_ai#!#Default locale, default code page, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_general_ci_as#!#Default locale, default code page, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_general_cs_ai#!#Default locale, default code page, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +bbf_unicode_general_cs_as#!#Default locale, default code page, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +bbf_unicode_general_pref_cs_as#!#Default locale, default code page, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +chinese_prc_cs_as#!#Chinese-PRC, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +chinese_prc_ci_ai#!#Chinese-PRC, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +chinese_prc_ci_as#!#Chinese-PRC, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +cyrillic_general_cs_as#!#Cyrillic-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +cyrillic_general_ci_ai#!#Cyrillic-General, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +cyrillic_general_ci_as#!#Cyrillic-General, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +finnish_swedish_cs_as#!#Finnish-Swedish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +finnish_swedish_ci_as#!#Finnish-Swedish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +finnish_swedish_ci_ai#!#Finnish-Swedish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +french_cs_as#!#French, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +french_ci_as#!#French, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +french_ci_ai#!#French, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +korean_wansung_cs_as#!#Korean-Wansung, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +korean_wansung_ci_as#!#Korean-Wansung, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +korean_wansung_ci_ai#!#Korean-Wansung, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +latin1_general_bin2#!#Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +latin1_general_90_bin2#!#Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +latin1_general_100_bin2#!#Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +latin1_general_140_bin2#!#Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +latin1_general_ci_ai#!#Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +latin1_general_ci_as#!#Virtual, default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +latin1_general_cs_ai#!#Virtual, default locale, code page 1252, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive +latin1_general_cs_as#!#Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +modern_spanish_cs_as#!#Traditional-Spanish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +modern_spanish_ci_as#!#Traditional-Spanish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +modern_spanish_ci_ai#!#Traditional-Spanish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +polish_cs_as#!#Polish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +polish_ci_as#!#Polish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +polish_ci_ai#!#Polish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1250_ci_as#!#Virtual, default locale, code page 1250, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1250_cs_as#!#Virtual, default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1251_ci_as#!#Virtual, default locale, code page 1251, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1251_cs_as#!#Virtual, default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1_ci_ai#!#Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1_ci_as#!#Virtual, default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1_ci_ai#!#Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1_cs_as#!#Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_pref_cp1_cs_as#!#Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first +sql_latin1_general_cp1253_ci_as#!#Virtual, default locale, code page 1253, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1253_cs_as#!#Virtual, default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1254_ci_as#!#Virtual, default locale, code page 1254, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1254_cs_as#!#Virtual, default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1255_ci_as#!#Virtual, default locale, code page 1255, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1255_cs_as#!#Virtual, default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1256_ci_as#!#Virtual, default locale, code page 1256, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1256_cs_as#!#Virtual, default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1257_ci_as#!#Virtual, default locale, code page 1257, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1257_cs_as#!#Virtual, default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1258_ci_as#!#Virtual, default locale, code page 1258, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +sql_latin1_general_cp1258_cs_as#!#Virtual, default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +thai_cs_as#!#Thai, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +thai_ci_as#!#Thai, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +thai_ci_ai#!#Thai, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +traditional_spanish_cs_as#!#Traditional-Spanish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +traditional_spanish_ci_as#!#Traditional-Spanish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +traditional_spanish_ci_ai#!#Traditional-Spanish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +turkish_cs_as#!#Turkish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +turkish_ci_as#!#Turkish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +turkish_ci_ai#!#Turkish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +ukrainian_cs_as#!#Ukrainian, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +ukrainian_ci_as#!#Ukrainian, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +ukrainian_ci_ai#!#Ukrainian, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +vietnamese_cs_as#!#Vietnamese, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive +vietnamese_ci_as#!#Vietnamese, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive +vietnamese_ci_ai#!#Vietnamese, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +~~END~~ + + +-- BABEL-1697 Collation and Codepage information for DMS +SELECT CAST( COLLATIONPROPERTY(Name, 'CodePage') AS INT) FROM fn_helpcollations() where Name = DATABASEPROPERTYEX('template1', 'Collation'); +go +~~START~~ +int +1252 +~~END~~ + + +SELECT CAST( COLLATIONPROPERTY(Name, 'lcid') AS INT) FROM fn_helpcollations() where Name = DATABASEPROPERTYEX('template1', 'Collation'); +go +~~START~~ +int +1033 +~~END~~ + + +-- clean up +drop table testing_collation; +go diff --git a/contrib/test/JDBC/expected/babel_iif.out b/contrib/test/JDBC/expected/babel_iif.out new file mode 100644 index 00000000000..95bec63789c --- /dev/null +++ b/contrib/test/JDBC/expected/babel_iif.out @@ -0,0 +1,95 @@ +select iif (2 < 1, cast('2020-10-20 09:00:00' as datetime), cast('2020-10-21' as date)); +go +~~START~~ +datetime +2020-10-21 00:00:00.0 +~~END~~ + +select iif (2 > 1, cast('abc' as varchar(3)), cast('cba' as char(3))); +go +~~START~~ +varchar +abc +~~END~~ + +select iif (2 > 1, cast(3.14 as float), cast(31.4 as numeric(3, 1))); +go +~~START~~ +float +3.14 +~~END~~ + +select iif (2 > 1, cast(3.14 as float), cast(1 as int)); +go +~~START~~ +float +3.14 +~~END~~ + +select iif (2 > 1, cast('$123.123' as money), cast(3.14 as float)); +go +~~START~~ +float +123.123 +~~END~~ + +select iif (2 > 1, cast('2020-10-20 09:00:00' as datetime), cast('09:00:00' as time)); +go +~~START~~ +datetime +2020-10-20 09:00:00.0 +~~END~~ + +select iif (2 > 1, cast(3.14 as float), cast('$123.123' as money)); +go +~~START~~ +float +3.14 +~~END~~ + + +-- Error, unknown literal cannot fit target type typinput func +select iif (2 > 1, 1, 'abc'); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +-- Error, different categories +select iif (2 > 1, cast(1 as int), cast('abc' as varchar(3))); +go +~~START~~ +int +1 +~~END~~ + +select iif (2 > 1, cast(0 as bit), cast(1 as int)); +go +~~START~~ +int +0 +~~END~~ + + +-- Null handling +select iif (2 > 1, null, 0); +go +~~START~~ +int + +~~END~~ + +select iif (null, 1, 0); +go +~~START~~ +int +0 +~~END~~ + +select iif (null, null, null); +go +~~START~~ +int + +~~END~~ + diff --git a/contrib/test/JDBC/expected/babel_isnull.out b/contrib/test/JDBC/expected/babel_isnull.out new file mode 100644 index 00000000000..23e5ba41dfc --- /dev/null +++ b/contrib/test/JDBC/expected/babel_isnull.out @@ -0,0 +1,215 @@ +-- tsql +CREATE TABLE test_numeric ( + num1 int, + num2 numeric +); +INSERT INTO test_numeric(num1, num2) +VALUES + (10, NULL), + (20, 2000), + (30, 3000), + (NULL, 4000), + (50, NULL); +GO +~~ROW COUNT: 5~~ + + +-- psql currentSchema=master_dbo,public +SELECT * FROM test_numeric; +GO +~~START~~ +int4#!#numeric +10#!# +20#!#2000 +30#!#3000 +#!#4000 +50#!# +~~END~~ + + +SELECT ISNULL(SUM(num1 - ISNULL(num2, 0)), 0) +FROM test_numeric +GO +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: function isnull(numeric, integer) does not exist + Hint: No function matches the given name and argument types. You might need to add explicit type casts. + Position: 26 + Server SQLState: 42883)~~ + + +-- tsql +-- Prove that a user may replace null values with a specified value using ISNULL +SELECT ISNULL(SUM(num1 - ISNULL(num2, 0)), 0) +FROM test_numeric +GO +~~START~~ +numeric +-4890 +~~END~~ + + +DROP TABLE test_numeric +GO + +-- Test cases with timestamps and sub-queries inside ISNULL +CREATE TABLE test_timestamp ( + serial_no numeric, + ts datetime2, +) +GO + +INSERT INTO test_timestamp(serial_no, ts) +VALUES + (1, NULL), + (2, '2020-06-22 00:10:00'), + (3, '2020-07-23 00:10:00'), + (4, '2020-08-24 00:10:00'), + (5, NULL), + (6, '2020-09-25 00:10:00') +GO +~~ROW COUNT: 6~~ + + +SELECT * FROM test_timestamp +GO +~~START~~ +numeric#!#datetime2 +1#!# +2#!#2020-06-22 00:10:00.000000 +3#!#2020-07-23 00:10:00.000000 +4#!#2020-08-24 00:10:00.000000 +5#!# +6#!#2020-09-25 00:10:00.000000 +~~END~~ + + +SELECT ISNULL(ts, '2021-01-01 00:00:00') +FROM test_timestamp +GO +~~START~~ +datetime2 +2021-01-01 00:00:00.000000 +2020-06-22 00:10:00.000000 +2020-07-23 00:10:00.000000 +2020-08-24 00:10:00.000000 +2021-01-01 00:00:00.000000 +2020-09-25 00:10:00.000000 +~~END~~ + + +CREATE TABLE default_time ( + ts datetime2 +) +GO + +INSERT INTO default_time(ts) VALUES('1970-01-01 00:00:00') +GO +~~ROW COUNT: 1~~ + + +SELECT ISNULL(ts, (SELECT ts FROM default_time)) +FROM test_timestamp +GO +~~START~~ +datetime2 +1970-01-01 00:00:00.000000 +2020-06-22 00:10:00.000000 +2020-07-23 00:10:00.000000 +2020-08-24 00:10:00.000000 +1970-01-01 00:00:00.000000 +2020-09-25 00:10:00.000000 +~~END~~ + + +DROP TABLE test_timestamp +GO +DROP TABLE default_time +GO + +-- Test cases with characters and casts +CREATE TABLE test_char ( + name varchar(20), + employee_type numeric, + age numeric +) +GO + +INSERT INTO test_char(name, employee_type, age) +VALUES + ('John', 1, 45), + (NULL, 0, 21), + ('Jake', 3, 33), + ('Jack', 1, 64), + ('Jane', NULL, 51) +GO +~~ROW COUNT: 5~~ + + +SELECT * FROM test_char +GO +~~START~~ +varchar#!#numeric#!#numeric +John#!#1#!#45 +#!#0#!#21 +Jake#!#3#!#33 +Jack#!#1#!#64 +Jane#!##!#51 +~~END~~ + + +SELECT ISNULL(name, 'Unknown'), ISNULL(employee_type, 'N/A'), age +FROM test_char +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid input syntax for type numeric: "N/A")~~ + + +DROP TABLE test_char +GO + +-- Test cases for incorrect number of arguments +CREATE TABLE test_args ( + word char(10) +) +GO +INSERT INTO test_args(word) +VALUES + ('hello'), + (NULL), + ('goodbye') +GO +~~ROW COUNT: 3~~ + + +SELECT ISNULL() from test_args +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error at or near ")")~~ + +SELECT ISNULL(word) FROM test_args +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error at or near ")")~~ + +SELECT ISNULL(word, 'no', 'yes') FROM test_args +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error at or near ",")~~ + + +-- Test case for varbinary +SELECT ISNULL(null, CAST(0xfe AS VARBINARY)) +GO +~~START~~ +varbinary +FE +~~END~~ + + +DROP TABLE test_args +GO diff --git a/contrib/test/JDBC/expected/babel_money.out b/contrib/test/JDBC/expected/babel_money.out new file mode 100644 index 00000000000..c2f0362996b --- /dev/null +++ b/contrib/test/JDBC/expected/babel_money.out @@ -0,0 +1,544 @@ +SELECT set_config('extra_float_digits', '0', 'false') +go +~~START~~ +text +0 +~~END~~ + + +-- test money operators return type money +create table t1(a money, b smallmoney); +insert into t1 values (1.1234, 2.1234); +insert into t1 values (2.5678, 3.5678); +insert into t1 values (4.9012, 5.9012); +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +select * from t1 order by a; +go +~~START~~ +money#!#smallmoney +1.1234#!#2.1234 +2.5678#!#3.5678 +4.9012#!#5.9012 +~~END~~ + + +-- test implicit casting for money +create table t2(a money, b smallmoney); +insert into t2 values (CAST( '1.1234' AS CHAR(10)), CAST( '2.1234' AS CHAR(10))); +insert into t2 values (CAST( '$2.56789' AS VARCHAR), CAST( '$3.56789' AS VARCHAR)); +insert into t2 values (CAST( '¥4.91' AS TEXT), CAST( '¥5.91' AS TEXT)); +insert into t2 values (CAST( '0006.' AS TEXT), CAST( '0000' AS TEXT)); +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +select * from t2 order by a; +go +~~START~~ +money#!#smallmoney +1.1234#!#2.1234 +2.5678#!#3.5678 +4.9100#!#5.9100 +6.0000#!#0.0000 +~~END~~ + + +select sum(a), sum(b) from t1; +go +~~START~~ +money#!#money +8.5924#!#11.5924 +~~END~~ + + +select cast(pg_typeof(sum(a)) AS VARCHAR(10)), cast(pg_typeof(sum(b)) AS VARCHAR(10)) from t1; +go +~~START~~ +varchar#!#varchar +money#!#money +~~END~~ + + +select avg(a), avg(b) from t1; +go +~~START~~ +money#!#money +2.8641#!#3.8641 +~~END~~ + + +select cast(pg_typeof(avg(a)) AS VARCHAR(10)), cast(pg_typeof(avg(b)) AS VARCHAR(10)) from t1; +go +~~START~~ +varchar#!#varchar +money#!#money +~~END~~ + + +select a+b from t1 order by a; +go +~~START~~ +money +3.2468 +6.1356 +10.8024 +~~END~~ + + +select cast(pg_typeof(a+b) AS VARCHAR(10)) from t1 order by a; +go +~~START~~ +varchar +money +money +money +~~END~~ + + +select b-a from t1 order by a; +go +~~START~~ +money +1.0000 +1.0000 +1.0000 +~~END~~ + + +select cast(pg_typeof(b-a) AS VARCHAR(10)) from t1 order by a; +go +~~START~~ +varchar +money +money +money +~~END~~ + + +select a*b from t1 order by a; +go +~~START~~ +money +2.3854 +9.1613 +28.9229 +~~END~~ + + +select cast(pg_typeof(a*b) AS VARCHAR(10)) from t1 order by a; +go +~~START~~ +varchar +money +money +money +~~END~~ + + +select a/b from t1 order by a; +go +~~START~~ +money +0.5290 +0.7197 +0.8305 +~~END~~ + + +select cast(pg_typeof(a/b) AS VARCHAR(10)) from t1 order by a; +go +~~START~~ +varchar +money +money +money +~~END~~ + + + +drop table t1, t2; +-- BABEL-598 Money type as procedure parameter should work without explicit cast +create table employees(pers_id int, fname nvarchar(20), lname nvarchar(30), sal money); +go + +create procedure p_employee_select +as +begin + select * from employees +end; +go + +create procedure p_employee_insert +@pers_id int, @fname nvarchar(20), @lname nvarchar(30), @sal money +as +begin + insert into employees values (@pers_id, @fname, @lname, @sal) +end; +go + +-- test const 123.1234 and 200 are valid MONEY inputs for the procedure without explicit cast +execute p_employee_insert @pers_id=1, @fname='John', @lname='Johnson', @sal=123.1234; +execute p_employee_insert @pers_id=1, @fname='Adam', @lname='Smith', @sal=200; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +execute p_employee_select; +go +~~START~~ +int#!#nvarchar#!#nvarchar#!#money +1#!#John#!#Johnson#!#123.1234 +1#!#Adam#!#Smith#!#200.0000 +~~END~~ + + +drop procedure p_employee_select; +drop procedure p_employee_insert; +drop table employees; +go + +-- BABEL-920 +-- Test operations(e.g. +,-,*,/) between fixeddecimal(money/smallmoney) and int8(bigint) +select CAST(2.56 as bigint) + CAST(3.60 as money); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(3.60 as money) + CAST(2.56 as bigint); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(2.56 as bigint) - CAST(3.60 as money); +go +~~START~~ +money +-1.6000 +~~END~~ + +select CAST(3.60 as money) - CAST(2.56 as bigint); +go +~~START~~ +money +1.6000 +~~END~~ + +select CAST(2.56 as bigint) * CAST(3.60 as money); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(3.60 as money) * CAST(2.56 as bigint); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(2.56 as bigint) / CAST(3.60 as money); +go +~~START~~ +float +0.5555555555555556 +~~END~~ + +select CAST(3.60 as money) / CAST(2.56 as bigint); +go +~~START~~ +money +1.8000 +~~END~~ + + +select CAST(2.56 as bigint) + CAST(3.60 as smallmoney); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(3.60 as smallmoney) + CAST(2.56 as bigint); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(2.56 as bigint) - CAST(3.60 as smallmoney); +go +~~START~~ +money +-1.6000 +~~END~~ + +select CAST(3.60 as smallmoney) - CAST(2.56 as bigint); +go +~~START~~ +money +1.6000 +~~END~~ + +select CAST(2.56 as bigint) * CAST(3.60 as smallmoney); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(3.60 as smallmoney) * CAST(2.56 as bigint); +go +~~START~~ +money +7.2000 +~~END~~ + +-- select CAST(2.56 as bigint) / CAST(3.60 as smallmoney); -> see BABEL-977 +-- go +select CAST(3.60 as smallmoney) / CAST(2.56 as bigint); +go +~~START~~ +money +1.8000 +~~END~~ + + +-- Test operations(e.g. +,-,*,/) between fixeddecimal(money/smallmoney) and int4(int) +select CAST(2.56 as int) + CAST(3.60 as money); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(3.60 as money) + CAST(2.56 as int); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(2.56 as int) - CAST(3.60 as money); +go +~~START~~ +money +-1.6000 +~~END~~ + +select CAST(3.60 as money) - CAST(2.56 as int); +go +~~START~~ +money +1.6000 +~~END~~ + +select CAST(2.56 as int) * CAST(3.60 as money); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(3.60 as money) * CAST(2.56 as int); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(2.56 as int) / CAST(3.60 as money); +go +~~START~~ +float +0.5555555555555556 +~~END~~ + +select CAST(3.60 as money) / CAST(2.56 as int); +go +~~START~~ +money +1.8000 +~~END~~ + + +select CAST(2.56 as int) + CAST(3.60 as smallmoney); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(3.60 as smallmoney) + CAST(2.56 as int); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(2.56 as int) - CAST(3.60 as smallmoney); +go +~~START~~ +money +-1.6000 +~~END~~ + +select CAST(3.60 as smallmoney) - CAST(2.56 as int); +go +~~START~~ +money +1.6000 +~~END~~ + +select CAST(2.56 as int) * CAST(3.60 as smallmoney); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(3.60 as smallmoney) * CAST(2.56 as int); +go +~~START~~ +money +7.2000 +~~END~~ + +-- select CAST(2.56 as int) / CAST(3.60 as smallmoney); -> see BABEL-977 +-- go +select CAST(3.60 as smallmoney) / CAST(2.56 as int); +go +~~START~~ +money +1.8000 +~~END~~ + + +-- Test operations(e.g. +,-,*,/) between fixeddecimal(money/smallmoney) and int2(smallint) +select CAST(2.56 as smallint) + CAST(3.60 as money); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(3.60 as money) + CAST(2.56 as smallint); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(2.56 as smallint) - CAST(3.60 as money); +go +~~START~~ +money +-1.6000 +~~END~~ + +select CAST(3.60 as money) - CAST(2.56 as smallint); +go +~~START~~ +money +1.6000 +~~END~~ + +select CAST(2.56 as smallint) * CAST(3.60 as money); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(3.60 as money) * CAST(2.56 as smallint); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(2.56 as smallint) / CAST(3.60 as money); +go +~~START~~ +float +0.5555555555555556 +~~END~~ + +select CAST(3.60 as money) / CAST(2.56 as smallint); +go +~~START~~ +money +1.8000 +~~END~~ + + +select CAST(2.56 as smallint) + CAST(3.60 as smallmoney); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(3.60 as smallmoney) + CAST(2.56 as smallint); +go +~~START~~ +money +5.6000 +~~END~~ + +select CAST(2.56 as smallint) - CAST(3.60 as smallmoney); +go +~~START~~ +money +-1.6000 +~~END~~ + +select CAST(3.60 as smallmoney) - CAST(2.56 as smallint); +go +~~START~~ +money +1.6000 +~~END~~ + +select CAST(2.56 as smallint) * CAST(3.60 as smallmoney); +go +~~START~~ +money +7.2000 +~~END~~ + +select CAST(3.60 as smallmoney) * CAST(2.56 as smallint); +go +~~START~~ +money +7.2000 +~~END~~ + +-- select CAST(2.56 as smallint) / CAST(3.60 as smallmoney); -> see BABEL-977 +-- go +select CAST(3.60 as smallmoney) / CAST(2.56 as smallint); +go +~~START~~ +money +1.8000 +~~END~~ + diff --git a/contrib/test/JDBC/expected/babel_numeric.out b/contrib/test/JDBC/expected/babel_numeric.out new file mode 100644 index 00000000000..40988a68576 --- /dev/null +++ b/contrib/test/JDBC/expected/babel_numeric.out @@ -0,0 +1,123 @@ +-- Test numeric in cast function +select cast(1.123 as numeric(38, 10)); +go +~~START~~ +numeric +1.1230000000 +~~END~~ + +select cast(1.123 as numeric(39, 10)); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The size (39) given to the type 'numeric' exceeds the maximum allowed (38))~~ + + +-- Test decimal in cast function +select cast(1.123 as decimal(38, 10)); +go +~~START~~ +numeric +1.1230000000 +~~END~~ + +select cast(1.123 as decimal(39, 10)); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The size (39) given to the type 'decimal' exceeds the maximum allowed (38))~~ + + +-- Test dec in cast function +select cast(1.123 as dec(38, 10)); +go +~~START~~ +numeric +1.1230000000 +~~END~~ + +select cast(1.123 as dec(39, 10)); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The size (39) given to the type 'decimal' exceeds the maximum allowed (38))~~ + + +-- Test numeric in create table +create table t1 (col numeric(38,37)); +drop table t1; +go + +create table t1 (col numeric(39, 37)); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The size (39) given to the type 'numeric' exceeds the maximum allowed (38))~~ + + +-- Test decimal in create table +create table t1 (col decimal(38,37)); +drop table t1; +go + +create table t1 (col decimal(39, 37)); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The size (39) given to the type 'decimal' exceeds the maximum allowed (38))~~ + + +-- Test dec in create table +create table t1 (col decimal(38,37)); +drop table t1; +go + +create table t1 (col decimal(39, 37)); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: The size (39) given to the type 'decimal' exceeds the maximum allowed (38))~~ + + +-- Test default precision and scale is set to 18, 0 +create table t1 (col numeric); +insert into t1 values (1.2); +insert into t1 values (123456789012345678); +select * from t1; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +numeric +1 +123456789012345678 +~~END~~ + +insert into t1 values (1234567890123456789); +select * from t1; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: numeric field overflow)~~ + + +drop table t1; +go + +-- Test default scale is set to 0 if only precision is specified +create table t1 (col numeric(4)); +insert into t1 values (1.2); +select * from t1; +go +~~ROW COUNT: 1~~ + +~~START~~ +numeric +1 +~~END~~ + + +drop table t1; +go diff --git a/contrib/test/JDBC/expected/babel_operators.out b/contrib/test/JDBC/expected/babel_operators.out new file mode 100644 index 00000000000..188153324d7 --- /dev/null +++ b/contrib/test/JDBC/expected/babel_operators.out @@ -0,0 +1,208 @@ +-- test adding with varbinary +SELECT (123 + 0x42); +SELECT (0x42 + 123); +GO +~~START~~ +bigint +189 +~~END~~ + +~~START~~ +bigint +189 +~~END~~ + + +-- test subtracting with varbinary +SELECT (123 - 0x42); +SELECT (0x42 - 123); +GO +~~START~~ +bigint +57 +~~END~~ + +~~START~~ +bigint +-57 +~~END~~ + + +-- test multiplication with varbinary +SELECT (123 * CAST(123 AS varbinary(4))); +SELECT (CAST(123 AS varbinary(4)) * 123); +GO +~~START~~ +bigint +15129 +~~END~~ + +~~START~~ +bigint +15129 +~~END~~ + + +-- test division with varbinary +SELECT (12345 / CAST(12 AS varbinary(4))); +SELECT (CAST(12345 AS varbinary(4)) / 12); +GO +~~START~~ +bigint +1028 +~~END~~ + +~~START~~ +bigint +1028 +~~END~~ + + +-- test & operator with varbinary +SELECT (CAST(123 AS varbinary(1)) & 21); +SELECT (CAST(123 AS varbinary(2)) & 321); +SELECT (CAST(12345 AS varbinary(4)) & 54321); +GO +~~START~~ +int +17 +~~END~~ + +~~START~~ +int +65 +~~END~~ + +~~START~~ +int +4145 +~~END~~ + + +SELECT (CAST(9876543210 AS BIGINT) & CAST(1234567890 AS varbinary(8))); +SELECT (543210 & CAST(12345 AS varbinary(4))); +SELECT (CAST(321 AS smallint) & CAST(123 AS varbinary(2))); +SELECT (CAST(12 AS tinyint) & CAST(21 AS varbinary(1))); +GO +~~START~~ +bigint +1217397442 +~~END~~ + +~~START~~ +int +40 +~~END~~ + +~~START~~ +smallint +65 +~~END~~ + +~~START~~ +smallint +4 +~~END~~ + + +-- test | operator with varbinary +SELECT (CAST(123 AS varbinary(1)) | 21); +SELECT (CAST(123 AS varbinary(2)) | 321); +SELECT (CAST(12345 AS varbinary(4)) | 54321); +GO +~~START~~ +int +127 +~~END~~ + +~~START~~ +int +379 +~~END~~ + +~~START~~ +int +62521 +~~END~~ + + +SELECT (CAST(9876543210 AS BIGINT) | CAST(1234567890 AS varbinary(8))); +SELECT (543210 | CAST(12345 AS varbinary(4))); +SELECT (CAST(321 AS smallint) | CAST(123 AS varbinary(2))); +SELECT (CAST(12 AS tinyint) | CAST(21 AS varbinary(1))); +GO +~~START~~ +bigint +9893713658 +~~END~~ + +~~START~~ +int +555515 +~~END~~ + +~~START~~ +smallint +379 +~~END~~ + +~~START~~ +smallint +29 +~~END~~ + + +-- test ^ operator with varbinary +SELECT (17 ^ 5); +GO +~~START~~ +int +20 +~~END~~ + + +SELECT (CAST(123 AS varbinary(1)) ^ 21); +SELECT (CAST(123 AS varbinary(2)) ^ 321); +SELECT (CAST(12345 AS varbinary(4)) ^ 54321); +GO +~~START~~ +int +110 +~~END~~ + +~~START~~ +int +314 +~~END~~ + +~~START~~ +int +58376 +~~END~~ + + +SELECT (CAST(9876543210 AS BIGINT) ^ CAST(1234567890 AS varbinary(8))); +SELECT (543210 ^ CAST(12345 AS varbinary(4))); +SELECT (CAST(321 AS smallint) ^ CAST(123 AS varbinary(2))); +SELECT (CAST(12 AS tinyint) ^ CAST(21 AS varbinary(1))); +GO +~~START~~ +bigint +8676316216 +~~END~~ + +~~START~~ +int +555475 +~~END~~ + +~~START~~ +smallint +314 +~~END~~ + +~~START~~ +smallint +25 +~~END~~ + diff --git a/contrib/test/JDBC/expected/babel_print.out b/contrib/test/JDBC/expected/babel_print.out new file mode 100644 index 00000000000..dce20955c06 --- /dev/null +++ b/contrib/test/JDBC/expected/babel_print.out @@ -0,0 +1,39 @@ +-- Test PRINT with T-sql procedures -- +-- Printing a pre-defined text +CREATE PROCEDURE tsql_print_text AS + PRINT 'Pre-defined text for tsql_print_text' +GO +EXEC tsql_print_text +GO + +-- Printing a user input text +CREATE PROCEDURE tsql_print_message(@message varchar(50)) AS +BEGIN + PRINT @message +END; +GO +EXEC tsql_print_message 'Testing message for tsql_print_message' +GO + +-- Printing a pre-defined and a user input text +CREATE PROCEDURE tsql_print_message_and_text(@message varchar(50)) AS +BEGIN + PRINT 'Pre-defined text for tsql_print_message_and_text. User input: '+ @message +END +GO +EXEC tsql_print_message_and_text 'Testing message for tsql_print_message_and_text' +GO + +-- Making a call to another function that prints +CREATE PROCEDURE tsql_print_function AS + EXECUTE tsql_print_text +GO +EXEC tsql_print_function +GO + +-- Cleanup -- +DROP PROCEDURE tsql_print_text; +DROP PROCEDURE tsql_print_message; +DROP PROCEDURE tsql_print_message_and_text; +DROP PROCEDURE tsql_print_function; +GO diff --git a/contrib/test/JDBC/expected/babel_smalldatetime.out b/contrib/test/JDBC/expected/babel_smalldatetime.out new file mode 100644 index 00000000000..4e3d97aad26 --- /dev/null +++ b/contrib/test/JDBC/expected/babel_smalldatetime.out @@ -0,0 +1,388 @@ +-- Testing rounding behaviour when inserting into the table +create table smalldatetime_testing ( sm smalldatetime ); +INSERT INTO smalldatetime_testing VALUES('23:40:29.998'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:29.998'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:29.998'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:29.999'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:30.000'); +INSERT INTO smalldatetime_testing VALUES('2002-05-23 23:41:29.998'); +INSERT INTO smalldatetime_testing VALUES('2002-05-23 23:41:29.999'); +INSERT INTO smalldatetime_testing VALUES('2002-05-23 23:41:30.000'); +INSERT INTO smalldatetime_testing VALUES('2000-01-01 00:00:29.998'); +INSERT INTO smalldatetime_testing VALUES('2000-01-01 00:00:29.999'); +INSERT INTO smalldatetime_testing VALUES('1999-12-31 23:59:30.000'); +INSERT INTO smalldatetime_testing VALUES('1999-12-31 23:59:29.999'); +INSERT INTO smalldatetime_testing VALUES('1999-12-31 23:59:29.998'); +select * from smalldatetime_testing; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smalldatetime +1900-01-01 23:40:00.0 +1992-05-23 23:40:00.0 +1992-05-23 23:40:00.0 +1992-05-23 23:41:00.0 +1992-05-23 23:41:00.0 +2002-05-23 23:41:00.0 +2002-05-23 23:42:00.0 +2002-05-23 23:42:00.0 +2000-01-01 00:00:00.0 +2000-01-01 00:01:00.0 +2000-01-01 00:00:00.0 +2000-01-01 00:00:00.0 +1999-12-31 23:59:00.0 +~~END~~ + + +-- Test comparision with datetime/smalldatetime/date +select * from smalldatetime_testing where sm >= cast('2000-01-01 00:00:59' as smalldatetime); +select * from smalldatetime_testing where sm >= cast('1992-05-23 23:40:00' as datetime) + and sm < cast('1992-05-23 23:41:00' as datetime); +select * from smalldatetime_testing where sm < cast(cast('1992-05-24' as date) as smalldatetime); +go +~~START~~ +smalldatetime +2002-05-23 23:41:00.0 +2002-05-23 23:42:00.0 +2002-05-23 23:42:00.0 +2000-01-01 00:01:00.0 +~~END~~ + +~~START~~ +smalldatetime +1992-05-23 23:40:00.0 +1992-05-23 23:40:00.0 +~~END~~ + +~~START~~ +smalldatetime +1900-01-01 23:40:00.0 +1992-05-23 23:40:00.0 +1992-05-23 23:40:00.0 +1992-05-23 23:41:00.0 +1992-05-23 23:41:00.0 +~~END~~ + + +-- Test rounding for 23:59:59 +SELECT CAST('1992-05-09 23:59:59' AS SMALLDATETIME); +SELECT CAST('2002-05-09 23:59:59' AS SMALLDATETIME); +SELECT CAST('1999-12-31 23:59:59' AS SMALLDATETIME); +go +~~START~~ +smalldatetime +1992-05-10 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +2002-05-10 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +2000-01-01 00:00:00.0 +~~END~~ + + +-- Test type cast to/from other time formats +-- Cast to smalldatetime +select CAST(CAST('00:00:00.234' AS time) AS smalldatetime); +select CAST(CAST('01:02:03.456' AS time) AS smalldatetime); +select CAST(CAST('2020-03-15' AS date) AS smalldatetime); +select CAST(CAST('2020-03-15' AS datetime) AS smalldatetime); +select CAST(CAST('2010-07-08 23:59:29.998' AS datetime) AS smalldatetime); +select CAST(CAST('1980-07-08 23:59:29.123456 +8:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('2010-07-08 23:59:29.123456 +8:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('1980-07-08 23:59:29.123456 -8:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('2010-07-08 23:59:29.123456 -8:00' AS datetimeoffset) AS smalldatetime); +go +~~START~~ +smalldatetime +1900-01-01 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +1900-01-01 01:02:00.0 +~~END~~ + +~~START~~ +smalldatetime +2020-03-15 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +2020-03-15 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +2010-07-08 23:59:00.0 +~~END~~ + +~~START~~ +smalldatetime +1980-07-08 15:59:00.0 +~~END~~ + +~~START~~ +smalldatetime +2010-07-08 15:59:00.0 +~~END~~ + +~~START~~ +smalldatetime +1980-07-09 07:59:00.0 +~~END~~ + +~~START~~ +smalldatetime +2010-07-09 07:59:00.0 +~~END~~ + +-- Cast from smalldatetime +select CAST(CAST('2010-07-08' AS smalldatetime) AS time); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS time); +select CAST(CAST('2010-07-08 23:59:31.998' AS smalldatetime) AS time); +select CAST(CAST('1980-07-08 23:59:29.998' AS smalldatetime) AS time); +select CAST(CAST('1980-07-08 23:59:31.998' AS smalldatetime) AS time); +select CAST(CAST('2020-03-15' AS smalldatetime) AS date); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS date); +select CAST(CAST('2010-07-08 23:59:30.000' AS smalldatetime) AS date); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS datetime); +select CAST(CAST('1992-07-08 23:59:29.998' AS smalldatetime) AS datetime); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS datetimeoffset); +select CAST(CAST('1990-07-08 23:59:29.998' AS smalldatetime) AS datetimeoffset); +go +~~START~~ +time +00:00:00.000000 +~~END~~ + +~~START~~ +time +23:59:00.000000 +~~END~~ + +~~START~~ +time +00:00:00.000000 +~~END~~ + +~~START~~ +time +23:59:00.000000 +~~END~~ + +~~START~~ +time +00:00:00.000000 +~~END~~ + +~~START~~ +date +2020-03-15 +~~END~~ + +~~START~~ +date +2010-07-08 +~~END~~ + +~~START~~ +date +2010-07-09 +~~END~~ + +~~START~~ +datetime +2010-07-08 23:59:00.0 +~~END~~ + +~~START~~ +datetime +1992-07-08 23:59:00.0 +~~END~~ + +~~START~~ +datetimeoffset +2010-07-08 23:59:00.000000 +00:00 +~~END~~ + +~~START~~ +datetimeoffset +1990-07-08 23:59:00.000000 +00:00 +~~END~~ + + +-- Test smalldatetime value ranges +select cast('1900-01-01' as smalldatetime); +select cast('2079-06-06' as smalldatetime); +select cast('1899-12-31 23:59:29.999' as smalldatetime); +select cast('2079-06-06 23:59:29.998' as smalldatetime); +select CAST(CAST('1899-12-31 23:59:30.000' AS datetime) AS smalldatetime); +select CAST(CAST('1899-12-31 23:59:30.000 +0:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('2079-06-06 23:59:30.000 +1:00' AS datetimeoffset) AS smalldatetime); +select cast('1899-12-31' as smalldatetime); -- out of range +select cast('2079-06-07' as smalldatetime); -- out of range +select cast('2079-06-06 23:59:29.999' as smalldatetime); -- out of range +select CAST(CAST('2099-03-15' AS date) AS smalldatetime); -- out of range +select CAST(CAST('1800-03-15 23:59:29.998' AS datetime) AS smalldatetime);-- out of range +select CAST(CAST('2099-03-15 23:59:29.998' AS datetime) AS smalldatetime);-- out of range +select CAST(CAST('1899-12-31 23:59:30.000 +1:00' AS datetimeoffset) AS smalldatetime);-- out of range +select CAST(CAST('2099-03-15 23:59:29.998 +6:00' AS datetimeoffset) AS smalldatetime);-- out of range +go +~~START~~ +smalldatetime +1900-01-01 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +2079-06-06 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +1900-01-01 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +2079-06-06 23:59:00.0 +~~END~~ + +~~START~~ +smalldatetime +1900-01-01 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +1900-01-01 00:00:00.0 +~~END~~ + +~~START~~ +smalldatetime +2079-06-06 23:00:00.0 +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: data out of range for smalldatetime)~~ + + +-- Test smalldatetime default value +create table t1 (a smalldatetime, b int); +insert into t1 (b) values (1); +select a from t1 where b = 1; +go +~~ROW COUNT: 1~~ + +~~START~~ +smalldatetime +1900-01-01 00:00:00.0 +~~END~~ + + +-- Test smalldatetime as parameter for time related functions +select day(cast('2002-05-23 23:41:29.998' as smalldatetime)); +select month(cast('2002-05-23 23:41:29.998' as smalldatetime)); +select year(cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(quarter, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(hour, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(dayofyear, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(second, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datename(year, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datename(dw, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datename(month, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select dateadd(second, 56, cast('2016-12-26 23:29:29' as smalldatetime)); +select dateadd(minute, 56, cast('2016-12-26 23:29:29' as smalldatetime)); +select dateadd(year, 150, cast('2016-12-26 23:29:29' as smalldatetime)); -- Expect error +go +~~START~~ +int +23 +~~END~~ + +~~START~~ +int +5 +~~END~~ + +~~START~~ +int +2002 +~~END~~ + +~~START~~ +int +2 +~~END~~ + +~~START~~ +int +23 +~~END~~ + +~~START~~ +int +143 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +~~START~~ +text +2002 +~~END~~ + +~~START~~ +text +Thursday +~~END~~ + +~~START~~ +text +May +~~END~~ + +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: operator is not unique: smalldatetime + interval)~~ + + +-- Clean up +drop table smalldatetime_testing; +drop table t1; +go diff --git a/contrib/test/JDBC/expected/babel_top.out b/contrib/test/JDBC/expected/babel_top.out new file mode 100644 index 00000000000..df5d7b3efb2 --- /dev/null +++ b/contrib/test/JDBC/expected/babel_top.out @@ -0,0 +1,91 @@ + +-- +-- Tests for TOP clause +-- +create table students (fname varchar(10), lname varchar(10), score double precision) +go + +insert into students (fname, lname, score) +values + ('John', 'Doe', 72.5), + ('Jane', 'Smith', 88), + ('Jill', 'Johnson', 98), + ('Jack', 'Green', 67), + ('Jennifer', 'Ross', 75.7), + ('Jacob', 'Brown', 95.2) +go +~~ROW COUNT: 6~~ + + +select top 3 * from students +go +~~START~~ +varchar#!#varchar#!#float +John#!#Doe#!#72.5 +Jane#!#Smith#!#88.0 +Jill#!#Johnson#!#98.0 +~~END~~ + + +select top 4 score from students +go +~~START~~ +float +72.5 +88.0 +98.0 +67.0 +~~END~~ + + +select top 3 from students +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near 'from' at line 1 and character position 13)~~ + + +-- test top bigint +select top 2147483648 * from students +go +~~START~~ +varchar#!#varchar#!#float +John#!#Doe#!#72.5 +Jane#!#Smith#!#88.0 +Jill#!#Johnson#!#98.0 +Jack#!#Green#!#67.0 +Jennifer#!#Ross#!#75.7 +Jacob#!#Brown#!#95.2 +~~END~~ + + +-- test top 100 percent +select top 100 percent * from students +go +~~START~~ +varchar#!#varchar#!#float +John#!#Doe#!#72.5 +Jane#!#Smith#!#88.0 +Jill#!#Johnson#!#98.0 +Jack#!#Green#!#67.0 +Jennifer#!#Ross#!#75.7 +Jacob#!#Brown#!#95.2 +~~END~~ + + +select top 100.00 percent lname from students +go +~~START~~ +varchar +Doe +Smith +Johnson +Green +Ross +Brown +~~END~~ + + +-- cleanup +drop table students +go diff --git a/contrib/test/JDBC/expected/babel_trigger.out b/contrib/test/JDBC/expected/babel_trigger.out new file mode 100644 index 00000000000..51617b0ac5d --- /dev/null +++ b/contrib/test/JDBC/expected/babel_trigger.out @@ -0,0 +1,407 @@ +-- a simple test +create table testing1(col nvarchar(60)) +GO + +create trigger notify on testing1 after insert +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Muffler' +GO +~~START~~ +varchar +trigger invoked +~~END~~ + +~~ROW COUNT: 1~~ + + +drop trigger notify +GO + +-- test drop trigger if exists +create trigger notify on testing1 after insert +as +begin + SELECT 'trigger invoked' +end +GO + +drop trigger if exists notify +GO + +drop trigger if exists notify +GO + +-- test comma separator +create trigger notify on testing1 after insert, delete +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Apple' +GO +~~START~~ +varchar +trigger invoked +~~END~~ + +~~ROW COUNT: 1~~ + + +delete from testing1 where col = N'Apple' +GO +~~START~~ +varchar +trigger invoked +~~END~~ + +~~ROW COUNT: 1~~ + + +drop trigger notify +GO + +-- test inserted and deleted transition tables +CREATE TABLE products( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL +) +GO + +CREATE TABLE product_audits( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL, + operation CHAR(3) NOT NULL, + CHECK(operation = 'INS' or operation='DEL') +) +GO + +CREATE TRIGGER trg_product_audit +ON products +AFTER INSERT +AS +BEGIN + INSERT INTO product_audits( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price, + operation + ) + SELECT + i.product_id, + product_name, + brand_id, + category_id, + model_year, + i.list_price, + 'INS' + FROM + inserted i +END +GO + +INSERT INTO products( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price +) +VALUES ( + 1, + 'Test product', + 1, + 1, + 2018, + 599 +) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +SELECT * FROM PRODUCT_AUDITS +GO +~~START~~ +int#!#varchar#!#int#!#int#!#smallint#!#numeric#!#char +1#!#Test product#!#1#!#1#!#2018#!#599.00#!#INS +~~END~~ + + +drop trigger trg_product_audit +GO + +-- clean up +drop table testing1 +GO +drop table product_audits +GO +drop table products +GO + + +-- CARRY OUT THE SAME TESTS WITH THE FOR KEYWORD -- +-- a simple test +create table testing1(col nvarchar(60)) +GO + +create trigger notify on testing1 for insert +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Muffler' +GO +~~START~~ +varchar +trigger invoked +~~END~~ + +~~ROW COUNT: 1~~ + + +drop trigger notify +GO + +-- test drop trigger if exists +create trigger notify on testing1 for insert +as +begin + SELECT 'trigger invoked' +end +GO + +drop trigger if exists notify +GO + +drop trigger if exists notify +GO + +-- test comma separator +create trigger notify on testing1 for insert, delete +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Apple' +GO +~~START~~ +varchar +trigger invoked +~~END~~ + +~~ROW COUNT: 1~~ + + +delete from testing1 where col = N'Apple' +GO +~~START~~ +varchar +trigger invoked +~~END~~ + +~~ROW COUNT: 1~~ + + +drop trigger notify +GO + +-- test inserted and deleted transition tables +CREATE TABLE products( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL +) +GO + +CREATE TABLE product_audits( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL, + operation CHAR(3) NOT NULL, + CHECK(operation = 'INS' or operation='DEL') +) +GO + +CREATE TRIGGER trg_product_audit +ON products +FOR INSERT +AS +BEGIN + INSERT INTO product_audits( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price, + operation + ) + SELECT + i.product_id, + product_name, + brand_id, + category_id, + model_year, + i.list_price, + 'INS' + FROM + inserted i +END +GO + +INSERT INTO products( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price +) +VALUES ( + 1, + 'Test product', + 1, + 1, + 2018, + 599 +) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +SELECT * FROM PRODUCT_AUDITS +GO +~~START~~ +int#!#varchar#!#int#!#int#!#smallint#!#numeric#!#char +1#!#Test product#!#1#!#1#!#2018#!#599.00#!#INS +~~END~~ + + +drop trigger trg_product_audit +GO + +-- Test drop trigger without table name -- +-- First, test that triggers must have unique names +create trigger notify on testing1 after insert +as +begin + SELECT 'trigger invoked' +end +GO + +create table testing2(col nvarchar(60)) +GO + +create trigger notify on testing2 after insert +as +begin + SELECT 'trigger invoked' +end +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: trigger "notify" already exists in the database)~~ + + +drop table testing2 +GO + +-- Now, test that drop trigger works without tablename +drop trigger notify +GO + +-- Test that drop trigger statement on non-existent trigger throws error +drop trigger notify +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: trigger "notify" does not exist)~~ + +drop trigger if exists notify +GO + +-- Test that dropping a table with triggers defined on it succeeds +create table testTbl(colA int not null primary key, colB varchar(20)) +GO + +create trigger trig1 on testTbl after insert +as +begin + SELECT 'trigger invoked' +end +GO + +create trigger trig2 on testTbl after insert +as +begin + SELECT 'trigger2 invoked' +end +GO + +drop table testTbl +GO + +-- Test 'NOT FOR REPLICATION' syntax +create trigger notify on testing1 after insert +NOT FOR REPLICATION +as +begin + SELECT 'trigger invoked' +end +GO +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: 'NOT FOR REPLICATION' is not currently supported in Babelfish. please use babelfishpg_tsql.escape_hatch_for_replication to ignore)~~ + + +insert into testing1 (col) select N'Muffler' +GO +~~ROW COUNT: 1~~ + + +drop trigger notify +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: trigger "notify" does not exist)~~ + + +-- clean up +drop table testing1 +GO +drop table product_audits +GO +drop table products +GO diff --git a/contrib/test/JDBC/expected/babel_varbinary.out b/contrib/test/JDBC/expected/babel_varbinary.out new file mode 100644 index 00000000000..568ec8c8ee6 --- /dev/null +++ b/contrib/test/JDBC/expected/babel_varbinary.out @@ -0,0 +1,206 @@ +-- [BABEL-448] Test support of unquoted hexadecimal string input +select 0x1a2b3c4f; +go +~~START~~ +varbinary +1A2B3C4F +~~END~~ + +select 0X1A2B3C4F; +go +~~START~~ +varbinary +1A2B3C4F +~~END~~ + +select pg_typeof(0X1A2B3C4F); +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: data type regtype is not supported yet)~~ + + +-- BABEL-631 Test odd number of hex digigts is allowed +select 0xF; +go +~~START~~ +varbinary +0F +~~END~~ + +select 0x1; +go +~~START~~ +varbinary +01 +~~END~~ + +select 0x0; +go +~~START~~ +varbinary +00 +~~END~~ + +select 0x1F1; +go +~~START~~ +varbinary +01F1 +~~END~~ + + +-- invalid hex input +select 0x1G2A; +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid hexadecimal digit: "S")~~ + + +-- test insert of hex string +create table t1(a varbinary(8), b binary(8)); +insert into t1 values(0x1a2b3c4f, cast('1a2b3c4f' as binary(8))); +insert into t1 values(cast('1a2b3c4f' as varbinary(8)), cast('1a2b3c4f' as binary(8))); +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +-- 0x1a2b3c4f and '1a2b3c4f' are different as varbinary +select * from t1; +go +~~START~~ +varbinary#!#binary +1A2B3C4F#!#3161326233633466 +3161326233633466#!#3161326233633466 +~~END~~ + + +-- test bitwise operators on hex string and int +select 0x1F & 10; +go +~~START~~ +int +10 +~~END~~ + +select 10 & 0x1F; +go +~~START~~ +int +10 +~~END~~ + +select 0x1F | 10; +go +~~START~~ +int +31 +~~END~~ + +select 10 | 0x1F; +go +~~START~~ +int +31 +~~END~~ + +select 0x1F ^ 10; +go +~~START~~ +int +21 +~~END~~ + +select 10 ^ 0x1F; +go +~~START~~ +int +21 +~~END~~ + +select 0x1F * 10; +go +~~START~~ +bigint +310 +~~END~~ + +select 10 * 0x1F; +go +~~START~~ +bigint +310 +~~END~~ + +select 0x1F / 10; +go +~~START~~ +bigint +3 +~~END~~ + +select 100 / 0x1F; +go +~~START~~ +bigint +3 +~~END~~ + + +-- division by 0 +select 0x1F / 0; +go +~~ERROR (Code: 8134)~~ + +~~ERROR (Message: division by zero)~~ + +select 10 / 0x00; +go +~~ERROR (Code: 8134)~~ + +~~ERROR (Message: division by zero)~~ + + +-- test hex string in procedure +create procedure test_hex_bitop as +begin + select 0x1A2B3C4F ^ 101; +end; +go + +execute test_hex_bitop; +go +~~START~~ +int +439041066 +~~END~~ + + +create procedure test_hex_insert as +begin + insert into t1 values(0x1f, cast('1f' as binary(2))); +end; +go + +execute test_hex_insert; +go +~~ROW COUNT: 1~~ + +select * from t1; +go +~~START~~ +varbinary#!#binary +1A2B3C4F#!#3161326233633466 +3161326233633466#!#3161326233633466 +1F#!#3166000000000000 +~~END~~ + + +-- clean up +drop table t1; +drop procedure test_hex_bitop; +drop procedure test_hex_insert; +go diff --git a/contrib/test/JDBC/expected/pg_stat_activity.out b/contrib/test/JDBC/expected/pg_stat_activity.out new file mode 100644 index 00000000000..f5ca8ce8e2e --- /dev/null +++ b/contrib/test/JDBC/expected/pg_stat_activity.out @@ -0,0 +1,62 @@ +SELECT sys.babelfish_set_role(session_user); +~~START~~ +int +1 +~~END~~ + + +# Babel-1294 Support application_name in pg_stat_activity +select application_name from pg_stat_activity where pid = pg_backend_pid(); +~~START~~ +text +Microsoft JDBC Driver for SQL Server +~~END~~ + + +# BABEL-1326: Support query in pg_stat_activity +select query from pg_stat_activity where pid = pg_backend_pid(); +~~START~~ +text +select query from pg_stat_activity where pid = pg_backend_pid(); +~~END~~ + + +# BABEL-1326: Checking if the right query is returned for Prepexec with batch too +prepst#!#select query from pg_stat_activity where pid = pg_backend_pid();select ? as a#!#int|-|a|-|1 +~~START~~ +text +select query from pg_stat_activity where pid = pg_backend_pid();select @P0 as a +~~END~~ + +~~START~~ +int +1 +~~END~~ + +prepst#!#exec#!#int|-|a|-|1 +~~START~~ +text +select query from pg_stat_activity where pid = pg_backend_pid();select @P0 as a +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +# BABEL-1326: Checking if the right query is returned from a procedure and a nested procedure as well +Create procedure proc_pg_stat_activity as begin select query from pg_stat_activity where pid = pg_backend_pid(); end; +exec proc_pg_stat_activity; +~~START~~ +text +exec proc_pg_stat_activity; +~~END~~ + +Create procedure proc1_pg_stat_activity as begin exec proc_pg_stat_activity end; +exec proc1_pg_stat_activity; +~~START~~ +text +exec proc1_pg_stat_activity; +~~END~~ + diff --git a/contrib/test/JDBC/expected/tds_faultinjection.out b/contrib/test/JDBC/expected/tds_faultinjection.out new file mode 100644 index 00000000000..a8f012e79df --- /dev/null +++ b/contrib/test/JDBC/expected/tds_faultinjection.out @@ -0,0 +1,79 @@ +---- Inject an error in tds comm layer while reading the request +---- Test both SQL Batch and prepare exec +--SELECT inject_fault('tds_comm_throw_error', 2); +--SELECT 1; +--prepst#!# SELECT @a #!#BIGINT|-|a|-|0 + +---- test normal execution +--SELECT 1; + +-- Inject a pre-parsing error +-- Test both SQL Batch and prepare exec +SELECT inject_fault('pre_parsing_throw_error', 2); +~~START~~ +text +enabled, pending occurrences: 2 +~~END~~ + +SELECT 1; +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: error triggered from fault injection)~~ + +prepst#!# SELECT ? #!#BIGINT|-|a|-|0 +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: error triggered from fault injection)~~ + + +-- test normal execution +SELECT 1; +~~START~~ +int +1 +~~END~~ + + +-- Inject a post-parsing error +-- Test both SQL Batch and prepare exec +SELECT inject_fault('post_parsing_throw_error', 2); +~~START~~ +text +enabled, pending occurrences: 2 +~~END~~ + +SELECT 1; +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: error triggered from fault injection)~~ + +prepst#!# SELECT ? #!#BIGINT|-|a|-|0 +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: error triggered from fault injection)~~ + + +-- test normal execution +SELECT 1; +~~START~~ +int +1 +~~END~~ + + +-- Inject fault to tamper TDS request +-- Test both SQL Batch and prepare exec (but prepare exec is failing now) +SELECT inject_fault('pre_parsing_tamper_request', 1); +~~START~~ +text +enabled, pending occurrences: 1 +~~END~~ + +SELECT 1; +~~START~~ +int +1 +~~END~~ + +--prepst#!# SELECT @a #!#BIGINT|-|a|-|0 + diff --git a/contrib/test/JDBC/init.sh b/contrib/test/JDBC/init.sh new file mode 100755 index 00000000000..0eb5a19864e --- /dev/null +++ b/contrib/test/JDBC/init.sh @@ -0,0 +1,21 @@ + +#create test user and database from psql terminal +echo "============================== CREATING USER AND DATABASE ==============================" +psql -U "$USER" -d postgres -a << EOF +CREATE USER jdbc_user WITH SUPERUSER CREATEDB CREATEROLE PASSWORD '12345678' INHERIT; +DROP DATABASE IF EXISTS jdbc_testdb; +CREATE DATABASE jdbc_testdb OWNER jdbc_user; +\c jdbc_testdb +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tds" CASCADE; +GRANT ALL ON SCHEMA sys to jdbc_user; +ALTER USER jdbc_user CREATEDB; +\c jdbc_testdb +ALTER SYSTEM SET babelfishpg_tsql.database_name = 'jdbc_testdb'; +ALTER SYSTEM SET babelfishpg_tds.set_db_session_property = true; +SELECT pg_reload_conf(); +\c jdbc_testdb +show babelfishpg_tsql.database_name; +show babelfishpg_tds.set_db_session_property; +CALL sys.initialize_babelfish('jdbc_user'); +EOF +echo "============================= BUILDING JDBC TEST FRAMEWORK =============================" diff --git a/contrib/test/JDBC/input/BABEL-1000.sql b/contrib/test/JDBC/input/BABEL-1000.sql new file mode 100644 index 00000000000..82ab131b068 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1000.sql @@ -0,0 +1,177 @@ +-- note: the default typmod for varchar should be 1 +CREATE FUNCTION babel_1000_test1 (@arg1 varchar) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a) +GO +SELECT * FROM babel_1000_test1('babel_1000_varchar') +GO + +CREATE FUNCTION babel_1000_test2 (@arg1 varchar(10), @arg2 varchar(20)) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a, @arg2 as b) +GO + +SELECT * FROM babel_1000_test2('babel_1000_varchar', 'babel_1000_varchar2') +GO +SELECT * FROM babel_1000_test2('babel_1000', 'abcdefghijklmnopqrstuvwxyz') +GO + +CREATE FUNCTION babel_1000_test3 (@arg1 nvarchar(10), @arg2 nvarchar(20)) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a, @arg2 as b) +GO + +SELECT * FROM babel_1000_test3('babel_1000_varchar', 'babel_1000_varchar3') +GO +SELECT * FROM babel_1000_test3('12345678910', 'babel_1000_varchar3') +GO + +-- numeric default typmod is (18,0) +CREATE FUNCTION babel_1000_test4 (@arg1 nvarchar(10), @arg2 numeric) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a, @arg2 as b) +GO + +SELECT * FROM babel_1000_test4('babel_1000_test4', 123.456) +GO +SELECT * FROM babel_1000_test4('babel_1000_test4', 123.567) +GO +SELECT * FROM babel_1000_test4('test4', 123456789012345678) +GO +-- precision 19, expect error +SELECT * FROM babel_1000_test4('test4-2', 1234567890123456789) +GO + +CREATE FUNCTION babel_1000_test5 (@arg1 nvarchar(30), @arg2 numeric(6,3)) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a, @arg2 as b) +GO + +SELECT * FROM babel_1000_test5('babel_1000_test5', 123.4567) +GO +SELECT * FROM babel_1000_test5('babel_1000_test5', 567.89) +GO +-- expect overflow error +SELECT * FROM babel_1000_test5('babel_1000_test5', 5567.89) +GO + +CREATE FUNCTION babel_1000_test6 (@arg1 datetimeoffset(2), @arg2 datetime2(4)) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a, @arg2 as b) +GO + +SELECT * FROM babel_1000_test6(CAST('2030-05-06 13:59:29.123456 -8:00' AS datetimeoffset), + CAST('1234-12-31 23:59:59.999999' AS datetime2)) +GO +SELECT * FROM babel_1000_test6(CAST('2030-05-06 13:59:29.124456 +8:00' AS datetimeoffset(4)), + CAST('9999-12-31 23:59:59.999999' AS datetime2(5))) +GO + +CREATE PROCEDURE babel_1000_test7 (@val datetimeoffset(2)) AS +BEGIN + DECLARE @DF datetimeoffset = @val + SELECT @DF +END +GO + +EXEC babel_1000_test7 '2030-05-06 13:59:29.123456 -8:00' +GO + +-- test return types +CREATE FUNCTION babel_1000_test8(@arg1 numeric) +RETURNS numeric(6,2) AS +BEGIN + RETURN @arg1 + 1.055 +END +GO + +SELECT * FROM babel_1000_test8(1) +GO +SELECT * FROM babel_1000_test8(12.345678) +GO +-- overflow, expect error +SELECT * FROM babel_1000_test8(123456.345678) +GO + +CREATE FUNCTION babel_1000_test9(@arg1 varchar) +RETURNS varchar(5) AS +BEGIN + RETURN @arg1 +END +GO + +SELECT * FROM babel_1000_test9('babel_1000_test9') +GO +SELECT * FROM babel_1000_test9('abcdefghijkl') +GO + +CREATE PROCEDURE babel_1000_test10 (@val varchar(2)) AS +BEGIN + DECLARE @DF varchar = @val + SELECT @DF +END +GO + +EXEC babel_1000_test10 '2030-05-06 13:59:29.123456 -8:00' +GO + +CREATE PROCEDURE babel_1000_test10_2 (@val varchar(2)) AS +BEGIN + DECLARE @DF varchar(100) = @val + SELECT @DF +END +GO + +EXEC babel_1000_test10_2 '2030-05-06 13:59:29.123456 -8:00' +GO + + +CREATE FUNCTION babel_1000_test11 (@var varchar(10)) +RETURNS varchar(4) +AS +BEGIN + return @var +END +GO + +SELECT babel_1000_test11('abcdefghijkl') +GO + +CREATE FUNCTION babel_1000_test12 (@var varbinary(10)) +RETURNS varbinary(2) +AS +BEGIN + return @var +END +GO + +SELECT babel_1000_test12(0x0123456789) +GO + + +DROP FUNCTION babel_1000_test1 +GO +DROP FUNCTION babel_1000_test2 +GO +DROP FUNCTION babel_1000_test3 +GO +DROP FUNCTION babel_1000_test4 +GO +DROP FUNCTION babel_1000_test5 +GO +DROP FUNCTION babel_1000_test6 +GO +DROP PROCEDURE babel_1000_test7 +GO +DROP FUNCTION babel_1000_test8 +GO +DROP FUNCTION babel_1000_test9 +GO +DROP PROCEDURE babel_1000_test10 +GO +DROP PROCEDURE babel_1000_test10_2 +GO +DROP FUNCTION babel_1000_test11 +GO +DROP FUNCTION babel_1000_test12 +GO diff --git a/contrib/test/JDBC/input/BABEL-101.sql b/contrib/test/JDBC/input/BABEL-101.sql new file mode 100644 index 00000000000..aca85ab6e43 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-101.sql @@ -0,0 +1,82 @@ +CREATE PROCEDURE p1 +AS +BEGIN TRY; +PRINT 'Hello 1' +END TRY +BEGIN CATCH; +PRINT 'Hello 2' +END CATCH; +GO + +CREATE PROCEDURE p2 +AS +BEGIN TRY; +PRINT 'Hello 1' +END TRY +BEGIN CATCH +PRINT 'Hello 2' +END CATCH +GO + +CREATE PROCEDURE p3 +AS +BEGIN TRY +PRINT 'Hello 1' +END TRY; +BEGIN CATCH +PRINT 'Hello 2' +END CATCH +GO + +CREATE PROCEDURE p4 +AS +BEGIN TRY +PRINT 'Hello 1' +END TRY +BEGIN CATCH; +PRINT 'Hello 2' +END CATCH +GO + +CREATE PROCEDURE p5 +AS +BEGIN TRY +PRINT 'Hello 1' +END TRY +BEGIN CATCH +PRINT 'Hello 2' +END CATCH; +GO + +CREATE PROCEDURE p6 +AS +BEGIN TRY +PRINT 'Hello 1' +END TRY +BEGIN CATCH; +PRINT 'Hello 2' +END CATCH; +GO + +CREATE PROCEDURE p7 +AS +BEGIN TRY; +PRINT 'Hello 1' +END TRY +BEGIN CATCH; +PRINT 'Hello 2' +END CATCH +GO + +DROP PROCEDURE p1 +GO +DROP PROCEDURE p2 +GO +DROP PROCEDURE p4 +GO +DROP PROCEDURE p5 +GO +DROP PROCEDURE p6 +GO +DROP PROCEDURE p7 +GO diff --git a/contrib/test/JDBC/input/BABEL-1044.sql b/contrib/test/JDBC/input/BABEL-1044.sql new file mode 100644 index 00000000000..374a06c1512 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1044.sql @@ -0,0 +1,199 @@ +-- schema +create schema schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; + +-- table +create table schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij.table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij( + col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij integer +); +GO + +insert into schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij.table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij values (42); +GO + +-- accessed via original name +select * from schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij.table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; +GO + +-- accessed via truncated name +select * from schema_longer_than_63_0abcdefgi2f5cbde477221faa47c8d08e1d6bbb27.table_longer_than_63_0abcdefgijc2ed7b983a352405e4e26c711bd071bf; +GO + +-- monitoring with original name +select count(*) from pg_catalog.pg_class where relname = 'table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij'; +GO + +-- monitoring with truncated name +select count(*) from pg_catalog.pg_class where relname = 'table_longer_than_63_0abcdefgijc2ed7b983a352405e4e26c711bd071bf'; +GO + +-- object_id() for both original namd shortend name +select (case when object_id('table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij') is not null then 'exists' else 'error' end) result; +GO + +select (case when object_id('table_longer_than_63_0abcdefgijc2ed7b983a352405e4e26c711bd071bf') is not null then 'exists' else 'error' end) result; +GO + +select (case when object_id('table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij') = object_id('table_longer_than_63_0abcdefgijc2ed7b983a352405e4e26c711bd071bf') then 'correct' else 'error' end) result; +GO + +-- object_id(.) for both original namd shortend name +select (case when object_id('schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij.table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij') is not null then 'exists' else 'error' end) result; +GO + +select (case when object_id('schema_longer_than_63_0abcdefgi2f5cbde477221faa47c8d08e1d6bbb27.table_longer_than_63_0abcdefgijc2ed7b983a352405e4e26c711bd071bf') is not null then 'exists' else 'error' end) result; +GO + +select (case when object_id('schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij.table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij') = object_id('schema_longer_than_63_0abcdefgi2f5cbde477221faa47c8d08e1d6bbb27.table_longer_than_63_0abcdefgijc2ed7b983a352405e4e26c711bd071bf') then 'correct' else 'error' end) result; +GO + +drop table schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij.table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; + +drop schema schema_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; +GO + +create table table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij( + col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij integer +); +GO + +insert into table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij values (42); +GO + +-- char/varchar/text -> name casting +create table table_name_t(c1 char(128) COLLATE C, c2 varchar(128) COLLATE C, c3 text COLLATE C); +GO +insert into table_name_t values ( + 'table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij', + 'table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij', + 'table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij' +); +GO +-- note: use explicit cast here. since implicit cast is not added. there is the same issue in default PG truncation behavior +select count(*) from pg_catalog.pg_class where relname in (SELECT cast (c1 as name) from table_name_t); +GO +select count(*) from pg_catalog.pg_class where relname in (SELECT cast (c2 as name) from table_name_t); +GO +select count(*) from pg_catalog.pg_class where relname in (SELECT cast (c3 as name) from table_name_t); +GO +drop table table_name_t; +GO + +-- function +create function func_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij( + @argname_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij int) +RETURNS integer +AS +BEGIN + return (select @argname_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij + 1); +END; +GO + +-- view +create view view_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij as +select func_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij( + col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij) +from table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; +GO + +select * from view_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; +GO + +-- create table with the same name +create table table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij( + col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij integer +); +GO + +-- create table with a different name but have same prefix +create table table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghik( + col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij integer +); +GO + +-- disable multi-bytes test for now since it should depend on collation setting +-- table name with multi-byte characters +--create table tλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ(a int); +--GO +--insert into tλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ values (42); +--GO +--select * from tλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO + +--create table ttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ(a int); +--GO +--insert into ttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ values (42); +--GO +--select * from ttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO + +--create table tttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ(a int); +--GO +--insert into tttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ values (42); +--GO +--select * from tttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO + +--create table ttttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ(a int); +--GO +--insert into ttttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ values (42); +--GO +--select * from ttttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO + +drop view view_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; +GO + +drop function func_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; +GO + +drop table table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij; +GO + +drop table table_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghik; +GO + +--drop table tλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO +--drop table ttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO +--drop table tttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO +--drop table ttttλλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ_λλλλλ; +--GO + +create table babel_1200_t(id int, [BALA_#] varchar(40)); +GO +insert into babel_1200_t values (42, 'aaa'), (666, 'bbb'); +GO + +create procedure babel_1200_proc +@balano as varchar(40) +as +begin + select r.ID from babel_1200_t r where r.BALA_#=@balano +end +go + +-- currently, syntax error +--exec babel_1200_proc 'aaa' +--GO + +create procedure babel_1200_proc_quoted +@balano as varchar(40) +as +begin + select r.ID from babel_1200_t r where r.[BALA_#]=@balano +end +go + +exec babel_1200_proc_quoted 'aaa' +GO + +drop procedure babel_1200_proc_quoted; +GO + +drop procedure babel_1200_proc; +GO + +drop table babel_1200_t; +GO diff --git a/contrib/test/JDBC/input/BABEL-1056.sql b/contrib/test/JDBC/input/BABEL-1056.sql new file mode 100644 index 00000000000..3f64e102ca3 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1056.sql @@ -0,0 +1,48 @@ +CREATE SCHEMA [Babelfish_1056]; +GO + +CREATE TABLE [Babelfish_1056].[employees] +(person_id int IDENTITY PRIMARY KEY, firstname nvarchar(20), lastname nvarchar(30), salary money) +GO + +CREATE PROCEDURE p_employee_insert @fname nvarchar(20), @lname nvarchar(30), @sal money +AS BEGIN +DECLARE @next_pers_id INT; +SELECT @next_pers_id = MAX(person_id) FROM [Babelfish_1056].[employees]; +IF (@next_pers_id IS NULL) + SET @next_pers_id = 0 + INSERT INTO [Babelfish_1056].[employees] + (person_id, firstname, lastname, salary) + VALUES + (@next_pers_id+1, @fname, @lname, @sal); +END +GO + +SET IDENTITY_INSERT [Babelfish_1056].[employees] ON +GO +EXEC p_employee_insert @fname='First', @lname='Employee', @sal=123.1231; +GO +SET IDENTITY_INSERT [Babelfish_1056].[employees] OFF +GO + +SET IDENTITY_INSERT [Babelfish_1056].[employees] ON +GO +EXEC p_employee_insert @fname='Second', @lname='Employee', @sal=123.1232; +GO +SET IDENTITY_INSERT [Babelfish_1056].[employees] OFF +GO + +SET IDENTITY_INSERT [Babelfish_1056].[employees] ON +GO +EXEC p_employee_insert @fname='Third', @lname='Employee', @sal=123.1233; +GO +SET IDENTITY_INSERT [Babelfish_1056].[employees] OFF +GO + +SELECT * FROM [Babelfish_1056].[employees] +GO + +DROP PROCEDURE p_employee_insert +DROP TABLE [Babelfish_1056].[employees] +DROP SCHEMA [Babelfish_1056]; +GO diff --git a/contrib/test/JDBC/input/BABEL-1073.sql b/contrib/test/JDBC/input/BABEL-1073.sql new file mode 100644 index 00000000000..999c916d042 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1073.sql @@ -0,0 +1,27 @@ +SELECT set_config('babelfishpg_tsql.escape_hatch_for_replication', 'ignore', 'false') +GO + +-- Test 'NOT FOR REPLICATION' syntax +create table testing1(col nvarchar(60)); +GO +create trigger notify on testing1 after insert +NOT FOR REPLICATION +as +begin + PRINT 'trigger invoked' +end +; +GO + +insert into testing1 (col) select N'Muffler'; +GO + +drop trigger notify; +GO + +-- clean up +drop table testing1; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_for_replication', 'strict', 'false') +GO diff --git a/contrib/test/JDBC/input/BABEL-1091.sql b/contrib/test/JDBC/input/BABEL-1091.sql new file mode 100644 index 00000000000..59d680f8ca7 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1091.sql @@ -0,0 +1,173 @@ +CREATE TABLE t (k INT, c INT) +CREATE TABLE t2 (k INT, c1 INT, c2 INT) +GO + +-- test case 1: @v = column +DECLARE @a INT +SET @a = 0 +DECLARE @b INT +SET @b = 0 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c, k = 3 WHERE k = 1 +UPDATE t SET k = 4, @b = c WHERE k = 2 +SELECT * FROM t ORDER BY k +SELECT @a, @b + +DELETE FROM t +GO + +-- test case 2: @v = expression +DECLARE @a INT +SET @a = 0 +DECLARE @b INT +SET @b = 0 +DECLARE @d INT +SET @d = 100 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c + 100, k = 3 WHERE k = 1 +UPDATE t SET @b = @d + 100, k = 4 WHERE k = 2 +SELECT * FROM t ORDER BY k +SELECT @a, @b + +DELETE FROM t +GO + +-- test case 3: @v = column = value +DECLARE @a INT +SET @a = 0 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c = 40 WHERE k = 1 +SELECT * FROM t ORDER BY k +SELECT @a + +DELETE FROM t +GO + +-- test case 4: @v = CASE clause / parens +DECLARE @a INT +DECLARE @b INT +SET @a = 0 +SET @b = 0 +INSERT INTO t2 VALUES (1, 10, 30) +INSERT INTO t2 VALUES (2, 20, 40) +UPDATE t2 SET @a = CASE WHEN c1 = 10 THEN 30 ELSE 40 END, c2 = 50 WHERE k = 1 +UPDATE t2 SET @b = (SELECT c1 FROM t2 WHERE k = 2), c2 = 60 WHERE k = 2 +SELECT * FROM t2 ORDER BY k +SELECT @a, @b + +DELETE FROM t2 +GO + +-- test case 5: @v = column with no WHERE clause +-- @v will be mapped to the last row of available value +DECLARE @a INT +SET @a = 0 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c, k = 3 +SELECT * FROM t ORDER BY k +SELECT @a + +DELETE FROM t +GO + +-- test case 6: @v1 = column, @v2 = column +DECLARE @a INT +SET @a = 0 +DECLARE @b INT +SET @b = 0 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c, @b = c, k = 3 WHERE k = 1 +SELECT * FROM t ORDER BY k +SELECT @a, @b + +DELETE FROM t +GO + +-- test case 7: @v1 = column which is updated at the same time +-- INCORRECT RESULT: since OUTPUT clause is unsupported, we will make use of RETURNING clause +-- which will return the updated value. What we really interested is the pre-update values +-- We will need to update this case after BABEL-588 is fixed. +DECLARE @a INT +SET @a = 0 +DECLARE @b INT +SET @b = 0 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c, c = 70 WHERE k = 1 +UPDATE t SET c = 80, @b = c WHERE k = 2 +SELECT * FROM t ORDER BY k +SELECT @a, @b + +DELETE FROM t +GO + +-- test case 8: @v1 = column with no table updates +-- UNSUPPORTED +DECLARE @a INT +SET @a = 0 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c WHERE k = 1 +SELECT * FROM t ORDER BY k +SELECT @a + +DELETE FROM t +GO + +-- test case 9: @v1 = column and column = @v2 at the same time +DECLARE @a INT +SET @a = 0 +DECLARE @b INT +SET @b = 3 +INSERT INTO t VALUES (1, 10) +INSERT INTO t VALUES (2, 20) +UPDATE t SET @a = c, k = @b WHERE k = 1 +SELECT * FROM t ORDER BY k +SELECT @a, @b + +DELETE FROM t +GO + +-- test case 10: @v1 = column with OUTPUT clause +-- this test is disabled as OUTPUT clause is not yet supported: BABEL-588 +-- UNSUPPORTED +-- DECLARE @a INT +-- SET @a = 0 +-- INSERT INTO t VALUES (1, 10) +-- INSERT INTO t VALUES (2, 20) +-- UPDATE t SET @a = c, k = 3 OUTPUT deleted.* WHERE k = 1 +-- SELECT * FROM t ORDER BY k +-- SELECT @a +-- DELETE FROM t +-- GO + +-- SELECT SET with CASE statement with correct parser behavior on second '=' +DECLARE @a INT +SET @a = 0 +INSERT INTO t VALUES (1, 10) +SELECT @a = CASE WHEN c = 10 THEN 1 ELSE 2 END FROM t +SELECT @a + +DELETE FROM t +GO + + +-- SELECT SET with parameters with correct parser behavior on second '=' +DECLARE @a INT +SET @a = 0 +INSERT INTO t VALUES (1, 10) +SELECT @a = ( SELECT c FROM t WHERE k = 1 ) +SELECT @a + +DELETE FROM t +GO + +-- clean up +DROP TABLE t +DROP TABLE t2 +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1094.sql b/contrib/test/JDBC/input/BABEL-1094.sql new file mode 100644 index 00000000000..b163fd2a0aa --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1094.sql @@ -0,0 +1,28 @@ +CREATE PROCEDURE babel_1094_proc(@p0 VARCHAR(32)) +AS +BEGIN + UPDATE T set id = @id + 1 + BEGIN TRANSACTION + UPDATE T SET col1 = s.col2 FROM @tab_var s WHERE T.col3 = s.col4 + COMMIT + SELECT * FROM T WHERE col1 = @id +END +GO + +CREATE PROCEDURE babel_1094_proc_2(@p0 VARCHAR(32)) +AS +BEGIN + UPDATE T set id = @id + 1 + BEGIN TRANSACTION + UPDATE T SET col1 = s.col2 FROM @tab_var s WHERE T.col3 = s.col4 + ROLLBACK + SELECT * FROM T WHERE col1 = @id +END +GO + +DROP PROCEDURE babel_1094_proc +GO + +DROP PROCEDURE babel_1094_proc_2 +GO + diff --git a/contrib/test/JDBC/input/BABEL-1096.sql b/contrib/test/JDBC/input/BABEL-1096.sql new file mode 100644 index 00000000000..dd4c47c977f --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1096.sql @@ -0,0 +1,60 @@ +CREATE TABLE babel_1096_t1 (a int) +GO + +INSERT INTO babel_1096_t1 values (1); +GO + +CREATE TABLE babel_1096_t2 (a int); +GO + +CREATE PROCEDURE babel_1096_proc +AS + INSERT INTO babel_1096_t2 + SELECT * FROM babel_1096_t1 + + SELECT COUNT(*) FROM babel_1096_t2 +GO + +EXEC babel_1096_proc +GO + +CREATE TABLE babel_956_t (a int, n int identity) +GO + +CREATE PROCEDURE babel_956_proc +AS + INSERT babel_956_t(a) SELECT 123 + SELECT @@identity +GO + +CREATE FUNCTION babel_1095_proc ( @stringToSplit VARCHAR(MAX) ) +RETURNS + @returnList TABLE ([Name] [nvarchar] (500)) +AS +BEGIN + INSERT INTO @returnList + SELECT '' + + SELECT @stringToSplit = '' + RETURN; +END +GO + +DROP PROCEDURE babel_1096_proc +GO + +DROP TABLE babel_1096_t1 +GO + +DROP TABLE babel_1096_t2 +GO + +DROP PROCEDURE babel_956_proc +GO + +DROP TABLE babel_956_t +GO + +DROP FUNCTION babel_1095_proc +GO + diff --git a/contrib/test/JDBC/input/BABEL-1098.sql b/contrib/test/JDBC/input/BABEL-1098.sql new file mode 100644 index 00000000000..200d2b4d334 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1098.sql @@ -0,0 +1,403 @@ +USE master; +GO + +-- Testing delete statement +CREATE TABLE delete_test_tbl ( + age int, + fname char(10), + lname char(10), + city nchar(20) +) +GO + +INSERT INTO delete_test_tbl(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (50, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname4', 'new york'), + (26, 'fname5', 'lname5', 'los angeles'), + (74, 'fname6', 'lname6', 'tokyo'), + (44, 'fname7', 'lname7', 'oslo'), + (19, 'fname8', 'lname8', 'hong kong'), + (61, 'fname9', 'lname9', 'shanghai'), + (29, 'fname10', 'lname10', 'mumbai') +GO + +CREATE TABLE delete_test_tbl2 ( + year int, + lname char(10), +) +GO + +INSERT INTO delete_test_tbl2(year, lname) +VALUES (51, 'lname1'), + (34, 'lname3'), + (25, 'lname8'), + (95, 'lname9'), + (36, 'lname10') +GO + +CREATE TABLE delete_test_tbl3 ( + lname char(10), + city char(10), +) +GO + +INSERT INTO delete_test_tbl3(lname, city) +VALUES ('lname8','london'), + ('lname9','tokyo'), + ('lname10','mumbai') +GO + +CREATE TABLE delete_test_tbl4 ( + age int, + fname char(10), + lname char(10), + city nchar(20) +) +GO + +INSERT INTO delete_test_tbl4(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (50, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname4', 'new york'), + (26, 'fname5', 'lname5', 'los angeles'), + (35, 'fname6', 'lname6', 'mumbai') +GO + +CREATE TABLE dbo.delete_test_tbl5 ( + age int, + fname char(10), + lname char(10), + city nchar(20) +) +GO + +INSERT INTO dbo.delete_test_tbl5(age, fname, lname, city) +VALUES (51, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (102, 'fname3', 'lname10', 'brussels') +GO + +-- test using schema name +DELETE dbo.delete_test_tbl5 +FROM dbo.delete_test_tbl5 t5 +INNER JOIN delete_test_tbl3 t3 +ON t3.lname = t5.lname +GO + +SELECT * FROM dbo.delete_test_tbl5 ORDER BY lname +GO + +DELETE delete_test_tbl +FROM delete_test_tbl t1 +INNER JOIN delete_test_tbl2 t2 +ON t1.lname = t2.lname +WHERE year > 50 +GO + +SELECT * FROM delete_test_tbl ORDER BY lname +GO + +DELETE delete_test_tbl +FROM delete_test_tbl2 t2 +LEFT JOIN delete_test_tbl t1 +ON t1.lname = t2.lname +WHERE t2.year < 30 AND t1.age > 40 +GO + +SELECT * FROM delete_test_tbl ORDER BY lname +GO + +-- delete with outer join on multiple tables +DELETE delete_test_tbl +FROM delete_test_tbl3 t3 +LEFT JOIN delete_test_tbl t1 +ON t3.city = t1.city +LEFT JOIN delete_test_tbl2 t2 +ON t1.lname = t2.lname +WHERE t3.city = 'mumbai' +GO + +SELECT * FROM delete_test_tbl ORDER BY lname +GO + +-- delete when target table not shown in JoinExpr +DELETE delete_test_tbl +FROM delete_test_tbl3 t3 +LEFT JOIN delete_test_tbl2 t2 +ON t3.lname = t2.lname +GO + +SELECT * FROM delete_test_tbl ORDER BY lname +GO + +-- delete with self join +DELETE delete_test_tbl3 +FROM delete_test_tbl3 t1 +INNER JOIN delete_test_tbl3 t2 +on t1.lname = t2.lname +GO + +SELECT * FROM delete_test_tbl3 ORDER BY lname +GO + +DELETE delete_test_tbl4 +FROM delete_test_tbl4 c +JOIN +(SELECT lname, fname, age from delete_test_tbl4) b +on b.lname = c.lname +JOIN +(SELECT lname, city, age from delete_test_tbl4) a +on a.city = c.city +GO + +SELECT * FROM delete_test_tbl4 ORDER BY lname +GO + +DELETE delete_test_tbl2 +FROM +(SELECT lname, year from delete_test_tbl2) b +JOIN +(SELECT lname from delete_test_tbl2) a +on a.lname = b.lname +GO + +SELECT * FROM delete_test_tbl2 ORDER BY lname +GO + +DROP TABLE delete_test_tbl +GO + +DROP TABLE delete_test_tbl2 +GO + +DROP TABLE delete_test_tbl3 +GO + +DROP TABLE delete_test_tbl4 +GO + +DROP TABLE dbo.delete_test_tbl5 +GO + + +-- Tests for UPDATE clause +CREATE TABLE update_test_tbl ( + age int, + fname char(10), + lname char(10), + city nchar(20) +) +GO +INSERT INTO update_test_tbl(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (35, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname4', 'new york'), + (26, 'fname5', 'lname5', 'los angeles'), + (74, 'fname6', 'lname6', 'tokyo'), + (44, 'fname7', 'lname7', 'oslo'), + (19, 'fname8', 'lname8', 'hong kong'), + (61, 'fname9', 'lname9', 'shanghai'), + (29, 'fname10', 'lname10', 'mumbai') +GO + +CREATE TABLE update_test_tbl2 ( + year int, + lname char(10), +) +GO + +INSERT INTO update_test_tbl2(year, lname) +VALUES (51, 'lname1'), + (34, 'lname3'), + (25, 'lname8'), + (95, 'lname9'), + (36, 'lname10') +GO + +CREATE TABLE update_test_tbl3 ( + age int, + fname char(10), + lname char(10), + city nchar(20) +) +GO +INSERT INTO update_test_tbl3(lname, city) +VALUES ('lname8','london'), + ('lname9','tokyo'), + ('lname10','mumbai') +GO + +CREATE TABLE dbo.update_test_tbl4 ( + age int, + fname char(10), + lname char(10), + city nchar(20) +) +GO + +INSERT INTO dbo.update_test_tbl4(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (35, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname10', 'new york') +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO +SELECT * FROM update_test_tbl2 ORDER BY lname +GO +SELECT * FROM update_test_tbl3 ORDER BY lname +GO +SELECT * FROM dbo.update_test_tbl4 ORDER BY lname +GO +-- Simple update +UPDATE update_test_tbl SET fname = 'fname11' +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- Update with where clause +UPDATE update_test_tbl SET fname = 'fname12' +WHERE age > 50 AND city IN ('london','mumbai', 'new york' ) +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- Update with inner join +UPDATE update_test_tbl SET fname = 'fname13' +FROM update_test_tbl t1 +INNER JOIN update_test_tbl2 t2 +ON t1.lname = t2.lname +WHERE year > 50 +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +UPDATE update_test_tbl SET fname = 'fname14' +FROM update_test_tbl2 t2 +INNER JOIN update_test_tbl t1 +ON t1.lname = t2.lname +WHERE year < 50 AND city in ('tokyo', 'hong kong') +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- Update with outer join +UPDATE update_test_tbl SET fname = 'fname15' +FROM update_test_tbl2 t2 +LEFT JOIN update_test_tbl t1 +ON t1.lname = t2.lname +WHERE year > 50 +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +UPDATE update_test_tbl SET fname = 'fname16' +FROM update_test_tbl2 t2 +FULL JOIN update_test_tbl t1 +ON t1.lname = t2.lname +WHERE year > 50 AND age > 60 +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- update with outer join on multiple tables +UPDATE update_test_tbl +SET fname = 'fname17' +FROM update_test_tbl3 t3 +LEFT JOIN update_test_tbl t1 +ON t3.city = t1.city +LEFT JOIN update_test_tbl2 t2 +ON t1.lname = t2.lname +WHERE t3.city = 'mumbai' +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- update when target table not shown in JoinExpr but associated by where +UPDATE update_test_tbl +SET fname = 'fname18' +from update_test_tbl2 t2 +FULL JOIN update_test_tbl3 t3 +ON t2.lname = t3.lname +WHERE update_test_tbl.city = t3.city AND t3.lname='lname10' +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- update when target table not shown in JoinExpr +UPDATE update_test_tbl +SET fname = 'fname19' +from update_test_tbl2 t2 +FULL JOIN update_test_tbl3 t3 +ON t2.lname = t3.lname +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- update with self join +UPDATE update_test_tbl3 +SET lname = 'lname12' +FROM update_test_tbl3 t1 +INNER JOIN update_test_tbl3 t2 +on t1.lname = t2.lname +GO + +SELECT * FROM update_test_tbl3 ORDER BY lname +GO + +UPDATE update_test_tbl SET lname='lname13' +FROM update_test_tbl c +JOIN +(SELECT lname, fname, age from update_test_tbl) b +on b.lname = c.lname +JOIN +(SELECT lname, city, age from update_test_tbl) a +on a.city = c.city +GO + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- update when target table only appears in subselect +UPDATE update_test_tbl SET lname='lname14' +FROM +(SELECT lname, fname, age from update_test_tbl) b +JOIN +(SELECT lname, city, age from update_test_tbl) a +on a.lname = b.lname; + +SELECT * FROM update_test_tbl ORDER BY lname +GO + +-- update with schema +UPDATE dbo.update_test_tbl4 SET fname = 'fname11' +FROM dbo.update_test_tbl4 t4 +INNER JOIN update_test_tbl3 t3 +ON t3.city = t4.city +GO + +SELECT * FROM dbo.update_test_tbl4 ORDER BY lname +GO + +DROP TABLE update_test_tbl +GO +DROP TABLE update_test_tbl2 +GO +DROP TABLE update_test_tbl3 +GO +DROP TABLE dbo.update_test_tbl4 +GO diff --git a/contrib/test/JDBC/input/BABEL-1113.sql b/contrib/test/JDBC/input/BABEL-1113.sql new file mode 100644 index 00000000000..66a91effcfa --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1113.sql @@ -0,0 +1,34 @@ +create table cctab (a int, b as a*10, c int) +go +insert cctab values (1,2) +go +select * from cctab +go + +-- Expect Error +insert into cctab values (1,2,3) +go + +create table cctab2 (a int identity, b as a+10, c int) +go +insert cctab2 values (1) +go +select * from cctab2 +go + +create table cctab3 (a int, c int, b as a*10) +go +insert cctab3 values (1,2) +go +select * from cctab3 +go + +create table cctab4 (a int, c int, b as a*10, d as c*20) +go +insert cctab4 values (1,2) +go +select * from cctab4 +go + +DROP TABLE cctab, cctab2, cctab3, cctab4 +go diff --git a/contrib/test/JDBC/input/BABEL-1149.sql b/contrib/test/JDBC/input/BABEL-1149.sql new file mode 100644 index 00000000000..066f18e1df8 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1149.sql @@ -0,0 +1,62 @@ +-- test inline TVF which uses RETURN_QUERY statement - shouldn't send Done +-- tokens +create function itvf_1149 (@number int) returns table as return (select 1 as a, 2 as b); +GO + +select * from itvf_1149(5); +GO + +-- test multi-statement TVF which uses DECL_TABLE, INSERT/UPDATE/DELETE and +-- RETURN_TABLE statements - shouldn't send Done tokens +create function mstvf_1149(@i int) returns @tableVar table (a nvarchar(10), b int, c int) +as +begin +insert into @tableVar values('hello1', 1, 100); +insert into @tableVar values('hello2', 2, 200); +insert into @tableVar values('hello3', 3, 300); +update @tableVar set b = 2 where b = 3; +delete @tableVar where b = 2; +return; +end; +GO + +select * from mstvf_1149(10); +GO + +-- test user-defined function with DMLs on table variables - shouldn't send Done +-- tokens +create function func_1149(@i int) returns int as begin +declare @a as table (a int, b int) +insert into @a values (100, 200) +return 1; +end; +GO + +select func_1149(1); +GO + +-- test inline code blocks with DMLs on table variables - should send Done +-- tokens +declare @a table (a int, b int); +insert into @a values(1, 100); +GO + +-- test procedure with DMLs on table variables - should send Done tokens +create procedure proc_1149 as +declare @a table (a int, b int); +insert into @a values(1, 100); +update @a set b = 200; +GO + +exec proc_1149 +GO + +-- cleanup +drop function itvf_1149; +GO +drop function mstvf_1149; +GO +drop function func_1149; +GO +drop procedure proc_1149; +GO diff --git a/contrib/test/JDBC/input/BABEL-1161.sql b/contrib/test/JDBC/input/BABEL-1161.sql new file mode 100644 index 00000000000..654e9ad2c2d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1161.sql @@ -0,0 +1,134 @@ +-- issue 1: RETURN SELECT without parenthesis +CREATE PROCEDURE babel_1161_proc_1 +AS +BEGIN + declare @a int + declare @b int + set @a = 1 + return + select @b=@a+1 +END +GO + +CREATE PROCEDURE babel_1161_proc_1_wrapper +AS +BEGIN + DECLARE @ret int; + EXEC @ret = babel_1161_proc_1 + -- note: should show 0 since procedure succeeded. + PRINT '@ret: ' + cast(@ret as varchar(10)); +END +GO + +EXEC babel_1161_proc_1_wrapper +GO + +CREATE PROCEDURE babel_1161_proc_1_2 +AS +BEGIN + RETURN SELECT 1 +END +GO + +CREATE PROCEDURE babel_1161_proc_1_2_wrapper +AS +BEGIN + DECLARE @ret int; + EXEC @ret = babel_1161_proc_1_2 + PRINT '@ret: ' + cast(@ret as varchar(10)); +END +GO + +EXEC babel_1161_proc_1_2_wrapper +GO + +CREATE PROCEDURE babel_1161_proc_1_3 +AS +BEGIN + RETURN (SELECT 1) +END +GO + +CREATE PROCEDURE babel_1161_proc_1_3_wrapper +AS +BEGIN + DECLARE @ret int; + EXEC @ret = babel_1161_proc_1_3 + PRINT '@ret: ' + cast(@ret as varchar(10)); +END +GO + +EXEC babel_1161_proc_1_3_wrapper +GO + +-- issue 2: INSERT INTO ... (SELECT ...) SELECT +CREATE TABLE babel_1161_t21(a int); +INSERT INTO babel_1161_t21 values(1); +CREATE TABLE babel_1161_t22(a int); +GO + +CREATE PROCEDURE babel_1161_proc_2 +AS + DECLARE @inserted INT + + INSERT INTO babel_1161_t22 + (SELECT * FROM babel_1161_t21) + SELECT @inserted = @@ROWCOUNT +GO + +EXEC babel_1161_proc_2; +GO + +SELECT * FROM babel_1161_t22; +GO + +-- issue 3: support SELECT TOP () ... + +create table babel_1161_t31(a int, a2 char); +insert into babel_1161_t31 values (1, 'a'), (2, 'b'); +create table babel_1161_t32(b int); +insert into babel_1161_t32 values (1), (2), (3), (4); +GO + +select top (select a from babel_1161_t31 where a2 = 'a') * from babel_1161_t32 order by b; +GO + +select top (select a from babel_1161_t31 where a2 = 'b') * from babel_1161_t32 order by b; +GO + +-- empty scalar subquery +-- note: we have a bug here: BABEL-1181 +-- please update the comment and expected output once the bug is fixed. +select top (select a from babel_1161_t31 where a2 = 'c') * from babel_1161_t32 order by b; +GO + +-- not a single row +select top (select a from babel_1161_t31) * from babel_1161_t32 order by b; +GO + +-- not a single column +select top (select a, a2 from babel_1161_t31 where a2 = 'a') * from babel_1161_t32 order by b; +GO + +DROP PROCEDURE babel_1161_proc_1_wrapper; +GO +DROP PROCEDURE babel_1161_proc_1; +GO +DROP PROCEDURE babel_1161_proc_1_2_wrapper; +GO +DROP PROCEDURE babel_1161_proc_1_2; +GO +DROP PROCEDURE babel_1161_proc_1_3_wrapper; +GO +DROP PROCEDURE babel_1161_proc_1_3; +GO +DROP PROCEDURE babel_1161_proc_2; +GO +DROP TABLE babel_1161_t21; +GO +DROP TABLE babel_1161_t22; +GO +DROP TABLE babel_1161_t31; +GO +DROP TABLE babel_1161_t32; +GO diff --git a/contrib/test/JDBC/input/BABEL-1164.sql b/contrib/test/JDBC/input/BABEL-1164.sql new file mode 100644 index 00000000000..578834853b9 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1164.sql @@ -0,0 +1,52 @@ +-- Test inserting less values than columns +CREATE TABLE t1(c1 int, c2 numeric, c3 varchar(32)); +GO + +INSERT INTO t1 VALUES (1, 2.0, 'hello'); +GO + +-- Expect error +INSERT INTO t1 VALUES (1, 2.0); +GO + +SELECT * FROM t1; +GO + +CREATE TABLE t2(id int IDENTITY, c1 int, c2 numeric, c3 AS c1 * c2); +GO + +INSERT INTO t2 VALUES (1, 2.0); +GO + +-- Expect error +INSERT INTO t2 VALUES (5); +GO + +SELECT * FROM t2; +GO + +INSERT INTO t1 SELECT c1, c2, c3 FROM t2; +GO + +SELECT * FROM t1; +GO + +-- Expect error +INSERT INTO t1 SELECT id, c2 FROM t2; +GO + +SELECT * FROM t1; +GO + +CREATE TABLE t3(c1 int, c2 numeric); +GO + +INSERT INTO t3 VALUES (2, 4); +GO + +-- Expect error +INSERT INTO t1 SELECT * FROM t3; +GO + +DROP TABLE t1, t2, t3; +GO diff --git a/contrib/test/JDBC/input/BABEL-1167.sql b/contrib/test/JDBC/input/BABEL-1167.sql new file mode 100644 index 00000000000..79b4098727c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1167.sql @@ -0,0 +1,24 @@ +CREATE TABLE babel_1167_table (a INT); +go + +INSERT INTO babel_1167_table VALUES (111), (222) +go + +CREATE PROCEDURE babel_1167_proc AS +IF 1=1 + UPDATE babel_1167_table SET a = 0 +ELSE + UPDATE babel_1167_table SET a = 1 +go + +EXEC babel_1167_proc +go + +SELECT * FROM babel_1167_table +go + +DROP PROC babel_1167_proc +go + +DROP TABLE babel_1167_table +go diff --git a/contrib/test/JDBC/input/BABEL-1173.sql b/contrib/test/JDBC/input/BABEL-1173.sql new file mode 100644 index 00000000000..8c84057e820 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1173.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE proc_babel_1173 +( + @p INT NULL +) +AS PRINT 'helloworld' +GO + +EXEC proc_babel_1173 1 +GO + +DROP PROCEDURE proc_babel_1173 +GO + diff --git a/contrib/test/JDBC/input/BABEL-1179.sql b/contrib/test/JDBC/input/BABEL-1179.sql new file mode 100644 index 00000000000..14bba845592 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1179.sql @@ -0,0 +1,61 @@ +-- Test implicit cast: bit -> int2/int4/int8 +CREATE FUNCTION implicit_2int2(@i INT2) +RETURNS INT +AS +BEGIN + RETURN (@i) +END; +GO +SELECT implicit_2int2(CAST(1 AS bit)); +GO + +CREATE FUNCTION implicit_2int4(@i INT4) +RETURNS INT +AS +BEGIN + RETURN (@i) +END; +GO +SELECT implicit_2int4(CAST(1 AS bit)); +GO + +CREATE FUNCTION implicit_2int8(@i INT8) +RETURNS INT +AS +BEGIN + RETURN (@i) +END; +GO +SELECT implicit_2int8(CAST(1 AS bit)); +GO + +-- Test implicit cast: int2/int4/int8 -> bit +CREATE FUNCTION implicit_2bit(@i bit) +RETURNS bit +AS +BEGIN + RETURN (@i) +END; +GO +SELECT implicit_2bit(CAST(1 AS INT2)); +GO +SELECT implicit_2bit(CAST(1 AS INT4)); +GO +SELECT implicit_2bit(CAST(1 AS INT8)); +GO + + + +-- Test ISNULL() with bit and int arguments +SELECT ISNULL(CAST(1 AS bit), CAST(1 AS INT2)) +GO +SELECT ISNULL(CAST(1 AS bit), CAST(1 AS INT4)) +GO +SELECT ISNULL(CAST(1 AS bit), CAST(1 AS INT8)) +GO +SELECT ISNULL(CAST(1 AS INT2), CAST(1 AS bit)) +GO +SELECT ISNULL(CAST(1 AS INT4), CAST(1 AS bit)) +GO +SELECT ISNULL(CAST(1 AS INT8), CAST(1 AS bit)) +GO diff --git a/contrib/test/JDBC/input/BABEL-1184.sql b/contrib/test/JDBC/input/BABEL-1184.sql new file mode 100644 index 00000000000..9b1257f3fe1 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1184.sql @@ -0,0 +1,66 @@ +CREATE TABLE TMP_1 (a INT) +GO +INSERT INTO TMP_1 VALUES (1) +GO +CREATE TABLE TMP_2 (a INT, b INT) +GO + +-- test INSERT SELECT with UNION or UNION ALL in procedure +CREATE PROCEDURE TEST_INSERT_SELECT +AS +INSERT INTO TMP_2 (a) +SELECT a FROM TMP_1 +UNION +SELECT a FROM TMP_1 +UNION +SELECT a FROM TMP_1 + +INSERT INTO TMP_2 (a) +SELECT a FROM TMP_1 +UNION ALL +SELECT a FROM TMP_1 +UNION ALL +SELECT a FROM TMP_1 +GO + +SELECT * FROM TMP_2 +GO +EXECUTE TEST_INSERT_SELECT +GO +SELECT * FROM TMP_2 +GO + +-- test SELECT with UNION in procedure +CREATE PROCEDURE TEST_SELECT_1 +AS +SELECT a FROM TMP_1 +UNION +SELECT a FROM TMP_1 +UNION +SELECT a FROM TMP_1 +GO +EXECUTE TEST_SELECT_1 +GO + +-- test SELECT with UNION ALL in procedure +CREATE PROCEDURE TEST_SELECT_2 +AS +SELECT a FROM TMP_1 +UNION ALL +SELECT a FROM TMP_1 +UNION ALL +SELECT a FROM TMP_1 +GO +EXECUTE TEST_SELECT_2 +GO + +DROP TABLE TMP_1 +GO +DROP TABLE TMP_2 +GO +DROP PROCEDURE TEST_INSERT_SELECT +GO +DROP PROCEDURE TEST_SELECT_1 +GO +DROP PROCEDURE TEST_SELECT_2 +GO diff --git a/contrib/test/JDBC/input/BABEL-1185.sql b/contrib/test/JDBC/input/BABEL-1185.sql new file mode 100644 index 00000000000..2960b12cb8a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1185.sql @@ -0,0 +1,14 @@ +CREATE TABLE t_1185(a int); +go + +CREATE TRIGGER tr_1185 ON t_1185 AFTER UPDATE AS +BEGIN + IF NOT update(a) BEGIN RETURN END +END +go + +DROP TRIGGER tr_1185; +go + +DROP TABLE t_1185; +go diff --git a/contrib/test/JDBC/input/BABEL-1189.sql b/contrib/test/JDBC/input/BABEL-1189.sql new file mode 100644 index 00000000000..d685a87baaa --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1189.sql @@ -0,0 +1,27 @@ +create table babel_1189_t1(a int); +go + +create table babel_1189_t2(a int); +go + +create trigger babel_1189_trig on babel_1189_t2 +for insert as +update babel_1189_t1 set a = 2 +go + +insert babel_1189_t1 values(1); +go + +insert babel_1189_t2 values(1); +go + +select * from babel_1189_t1; +select * from babel_1189_t2; +go + +drop trigger babel_1189_trig; +drop table babel_1189_t1; +drop table babel_1189_t2; +go + + diff --git a/contrib/test/JDBC/input/BABEL-1193.sql b/contrib/test/JDBC/input/BABEL-1193.sql new file mode 100644 index 00000000000..baa0b49bc27 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1193.sql @@ -0,0 +1,158 @@ +-- String to numeric types +-- String -> numeric(p,s) +CREATE TABLE t1 (a text, b varchar(20), c char(10)); +GO + +INSERT INTO t1 values ('94.564', '-12.1246', '32.22'); +GO +INSERT INTO t1 values ('-94.564', '12.1246', '-32.22'); +GO +INSERT INTO t1 values ('0', '0', '0'); +GO +CREATE TABLE t2 (n1 numeric(3,1), n2 numeric(3,1), n3 numeric(3,1)); +GO +INSERT INTO t2 SELECT * from t1; +GO +SELECT * FROM t2; +GO + +-- String -> int2/int4/int8 +CREATE TABLE t3 (a text, b varchar(20), c char(10)); +GO + +INSERT INTO t3 values ('12', '1234', '5431'); +GO +INSERT INTO t3 values ('0', '0', '0'); +GO +INSERT INTO t3 values ('-12', '-1234', '-5431'); +GO +-- int2 +CREATE TABLE t4 (si1 smallint, si2 smallint, si3 smallint); +GO +INSERT INTO t4 SELECT * from t3; +GO +select * from t4; +GO +-- int4 +CREATE TABLE t5 (i1 int, i2 int, i3 int); +GO +INSERT INTO t5 SELECT * from t3; +GO +select * from t5; +GO +-- int8 +CREATE TABLE t6 (bi1 bigint, bi2 bigint, bi3 bigint); +GO +INSERT INTO t6 SELECT * from t3; +GO +select * from t6; +GO + +-- String -> float,fixeddecimal +CREATE TABLE t7 (a text, b varchar(20), c char(10)); +GO + +INSERT INTO t7 values ('11.23456', '12.34234', '60.54314'); +GO +INSERT INTO t7 values ('0', '0', '0'); +GO +INSERT INTO t7 values ('-12.01233', '-19.2346', '-43.88641'); +GO +-- real (=float4) +CREATE TABLE t8 (f1 real, f2 real, f3 real); +GO +INSERT INTO t8 SELECT * from t7; +GO +select * from t8; +GO +-- float (=float(53) =double precision) +CREATE TABLE t9 (f1 float, f2 float, f3 float); +GO +INSERT INTO t9 SELECT * from t7; +GO +select * from t9; +GO +-- smallmoney +CREATE TABLE t10 (sm1 smallmoney, sm2 smallmoney, sm3 smallmoney); +GO +INSERT INTO t10 SELECT * from t7; +GO +select * from t10; +GO +-- money +CREATE TABLE t11 (m1 money, m2 money, m3 money); +GO +INSERT INTO t11 SELECT * from t7; +GO +select * from t11; +GO + + +-- Numeric types to string types +-- numeric types -> text +CREATE TABLE t12 (si smallint, i int, bi bigint, f real, f8 float, n numeric(4,2), sm smallmoney, m money); +GO + +INSERT INTO t12 values (12, 4553, 123456, 12.345, 2344.456, 12.34, 456.3334, 1123.6777); +GO +INSERT INTO t12 values (-12, -1234, -123456, -12.345, -2344.456, 12.34, -456.3334, -1123.6777); +GO +INSERT INTO t12 values (0, 0, 0, 0, 0, 0, 0, 0); +GO +CREATE TABLE t13 (txt1 text,txt2 text,txt3 text,txt4 text,txt5 text,txt6 text,txt7 text,txt8 text); +GO +INSERT INTO t13 SELECT * from t12; +GO +SELECT * FROM t13; +GO +-- numeric types -> varchar(20) +CREATE TABLE t14 (v1 varchar(20),v2 varchar(20),v3 varchar(20),v4 varchar(20),v5 varchar(20),v6 varchar(20),v7 varchar(20),v8 varchar(20)); +GO +INSERT INTO t14 SELECT * from t12; +GO +SELECT * FROM t14; +GO +-- numeric types -> char(10) +CREATE TABLE t15 (c1 char(20),c2 char(20),c3 char(20),c4 char(20),c5 char(20),c6 char(20),c7 char(20),c8 char(20)); +GO +INSERT INTO t15 SELECT * from t12; +GO +SELECT * FROM t15; +GO + +-- functions +select upper(cast (1 as smallint)), upper(cast (1 as int)), upper(cast (1 as bigint)), upper(cast (1 as real)), upper(cast (1 as float)), upper(cast (2.1 as numeric(2,1))), upper(cast (1 as smallmoney)), upper(cast (1 as money)); +GO +select round(cast ('123' as text), 1), round(cast ('123' as char(3)), 1), round(cast ('123' as varchar(3)), 1); +GO + +DROP TABLE t1; +GO +DROP TABLE t2; +GO +DROP TABLE t3; +GO +DROP TABLE t4; +GO +DROP TABLE t5; +GO +DROP TABLE t6; +GO +DROP TABLE t7; +GO +DROP TABLE t8; +GO +DROP TABLE t9; +GO +DROP TABLE t10; +GO +DROP TABLE t11; +GO +DROP TABLE t12; +GO +DROP TABLE t13; +GO +DROP TABLE t14; +GO +DROP TABLE t15; +GO diff --git a/contrib/test/JDBC/input/BABEL-1206.sql b/contrib/test/JDBC/input/BABEL-1206.sql new file mode 100644 index 00000000000..c456d346397 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1206.sql @@ -0,0 +1,73 @@ +-- BABEL-1265 +create table babel_1265_t1 (a varbinary(8), b varbinary(8)); +go +insert into babel_1265_t1 values (0xaaa, 0xbbb); +go +select (case when ab then 1 else 0 end) r from babel_1265_t1; +go +select (case when a>=b then 1 else 0 end) r from babel_1265_t1; +go + +-- BABEL-1206 actual scenarios +CREATE TABLE babel_1206_t1( + [Login] varchar(50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL, + [PasswordSHA1] varbinary(20) NOT NULL DEFAULT ((0)), + [UserLicenseKey] int NOT NULL +) +ON [PRIMARY]; +go +CREATE NONCLUSTERED INDEX babel_1206_t1_i1 + ON babel_1206_t1 ([Login] ASC, [UserLicenseKey] ASC, [PasswordSHA1] ASC) + WITH (FILLFACTOR = 90); +go +insert into babel_1206_t1 values (0xaaa, 0xbbb, 1); +go + +CREATE TABLE babel_1206_t2( + [profile_id] int NOT NULL, + [result_hash] binary(32) NULL +) +ON [PRIMARY]; +go +CREATE NONCLUSTERED INDEX babel_1206_t2_i1 + ON babel_1206_t2 ([profile_id] ASC, [result_hash] ASC); +go +insert into babel_1206_t2 values (1, 0xaaa); +go + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +go + +CREATE TABLE babel_1206_t3( + [DistinctApplicationID] [bigint] IDENTITY(1,1) NOT NULL, + [Description] [nvarchar](1024) NOT NULL, + [DescriptionHash] AS (hashbytes('MD5',[Description])) PERSISTED NOT NULL, + CONSTRAINT babel_1206_t3_i3 UNIQUE NONCLUSTERED + ( + [DescriptionHash] ASC + ) + ) ON [PRIMARY] +go + +insert into babel_1206_t3 values (0xaaa); +go +--should throw an error because of duplicate index key +insert into babel_1206_t3 values (0xaaa); +go + +drop table babel_1265_t1; +go +drop table babel_1206_t1; +go +drop table babel_1206_t2; +go +drop table babel_1206_t3; +go +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; +go diff --git a/contrib/test/JDBC/input/BABEL-1208.sql b/contrib/test/JDBC/input/BABEL-1208.sql new file mode 100644 index 00000000000..ffaaf08dbef --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1208.sql @@ -0,0 +1,59 @@ +create table babel_1208_t1 (a uniqueidentifier); +GO +insert into babel_1208_t1 values (convert(uniqueidentifier, 0x00)); +insert into babel_1208_t1 values (convert(uniqueidentifier, 0x1)); +insert into babel_1208_t1 values (convert(uniqueidentifier, 0x01)); +insert into babel_1208_t1 values (convert(uniqueidentifier, 0x123)); +insert into babel_1208_t1 values (convert(uniqueidentifier, 0x00010203040506070809)); +insert into babel_1208_t1 values (convert(uniqueidentifier, 0x000102030405060708090a0b0c0d0e0f)); +insert into babel_1208_t1 values (convert(uniqueidentifier, 0x000102030405060708090a0b0c0d0e0f1011121314)); +GO +select * from babel_1208_t1 order by a; +GO + +-- customer scenario +CREATE TABLE babel_1208_t2([SourceActivityTypeId] [uniqueidentifier] NOT NULL) +go +ALTER TABLE babel_1208_t2 ADD DEFAULT (CONVERT([uniqueidentifier],0x00)) FOR [SourceActivityTypeId] +GO +INSERT INTO babel_1208_t2 values (default), (default); +GO +SELECT * FROM babel_1208_t2 order by 1; +GO + +-- implicit castings +CREATE FUNCTION babel_1208_f_binary(@v binary(16)) RETURNS binary(16) AS BEGIN RETURN @v; END +GO +--SELECT babel_1208_f_binary(a) FROM babel_1208_t1; +GO + +CREATE FUNCTION babel_1208_f_varbinary(@v varbinary(16)) RETURNS varbinary(16) AS BEGIN RETURN @v; END +GO +SELECT babel_1208_f_varbinary(a) FROM babel_1208_t1; +GO + + +create table babel_1208_t3 (c1 binary(16), c2 varbinary(16)); +GO +insert into babel_1208_t3 values (0x000102030405060708090a0b0c0d0e0f, 0x000102030405060708090a0b0c0d0e0f); +GO + +CREATE FUNCTION babel_1208_f_uuid(@u uniqueidentifier) RETURNS uniqueidentifier AS BEGIN RETURN @u; END +GO + +SELECT babel_1208_f_uuid(c1) binary_in, babel_1208_f_uuid(c2) varbinary_in from babel_1208_t3 +GO + +-- cleanup +drop table babel_1208_t1; +GO +drop table babel_1208_t2; +GO +drop table babel_1208_t3; +GO +drop function babel_1208_f_binary; +GO +drop function babel_1208_f_varbinary; +GO +drop function babel_1208_f_uuid; +GO diff --git a/contrib/test/JDBC/input/BABEL-1210.sql b/contrib/test/JDBC/input/BABEL-1210.sql new file mode 100644 index 00000000000..822daba3db1 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1210.sql @@ -0,0 +1,98 @@ +CREATE FUNCTION test_implicit_2int2(@i INT2) +RETURNS INT2 +AS +BEGIN + RETURN (@i) +END; +GO + +CREATE FUNCTION test_implicit_2int4(@i INT4) +RETURNS INT4 +AS +BEGIN + RETURN (@i) +END; +GO + +CREATE FUNCTION test_implicit_2int8(@i INT8) +RETURNS INT8 +AS +BEGIN + RETURN (@i) +END; +GO + +CREATE FUNCTION test_implicit_2float4(@i float4) +RETURNS float4 +AS +BEGIN + RETURN (@i) +END; +GO + +CREATE FUNCTION test_implicit_2float8(@i float8) +RETURNS float8 +AS +BEGIN + RETURN (@i) +END; +GO + + +CREATE TABLE t1 (a text, b varchar(20), c char(4)); +GO + +INSERT INTO t1 values ('1234.56', '-12.12', '3.14'); +GO +INSERT INTO t1 values ('0', '-10000', '1'); +GO +INSERT INTO t1 values ('-23.33', '12345', '0'); +GO +INSERT INTO t1 values ('-33', '22', '-145'); +GO + +-- Test implicit casting to float4 + +SELECT test_implicit_2float4(a), test_implicit_2float4(b), test_implicit_2float4(c) from t1; +GO + +-- Test implicit casting to float8 +SELECT test_implicit_2float8(a), test_implicit_2float8(b), test_implicit_2float8(c) from t1; +GO + +CREATE TABLE t2 (a text, b varchar(20), c char(4)); +GO + +INSERT INTO t2 values ('1234', '-12', '0'); +GO +INSERT INTO t2 values ('0', '10000', '-123'); +GO +INSERT INTO t2 values ('-1234', '0', '52'); +GO + +-- Test implicit casting to int2 +SELECT test_implicit_2int2(a), test_implicit_2int2(b), test_implicit_2int2(c) from t2; +GO + +-- Test implicit casting to int4 +SELECT test_implicit_2int4(a), test_implicit_2int4(b), test_implicit_2int4(c) from t2; +GO + +-- Test implicit casting to int8 +SELECT test_implicit_2int8(a), test_implicit_2int8(b), test_implicit_2int8(c) from t2; +GO + +DROP TABLE t1; +GO +DROP TABLE t2; +GO +DROP FUNCTION test_implicit_2int2; +GO +DROP FUNCTION test_implicit_2int4; +GO +DROP FUNCTION test_implicit_2int8; +GO +DROP FUNCTION test_implicit_2float4; +GO +DROP FUNCTION test_implicit_2float8; +GO diff --git a/contrib/test/JDBC/input/BABEL-1212.sql b/contrib/test/JDBC/input/BABEL-1212.sql new file mode 100644 index 00000000000..34c1d35eb42 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1212.sql @@ -0,0 +1,10 @@ +create table #tt(a int); +go +select (case when OBJECT_ID('#tt') IS NOT NULL then 'true' else 'false' end) result; +go +select (case when OBJECT_ID('tempdb..#tt') IS NOT NULL then 'true' else 'false' end) result; +go +select (case when OBJECT_ID('tempdb..#tt2') IS NULL then 'true' else 'false' end) result; +go +drop table #tt; +go diff --git a/contrib/test/JDBC/input/BABEL-1231.sql b/contrib/test/JDBC/input/BABEL-1231.sql new file mode 100644 index 00000000000..6e1b0c5e20d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1231.sql @@ -0,0 +1,29 @@ +CREATE FUNCTION custom_diff( + @one int, + @two int +) +RETURNS int +AS +BEGIN + RETURN (@one - @two); +END; +GO + +DECLARE @one int = 100; +DECLARE @two int = 200; +DECLARE @returnstatus int; +-- execute UDF +EXECUTE @returnstatus = custom_diff @one, @two; +SELECT @returnstatus; +-- execute UDF with named arguments +EXECUTE @returnstatus = custom_diff @one = @one, @two = @two; +SELECT @returnstatus; +EXECUTE @returnstatus = custom_diff @two = @one, @one = @two; +SELECT @returnstatus; +-- execute UDF with mixed arguments +EXECUTE @returnstatus = custom_diff @one, @two = @two; +SELECT @returnstatus; +GO + +DROP FUNCTION custom_diff +GO diff --git a/contrib/test/JDBC/input/BABEL-1234.sql b/contrib/test/JDBC/input/BABEL-1234.sql new file mode 100644 index 00000000000..58979e9bb67 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1234.sql @@ -0,0 +1,2 @@ +SELECT CAST(0 AS BIT) AS N'AdminConnection'; +GO diff --git a/contrib/test/JDBC/input/BABEL-1239.sql b/contrib/test/JDBC/input/BABEL-1239.sql new file mode 100644 index 00000000000..deffe7862dd --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1239.sql @@ -0,0 +1,15 @@ +create table babel1239 (a int); +go +SELECT * from babel1239 +OPTION (MAXRECURSION 256); +go +WITH +z +AS ( + SELECT a FROM babel1239 + ) +SELECT * from z +OPTION (MAXRECURSION 256); +go +drop table babel1239; +go diff --git a/contrib/test/JDBC/input/BABEL-1241.sql b/contrib/test/JDBC/input/BABEL-1241.sql new file mode 100644 index 00000000000..429699655eb --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1241.sql @@ -0,0 +1,73 @@ +create table t_babel_1241 (a int, b int); +insert into t_babel_1241 values (10, 1); +go + +declare @v int=1; +select @v+=a from t_babel_1241; +select @v +go + +declare @v int=11; +select @v-=a from t_babel_1241; +select @v; +go + +declare @v int=2; +select @v*=a from t_babel_1241; +select @v; +go + +declare @v int=20; +select @v/=a from t_babel_1241; +select @v; +go + +declare @v int=24; +select @v%=a from t_babel_1241; +select @v; +go + +declare @v int=63; +select @v&=a from t_babel_1241; +select @v; +go + +declare @v int=7; +select @v|=a from t_babel_1241; +select @v; +go + +declare @v int=7; +select @v^=a from t_babel_1241; +select @v; +go + +-- many compound operator +declare @v int=1; +declare @v2 int=2; +declare @v3 int=3; +select @v+=a, @v2-=a, @v3*=a from t_babel_1241; +select @v, @v2, @v3; +go + +-- compound operator on same target (we don't support this) +declare @v int=1; +select @v+=a, @v-=b from t_babel_1241; +select @v +go + +-- compound operator and equal operator +declare @v int=1; +declare @v2 int=2; +select @v+=a, @v2=b from t_babel_1241; +select @v, @v2; +go + +-- compound operator and non-assignment. error should be thrown +declare @v int=1; +declare @v2 int=2; +select @v+=a, b from t_babel_1241; +go + +drop table t_babel_1241; +go diff --git a/contrib/test/JDBC/input/BABEL-1249.sql b/contrib/test/JDBC/input/BABEL-1249.sql new file mode 100644 index 00000000000..fed743a269d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1249.sql @@ -0,0 +1,43 @@ +-- time +if CAST('01:01:01' AS TIME) < CAST(CAST('02:02:02' AS TIME) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- date +if CAST('2010-02-02' AS DATE) < CAST(CAST('2020-02-02' AS DATE) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- smalldatetime +if CAST('2010-02-02' AS SMALLDATETIME) < CAST(CAST('2020-02-02' AS SMALLDATETIME) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- datetimeoffset +if CAST('2020-10-05 09:00:00.123456-9:00' AS DATETIMEOFFSET) < CAST(CAST('2020-10-05 09:00:00.123456-8:00' AS DATETIMEOFFSET) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- int +if CAST(1 AS INT) < CAST(CAST(2 AS INT) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- bigint +if CAST(1 AS BIGINT) < CAST(CAST(2 AS BIGINT) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- bit +if CAST(0 AS BIT) < CAST(CAST(1 AS BIT) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- real +if CAST(0.1 AS REAL) < CAST(CAST(1.1 AS REAL) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- float +if CAST(0.1 AS FLOAT) < CAST(CAST(1.1 AS FLOAT) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- numeric +if CAST(0.1 AS NUMERIC(2,1)) < CAST(CAST(1.1 AS NUMERIC(2,1)) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO + +-- uniqueidentifier +if CAST('1E984725-C51C-4BF4-9960-E1C80E27ABA0' AS UNIQUEIDENTIFIER) < CAST(CAST('2E984725-C51C-4BF4-9960-E1C80E27ABA0' AS UNIQUEIDENTIFIER) AS SQL_VARIANT) SELECT 1 ELSE SELECT 2; +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1251.sql b/contrib/test/JDBC/input/BABEL-1251.sql new file mode 100644 index 00000000000..ffc9276c578 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1251.sql @@ -0,0 +1,76 @@ +CREATE SCHEMA babel_1251; +GO + +-- Test id as second column +CREATE TABLE babel_1251.t1(col1 INT, id INT IDENTITY(1, 1) NOT NULL); +go +SET IDENTITY_INSERT babel_1251.t1 ON; +go +INSERT INTO babel_1251.t1(col1, id) VALUES (1, 10); +go +SELECT @@IDENTITY; +go +SET IDENTITY_INSERT babel_1251.t1 OFF; +go +INSERT INTO babel_1251.t1(col1) VALUES (1); +go +SELECT * FROM babel_1251.t1; +go +SELECT @@IDENTITY; +go + +-- Test id as middle column +CREATE TABLE babel_1251.t2(col1 VARCHAR(32), id INT IDENTITY(1, -1), col2 INT); +go +SET IDENTITY_INSERT babel_1251.t2 ON; +go +INSERT INTO babel_1251.t2(col1, id, col2) VALUES ('hello', -10, 1); +go +SELECT @@IDENTITY; +go +SET IDENTITY_INSERT babel_1251.t2 OFF; +go +INSERT INTO babel_1251.t2(col1, col2) VALUES ('world', 1); +go +SELECT @@IDENTITY; +go +SELECT * FROM babel_1251.t2; +go + +-- Test id as last column +CREATE TABLE babel_1251.t3(col1 VARCHAR(32), col2 INT, id INT IDENTITY); +go +INSERT INTO babel_1251.t3(col1, col2) VALUES ('hello', 1); +go +SELECT @@IDENTITY; +go +SET IDENTITY_INSERT babel_1251.t3 ON; +go +INSERT INTO babel_1251.t3(col1, col2, id) VALUES ('hello', 1, 20); +go +SET IDENTITY_INSERT babel_1251.t3 OFF; +go +SELECT @@IDENTITY; +go +INSERT INTO babel_1251.t3(col1, col2) VALUES ('hello', 1); +go +SELECT @@IDENTITY; +go +SET IDENTITY_INSERT babel_1251.t3 ON; +go +INSERT INTO babel_1251.t3(col1, col2, id) VALUES ('hello', 1, 30); +go +SELECT @@IDENTITY; +go +SET IDENTITY_INSERT babel_1251.t3 OFF; +go +SELECT * FROM babel_1251.t3; +go +SELECT @@IDENTITY; +go + +DROP TABLE babel_1251.t1, babel_1251.t2, babel_1251.t3; +go + +DROP SCHEMA babel_1251; +GO diff --git a/contrib/test/JDBC/input/BABEL-1252.sql b/contrib/test/JDBC/input/BABEL-1252.sql new file mode 100644 index 00000000000..c84ee1a121d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1252.sql @@ -0,0 +1,65 @@ +use master; +go + +create table dttest (d datetime) +go +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) - 100) +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) - (-100)) +insert dttest values(100 - cast('10/10/2000 12:34:56.789' as datetime)) +insert dttest values((-100) - cast('10/10/2000 12:34:56.789' as datetime)) +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) + 100) +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) + (-100)) +insert dttest values(100 + cast('10/10/2000 12:34:56.789' as datetime)) +insert dttest values((-100) + cast('10/10/2000 12:34:56.789' as datetime)) +go + +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) - 1.5) +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) - (-1.5)) +insert dttest values(2.3 - cast('10/10/2000 12:34:56.789' as datetime)) +insert dttest values((-2.3) - cast('10/10/2000 12:34:56.789' as datetime)) +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) + .11) +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) + (-.11)) +insert dttest values(8.55 + cast('10/10/2000 12:34:56.789' as datetime)) +insert dttest values((-9.76) + cast('10/10/2000 12:34:56.789' as datetime)) +go + +-- should error, out of range +insert dttest values(10000000 + cast('10/10/2000 12:34:56.789' as datetime)) +go +insert dttest values(cast('10/10/2000 12:34:56.789' as datetime) - 10000000) +go + +select * from dttest +go + + +create table dttest2 (d smalldatetime) +go +insert dttest2 values(1000 - cast('1/10/1900 12:34:56.789' as smalldatetime)) +insert dttest2 values(cast('10/10/2000 12:34:56.789' as smalldatetime) + 100) +insert dttest2 values(cast('10/10/2000 12:34:56.789' as smalldatetime) - 10) +insert dttest2 values(100 + cast('10/10/2000 12:34:56.789' as smalldatetime)) +go + +insert dttest2 values(cast('10/10/2000 12:34:56.789' as smalldatetime) + 1.5) +insert dttest2 values(cast('10/10/2000 12:34:56.789' as smalldatetime) - (1.5)) +insert dttest2 values(20.3 - cast('1/2/1900 12:34:56.789' as smalldatetime)) +insert dttest2 values(cast('10/10/2000 12:34:56.789' as smalldatetime) + .11) +insert dttest2 values(8.55 + cast('10/10/2000 12:34:56.789' as smalldatetime)) +go + +-- should error, out of range +insert dttest2 values(100000 + cast('10/10/2000 12:34:56.789' as smalldatetime)) +go +insert dttest2 values((-100000)+ cast('10/10/2000 12:34:56.789' as smalldatetime)) +go +insert dttest2 values(cast('10/10/2000 12:34:56.789' as smalldatetime) - 10000000) +go + +select * from dttest2 +go + + +drop table dttest +drop table dttest2 +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1261.sql b/contrib/test/JDBC/input/BABEL-1261.sql new file mode 100644 index 00000000000..c7265aaf343 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1261.sql @@ -0,0 +1,73 @@ +CREATE PROCEDURE sp_babel_1261_1 (@a SMALLINT OUTPUT) AS +BEGIN + SET @a=100; + select @a as a; +END; +GO + +exec sp_babel_1261_1 2; +GO + +CREATE PROCEDURE sp_babel_1261_2 (@a SMALLINT OUTPUT, @b SMALLINT OUTPUT) AS +BEGIN + SET @a=100; + SET @b=200; + select @a+@b as r; +END; +GO + +EXEC sp_babel_1261_2 2, 3; +GO + +DECLARE @a INT; +EXEC sp_babel_1261_2 @a OUT, 3; +SELECT @a; +GO + +DECLARE @b INT; +EXEC sp_babel_1261_2 2, @b OUT; +SELECT @b; +GO + +DECLARE @a INT; +DECLARE @b INT; +EXEC sp_babel_1261_2 @a OUT, @b OUT; +SELECT @a+@b; +GO + +CREATE PROCEDURE sp_babel_1307_1 (@a numeric(10,4) OUTPUT) AS +BEGIN + SET @a=100.41; + select @a as a; +END; +GO + +exec sp_babel_1307_1 2.000; +GO + +CREATE PROCEDURE sp_babel_1307_2 (@a numeric(10,4) OUTPUT, @b numeric(10,4) OUTPUT) AS +BEGIN + SET @a=100.41; + SET @b=200.82; + select @a+@b as r; +END; +GO + +EXEC sp_babel_1307_2 2.000, 3.000; +GO + +DECLARE @a INT; +EXEC sp_babel_1307_2 @a OUT, 3.000; +SELECT @a; +GO + +DECLARE @b INT; +EXEC sp_babel_1307_2 2.000, @b OUT; +SELECT @b; +GO + +DECLARE @a INT; +DECLARE @b INT; +EXEC sp_babel_1307_2 @a OUT, @b OUT; +SELECT @a+@b; +GO diff --git a/contrib/test/JDBC/input/BABEL-1270.txt b/contrib/test/JDBC/input/BABEL-1270.txt new file mode 100644 index 00000000000..876ede7aaab --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1270.txt @@ -0,0 +1,15 @@ +CREATE TABLE test_cursors_fetch_next(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO test_cursors_fetch_next values(0, 0, 0, 0, 0) +INSERT INTO test_cursors_fetch_next values(NULL, NULL, NULL, NULL, NULL) +INSERT INTO test_cursors_fetch_next values(1, 2, 3, 4, 1) +INSERT INTO test_cursors_fetch_next values(211234, 9780, 891372401, 56, 1) +# SET escape_hatch_session_settings to ignore CURSOR_CLOSE_ON_COMMIT +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +cursor#!#open#!#SELECT * FROM test_cursors_fetch_next#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#afterlast +cursor#!#fetch#!#prev +cursor#!#fetch#!#beforefirst +cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursors_fetch_next +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; diff --git a/contrib/test/JDBC/input/BABEL-1287.sql b/contrib/test/JDBC/input/BABEL-1287.sql new file mode 100644 index 00000000000..0013e330913 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1287.sql @@ -0,0 +1,98 @@ +-- implicit casting: uniqueidentifier -> string + +create table babel_1287_t1 (a uniqueidentifier); +GO +insert into babel_1287_t1 values (convert(uniqueidentifier, 0x00)); +insert into babel_1287_t1 values (convert(uniqueidentifier, 0x1)); +insert into babel_1287_t1 values (convert(uniqueidentifier, 0x01)); +insert into babel_1287_t1 values (convert(uniqueidentifier, 0x123)); +insert into babel_1287_t1 values (convert(uniqueidentifier, 0x00010203040506070809)); +insert into babel_1287_t1 values (convert(uniqueidentifier, 0x000102030405060708090a0b0c0d0e0f)); +insert into babel_1287_t1 values (convert(uniqueidentifier, 0x000102030405060708090a0b0c0d0e0f1011121314)); +GO + +CREATE FUNCTION babel_1287_f_char(@v char(40)) +RETURNS varchar(40) AS +BEGIN + RETURN @v; +END +GO + +CREATE FUNCTION babel_1287_f_varchar(@v varchar(40)) +RETURNS varchar(40) AS +BEGIN + RETURN @v; +END +GO + +CREATE FUNCTION babel_1287_f_text(@v text) +RETURNS text AS +BEGIN + RETURN @v; +END +GO + +SELECT babel_1287_f_char(a) char_result, babel_1287_f_varchar(a) varchar_result, babel_1287_f_text(a) text_result FROM babel_1287_t1; +GO + +-- implicit casting: string -> uniqueidentifier +create table babel_1287_tmp(a varchar(50)); +insert into babel_1287_tmp values ('6F9619FF-8B86-D011-B42D-00C04FC964FF'); +insert into babel_1287_tmp values ('6F9619FF-8B86-D011-B42D-00C04FC964FFwrong'); +insert into babel_1287_tmp values ('{6F9619FF-8B86-D011-B42D-00C04FC964FF}'); +GO +create table babel_1287_t2 (a char(50), b varchar(50), c text); +insert into babel_1287_t2 select a, a, a from babel_1287_tmp; +truncate table babel_1287_tmp; +GO + +insert into babel_1287_tmp values ('wrong6F9619FF-8B86-D011-B42D-00C04FC964FF'); +create table babel_1287_t2_invalid1 (a char(50), b varchar(50), c text); +insert into babel_1287_t2_invalid1 select a, a, a from babel_1287_tmp; +truncate table babel_1287_tmp; +GO + +insert into babel_1287_tmp values ('6F9619FF-8B86-D011-B42D-WRONGFC964FF'); +create table babel_1287_t2_invalid2 (a char(50), b varchar(50), c text); +insert into babel_1287_t2_invalid2 select a, a, a from babel_1287_tmp; +truncate table babel_1287_tmp; +GO + +CREATE FUNCTION babel_1287_f_ui(@v uniqueidentifier) +RETURNS uniqueidentifier AS +BEGIN + RETURN @v; +END +GO + +SELECT babel_1287_f_ui(a) char_in, babel_1287_f_ui(b) varchar_in, babel_1287_f_ui(c) text_in from babel_1287_t2; +GO + +-- wrong input +SELECT babel_1287_f_ui(a) char_in from babel_1287_t2_invalid1; +GO +SELECT babel_1287_f_ui(b) varchar_in from babel_1287_t2_invalid1; +GO +SELECT babel_1287_f_ui(c) text_in from babel_1287_t2_invalid1; +GO + +-- wrong input +SELECT babel_1287_f_ui(a) char_in from babel_1287_t2_invalid2; +GO +SELECT babel_1287_f_ui(b) varchar_in from babel_1287_t2_invalid2; +GO +SELECT babel_1287_f_ui(c) text_in from babel_1287_t2_invalid2; +GO + +DROP FUNCTION babel_1287_f_char; +DROP FUNCTION babel_1287_f_varchar; +DROP FUNCTION babel_1287_f_text; +DROP FUNCTION babel_1287_f_ui; +GO + +DROP TABLE babel_1287_t1; +DROP TABLE babel_1287_t2; +DROP TABLE babel_1287_t2_invalid1; +DROP TABLE babel_1287_t2_invalid2; +DROP TABLE babel_1287_tmp; +GO diff --git a/contrib/test/JDBC/input/BABEL-1291.sql b/contrib/test/JDBC/input/BABEL-1291.sql new file mode 100644 index 00000000000..965b626ff33 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1291.sql @@ -0,0 +1,16 @@ +CREATE TABLE sql_variant_test(a sql_variant, b sql_variant); +GO + +INSERT INTO sql_variant_test VALUES (NULL,NULL); +GO + +SELECT * FROM sql_variant_test; +GO + +DROP TABLE sql_variant_test; +GO + +select cast(cast(NULL as bit) as sql_variant); +select cast(cast(NULL as VARCHAR(2)) as sql_variant); +select cast(null as sql_variant); +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1299.sql b/contrib/test/JDBC/input/BABEL-1299.sql new file mode 100644 index 00000000000..584d071fd8e --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1299.sql @@ -0,0 +1,29 @@ +CREATE PROC babel_1299_proc (@a INT OUT, @b VARCHAR(10) OUTPUT) AS +BEGIN + SET @a = @a + 1; + SET @b = 'hi'; +END +go + +DECLARE @x INT = 10; +DECLARE @y VARCHAR(10) = 'hello'; +EXEC babel_1299_proc @x OUT, @y OUT +SELECT @x AS x, @y AS y; +go + +-- If an INOUT param is called without OUTPUT, +-- it's treated like a regular input param +DECLARE @x INT = 10; +DECLARE @y VARCHAR(10) = 'hello'; +EXEC babel_1299_proc @x OUT, @y +SELECT @x AS x, @y AS y; +go + +DECLARE @x INT = 10; +DECLARE @y VARCHAR(10) = 'hello'; +EXEC babel_1299_proc @x, @y OUT +SELECT @x AS x, @y AS y; +go + +DROP PROC babel_1299_proc +go diff --git a/contrib/test/JDBC/input/BABEL-1311.sql b/contrib/test/JDBC/input/BABEL-1311.sql new file mode 100644 index 00000000000..73c699c030c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1311.sql @@ -0,0 +1,118 @@ +create table babel_1311_t(c_w_id int, c_first int); +insert into babel_1311_t values (1, 1), (2, 2); +GO + +DECLARE c_byname CURSOR STATIC FOR +SELECT customer.c_first +FROM babel_1311_t AS customer WITH (INDEX = [customer_i2], repeatableread) +INNER JOIN babel_1311_t AS C_BAL WITH (INDEX = [customer_i1], repeatableread) +ON C_BAL.c_w_id = customer.c_w_id; + +DECLARE @var_c_w_id int; + +OPEN c_byname; +FETCH c_byname into @var_c_w_id; +PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); +FETCH c_byname into @var_c_w_id; +PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); +CLOSE c_byname; +DEALLOCATE c_byname; +go + +DECLARE cur1 cursor for ((((select c_w_id from babel_1311_t)))); +DECLARE @var_c_w_id int; + +OPEN cur1; +FETCH cur1 into @var_c_w_id; +PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); +FETCH cur1 into @var_c_w_id; +PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); +CLOSE cur1; +DEALLOCATE cur1; +go + +DECLARE cur1 cursor for with cte_a as (select c_w_id from babel_1311_t where c_w_id = 2) select * from cte_a; +DECLARE @var_c_w_id int; + +OPEN cur1; +FETCH cur1 into @var_c_w_id; +PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); +CLOSE cur1; +DEALLOCATE cur1; +go + +create procedure babel_1311_proc as +begin + DECLARE c_byname CURSOR STATIC FOR + SELECT customer.c_first + FROM babel_1311_t AS customer WITH (INDEX = [customer_i2], repeatableread) + INNER JOIN babel_1311_t AS C_BAL WITH (INDEX = [customer_i1], repeatableread) + ON C_BAL.c_w_id = customer.c_w_id; + + DECLARE @var_c_w_id int; + + OPEN c_byname; + FETCH c_byname into @var_c_w_id; + PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); + FETCH c_byname into @var_c_w_id; + PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); + CLOSE c_byname; +end; +go + +exec babel_1311_proc; +go + +create procedure babel_1311_proc_multiple_parethesis as +begin + DECLARE cur1 cursor for ((((select c_w_id from babel_1311_t)))); + DECLARE @var_c_w_id int; + + OPEN cur1; + FETCH cur1 into @var_c_w_id; + PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); + FETCH cur1 into @var_c_w_id; + PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); + CLOSE cur1; +end; +go + +exec babel_1311_proc_multiple_parethesis; +go + +create procedure babel_1311_proc_multiple_parethesis_mismatch1 as +begin + DECLARE cur1 cursor for select c_w_id from babel_1311_t); +end; +go + +create procedure babel_1311_proc_multiple_parethesis_mismatch2 as +begin + DECLARE cur1 cursor for select (c_w_id from babel_1311_t)); +end; +go + +create procedure babel_1311_proc_with_clause as +begin + DECLARE cur1 cursor for with cte_a as (select c_w_id from babel_1311_t where c_w_id = 2) select * from cte_a; + DECLARE @var_c_w_id int; + + OPEN cur1; + FETCH cur1 into @var_c_w_id; + PRINT '@var_c_w_id: ' + cast(@var_c_w_id as varchar(10)); + CLOSE cur1; +end; +go + +exec babel_1311_proc_with_clause; +go + +drop procedure babel_1311_proc; +go +drop procedure babel_1311_proc_multiple_parethesis; +go +drop procedure babel_1311_proc_with_clause; +go + +drop table babel_1311_t; +go diff --git a/contrib/test/JDBC/input/BABEL-1319.sql b/contrib/test/JDBC/input/BABEL-1319.sql new file mode 100644 index 00000000000..ada3668e249 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1319.sql @@ -0,0 +1,30 @@ +CREATE TABLE [Item] ( + [_id] int NOT NULL IDENTITY, + [Name] nvarchar(max) NULL, + CONSTRAINT [PK_Item] PRIMARY KEY ([_id]) +) +GO + +CREATE TABLE [Tag] ( [_id] int NOT NULL IDENTITY, + [Label] nvarchar(max) NULL, + [Count] int NOT NULL, + [Item_id] int NOT NULL, + CONSTRAINT [PK_Tag] PRIMARY KEY ([_id]), + CONSTRAINT [FK_Tag_Item_Item_id] FOREIGN KEY ([Item_id]) REFERENCES [Item] ([_id]) ON DELETE CASCADE +) +GO + +CREATE INDEX [IX_Tag_Item_id] ON [Tag] ([Item_id]) +GO + +INSERT INTO [Item] ([Name]) +VALUES ('ItemOne') +GO + +DELETE FROM [Item] WHERE [Name] = 'ItemOne' +GO + +DROP TABLE [Tag] +GO +DROP TABLE [Item] +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1320.sql b/contrib/test/JDBC/input/BABEL-1320.sql new file mode 100644 index 00000000000..94c833b32e7 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1320.sql @@ -0,0 +1,46 @@ +USE master +GO + +CREATE SCHEMA tds72; +GO + +CREATE FUNCTION tds72.test_tds_72_date_datatypes(@a date) RETURNS date AS BEGIN RETURN @a; END; +GO + +DECLARE @a date = '2020-05-14'; +SELECT tds72.test_tds_72_date_datatypes(@a); +GO + +DROP FUNCTION tds72.test_tds_72_date_datatypes; +GO + +CREATE FUNCTION tds72.test_tds_72_date_datatypes(@a DATETIME2) RETURNS DATETIME2 AS BEGIN RETURN @a; END; +GO + +DECLARE @a DATETIME2 = '2016-10-23 12:45:37.333'; +SELECT tds72.test_tds_72_date_datatypes(@a); +GO + +DROP FUNCTION tds72.test_tds_72_date_datatypes; +GO + +CREATE FUNCTION tds72.test_tds_72_date_datatypes(@a DATETIMEOFFSET) RETURNS DATETIMEOFFSET AS BEGIN RETURN @a; END; +GO + +DECLARE @a DATETIMEOFFSET = '12-10-25 12:32:10 +01:00'; +SELECT tds72.test_tds_72_date_datatypes(@a); +GO + +DROP FUNCTION tds72.test_tds_72_date_datatypes; +GO + +CREATE FUNCTION tds72.test_tds_72_date_datatypes(@a TIME) RETURNS TIME AS BEGIN RETURN @a; END; +GO + +DECLARE @a TIME = '12:15:04.1237'; +SELECT tds72.test_tds_72_date_datatypes(@a); +GO + +DROP FUNCTION tds72.test_tds_72_date_datatypes; +DROP SCHEMA tds72; +GO diff --git a/contrib/test/JDBC/input/BABEL-1329.sql b/contrib/test/JDBC/input/BABEL-1329.sql new file mode 100644 index 00000000000..97786945ab3 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1329.sql @@ -0,0 +1,26 @@ +SELECT HASHBYTES('sha2_256', 'abc') +GO + +SELECT HASHBYTES('sha256', 'abc') +GO + +SELECT HASHBYTES('sha512', 'abc') +GO + +SELECT HASHBYTES('', '') +GO + +SELECT HASHBYTES('md5', 'abc') +GO + +SELECT HASHBYTES('sha2_512', 'abc') +GO + +SELECT HASHBYTES('md2', 'abc') +GO + +SELECT HASHBYTES('md4', 'abc') +GO + +SELECT HASHBYTES('asdf', 'asdf') +GO diff --git a/contrib/test/JDBC/input/BABEL-1331.sql b/contrib/test/JDBC/input/BABEL-1331.sql new file mode 100644 index 00000000000..140257b52b7 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1331.sql @@ -0,0 +1,16 @@ +create table t_babel_1331 (a int, b int); +insert into t_babel_1331 values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5); +go + +declare cur cursor for select * from t_babel_1331 +open cur +fetch cur +fetch next from cur +fetch prior from cur +fetch last from cur +fetch first from cur +fetch absolute 2 from cur +fetch relative 2 from cur +close cur +deallocate cur +go diff --git a/contrib/test/JDBC/input/BABEL-1363.mix b/contrib/test/JDBC/input/BABEL-1363.mix new file mode 100644 index 00000000000..d1224cee39a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1363.mix @@ -0,0 +1,32 @@ +-- NOTE: this test should not run in parallel due to DB config change. +GO + +-- tsql +-- VariableSetStmt doens't work in JDBC. use workaround +DECLARE @orig_force_parallel_mode varchar(10); +SET @orig_force_parallel_mode = (SELECT current_setting('force_parallel_mode')); + +SELECT 'enable force_parallel_mode' FROM (SELECT + set_config('force_parallel_mode', 'on', false)) sq; +GO + +-- tsql +create table babel_1363_t1 (a int); +insert into babel_1363_t1 values (1), (2), (3), (4), (5), (6); +GO + +-- psql currentSchema=master_dbo,public +explain (costs off) select * from babel_1363_t1; +GO + +-- tsql +select * from babel_1363_t1; +GO + +-- tsql +DECLARE @orig_force_parallel_mode varchar(10); +SET @orig_force_parallel_mode = (SELECT current_setting('force_parallel_mode')); +SELECT 'reset force_parallel_mode' FROM (SELECT + set_config('force_parallel_mode', @orig_force_parallel_mode, false)) sq; +GO + diff --git a/contrib/test/JDBC/input/BABEL-1381.sql b/contrib/test/JDBC/input/BABEL-1381.sql new file mode 100644 index 00000000000..a9151b5d219 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1381.sql @@ -0,0 +1,99 @@ +CREATE PROCEDURE p_babel_1381_tinyint (@a tinyint OUTPUT) AS +BEGIN + SET @a=42; + select @a as a; +END; +GO + +EXEC p_babel_1381_tinyint 1; +GO + +CREATE PROCEDURE p_babel_1381_nchar (@a nchar OUTPUT) AS +BEGIN + SET @a='helloworld'; + select @a as a; +END; +GO + +EXEC p_babel_1381_nchar 'a'; +GO + +CREATE PROCEDURE p_babel_1381_nchar_10 (@a nchar(10) OUTPUT) AS +BEGIN + SET @a='helloworld'; + select @a as a; +END; +GO + +EXEC p_babel_1381_nchar_10 'a'; +GO + +CREATE PROCEDURE p_babel_1381_varchar (@a varchar OUTPUT) AS +BEGIN + SET @a='helloworld'; + select @a as a; +END; +GO + +EXEC p_babel_1381_varchar 'a'; +GO + +CREATE PROCEDURE p_babel_1381_varchar_10 (@a varchar(10) OUTPUT) AS +BEGIN + SET @a='helloworld'; + select @a as a; +END; +GO + +EXEC p_babel_1381_varchar_10 'a'; +GO + +CREATE PROCEDURE p_babel_1381_nvarchar (@a nvarchar OUTPUT) AS +BEGIN + SET @a='helloworld'; + select @a as a; +END; +GO + +EXEC p_babel_1381_varchar 'a'; +GO + +CREATE PROCEDURE p_babel_1381_nvarchar_10 (@a nvarchar(10) OUTPUT) AS +BEGIN + SET @a='helloworld'; + select @a as a; +END; +GO + +EXEC p_babel_1381_nvarchar_10 'a'; +GO + +CREATE PROCEDURE p_babel_1381_binary (@a binary OUTPUT) AS +BEGIN + SET @a=0xabcdef; + select @a as a; +END; +GO + +EXEC p_babel_1381_binary 0x1; +GO + +CREATE PROCEDURE p_babel_1381_varbinary (@a varbinary OUTPUT) AS +BEGIN + SET @a=0xabcdef; + select @a as a; +END; +GO + +EXEC p_babel_1381_varbinary 0x1; +GO + +CREATE PROCEDURE p_babel_1381_varbinary_10 (@a varbinary(10) OUTPUT) AS +BEGIN + SET @a=0xabcdef; + select @a as a; +END; +GO + +EXEC p_babel_1381_varbinary_10 0x1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1389.sql b/contrib/test/JDBC/input/BABEL-1389.sql new file mode 100644 index 00000000000..e7ef26232af --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1389.sql @@ -0,0 +1,36 @@ +-- Test @@OPTIONS value changes when unsetting/setting CONCAT_NULL_YIELDS_NULL +SELECT @@OPTIONS AS OriginalOptionsValue; +SET CONCAT_NULL_YIELDS_NULL OFF; +SELECT 'abc' + NULL AS ResultWhen_OFF, @@OPTIONS AS OptionsValueWhen_OFF; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +SELECT 'abc' + NULL AS ResultWhen_ON, @@OPTIONS AS OptionsValueWhen_ON; +GO + +-- Test @@OPTIONS value changes with the setting of other options +SET IMPLICIT_TRANSACTIONS ON; +SELECT @@OPTIONS; +SET IMPLICIT_TRANSACTIONS OFF; +GO + +SET ANSI_NULLS OFF; +SELECT @@OPTIONS; +SET ANSI_NULLS ON; +GO + +SET QUOTED_IDENTIFIER ON; +SELECT @@OPTIONS; +SET QUOTED_IDENTIFIER OFF; +GO + +SET NOCOUNT ON; +SELECT @@OPTIONS; +SET NOCOUNT OFF; +GO + +SET XACT_ABORT ON; +SELECT @@OPTIONS; +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/input/BABEL-1400.sql b/contrib/test/JDBC/input/BABEL-1400.sql new file mode 100644 index 00000000000..91554706fb3 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1400.sql @@ -0,0 +1,53 @@ +-- BABEL-1400 +-- Test NCHAR/NCARCHAR/VARBINARY/CHAR/VARCHAR/BINARY parameter default length as a procedure parameter +CREATE PROCEDURE sp_test1 (@a nchar OUTPUT, @b nvarchar OUTPUT, @c varbinary OUTPUT, @d char OUTPUT, @e varchar OUTPUT, @f binary OUTPUT) AS BEGIN Select @a, @b, @c, @d, @e, @f; END; +GO +Declare @a nchar; Set @a = 'hello'; +Declare @b nvarchar; Set @b = 'world'; +Declare @c varbinary; Set @c = 0xABCD; +Declare @d char; Set @d = 'HELLO'; +Declare @e varchar; Set @e = 'WORLD'; +Declare @f binary; Set @f = 0x1234; +exec sp_test1 @a, @b, @c, @d, @e, @f; +GO + +CREATE PROCEDURE sp_test2 (@a nchar OUTPUT, @b nvarchar OUTPUT, @c varbinary OUTPUT, @d char OUTPUT, @e varchar OUTPUT, @f binary OUTPUT) AS BEGIN Select @a, @b, @c, @d, @e, @f; END; +GO +Declare @a nchar(2); Set @a = 'hello'; +Declare @b nvarchar(2); Set @b = 'world'; +Declare @c varbinary(2); Set @c = 0xABCD; +Declare @d char(2); Set @d = 'HELLO'; +Declare @e varchar(2); Set @e = 'WORLD'; +Declare @f binary(2); Set @f = 0x1234; +exec sp_test2 @a, @b, @c, @d, @e, @f; +GO + +CREATE PROCEDURE sp_test3 (@a nchar(2) OUTPUT, @b nvarchar(2) OUTPUT, @c varbinary(2) OUTPUT, @d char(2) OUTPUT, @e varchar(2) OUTPUT, @f binary(2) OUTPUT) AS BEGIN Select @a, @b, @c, @d, @e, @f; END; +GO +Declare @a nchar(2); Set @a = 'hello'; +Declare @b nvarchar(2); Set @b = 'world'; +Declare @c varbinary(2); Set @c = 0xABCD; +Declare @d char(2); Set @d = 'HELLO'; +Declare @e varchar(2); Set @e = 'WORLD'; +Declare @f binary(2); Set @f = 0x1234; +exec sp_test3 @a, @b, @c, @d, @e, @f; +GO + +-- Test changing parameter value inside procedure +CREATE PROCEDURE sp_test4 (@a nchar OUTPUT, @b nvarchar OUTPUT, @c varbinary OUTPUT, @d char OUTPUT, @e varchar OUTPUT, @f binary OUTPUT) AS BEGIN SET @a = 'world'; SET @b = 'hello'; SET @c = 0x1234; SET @d = 'WORLD'; SET @e = 'HELLO'; SET @f = 0xABCD; Select @a, @b, @c, @d, @e, @f; END; +GO +Declare @a nchar; Set @a = 'hello'; +Declare @b nvarchar; Set @b = 'world'; +Declare @c varbinary; Set @c = 0xABCD; +Declare @d char; Set @d = 'HELLO'; +Declare @e varchar; Set @e = 'WORLD'; +Declare @f binary; Set @f = 0x1234; +exec sp_test4 @a, @b, @c, @d, @e, @f; +GO + +-- Clean up +DROP PROCEDURE sp_test1; +DROP PROCEDURE sp_test2; +DROP PROCEDURE sp_test3; +DROP PROCEDURE sp_test4; +GO diff --git a/contrib/test/JDBC/input/BABEL-1435.sql b/contrib/test/JDBC/input/BABEL-1435.sql new file mode 100644 index 00000000000..722e7f22d00 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1435.sql @@ -0,0 +1,112 @@ +-- Test inital databases +SELECT name FROM sys.sysdatabases ORDER BY name; +GO + +SELECT COUNT(*) FROM pg_roles where rolname = 'sysadmin'; +GO + +SELECT COUNT(*) FROM pg_roles where rolname = 'master_dbo'; +SELECT COUNT(*) FROM pg_roles where rolname = 'master_db_owner'; +SELECT COUNT(*) FROM pg_namespace where nspname = 'master_dbo'; +GO + +SELECT COUNT(*) FROM pg_roles where rolname = 'tempdb_dbo'; +SELECT COUNT(*) FROM pg_roles where rolname = 'tempdb_db_owner'; +SELECT COUNT(*) FROM pg_namespace where nspname = 'tempdb_dbo'; +GO + +-- Test Create User Database +CREATE DATABASE db1; +GO + +SELECT name FROM sys.sysdatabases ORDER BY name; +GO + +-- test error +CREATE DATABASE db1; +GO + +SELECT COUNT(*) FROM pg_roles where rolname = 'dbo'; +SELECT COUNT(*) FROM pg_roles where rolname = 'db_owner'; +SELECT COUNT(*) FROM pg_namespace where nspname = 'dbo'; +GO + +CREATE DATABASE db2; +GO + +USE db1; +GO + +SELECT (case when db_id() = db_id('db1') then 'true' else 'false' end) result; +GO + +USE master; +GO + +SELECT (case when db_id() = db_id('master') then 'true' else 'false' end) result; +GO + +-- test error +USE db2; +GO + +DROP DATABASE db1; +GO + +-- Set current_user for testing db mode +IF (SELECT 1 FROM pg_roles WHERE rolname='jdbc_user') = 1 +BEGIN + WITH SET_CTE + AS + (SELECT set_config('role', 'jdbc_user', false)) + SELECT NULL + FROM SET_CTE +END +ELSE +BEGIN + WITH SET_CTE + AS + (SELECT set_config('role', 'babeltestuser', false)) + SELECT NULL + FROM SET_CTE +END +GO + +-- test multi-db mode +SELECT set_config('babelfishpg_tsql.migration_mode', 'multi-db', false); +GO + +SELECT name FROM sys.sysdatabases ORDER BY name; +GO + +CREATE DATABASE db1; +GO + +-- test error +CREATE DATABASE db1; +GO + +CREATE DATABASE db2; +GO + +SELECT COUNT(*) FROM pg_roles where rolname = 'db1_dbo'; +SELECT COUNT(*) FROM pg_roles where rolname = 'db1_db_owner'; +SELECT COUNT(*) FROM pg_namespace where nspname = 'db1_dbo'; +GO + +SELECT COUNT(*) FROM pg_roles where rolname = 'db2_dbo'; +SELECT COUNT(*) FROM pg_roles where rolname = 'db2_db_owner'; +SELECT COUNT(*) FROM pg_namespace where nspname = 'db2_dbo'; +GO + +DROP DATABASE db1; +GO + +DROP DATABASE db2; +GO + +SELECT name FROM sys.sysdatabases ORDER BY name; +GO + +SELECT set_config('babelfishpg_tsql.migration_mode', 'single-db', false); +GO diff --git a/contrib/test/JDBC/input/BABEL-1437.sql b/contrib/test/JDBC/input/BABEL-1437.sql new file mode 100644 index 00000000000..cdf75e5c288 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1437.sql @@ -0,0 +1,46 @@ +-- Test unexist db, expecting null +SELECT db_id('hello'); +SELECT db_name(1234); +GO + +-- Test master and tempdb +SELECT db_id('master'); +SELECT db_name(1); +GO + +USE master; +GO + +SELECT db_id(); +SELECT db_name(); +GO + +SELECT db_id('tempdb') +SELECT db_name(2); +GO + +USE tempdb; +GO +SELECT db_id(); +SELECT db_name(); +GO + +-- Test custom database +CREATE DATABASE babel_1437_db; +GO +USE babel_1437_db; +GO + +SELECT (case when db_name() = 'babel_1437_db' then 'true' else 'false' end) result; +SELECT (case when db_name(db_id()) = 'babel_1437_db' then 'true' else 'false' end) result; +SELECT (case when db_id('babel_1437_db') = db_id() then 'true' else 'false' end) result; +GO + +-- test dropped database, expecting db_id to return null +USE MASTER; +GO + +DROP DATABASE babel_1437_db; +GO +SELECT db_id('babel_1437_db'); +GO diff --git a/contrib/test/JDBC/input/BABEL-1438.sql b/contrib/test/JDBC/input/BABEL-1438.sql new file mode 100644 index 00000000000..18ccec49fc2 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1438.sql @@ -0,0 +1,3 @@ +-- Test with unexist db as param +EXEC sp_helpdb 'hello' +GO diff --git a/contrib/test/JDBC/input/BABEL-1442.sql b/contrib/test/JDBC/input/BABEL-1442.sql new file mode 100644 index 00000000000..c8e89bc582d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1442.sql @@ -0,0 +1,253 @@ +-- Test syntax without escape hatches +CREATE LOGIN r1 WITH PASSWORD = 'abc' MUST_CHANGE, CHECK_EXPIRATION = ON; +GO + +CREATE LOGIN r2 WITH PASSWORD = '123', CREDENTIAL = cred1; +GO + +CREATE LOGIN r3 FROM CERTIFICATE cert1; +GO + +CREATE LOGIN r4 FROM WINDOWS; +GO + +CREATE LOGIN r5 WITH PASSWORD = 'xyz', SID = 0x123ABC; +GO + +CREATE LOGIN r6 +WITH PASSWORD = '1000101', +DEFAULT_DATABASE = master, +CHECK_POLICY = OFF, +CHECK_EXPIRATION = OFF; +GO + +CREATE LOGIN r7 +WITH PASSWORD = 0x789DEF HASHED MUST_CHANGE, +DEFAULT_LANGUAGE = english, +CREDENTIAL = cred2; +GO + +CREATE LOGIN r8 +FROM WINDOWS +WITH DEFAULT_DATABASE = db2, +DEFAULT_LANGUAGE = English; +GO + +CREATE LOGIN r9 FROM ASYMMETRIC KEY asymkey1; +GO + +CREATE LOGIN r10 WITH PASSWORD = N'abc', CHECK_EXPIRATION = ON; +GO + +CREATE LOGIN r10 WITH PASSWORD = N'abc', CREDENTIAL = cred; +GO + +-- Test ALTER LOGIN syntax without escape hatches +ALTER LOGIN r1 WITH PASSWORD = '123' OLD_PASSWORD = 'abc'; +GO + +ALTER LOGIN r1 WITH PASSWORD = 0xABC123 HASHED MUST_CHANGE UNLOCK; +GO + +ALTER LOGIN r1 WITH PASSWORD = '123' MUST_CHANGE; +GO + +ALTER LOGIN r1 WITH PASSWORD = '123' UNLOCK; +GO + +ALTER LOGIN r1 WITH DEFAULT_LANGUAGE = english, +NAME = r5, +CHECK_POLICY = ON, +CHECK_EXPIRATION = ON, +NO CREDENTIAL; +GO + +ALTER LOGIN r1 WITH DEFAULT_LANGUAGE = english, +CHECK_POLICY = ON, +CHECK_EXPIRATION = ON, +NO CREDENTIAL; +GO + +ALTER LOGIN r1 WITH DEFAULT_LANGUAGE = english, +CHECK_EXPIRATION = ON, +NO CREDENTIAL; +GO + +ALTER LOGIN r1 WITH DEFAULT_LANGUAGE = english, +NO CREDENTIAL; +GO + +ALTER LOGIN r2 WITH PASSWORD = 'abc', +CREDENTIAL = cred1; +GO + +ALTER LOGIN r3 ADD CREDENTIAL cred2; +GO + +ALTER LOGIN r3 DROP CREDENTIAL cred2; +GO + +-- Test syntax with escape hatches +SELECT set_config('babelfishpg_tsql.escape_hatch_login_hashed_password', 'ignore', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_old_password', 'ignore', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_password_must_change', 'ignore', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_password_unlock', 'ignore', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_misc_options', 'ignore', 'false') +GO + +CREATE LOGIN r1 WITH PASSWORD = 'abc' MUST_CHANGE, CHECK_EXPIRATION = ON; +GO + +SELECT default_database_name FROM sys.server_principals WHERE name = 'r1'; +GO + +SELECT sys.babelfish_get_login_default_db('r1'); +GO + +CREATE LOGIN r2 WITH PASSWORD = '123', CREDENTIAL = cred1; +GO + +CREATE LOGIN r3 FROM CERTIFICATE cert1; +GO + +CREATE LOGIN r4 FROM WINDOWS; +GO + +CREATE LOGIN r5 WITH PASSWORD = 'xyz', SID = 0x123ABC; +GO + +CREATE LOGIN r6 +WITH PASSWORD = '1000101', +DEFAULT_DATABASE = master, +CHECK_POLICY = OFF, +CHECK_EXPIRATION = OFF; +GO + +SELECT default_database_name FROM sys.server_principals WHERE name = 'r6'; +GO + +SELECT sys.babelfish_get_login_default_db('r6'); +GO + +CREATE LOGIN r7 +WITH PASSWORD = 0x789DEF HASHED MUST_CHANGE, +DEFAULT_LANGUAGE = english, +CREDENTIAL = cred2; +GO + +CREATE LOGIN r8 +FROM WINDOWS +WITH DEFAULT_DATABASE = db2, +DEFAULT_LANGUAGE = English; +GO + +CREATE LOGIN r9 FROM ASYMMETRIC KEY asymkey1; +GO + +CREATE LOGIN r10 WITH PASSWORD = N'abc'; +GO + +CREATE LOGIN r11 WITH PASSWORD = N'abc' MUST_CHANGE, CHECK_EXPIRATION = ON; +GO + +-- Test ALTER syntax +ALTER LOGIN r1 ENABLE; +GO + +ALTER LOGIN r1 DISABLE; +GO + +ALTER LOGIN r1 WITH PASSWORD = '123'; +GO + +ALTER LOGIN r1 WITH PASSWORD = '123' OLD_PASSWORD = 'abc'; +GO + +ALTER LOGIN r1 WITH PASSWORD = 0xABC123 HASHED MUST_CHANGE UNLOCK; +GO + +ALTER LOGIN r1 WITH DEFAULT_DATABASE = tempdb; +GO + +SELECT default_database_name FROM sys.server_principals WHERE name = 'r1'; +GO + +SELECT sys.babelfish_get_login_default_db('r1'); +GO + +ALTER LOGIN r1 WITH DEFAULT_LANGUAGE = english, +NAME = r10, +CHECK_POLICY = ON, +CHECK_EXPIRATION = ON, +NO CREDENTIAL; +GO + +ALTER LOGIN r2 WITH PASSWORD = 'abc' UNLOCK, +DEFAULT_LANGUAGE = english, +NAME = r10, +CHECK_POLICY = ON, +CHECK_EXPIRATION = ON, +CREDENTIAL = cred1; +GO + +ALTER LOGIN r3 ADD CREDENTIAL cred2; +GO + +ALTER LOGIN r3 DROP CREDENTIAL cred2; +GO + +ALTER LOGIN r10 WITH PASSWORD = N'123'; +GO + +ALTER LOGIN r11 WITH PASSWORD = N'123' OLD_PASSWORD = N'abc'; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_login_hashed_password', 'strict', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_old_password', 'strict', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_password_must_change', 'strict', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_password_unlock', 'strict', 'false') +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_login_misc_options', 'strict', 'false') +GO + +-- Expect errors. Try creating login with non-existent db +CREATE LOGIN r12 WITH PASSWORD = '123', +DEFAULT_DATABASE = zzz; +GO + +ALTER LOGIN r1 WITH DEFAULT_DATABASE = zzz; +GO + +-- Clean up +DROP LOGIN r1; +GO +DROP LOGIN r2; +GO +DROP LOGIN r3; +GO +DROP LOGIN r4; +GO +DROP LOGIN r5; +GO +DROP LOGIN r6; +GO +DROP LOGIN r7; +GO +DROP LOGIN r8; +GO +DROP LOGIN r9; +GO +DROP LOGIN r10; +GO +DROP LOGIN r11; +GO + +SELECT sys.babelfish_get_login_default_db('r1'); +GO diff --git a/contrib/test/JDBC/input/BABEL-1444.sql b/contrib/test/JDBC/input/BABEL-1444.sql new file mode 100644 index 00000000000..5ab60ceaa63 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1444.sql @@ -0,0 +1,110 @@ +USE MASTER; +GO + +DECLARE @usr CHAR(30) +DECLARE @cur_usr CHAR(30) +SET @usr = user +SET @cur_usr = current_user +SELECT 'user: '+ @usr +SELECT 'current_user: '+ @cur_usr +GO + +CREATE TABLE dbo.t1 +(id INT IDENTITY(100, 1) NOT NULL, + description VARCHAR(30) NOT NULL, + usr VARCHAR(30) NOT NULL DEFAULT USER, + cur_usr VARCHAR(30) NOT NULL DEFAULT CURRENT_USER); +GO + +INSERT INTO dbo.t1 (description) VALUES ('Orange'); +INSERT INTO dbo.t1 (description) VALUES ('Blue'); +INSERT INTO dbo.t1 (description, usr) VALUES ('Green', 'Bob'); +INSERT INTO dbo.t1 (description, cur_usr) VALUES ('Purple', 'Alice'); +INSERT INTO dbo.t1 (description, usr, cur_usr) VALUES ('Red', 'Mike', 'Dave'); +GO + +SELECT * FROM dbo.t1 ORDER BY id; +GO + +DROP TABLE dbo.t1; +GO + +-- Test properties after USE +CREATE DATABASE db1; +GO + +SELECT current_setting('role'); +GO +SELECT current_setting('search_path'); +GO +SELECT session_user, current_user, user; +GO +SELECT user_name(); +GO + +USE db1; +GO + +SELECT current_setting('role'); +GO +SELECT current_setting('search_path'); +GO +SELECT session_user, current_user, user; +GO +SELECT user_name(); +GO + +-- Error: Test DROP +DROP DATABASE db1; +GO + +SELECT current_setting('role'); +GO +SELECT current_setting('search_path'); +GO +SELECT session_user, current_user, user; +GO +SELECT user_name(); +GO + +-- Test DROP when using another database +USE MASTER; +GO + +SELECT current_setting('role'); +GO +SELECT current_setting('search_path'); +GO +SELECT session_user, current_user, user; +GO +SELECT user_name(); +GO + +DROP DATABASE db1; +GO + +SELECT current_setting('role'); +GO +SELECT current_setting('search_path'); +GO +SELECT session_user, current_user, user; +GO +SELECT user_name(); +GO + +-- Test CREATE +CREATE DATABASE db1; +GO + +SELECT current_setting('role'); +GO +SELECT current_setting('search_path'); +GO +SELECT session_user, current_user, user; +GO +SELECT user_name(); +GO + +-- Clean up +DROP DATABASE db1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1446.sql b/contrib/test/JDBC/input/BABEL-1446.sql new file mode 100644 index 00000000000..9603dd2cccf --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1446.sql @@ -0,0 +1,93 @@ +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'db_owner'); +GO + +CREATE DATABASE db1; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'db_owner'); +GO + +CREATE LOGIN login1 WITH PASSWORD = '123'; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'login1'); +GO + +CREATE LOGIN login2 WITH PASSWORD = 'abc'; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'login2'); +GO + +DROP LOGIN login1; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'login1'); +GO + +DROP LOGIN login2; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'login2'); +GO + +DROP DATABASE db1; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'db_owner'); +GO + +-- test multi-db mode +SELECT set_config('role', 'jdbc_user', false); +GO +SELECT set_config('babelfishpg_tsql.migration_mode', 'multi-db', false); +GO + +CREATE DATABASE db1; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'db1_guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'db1_db_owner'); +GO + +CREATE DATABASE db2; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'db2_guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'db2_db_owner'); +GO + +DROP DATABASE db1; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'db1_guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'db1_db_owner'); +GO + +DROP DATABASE db2; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'db2_guest') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'db2_db_owner'); +GO + +SELECT set_config('babelfishpg_tsql.migration_mode', 'single-db', false); +GO diff --git a/contrib/test/JDBC/input/BABEL-1454.mix b/contrib/test/JDBC/input/BABEL-1454.mix new file mode 100644 index 00000000000..175f87ea4cb --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1454.mix @@ -0,0 +1,273 @@ +-- psql +ALTER SYSTEM SET babelfishpg_tsql.migration_mode = 'multi-db'; +ALTER SYSTEM SET babelfishpg_tsql.allow_antlr_to_unsupported_grammar_for_testing = true; +SELECT pg_reload_conf(); +GO + +-- tsql +CREATE DATABASE db1; +GO + +USE db1; +GO + +-- Create Basic Table +CREATE SCHEMA babel1454; +GO + +SELECT nspname FROM pg_namespace WHERE nspname = 'db1_babel1454'; +GO + +CREATE TABLE babel1454.t1 ( a int, b int ); +GO + + +-- Test Insert + +INSERT INTO db1.babel1454.t1 VALUES ( 1, 2); +GO + +INSERT INTO babel1454.t1 VALUES (2, 3); +GO + +INSERT INTO babel1454.t1 SELECT a + 10, b + 10 from db1.babel1454.t1; +GO + +SELECT * FROM db1.babel1454.t1 ORDER BY 1,2; +GO + +-- Test Delete +DELETE FROM db1.babel1454.t1 where babel1454.t1.b = 2; +GO + +DELETE FROM babel1454.t1 where db1.babel1454.t1.a = 2; +GO + +SELECT * FROM db1.babel1454.t1 ORDER BY 1,2; +GO + +-- Test Update +UPDATE babel1454.t1 SET a = babel1454.t1.a - 10, b = b - 10 WHERE db1.babel1454.t1.b = 12; +GO + +UPDATE babel1454.t1 SET a = db1.babel1454.t1.a - 10, b = db1.babel1454.t1.b - 10 WHERE babel1454.t1.b = 13; +GO + +SELECT * FROM db1.babel1454.t1 ORDER BY 1,2; +GO + +-- Test Select +SELECT db1.babel1454.t1.a , babel1454.t1.b FROM babel1454.t1 ORDER BY babel1454.t1.a, db1.babel1454.t1.b; +GO + +WITH cte as ( +SELECT db1.babel1454.t1.a , babel1454.t1.b FROM babel1454.t1 ORDER BY babel1454.t1.a, db1.babel1454.t1.b +) +SELECT * FROM cte; +GO + +SELECT distinct(db1.babel1454.t1.a) FROM babel1454.t1 ORDER BY 1; +GO + +SELECT sum(db1.babel1454.t1.a) FROM babel1454.t1 GROUP BY babel1454.t1.b ORDER BY 1; +GO + +SELECT COUNT(*) FROM db1.babel1454.t1 inner join babel1454.t1 as t2 on db1.babel1454.t1.a = t2.a; +GO + +-- Alter table stmt +ALTER TABLE db1.babel1454.t1 ADD c int; +GO + +ALTER TABLE babel1454.t1 ADD d int; +GO + +-- CRANT (error message will tell if mapping is correct ) +GRANT ALL ON db1.babel1454.t2 TO PUBLIC; +GO + +GRANT ALL ON babel1454.t2 TO PUBLIC; +GO + +GRANT ALL ON db1.babel1454.foo TO PUBLIC; +GO + +GRANT ALL ON babel1454.foo TO PUBLIC; +GO + +GRANT ALL ON db1.babel1454.foo TO PUBLIC; +GO + +GRANT ALL ON babel1454.foo TO PUBLIC; +GO + +GRANT ALL ON dummy_schema TO PUBLIC; +GO + +GRANT ALL ON db1.dummy_schema TO PUBLIC; +GO + +GRANT ALL ON babel1454.dummy_type TO PUBLIC; +GO + +GRANT ALL ON db1.babel1454.dummy_type TO PUBLIC; +GO + +--GRANT ALL ON ALL TABLES IN SCHEMA dummy_schema TO DUMMY; +--GO + +-- DROP stmt +DROP TABLE db1.dummy_schema.t2; +GO + +DROP TABLE dummy_schema.t2; +GO + +DROP SCHEMA dummy_schema; +GO + +-- Trigger +CREATE TRIGGER tri on db1.babel1454.t1 AFTER INSERT +AS BEGIN +END; +GO + +CREATE TRIGGER tri2 on babel1454.t1 AFTER INSERT +AS BEGIN +END; +GO + +DROP TRIGGER tri; +GO + +DROP TRIGGER tri2; +GO + +-- Truncate +TRUNCATE TABLE db1.babel1454.t1; +GO + +TRUNCATE TABLE babel1454.t1; +GO + +-- Index +CREATE INDEX babel1454_t1_i1 ON db1.babel1454.t1 ( a , b ); +GO + +CREATE INDEX babel1454_t1_i2 ON db1.babel1454.t1 ( a ); +GO + +-- Create Function + +-- CREATE FUNCTION doesn't allow to use [dbname]. +--CREATE FUNCTION db1.babel1454.foo() RETURNS INT AS BEGIN RETURN 0; END +--GO + +CREATE FUNCTION babel1454.foo2() RETURNS INT AS BEGIN RETURN 0; END +GO + +-- ALTER FUNCTION doesn't allow to use [dbname]. +--ALTER FUNCTION db1.babel1454.foo2() IMMUTABLE; +--GO + +-- We don't support T-SQL ALTER FUNCTION properly +--ALTER FUNCTION babel1454.foo() IMMUTABLE; +--GO + +-- DROP FUNCTION doesn't allow to use [dbname]. +--DROP FUNCTION db1.babel1454.foo(); +--GO + +DROP FUNCTION babel1454.foo2; +GO + +-- Rename +-- ALTER ... RENAME is not valid in T-SQL + +--ALTER FUNCTION db1.dummy.foo() RENAME TO foo2; +--GO + +--ALTER FUNCTION dummy.foo RENAME TO foo2; +--GO + +--ALTER SCHEMA dummy RENAME TO DUMMY2; +--GO + +--ALTER TABLE db1.dummy.t1 RENAME TO dummy; +--GO + +--ALTER TABLE dummy.t1 RENAME TO dummy; +--GO + +-- Create View +CREATE VIEW babel1454.v1 AS SELECT db1.babel1454.t1.b from babel1454.t1; +GO + +DROP VIEW babel1454.v1; +GO + +-- CREATE SEQUENCE doesn't allow to use [dbname]. +--CREATE SEQUENCE db1.babel1454.seq1; +--GO + +CREATE SEQUENCE babel1454.seq2; +GO + +-- ALTER SEQUENCE doesn't allow to use [dbname]. +--ALTER SEQUENCE db1.babel1454.seq2 RESTART WITH 2; +--GO + +ALTER SEQUENCE babel1454.seq2 RESTART WITH 2; +GO + +-- Alter owner +-- ALTER ... OWNER is not valid in T-SQL + +--ALTER PROCEDURE db1.dummy.foo() OWNER TO DUMMY; +--GO + +--ALTER PROCEDURE dummy.foo OWNER TO DUMMY; +--GO + +--ALTER TABLE dummy.foo OWNER TO DUMMY; +--GO + +--ALTER TABLE db1.dummy.foo OWNER TO DUMMY; +--GO + +--ALTER SCHEMA dummy2 OWNER TO jdbc_test; +--GO + +-- Create Statistics +-- CREATE STATISTICS in T-SQL and postgres is not compatible + +--CREATE STATISTICS mystat ON a,b FROM db1.dummy.t1; +--GO + +--CREATE STATISTICS mystat2 ON b,b FROM dummy.t1; +--GO + +-- CALL +EXEC dummy.foo +GO + +EXEC db1.dummy.foo +GO + +--- Cleanup +DROP TABLE babel1454.t1; +DROP SEQUENCE babel1454.seq2; +DROP SCHEMA babel1454; +GO + +USE MASTER; +GO + +DROP DATABASE db1; +GO + +-- psql +ALTER SYSTEM SET babelfishpg_tsql.migration_mode = 'single-db'; +ALTER SYSTEM SET babelfishpg_tsql.allow_antlr_to_unsupported_grammar_for_testing = false; +SELECT pg_reload_conf(); +GO diff --git a/contrib/test/JDBC/input/BABEL-1465.sql b/contrib/test/JDBC/input/BABEL-1465.sql new file mode 100644 index 00000000000..61becb2a4a3 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1465.sql @@ -0,0 +1,258 @@ +-- BABEL-1645 +-- CAST is VOLATILE if one of these conditions exists: +-- 1. Source type is sql_variant. +-- 2. Target type is sql_variant and its source type is nondeterministic. +-- 3. Source or target type is datetime/datetime2/smalldatetime, the other source or target type is a character string/sql_variant/other datetime types. + +-- VOLATILE functions can not be used to generate persisted computed columns, the following +-- tests are based on this rule. + +create table t1 (id int, a varchar(10)); +GO + +-- CAST from char to varchar is immutable/deterministic and can be used to generate computed column +alter table t1 add b as cast(cast('01-01-2012' as char(10)) as varchar(10)) persisted; +GO + +-- CAST from sql_variant to other type is VOLATILE and is not allowed to generate computed column +alter table t1 add c as cast(cast('01-01-2012' as sql_variant) as varchar(10)) persisted; +GO + +-- CAST to sql_variant from VOLATILE source type is also VOLATILE +alter table t1 add c as cast(GETDATE() as sql_variant) persisted; +GO + +-- CAST to sql_variant from datetime is VOLATILE +alter table t1 add c as cast(cast('01-01-2012' as datetime) as sql_variant) persisted; +GO + +-- CAST to sql_variant from smalldatetime is VOLATILE +alter table t1 add c as cast(cast('01-01-2012' as smalldatetime) as sql_variant) persisted; +GO + +-- CAST from datetime/datetime2/smalldatetime to character string type/other datetime typee is VOLATILE +alter table t1 add c as cast(cast('01-01-2012' as datetime) as varchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime) as nvarchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime) as char(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime) as nchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime) as date) persisted; +GO + +alter table t1 add c as cast(cast('01:01' as datetime) as time) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime2) as varchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime2) as nvarchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime2) as char(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime2) as nchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as datetime2) as date) persisted; +GO + +alter table t1 add c as cast(cast('01:01' as datetime2) as time) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as smalldatetime) as varchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as smalldatetime) as nvarchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as smalldatetime) as char(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as smalldatetime) as nchar(10)) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as smalldatetime) as date) persisted; +GO + +alter table t1 add c as cast(cast('01:01' as smalldatetime) as time) persisted; +GO + +-- CAST to datetime/datetime2/smalldatetime from character string type/datetime type is VOLATILE +alter table t1 add c as cast(cast('01-01-2012' as varchar(10)) as datetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as nvarchar(10)) as datetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as char(10)) as datetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as nchar(10)) as datetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as date) as datetime) persisted; +GO + +alter table t1 add c as cast(cast('01:01' as time) as datetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as varchar(10)) as datetime2) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as nvarchar(10)) as datetime2) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as char(10)) as datetime2) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as nchar(10)) as datetime2) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as date) as datetime2) persisted; +GO + +alter table t1 add c as cast(cast('01:01' as time) as datetime2) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as varchar(10)) as smalldatetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as nvarchar(10)) as smalldatetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as char(10)) as smalldatetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as nchar(10)) as smalldatetime) persisted; +GO + +alter table t1 add c as cast(cast('01-01-2012' as date) as smalldatetime) persisted; +GO + +alter table t1 add c as cast(cast('01:01' as time) as smalldatetime) persisted; +GO + +-- Test cast between datetime/smalldatetime and varchar/nvarchar/char/nchar since we +-- added the cast functions in this patch +select cast(cast('01-01-2012' as datetime) as varchar(10)); +GO +select cast(cast('01-01-2012' as datetime) as varchar(5)); +GO + +select cast(cast('01-01-2012' as datetime) as nvarchar(10)); +GO +select cast(cast('01-01-2012' as datetime) as nvarchar(5)); +GO + +select cast(cast('01-01-2012' as datetime) as char(10)); +GO +select cast(cast('01-01-2012' as datetime) as char(5)); +GO + +select cast(cast('01-01-2012' as datetime) as nchar(10)); +GO +select cast(cast('01-01-2012' as datetime) as nchar(5)); +GO + +select cast(cast('01-01-2012' as varchar(10)) as datetime); +GO + +select cast(cast('01-01-2012' as nvarchar(10)) as datetime); +GO + +select cast(cast('01-01-2012' as char(10)) as datetime); +GO + +select cast(cast('01-01-2012' as nchar(10)) as datetime); +GO + +-- test cast between datetime2 and varchar/char... +select cast(cast('01-01-2012' as datetime2) as varchar(10)); +GO +select cast(cast('01-01-2012' as datetime2) as varchar(5)); +GO + +select cast(cast('01-01-2012' as datetime2) as nvarchar(10)); +GO +select cast(cast('01-01-2012' as datetime2) as nvarchar(5)); +GO + +select cast(cast('01-01-2012' as datetime2) as char(10)); +GO +select cast(cast('01-01-2012' as datetime2) as char(5)); +GO + +select cast(cast('01-01-2012' as datetime2) as nchar(10)); +GO +select cast(cast('01-01-2012' as datetime2) as nchar(5)); +GO + +select cast(cast('01-01-2012' as varchar(10)) as datetime2); +GO + +select cast(cast('01-01-2012' as nvarchar(10)) as datetime2); +GO + +select cast(cast('01-01-2012' as char(10)) as datetime2); +GO + +select cast(cast('01-01-2012' as nchar(10)) as datetime2); +GO + +-- test cast between smalldatetime and varchar/char... +select cast(cast('01-01-2012' as smalldatetime) as varchar(10)); +GO +select cast(cast('01-01-2012' as smalldatetime) as varchar(5)); +GO + +select cast(cast('01-01-2012' as smalldatetime) as nvarchar(10)); +GO +select cast(cast('01-01-2012' as smalldatetime) as nvarchar(5)); +GO + +select cast(cast('01-01-2012' as smalldatetime) as char(10)); +GO +select cast(cast('01-01-2012' as smalldatetime) as char(5)); +GO + +select cast(cast('01-01-2012' as smalldatetime) as nchar(10)); +GO +select cast(cast('01-01-2012' as smalldatetime) as nchar(5)); +GO + +select cast(cast('01-01-2012' as varchar(10)) as smalldatetime); +GO + +select cast(cast('01-01-2012' as nvarchar(10)) as smalldatetime); +GO + +select cast(cast('01-01-2012' as char(10)) as smalldatetime); +GO + +select cast(cast('01-01-2012' as nchar(10)) as smalldatetime); +GO + +-- TODO BABEL-1624: CAST from datetime/smalldatetime to text/ntext type should not be allowed +-- alter table t1 add c as cast(cast('01-01-2012' as datetime) as text) persisted; +-- GO + +-- alter table t1 add d as cast(cast('01-01-2012' as datetime) as ntext) persisted; +-- GO + +-- alter table t1 add c as cast(cast('01-01-2012' as smalldatetime) as text) persisted; +-- GO + +-- alter table t1 add d as cast(cast('01-01-2012' as smalldatetime) as ntext) persisted; +-- GO + +-- clean up +drop table t1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1466.sql b/contrib/test/JDBC/input/BABEL-1466.sql new file mode 100644 index 00000000000..a8497161c39 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1466.sql @@ -0,0 +1,73 @@ +-- BABEL-1466 +-- CONVERT is VOLATILE if one of these conditions exists: +-- 1. Source type is sql_variant. +-- 2. Target type is sql_variant and its source type is nondeterministic. +-- 3. Source or target type is datetime/datetime2/smalldatetime, the other source or target type is a character string/sql_variant/other datetime types. + +-- VOLATILE functions can not be used to generate persisted computed columns, the following +-- tests are based on this rule. + +create table t1 (id int, a varchar(10), t datetime); +GO + +-- CONVERT from int to smallint is immutable/deterministic and can be used to generate computed column +alter table t1 add b as convert(smallint, 1) persisted; +GO + +-- TODO CONVERT from int to string type is immutable and can be used to generate computed column +-- This is not straightforward to change atm because we currently use a wrapper function babelfish_conv_helper_to_varchar +-- to implement convert to varchar (in order to support the style parameter in convert), +-- and the function is defined as either VOLAITLE or IMMUTABLE (can't be IMMUTABLE conditionally) +-- However, in SQL Server convert to varchar is IMMUTABLE conditionally. +alter table t1 add c as convert(varchar(2), 1) persisted; +GO + +-- CONVERT from sql_variant to other types are volatile and can not be used to generate computed column +alter table t1 add d as convert(smallint, convert(sql_variant, 1)) persisted; +GO + +-- CONVERT from datetime/datetime2/smalldatetime to string type is volatile and can not be used to generate computed column +alter table t1 add d as convert(varchar(15), convert(datetime, '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(char(15), convert(datetime, '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(varchar(15), convert(datetime2, '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(char(15), convert(datetime2, '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(varchar(15), convert(smalldatetime, '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(char(15), convert(smalldatetime, '01-01-2012')) persisted; +GO + +-- CONVERT from datetime column to string type is volatile and can not be used to generate computed column +alter table t1 add d as convert(varchar(15), t) persisted; +GO + +-- CONVERT from string type to datetime/datetime2/smalldatetime is volatile and can not be used to generate computed column +alter table t1 add d as convert(datetime, convert(varchar(15), '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(datetime, convert(char(15), '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(datetime2, convert(varchar(15), '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(datetime2, convert(char(15), '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(smalldatetime, convert(varchar(15), '01-01-2012')) persisted; +GO + +alter table t1 add d as convert(smalldatetime, convert(char(15), '01-01-2012')) persisted; +GO + +-- Clean up +drop table t1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1475.sql b/contrib/test/JDBC/input/BABEL-1475.sql new file mode 100644 index 00000000000..2e43e677dab --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1475.sql @@ -0,0 +1,109 @@ +-- Test DAY function for computed columns +CREATE TABLE dateFunctions (dt1 datetime2, dt2 datetimeoffset(6), day1 as DAY(dt1), day2 as DAY(dt2)); +INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10.1111111', '1912-10-25 12:24:32 +10:0'); +SELECT day1, day2 from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test MONTH function for computed columns +CREATE TABLE dateFunctions (dt1 datetime2, dt2 datetimeoffset(6), month1 as MONTH(dt1), month2 as MONTH(dt2)); +INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10.1111111', '1912-10-25 12:24:32 +10:0'); +SELECT month1, month2 from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test YEAR function for computed columns +CREATE TABLE dateFunctions (dt1 datetime2, dt2 datetimeoffset(6), year1 as YEAR(dt1), year2 as YEAR(dt2)); +INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10.1111111', '1912-10-25 12:24:32 +10:0'); +SELECT year1, year2 from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test DATEADD function for computed columns +-- WRONG OUTPUT with datetimeoffset +-- CREATE TABLE dateFunctions (dt1 datetime2, dt2 datetimeoffset(6), addMonthInDate1 as DATEADD(month,1,dt1), addMonthInDate2 as DATEADD(month,1,dt2)); +-- INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10.1111111', '1912-10-25 12:24:32 +10:0'); +-- SELECT addMonthInDate1, addMonthInDate2 from dateFunctions; +-- DROP TABLE dateFunctions; +-- GO + +-- Test DATEDIFF function with DATE datatype for computed columns +CREATE TABLE dateFunctions (dt1 date, dt2 date, diffMonthInDates as DATEDIFF(month,dt1,dt2)); +INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01', '1912-10-25'); +SELECT diffMonthInDates from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test DATEDIFF function with DATETIME2 datatype for computed columns +CREATE TABLE dateFunctions (dt1 datetime2, dt2 datetime2, diffMonthInDates as DATEDIFF(month,dt1,dt2)); +INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10', '1912-10-25 12:24:32'); +SELECT diffMonthInDates from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test DATEDIFF function with DATETIMEOFFSET datatype for computed columns +-- CREATE TABLE dateFunctions (dt1 datetimeoffset(6), dt2 datetimeoffset(6), diffMonthInDates as DATEDIFF(month,dt1,dt2)); +-- INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10', '1912-10-25 12:24:32'); +-- SELECT diffMonthInDates from dateFunctions; +-- DROP TABLE dateFunctions; +-- GO + +-- Test DATEFROMPARTS function for computed columns +CREATE TABLE dateFunctions (year int, month int, day int, dateresult as DATEFROMPARTS(year, month, day)); +INSERT INTO dateFunctions (year, month, day) values (1912, 10, 25); +SELECT dateresult from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test DATENAME function for computed columns +CREATE TABLE dateFunctions (dt date, year as DATENAME(year, dt), month as DATENAME(month, dt), weekday as DATENAME(dow, dt), dayofyear as DATENAME(dayofyear, dt), day as DATENAME(day, dt)); +INSERT INTO dateFunctions (dt) values ('1912-10-25'); +SELECT year, month, weekday, dayofyear, day from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test DATEPART function for computed columns +CREATE TABLE dateFunctions (dt1 datetime2, dt2 datetimeoffset(6), datepart1 as DATEPART(month, dt1), datepart2 as DATEPART(month, dt2)); +INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10.111111', '1912-10-25 12:24:32 +10:0'); +SELECT datepart1, datepart2 from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test DATEPART function for computed columns +CREATE TABLE dateFunctions (dt1 datetime2, dt2 datetimeoffset(6), datepart1 as DATEPART(dow, dt1), datepart2 as DATEPART(dow, dt2)); +INSERT INTO dateFunctions (dt1, dt2) values ('2007-01-01 13:10:10.1111111', '1912-10-25 12:24:32 +10:0'); +SELECT datepart1, datepart2 from dateFunctions; +DROP TABLE dateFunctions; +GO + +-- Test DATETIME2FROMPARTS function with numeric arguments for computed columns +-- WRONG OUTPUT +-- CREATE TABLE dateFunctions (year int, month int, day int, hour int, minute int, seconds int, fractions int, precision int, dateresult as DATETIME2FROMPARTS (year, month, day, hour, minute, seconds, fractions, precision)); +-- INSERT INTO dateFunctions (year, month, day, hour, minute, seconds, fractions, precision) values (2011, 8, 15, 14, 23, 44, 5, 1 ); +-- SELECT dateresult from dateFunctions; +-- DROP TABLE dateFunctions; +-- GO + +-- Test DATETIME2FROMPARTS function with textual arguments for computed columns +-- WRONG OUTPUT +-- CREATE TABLE dateFunctions (year text, month text, day text, hour text, minute text, seconds text, fractions text, precision text, dateresult as DATETIME2FROMPARTS (year, month, day, hour, minute, seconds, fractions, precision)); +-- INSERT INTO dateFunctions (year, month, day, hour, minute, seconds, fractions, precision) values ('2011', '8', '15', '14', '23', '44', '5', '1'); +-- SELECT dateresult from dateFunctions; +-- DROP TABLE dateFunctions; +-- GO + +-- Test DATETIMEFROMPARTS function with numeric arguments for computed columns +-- WRONG OUTPUT +-- CREATE TABLE dateFunctions (year int, month int, day int, hour int, minute int, seconds int, milliseconds int, dateresult as DATETIMEFROMPARTS (year, month, day, hour, minute, seconds, milliseconds)); +-- INSERT INTO dateFunctions (year, month, day, hour, minute, seconds, milliseconds) values (2010, 12, 31, 23, 59, 59, 456); +-- SELECT dateresult from dateFunctions; +-- DROP TABLE dateFunctions; +-- GO + +-- Test DATETIMEFROMPARTS function with textual arguments for computed columns +-- WRONG OUTPUT +-- CREATE TABLE dateFunctions (year text, month text, day text, hour text, minute text, seconds text, milliseconds text, dateresult as DATETIMEFROMPARTS (year, month, day, hour, minute, seconds, milliseconds)); +-- INSERT INTO dateFunctions (year, month, day, hour, minute, seconds, milliseconds) values ('2010', '12', '31', '23', '59', '59', '456'); +-- SELECT dateresult from dateFunctions; +-- DROP TABLE dateFunctions; +-- GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1481.sql b/contrib/test/JDBC/input/BABEL-1481.sql new file mode 100644 index 00000000000..54c373a586c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1481.sql @@ -0,0 +1,83 @@ +CREATE TABLE babel_1481_t(a VARCHAR(8000)) +GO + +INSERT INTO babel_1481_t VALUES ('Test Value 1'), ('Test Value 2'); +GO + +SELECT * FROM babel_1481_t; +GO + +DROP TABLE babel_1481_t; +GO + +CREATE TABLE babel_1481_t(a CHAR(8000)); +GO + +INSERT INTO babel_1481_t VALUES ('Test Value 1'), ('Test Value 2'); +GO + +SELECT * FROM babel_1481_t; +GO + +DROP TABLE babel_1481_t; +GO + +CREATE TABLE babel_1481_t(a VARCHAR(8001)) +GO + +CREATE TABLE babel_1481_t(a CHAR(8001)); +GO + +CREATE TABLE babel_1481_t(a NVARCHAR(4000)) +GO + +INSERT INTO babel_1481_t VALUES ('Test Value 1'), ('Test Value 2'); +GO + +SELECT * FROM babel_1481_t; +GO + +DROP TABLE babel_1481_t; +GO + +CREATE TABLE babel_1481_t(a NCHAR(4000)) +GO + +INSERT INTO babel_1481_t VALUES ('Test Value 1'), ('Test Value 2'); +GO + +SELECT * FROM babel_1481_t; +GO + +DROP TABLE babel_1481_t; +GO + +CREATE TABLE babel_1481_t(a NVARCHAR(4001)) +GO + +CREATE TABLE babel_1481_t(a NCHAR(4001)) +GO + +SELECT cast(12 as VARCHAR(8000)) +GO + +SELECT cast(12 as CHAR(8000)) +GO + +SELECT cast(12 as NVARCHAR(4000)) +GO + +SELECT cast(12 as NCHAR(4000)) +GO + +SELECT cast(12 as VARCHAR(8001)) +GO + +SELECT cast(12 as CHAR(8001)) +GO + +SELECT cast(12 as NVARCHAR(4001)) +GO + +SELECT cast(12 as NCHAR(4001)) +GO diff --git a/contrib/test/JDBC/input/BABEL-1499.sql b/contrib/test/JDBC/input/BABEL-1499.sql new file mode 100644 index 00000000000..a01a9abe8d2 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1499.sql @@ -0,0 +1,25 @@ +-- implict castings +DECLARE @a binary(10); SET @a = CAST('21' AS char(10)); SELECT @a +go + +DECLARE @a binary(10); SET @a = CAST('21' AS varchar(10)); SELECT @a +go + +DECLARE @a varbinary(10); SET @a = CAST('21' AS char(10)); SELECT @a +go + +DECLARE @a varbinary(10); SET @a = CAST('21' AS varchar(10)); SELECT @a +go + +-- explicit castings +DECLARE @a binary(10); SET @a = CONVERT(binary(10), CAST('21' AS char(10))); SELECT @a +go + +DECLARE @a binary(10); SET @a = CONVERT(binary(10), CAST('21' AS varchar(10))); SELECT @a +go + +DECLARE @a varbinary(10); SET @a = CONVERT(varbinary(10), CAST('21' AS char(10))); SELECT @a +go + +DECLARE @a varbinary(10); SET @a = CONVERT(varbinary(10), CAST('21' AS varchar(10))); SELECT @a +go diff --git a/contrib/test/JDBC/input/BABEL-1502.sql b/contrib/test/JDBC/input/BABEL-1502.sql new file mode 100644 index 00000000000..24ac16485ac --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1502.sql @@ -0,0 +1,22 @@ +CREATE TABLE BABEL_1502_t( + Id nvarchar(100) NOT NULL, + Gastly nvarchar(100) NULL, + FawfulId nvarchar(100) NOT NULL, + SwankyId nvarchar(100) NOT NULL, + WrinklyId nvarchar(100) NOT NULL, + Sandshrew int NULL, + Charmeleon int NULL, + Omastar nvarchar(100) NULL, + KamekId nvarchar(100) NOT NULL, + ThwompId nvarchar(100) NOT NULL, + Uuid uniqueidentifier NOT NULL, + UpdatedAt datetime NOT NULL, + ValidatedAt datetime NULL, + PublishedAt datetime NULL, + ImportedAt datetime NULL, + DistributedAt datetime NULL, + Mankey int NOT NULL, + FuzzyId nvarchar(100) NULL, + UpdatedBy varchar(200) NULL, +CONSTRAINT PK_Sylux PRIMARY KEY (Id) +go diff --git a/contrib/test/JDBC/input/BABEL-1510.sql b/contrib/test/JDBC/input/BABEL-1510.sql new file mode 100644 index 00000000000..04ad8d0276e --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1510.sql @@ -0,0 +1,74 @@ +USE master +GO + +create table babel_1510_t(dto datetimeoffset, dt2 datetime2, dt datetime, sdt smalldatetime, d date, t time); +go + +insert into babel_1510_t values ('2021-05-01 11:11:11.111', '2021-05-02 22:22:22.222', '2021-05-03 11:33:33.333', '2021-05-04 22:44:44.444', '2021-05-05', '11:55:55.555'); +go + +select datediff(dd, dto, dt2) from babel_1510_t; +go +select datediff(dd, dto, dt) from babel_1510_t; +go +select datediff(dd, dto, sdt) from babel_1510_t; +go +select datediff(dd, dto, d) from babel_1510_t; +go +select datediff(dd, dto, t) from babel_1510_t; +go + +select datediff(dd, dt2, dto) from babel_1510_t; +go +select datediff(dd, dt2, dt2) from babel_1510_t; +go +select datediff(dd, dt2, sdt) from babel_1510_t; +go +select datediff(dd, dt2, d) from babel_1510_t; +go +select datediff(dd, dt2, t) from babel_1510_t; +go + +select datediff(dd, dt, dto) from babel_1510_t; +go +select datediff(dd, dt, dt2) from babel_1510_t; +go +select datediff(dd, dt, sdt) from babel_1510_t; +go +select datediff(dd, dt, d) from babel_1510_t; +go +select datediff(dd, dt, t) from babel_1510_t; +go + +select datediff(dd, sdt, dto) from babel_1510_t; +go +select datediff(dd, sdt, dt2) from babel_1510_t; +go +select datediff(dd, sdt, dt) from babel_1510_t; +go +select datediff(dd, sdt, d) from babel_1510_t; +go +select datediff(dd, sdt, t) from babel_1510_t; +go + +select datediff(dd, d, dto) from babel_1510_t; +go +select datediff(dd, d, dt2) from babel_1510_t; +go +select datediff(dd, d, dt) from babel_1510_t; +go +select datediff(dd, d, sdt) from babel_1510_t; +go +select datediff(dd, d, t) from babel_1510_t; +go + +select datediff(dd, t, dto) from babel_1510_t; +go +select datediff(dd, t, dt2) from babel_1510_t; +go +select datediff(dd, t, dt) from babel_1510_t; +go +select datediff(dd, t, sdt) from babel_1510_t; +go +select datediff(dd, t, d) from babel_1510_t; +go diff --git a/contrib/test/JDBC/input/BABEL-1513.sql b/contrib/test/JDBC/input/BABEL-1513.sql new file mode 100644 index 00000000000..203e400e171 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1513.sql @@ -0,0 +1,41 @@ +DECLARE @a varchar +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a nvarchar +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a sys.varchar +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a pg_catalog.varchar +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a char +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a nchar +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a sys.char +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO diff --git a/contrib/test/JDBC/input/BABEL-1515.sql b/contrib/test/JDBC/input/BABEL-1515.sql new file mode 100644 index 00000000000..d7074050b70 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1515.sql @@ -0,0 +1,23 @@ +DECLARE @a char(10) +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a nchar(10) +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a varchar(10) +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO + +DECLARE @a nvarchar(10) +SELECT @a = '12345678901234567890123456789012345'; +SELECT LEN(@a), DATALENGTH(@a) +SELECT @a +GO diff --git a/contrib/test/JDBC/input/BABEL-1531.sql b/contrib/test/JDBC/input/BABEL-1531.sql new file mode 100644 index 00000000000..0d9b88c8260 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1531.sql @@ -0,0 +1,18 @@ +SELECT CONVERT(varchar(50), CAST($23.12 AS money), 0); +GO +SELECT CONVERT(varchar(50), CAST($23.12 AS money), 2); +GO +SELECT CONVERT(varchar(50), CAST($23.12 as money)); +GO +SELECT CONVERT(float, CAST($23.12 as money)); +GO +SELECT CONVERT(decimal, CAST($23.12 as money)); +GO +SELECT CONVERT(numeric, CAST($23.12 as money)); +GO +SELECT CONVERT(numeric(10,4), CAST($23.12 as money)); +GO +declare @mon money; +set @mon = $23.12; +SELECT CONVERT(varchar(50), @mon); +GO diff --git a/contrib/test/JDBC/input/BABEL-1566.sql b/contrib/test/JDBC/input/BABEL-1566.sql new file mode 100644 index 00000000000..e47c120cd3d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1566.sql @@ -0,0 +1,24 @@ +-- Test CHECKSUM function works on string input +select CHECKSUM('abcd'); +go + +-- Test CHECKSUM function works on scalar input +select CHECKSUM(123); +go + +select CHECKSUM(10.12345); +go + +-- Test CHECKSUM works on table column +create table t1 (a int, b varchar(10)); +insert into t1 values (12345, 'abcd'); +insert into t1 values (12345, 'abcd'); +insert into t1 values (23456, 'bcd'); +go + +select CHECKSUM(a), CHECKSUM(b) from t1; +go + +-- clean up +drop table t1; +go diff --git a/contrib/test/JDBC/input/BABEL-1569.sql b/contrib/test/JDBC/input/BABEL-1569.sql new file mode 100644 index 00000000000..038ccb2301e --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1569.sql @@ -0,0 +1,57 @@ +-- Test CHARINDEX function with computed columns (general gives case-insensitive result) +--WRONG OUTPUT +-- CREATE TABLE check_charindex_general(check_for VARCHAR(10), document VARCHAR(64), ci as charindex(check_for, document)); +-- GO + +-- INSERT INTO check_charindex_general (check_for, document) VALUES ('Test','This is a Test'); +-- INSERT INTO check_charindex_general (check_for, document) VALUES ('Test','test Test test'); +-- INSERT INTO check_charindex_general (check_for, document) VALUES ('TEST','This is a test'); +-- GO + +-- Select ci from check_charindex_general; +-- GO + +-- DROP TABLE check_charindex_general; +-- GO + +-- Test CHARINDEX function with computed columns (case-insensitive) +CREATE TABLE check_charindex_case_insensitive(check_for VARCHAR(10), document VARCHAR(64), ci as charindex(check_for, document COLLATE SQL_Latin1_General_CP1_CI_AS)); +GO + +INSERT INTO check_charindex_case_insensitive (check_for, document) VALUES ('Test','This is a Test'); +INSERT INTO check_charindex_case_insensitive (check_for, document) VALUES ('Test','test Test test'); +INSERT INTO check_charindex_case_insensitive (check_for, document) VALUES ('TEST','This is a test'); +GO + +Select ci from check_charindex_case_insensitive; +GO + +DROP TABLE check_charindex_case_insensitive; +GO + +-- Test CHARINDEX function with computed columns (case-sensitive) +CREATE TABLE check_charindex_case_sensitive(check_for VARCHAR(10), document VARCHAR(64), ci as charindex(check_for, document COLLATE SQL_Latin1_General_CP1_CS_AS)); +GO + +INSERT INTO check_charindex_case_sensitive (check_for, document) VALUES ('Test','This is a Test'); +INSERT INTO check_charindex_case_sensitive (check_for, document) VALUES ('Test','test Test test'); +INSERT INTO check_charindex_case_sensitive (check_for, document) VALUES ('TEST','This is a test'); +GO + +Select ci from check_charindex_case_sensitive; +GO + +DROP TABLE check_charindex_case_sensitive; +GO + +-- Test CHECKSUM function with computed columns +CREATE TABLE country(name VARCHAR(64), cs as CHECKSUM(name)) +GO + +INSERT INTO country VALUES ('India'); +INSERT INTO country VALUES ('US'); +INSERT INTO country VALUES ('China'); +GO + +DROP TABLE country; +GO diff --git a/contrib/test/JDBC/input/BABEL-1577.sql b/contrib/test/JDBC/input/BABEL-1577.sql new file mode 100644 index 00000000000..f6d971eda91 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1577.sql @@ -0,0 +1,25 @@ +CREATE TABLE babel_1577_table (col# int, #col int, co##l int, col$$ int, co_#$#$l int, col_# int) +go + +INSERT INTO babel_1577_table VALUES (1, 2, 3, 4, 5, 6); +go + +CREATE PROC babel_1577_proc AS +SELECT + col# AS a, + #col AS b, + co##l AS c, + col$$ AS d, + co_#$#$l AS e, + col_# AS f +FROM babel_1577_table +go + +EXEC babel_1577_proc +go + +DROP PROC babel_1577_proc +go + +DROP TABLE babel_1577_table +go diff --git a/contrib/test/JDBC/input/BABEL-1603.sql b/contrib/test/JDBC/input/BABEL-1603.sql new file mode 100644 index 00000000000..d85016f4141 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1603.sql @@ -0,0 +1,35 @@ +SELECT DATEPART(weekday, CAST( '2021-12-31' as DATE)); +GO + +SELECT @@DATEFIRST; +GO + +SET DATEFIRST 1 +GO + +SELECT @@DATEFIRST; +GO + +SELECT DATEPART(weekday, CAST( '2021-12-31' as DATE)); +GO + +-- reset it to 7 +SET DATEFIRST 7 +GO + +SELECT @@DATEFIRST; +GO + +SELECT DATEPART(weekday, CAST( '2021-12-31' as DATE)); +GO + +-- invalid settings +SET DATEFIRST 0 +GO + +SET DATEFIRST 8 +GO + +SELECT @@DATEFIRST; +GO + diff --git a/contrib/test/JDBC/input/BABEL-1621.sql b/contrib/test/JDBC/input/BABEL-1621.sql new file mode 100644 index 00000000000..49223f8bf90 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1621.sql @@ -0,0 +1,5 @@ +SELECT is_member(CAST('fake_role' AS varchar)) +GO + +SELECT is_member(NULL) +GO diff --git a/contrib/test/JDBC/input/BABEL-1636.sql b/contrib/test/JDBC/input/BABEL-1636.sql new file mode 100644 index 00000000000..e1d83c11404 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1636.sql @@ -0,0 +1,50 @@ +create type tableType as table (i int, j int); +go + +-- ROLLBACK should get unsupported error message +create proc p1 as +begin + declare @tv tableType; + begin tran insert @tv values (1,2); + rollback; + select * from @tv +end +go +exec p1 +go + +-- ROLLBACK should get unsupported error message +declare @t table (a int) +begin tran +insert into @t values(1) +select @@rowcount as rows_inserted +rollback +select count(*) from @t +print 'after select' +go + +declare @tv1 tableType; +begin transaction +insert @tv1 values (1,2), (2,1); +select * from @tv1; +go + +-- ROLLBACK here is fine because @tv1 is out of scope +rollback +select * from @tv1; +go + +-- ROLLBACK should get unsupported error message +declare @tv2 tableType; +begin transaction +insert @tv2 values (3,4), (4,3); +select * from @tv2; +rollback +select * from @tv2; +go + +-- cleanup +drop type tableType; +go +drop proc p1; +go diff --git a/contrib/test/JDBC/input/BABEL-1661.sql b/contrib/test/JDBC/input/BABEL-1661.sql new file mode 100644 index 00000000000..e5c085144ac --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1661.sql @@ -0,0 +1,13 @@ +-- BABEL-1661 +-- Test CONVERT to VARCHAR applies typmod +SELECT CONVERT(VARCHAR(10), SERVERPROPERTY('productversion')); +GO + +SELECT DATALENGTH(CONVERT(VARCHAR(10), SERVERPROPERTY('productversion'))); +GO + +SELECT CONVERT(VARCHAR(2), SERVERPROPERTY('productversion')); +GO + +SELECT DATALENGTH(CONVERT(VARCHAR(2), SERVERPROPERTY('productversion'))); +GO diff --git a/contrib/test/JDBC/input/BABEL-1666.sql b/contrib/test/JDBC/input/BABEL-1666.sql new file mode 100644 index 00000000000..7c16fad70a8 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1666.sql @@ -0,0 +1,19 @@ +create procedure p_babel_1666 as + declare @p1 decimal(5,2); + set @p1 = 3.14; + select @p1; + return @p1; +go + +declare @a decimal(5,2); +exec @a = p_babel_1666; +select @a; +go + +declare @i int; +exec @i = p_babel_1666; +select @i; +go + +drop procedure p_babel_1666 +go diff --git a/contrib/test/JDBC/input/BABEL-1682.sql b/contrib/test/JDBC/input/BABEL-1682.sql new file mode 100644 index 00000000000..77bde2fde66 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1682.sql @@ -0,0 +1,26 @@ +use master +go + +create schema [Babelfish]; +go + +create type [Babelfish].[temp_type] from int; +go + +declare @tint [Babelfish].[temp_type]; +set @tint = 3; +print 'Value of variable is: ' + cast(@tint as varchar(5)); +go + +create type [Babelfish].[EmployeesType] as TABLE (first_name NVARCHAR(10), last_name NVARCHAR(10), emp_sal money); +go + +declare @emps [Babelfish].[EmployeesType]; +go + +drop type [Babelfish].[temp_type]; +go +drop type [Babelfish].[EmployeesType]; +go +drop schema [Babelfish]; +go diff --git a/contrib/test/JDBC/input/BABEL-1683.sql b/contrib/test/JDBC/input/BABEL-1683.sql new file mode 100644 index 00000000000..52acb36ab06 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1683.sql @@ -0,0 +1,106 @@ +-- CREATE TABLE stmt +-- NVARCHAR(128) +CREATE TABLE babel_1683_table_nvarchar (a INT, b NVARCHAR(128)) +go + +INSERT INTO babel_1683_table_nvarchar VALUES (1, NULL) +go + +INSERT INTO babel_1683_table_nvarchar(a) VALUES (2) +go + +SELECT * FROM babel_1683_table_nvarchar +go + +DROP TABLE babel_1683_table_nvarchar +go + +-- SYSNAME not explicitly defined as NULL +CREATE TABLE babel_1683_table_sysname (a INT, b SYSNAME) +go + +INSERT INTO babel_1683_table_sysname VALUES (1, NULL) +go + +INSERT INTO babel_1683_table_sysname(a) VALUES (2) +go + +SELECT * FROM babel_1683_table_sysname +go + +DROP TABLE babel_1683_table_sysname +go + +-- SYSNAME explicitly defined as NULL +CREATE TABLE babel_1683_table_sysname (a INT, b SYSNAME NULL) +go + +INSERT INTO babel_1683_table_sysname VALUES (1, NULL) +go + +INSERT INTO babel_1683_table_sysname(a) VALUES (2) +go + +SELECT * FROM babel_1683_table_sysname +go + +DROP TABLE babel_1683_table_sysname +go + +-- ALTER TABLE ADD stmt +-- NVARCHAR(128) +CREATE TABLE babel_1683_table_nvarchar (a INT) +go + +ALTER TABLE babel_1683_table_nvarchar ADD b NVARCHAR(128) +go + +INSERT INTO babel_1683_table_nvarchar VALUES (1, NULL) +go + +INSERT INTO babel_1683_table_nvarchar(a) VALUES (2) +go + +SELECT * FROM babel_1683_table_nvarchar +go + +DROP TABLE babel_1683_table_nvarchar +go + +-- SYSNAME not explicitly defined as NULL +CREATE TABLE babel_1683_table_sysname (a INT) +go + +ALTER TABLE babel_1683_table_sysname ADD b SYSNAME +go + +INSERT INTO babel_1683_table_sysname VALUES (1, NULL) +go + +INSERT INTO babel_1683_table_sysname(a) VALUES (2) +go + +SELECT * FROM babel_1683_table_sysname +go + +DROP TABLE babel_1683_table_sysname +go + +-- SYSNAME explicitly defined as NULL +CREATE TABLE babel_1683_table_sysname (a INT) +go + +ALTER TABLE babel_1683_table_sysname ADD b SYSNAME NULL +go + +INSERT INTO babel_1683_table_sysname VALUES (1, NULL) +go + +INSERT INTO babel_1683_table_sysname(a) VALUES (2) +go + +SELECT * FROM babel_1683_table_sysname +go + +DROP TABLE babel_1683_table_sysname +go diff --git a/contrib/test/JDBC/input/BABEL-1708.sql b/contrib/test/JDBC/input/BABEL-1708.sql new file mode 100644 index 00000000000..80523fdbe20 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1708.sql @@ -0,0 +1,76 @@ +-- Test valid maximum/minimum values for smallmoney +SELECT CAST($214748.3647 AS smallmoney); +GO + +SELECT CAST(-214748.3648 AS smallmoney); +GO + +SELECT CAST('214748.3647' AS smallmoney); +GO + +SELECT CAST('-214748.3648' AS smallmoney); +GO + +-- Test valid maximum/minimum values for money +SELECT CAST($922337203685477.5807 AS money); +GO + +SELECT CAST(-922337203685477.5808 AS money); +GO + +SELECT CAST('922337203685477.5807' AS money); +GO + +SELECT CAST('-922337203685477.5808' AS money); +GO + +-- Test out of range value for smallmoney +SELECT CAST($214748.3648 AS smallmoney); +GO + +SELECT CAST(-214748.3649 AS smallmoney); +GO + +SELECT CAST('214748.3648' AS smallmoney); +GO + +SELECT CAST('-214748.3649' AS smallmoney); +GO + +-- Test out of range values for money +SELECT CAST($922337203685477.5808 AS money); +GO + +SELECT CAST(-922337203685477.5809 AS money); +GO + +SELECT CAST('922337203685477.5808' AS money); +GO + +SELECT CAST('-922337203685477.5809' AS money); +GO + +-- Test table insert of max/min/out of range values +CREATE TABLE t1 (a smallmoney, b money) +GO + +-- Insert valid values +INSERT INTO t1 VALUES ($214748.3647, 0), (-214748.3648, 0), (0, 922337203685477.5807), (0, -922337203685477.5808) +GO + +-- Insert invalid values +INSERT INTO t1 VALUES ($214748.3648, 0) +GO + +INSERT INTO t1 VALUES (-214748.3649, 0) +GO + +INSERT INTO t1 VALUES (0, 922337203685477.5808) +GO + +INSERT INTO t1 VALUES (0, -922337203685477.5809) +GO + +-- Clean up +DROP TABLE t1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1712.sql b/contrib/test/JDBC/input/BABEL-1712.sql new file mode 100644 index 00000000000..6fefeaaf2ef --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1712.sql @@ -0,0 +1,41 @@ +--Under Single-DB mode +USE master; +GO +CREATE DATABASE db1; +GO +USE db1; +GO +CREATE schema test; +GO +SELECT nspname FROM pg_namespace WHERE nspname = 'test'; +GO +CREATE table t1 ( a int, b int); -- should be created into dbo.t1 +GO +INSERT INTO t1 VALUES ( 1, 1); +GO +SELECT * FROM t1; +GO +-- cross DB reference +USE master; +GO +SELECT * FROM t1; -- doesn't exist expected, querying master.dbo.t1 +GO +SELECT * FROM db1.dbo.t1; +GO +SELECT * FROM dbo.t1; -- error expected, querying master.dbo.t1 +GO +-- search path +USE db1; +GO +CREATE TABLE test.t1 ( a int, b int, c int); +GO +INSERT INTO test.t1 VALUES (1,2,3); +GO +SELECT * FROM t1; -- selecting 2 column db1.dbo.t1 +GO +SELECT * FROM test.t1; -- selecting 3 column db1.test.t1 +GO +USE MASTER; +GO +DROP DATABASE db1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1719.sql b/contrib/test/JDBC/input/BABEL-1719.sql new file mode 100644 index 00000000000..62e3b5be8d5 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1719.sql @@ -0,0 +1,26 @@ +CREATE DATABASE db1; +GO + +USE db1; +GO + +CREATE LOGIN login1 WITH PASSWORD = '123'; +GO + +ALTER LOGIN login1 disable; +GO + +SELECT is_disabled FROM sys.server_principals WHERE name = 'login1'; +GO + +ALTER LOGIN login1 enable; +GO + +USE MASTER; +GO + +DROP DATABASE db1; +GO + +DROP LOGIN login1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1746.sql b/contrib/test/JDBC/input/BABEL-1746.sql new file mode 100644 index 00000000000..ab9a91631d3 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1746.sql @@ -0,0 +1,5 @@ +DROP DATABASE master; +GO + +DROP DATABASE tempdb; +GO diff --git a/contrib/test/JDBC/input/BABEL-1756.sql b/contrib/test/JDBC/input/BABEL-1756.sql new file mode 100644 index 00000000000..866e00b0b49 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1756.sql @@ -0,0 +1,13 @@ +CREATE TABLE foo(test_collation TEXT COLLATE "default") +GO + +SELECT colid, name, collation_100 FROM sys.spt_tablecollations_view WHERE object_id = sys.object_id('foo') ORDER BY colid +GO + +exec sp_tablecollations_100 'foo' +GO + +exec ..sp_tablecollations_100 'foo' +GO + +DROP TABLE foo; diff --git a/contrib/test/JDBC/input/BABEL-1757.sql b/contrib/test/JDBC/input/BABEL-1757.sql new file mode 100644 index 00000000000..12ba2623f0b --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1757.sql @@ -0,0 +1,9 @@ +CREATE TABLE t1(a int) +GO + +SET FMTONLY ON SELECT * FROM t1 SET FMTONLY OFF +GO + +DROP TABLE t1 +GO + diff --git a/contrib/test/JDBC/input/BABEL-1771.sql b/contrib/test/JDBC/input/BABEL-1771.sql new file mode 100644 index 00000000000..f9d8a7d7b49 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1771.sql @@ -0,0 +1,63 @@ +USE master; +GO + +CREATE SCHEMA test +GO + +CREATE TABLE t (a int) +GO + +CREATE PROCEDURE foo AS SELECT 1 +GO + +CREATE PROCEDURE test.bar AS SELECT 2 +GO + +SELECT * FROM t +GO + +SELECT * FROM ..t +GO + +SELECT * FROM master..t +GO + +SELECT * FROM .fake_schema.t +GO + +EXEC test.bar +GO + +EXEC .test.bar +GO + +EXEC master..bar +GO + +EXEC ..bar +GO + +EXEC foo +GO + +EXEC ..foo +GO + +EXEC master..foo +GO + +EXEC .schema.foo +GO + +DROP TABLE t +GO + +DROP PROCEDURE foo +GO + +DROP PROCEDURE test.bar +GO + +DROP SCHEMA test +GO + diff --git a/contrib/test/JDBC/input/BABEL-1792.sql b/contrib/test/JDBC/input/BABEL-1792.sql new file mode 100644 index 00000000000..fd84028d1f5 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1792.sql @@ -0,0 +1,51 @@ +USE MASTER; +GO + +CREATE DATABASE db1; +GO + +USE db1; +GO + +CREATE TABLE t1 (id INT, c1 INT); +GO + +INSERT INTO t1 (id, c1) VALUES (1, 2); +GO + +USE MASTER; +GO + +-- Cannot find db1 table t1 +INSERT INTO t1 (id, c1) VALUES (2, 4); +GO + +BEGIN TRAN; +GO + +USE db1; +GO + +INSERT INTO t1 (id, c1) VALUES (3, 8); +GO + +ROLLBACK; +GO + +SELECT current_user; +GO + +SELECT current_schema; +GO + +INSERT INTO t1 (id, c1) VALUES (4, 16); +GO + +SELECT * FROM t1; +GO + +USE MASTER; +GO + +DROP DATABASE db1; +GO diff --git a/contrib/test/JDBC/input/BABEL-1797.sql b/contrib/test/JDBC/input/BABEL-1797.sql new file mode 100644 index 00000000000..9fdb5987487 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1797.sql @@ -0,0 +1,13 @@ +USE master +GO + +create table [dbo].[t23]([id] int, [a] money, [b] datetime) +go + +exec sp_describe_undeclared_parameters +N'INSERT INTO [dbo].[t23]([id],[a],[b]) values (@P1,@P2,@P3)' +go + +-- cleanup +drop table [dbo].[t23]; +go diff --git a/contrib/test/JDBC/input/BABEL-1808.sql b/contrib/test/JDBC/input/BABEL-1808.sql new file mode 100644 index 00000000000..956dfff7d46 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1808.sql @@ -0,0 +1,40 @@ +-- Test DDL +CREATE TABLE t1 ( a [Decimal], b [DeCimal](18,0)); +GO + +-- Test Default value +CREATE TABLE t2 ( a Decimal, b [DeCimal]); +GO + +INSERT INTO t2 values (3.14, 3.14); +SELECT * FROM t2; +GO + +-- Test Identity column + +CREATE TABLE t3 ( a Decimal IDENTITY, b INT); +GO + +INSERT INTO t3 (b) VALUES (10); +SELECT * FROM t3; +GO + +CREATE TABLE t4 ( a [Decimal] IDENTITY, b INT); +GO + +INSERT INTO t4 (b) VALUES (10); +SELECT * FROM t4; +GO + +-- test error +CREATE TABLE t5 ( a Decimal(18,2) IDENTITY, b int); +GO + +DROP TABLE t1; +GO +DROP TABLE t2; +GO +DROP TABLE t3; +GO +DROP TABLE t4; +GO diff --git a/contrib/test/JDBC/input/BABEL-1839.sql b/contrib/test/JDBC/input/BABEL-1839.sql new file mode 100644 index 00000000000..0b50be76657 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1839.sql @@ -0,0 +1,39 @@ +SELECT + current_setting('babelfishpg_tsql.quoted_identifier', true), + current_setting('babelfishpg_tsql.nocount', true), + current_setting('babelfishpg_tsql.concat_null_yields_null', true); +GO + +SET NOCOUNT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER ON; +GO + +SET NOCOUNT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER OFF; +GO + +SELECT + current_setting('babelfishpg_tsql.quoted_identifier', true), + current_setting('babelfishpg_tsql.nocount', true), + current_setting('babelfishpg_tsql.concat_null_yields_null', true); +GO + +-- error expected +SET NOCOUNT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER, NOTHING ON; +GO + +-- value shall not change +SELECT + current_setting('babelfishpg_tsql.quoted_identifier', true), + current_setting('babelfishpg_tsql.nocount', true), + current_setting('babelfishpg_tsql.concat_null_yields_null', true); +GO + +-- error expected 2 +SET NOCOUNT, CONCAT_NULL_YIELDS_NULL, QUOTED_IDENTIFIER, LANGUAGE ON; +GO + +-- value shall not change +SELECT + current_setting('babelfishpg_tsql.quoted_identifier', true), + current_setting('babelfishpg_tsql.nocount', true), + current_setting('babelfishpg_tsql.concat_null_yields_null', true); +GO diff --git a/contrib/test/JDBC/input/BABEL-1845.sql b/contrib/test/JDBC/input/BABEL-1845.sql new file mode 100644 index 00000000000..cf267975b95 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1845.sql @@ -0,0 +1,35 @@ +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +GO + +-- Test that ANSI_NULL_DFLT_ON can be set to ON and ANSI_NULL_DFLT_OFF can be set to OFF +set ANSI_NULL_DFLT_ON ON; +go + +set ANSI_NULL_DFLT_OFF OFF; +go + +-- Test a table column is nullable with the above settings (which is also the default setting) +create table t1 (c1 int); +go + +insert into t1 values (NULL); +go + +-- Expect one row of NULL value +select c1 from t1; +go + +-- Test that ANSI_NULL_DFLT_ON can not be set to OFF and ANSI_NULL_DFLT_OFF can not be set to ON +set ANSI_NULL_DFLT_ON OFF; +go + +set ANSI_NULL_DFLT_OFF ON; +go + +-- Clean up +drop table if exists t1; +go + +-- reset to default +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO diff --git a/contrib/test/JDBC/input/BABEL-1858.sql b/contrib/test/JDBC/input/BABEL-1858.sql new file mode 100644 index 00000000000..b9ba9b674fb --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1858.sql @@ -0,0 +1,38 @@ +USE master; +GO + +-- test volatility of function in batch +-- should see different id values with each iteration +DECLARE @i INT = 1 +DECLARE @num_iter INT = 5 +CREATE TABLE newid_volatile (u uniqueidentifier) +WHILE @i <= @num_iter +BEGIN + INSERT INTO newid_volatile VALUES (NEWID()) + SET @i = @i + 1 +END + +-- should be equal to @num_iter +select count(distinct u) from newid_volatile +go + +-- test volatility of function in procedure +-- should see different id values with each iteration +CREATE PROC p_newid AS +DECLARE @num_iter INT = 5 +DECLARE @i INT = 1 +CREATE TABLE newid_volatile_proc (u uniqueidentifier) +WHILE @i <= @num_iter +BEGIN + INSERT INTO newid_volatile_proc VALUES (NEWID()) + SET @i = @i + 1 +END +go +EXEC p_newid +-- should be equal to @num_iter +select count(distinct u) from newid_volatile_proc +GO + + +DROP TABLE newid_volatile +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1944.sql b/contrib/test/JDBC/input/BABEL-1944.sql new file mode 100644 index 00000000000..f23d696a079 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1944.sql @@ -0,0 +1,140 @@ +create table t1(a int); +go +create table t2(a int); +go +create procedure sp_trancount as +select @@trancount; +GO +create procedure sp_commit_no_begin as +insert into t1 values(3); +commit; +select * from t1; +GO +create procedure sp_rollback_no_begin as +insert into t1 values(4); +rollback; +select * from t1; +GO +create procedure sp_rollback_with_begin as +begin tran; +insert into t1 values(4); +rollback; +select * from t1; +go + +select @@trancount; +go +-- trancount inside normal EXEC should be same as outside +exec sp_trancount; +go +-- trancount inside INSERT-EXEC should be 1 more than outside +insert into t1 exec sp_trancount; +go +select * from t1; +go +delete t1; +go + +-- zero level - normal EXEC should succeed with warning for no BEGIN TRAN on the +-- COMMIT +exec sp_commit_no_begin; +go +-- zero level - INSERT-EXEC should fail with error message about the COMMIT +-- inside INSERT-EXEC must have a BEGIN TRAN +delete t1; +go +insert into t2 exec sp_commit_no_begin; +go +select count(*) from t1; +select count(*) from t2; +go + +-- one level - should fail and abort that level of transaction +begin tran; +go +select @@trancount; +go +insert into t2 execute sp_commit_no_begin; +go +select @@trancount; +go +select count(*) from t1; +select count(*) from t2; +go + +-- previous level aborted, this should be the same as before +begin tran; +go +select @@trancount; +go +insert into t2 execute sp_commit_no_begin; +go +select count(*) from t1; +select count(*) from t2; +go + +-- normal EXEC with COMMIT is allowed with one level - should succeed with +-- unbalanced level warning from 1 to 0 +begin tran; +go +select @@trancount; +go +execute sp_commit_no_begin; +go +select count(*) from t1; +select count(*) from t2; +go + +-- two levels - should succeed with unbalanced level warning from 2 to 1, and +-- should not send any Row tokens +select @@trancount; +go +begin tran; +go +begin tran; +go +select @@trancount; +go +insert into t2 execute sp_commit_no_begin; +go +select count(*) from t1; +select count(*) from t2; +go +select @@trancount; +go +commit; +go + +-- INSERT-EXEC with ROLLBACK in one level of transaction - should fail and abort +-- that level of transaction +begin tran; +go +select @@trancount; +go +insert into t2 exec sp_rollback_no_begin; +go +select @@trancount; +go + +begin tran; +go +select @@trancount; +go +insert into t2 exec sp_rollback_with_begin; +go +select @@trancount; +go + +-- cleanup +drop table t1; +go +drop table t2; +go +drop procedure sp_trancount; +go +drop procedure sp_commit_no_begin; +go +drop procedure sp_rollback_no_begin; +go +drop procedure sp_rollback_with_begin; +go diff --git a/contrib/test/JDBC/input/BABEL-1963.sql b/contrib/test/JDBC/input/BABEL-1963.sql new file mode 100644 index 00000000000..2da65ad6a90 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1963.sql @@ -0,0 +1,32 @@ +-- recursive procedure +-- should fail with stack depth reached error +CREATE PROC p1 AS +BEGIN + exec p1 +END +GO + +exec p1 +go + +-- recursive trigger +-- should fail with stack depth reached error +CREATE TABLE table2_1963 (a int) +GO + +CREATE TRIGGER trig2_1963 +ON table2_1963 +AFTER INSERT +AS insert into table2_1963 values (1) +GO + +insert into table2_1963 values(5) +go + + +-- cleanup +drop table table2_1963; +go + +drop procedure p1 +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1975.sql b/contrib/test/JDBC/input/BABEL-1975.sql new file mode 100644 index 00000000000..e1c29f861ac --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1975.sql @@ -0,0 +1,34 @@ +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +GO + +DROP TABLE if EXISTS t1; +CREATE TABLE t1 (c1 int); +INSERT INTO t1 values (1); +INSERT INTO t1 values (2); +INSERT INTO t1 values (NULL); +GO + +SELECT * FROM t1; +SELECT @@rowcount; +GO + +SET ROWCOUNT 0; +SELECT @@rowcount; +GO + +SELECT * FROM t1; +SELECT @@rowcount; +GO + +-- test invalid settings +SET ROWCOUNT 1; +SELECT @@rowcount; +GO + +-- clean up +DROP TABLE t1; +GO + +-- reset to default +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO diff --git a/contrib/test/JDBC/input/BABEL-1978.sql b/contrib/test/JDBC/input/BABEL-1978.sql new file mode 100644 index 00000000000..0caeb5fe5ee --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1978.sql @@ -0,0 +1,24 @@ +SET blah ON; +GO + +SET blahblah oh_yes; +GO + +SET auto_commit_batch on; -- existing bbf GUC +GO + +-- should fail even if escape_hatch_session_settings = 'ignore' +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO + +SET blah ON; +GO + +SET blahblah oh_yes; +GO + +SET auto_commit_batch on; -- existing bbf GUC +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +GO diff --git a/contrib/test/JDBC/input/BABEL-1994-CHAR.sql b/contrib/test/JDBC/input/BABEL-1994-CHAR.sql new file mode 100644 index 00000000000..02dfb09c6ed --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1994-CHAR.sql @@ -0,0 +1,146 @@ +drop table if exists pad; +go + +create table pad( + c5nn char(5) not null, + c5n character(5) null +); +go + +insert into pad values('ab ', 'ab '); +create index pad_c5nn_idx on pad(c5nn); +go + +select + concat('[', c5nn, ']') as c5nn_concat, + '[' + c5nn +']' as c5nn_to_text_concat, + concat('[', cast(c5nn as varchar), ']') as c5nn_to_varchar_concat, + concat('[', c5n, ']') as c5n_concat, + '[' + c5n + ']' as c5n_to_text_concat, + concat('[', cast(c5n as varchar), ']') as c5n_to_varchar_concat, + concat('[', cast(c5n as name), ']') as c5n_to_name_concat +from pad; +go + +drop table pad; +go + +-- default length of CHAR should be 1. Otherwise, it will crash on the below select statement. +drop table if exists t1; +go + +CREATE TABLE t1 (c1 CHAR); +INSERT INTO t1 VALUES ('A'); +GO + +SELECT c1 from t1; +go + +drop table t1; +go + +-- In case of CAST, the default length of CHAR should be 30. +select datalength(cast('123 ' as char)); +go + +-- Cast from sys.BPCHAR +select + cast(cast('2021-08-15 ' as char(11)) as sys.smalldatetime), + cast(cast('2021-08-15 ' as char(11)) as sys.datetime2), + cast(cast('2021-08-15 ' as char(11)) as sys.datetime) +; +go + +select + cast(cast('decimal ' as char(8)) as sys.sql_variant), + cast(cast('abc ' as char(5)) as pg_catalog.varchar), + cast(cast('abc ' as char(5)) as pg_catalog.bpchar(4)), + cast(cast(' ' as char(12)) as pg_catalog.xml), + cast(cast('abc ' as char(5)) as pg_catalog.text), + cast(cast('abc ' as char(5)) as pg_catalog.name), + cast(cast('abc ' as char(5)) as pg_catalog.char(1)) +; +go + +-- Convert from sys.BPCHAR +select + convert(sys.sql_variant, cast('decimal ' as char(8))), + convert(sys.smalldatetime, cast('2021-08-15 ' as char(11))), + convert(sys.datetime2, cast('2021-08-15 ' as char(11))) +; +go + +select + convert(pg_catalog.varchar, cast('abc ' as char(5))), + convert(pg_catalog.bpchar(4), cast('abc ' as char(5))), + convert(pg_catalog.xml, cast(' ' as char(12))), + convert(pg_catalog.text, cast('abc ' as char(5))), + convert(pg_catalog.name, cast('abc ' as char(5))), + convert(pg_catalog.char(1), cast('abc ' as char(5))) +; +go + +-- CAST to sys.BPCHAR +select + cast(cast('2021-08-15 ' as sys.datetime) as char(20)), + cast(cast('2021-08-15 ' as sys.datetime2) as char(20)), + cast(cast('2021-08-15 ' as sys.smalldatetime) as char(20)), + cast(cast('decimal ' as sys.sql_variant) as char(8)), + cast(FALSE as char(6)), + cast(cast('a' as pg_catalog.char(1)) as char(3)) +; +go + +select + cast(cast('abc ' as pg_catalog.name) as char(4)), + cast(cast('128' as pg_catalog.cidr) as char(13)), + cast(cast('2001:4f8:3:ba::/64' as pg_catalog.inet) as char(20)), + cast(cast('abc ' as pg_catalog.text) as char(4)), + cast(cast(' ' as pg_catalog.xml) as char(12)), + cast(cast('abc ' as pg_catalog.bpchar(5)) as char(4)), + cast(cast('abc ' as pg_catalog.varchar) as char(4)) +; +go + +-- Convert to sys.BPCHAR +select + convert(char(20), cast('2021-08-15 ' as sys.datetime)), + convert(char(20), cast('2021-08-15 ' as sys.datetime2)), + convert(char(20), cast('2021-08-15 ' as sys.smalldatetime)), + convert(char(8), cast('decimal ' as sys.sql_variant)), + convert(char(6), FALSE), + convert(char(3), cast('a' as pg_catalog.char(1))) +; +go + +select + convert(char(4), cast('abc ' as pg_catalog.name)), + convert(char(13), cast('128' as pg_catalog.cidr)), + convert(char(20), cast('2001:4f8:3:ba::/64' as pg_catalog.inet)), + convert(char(4), cast('abc ' as pg_catalog.text)), + convert(char(12), cast(' ' as pg_catalog.xml)), + convert(char(4), cast('abc ' as pg_catalog.bpchar(5))), + convert(char(4), cast('abc ' as pg_catalog.varchar)) +; +go + +-- String functions +select + unicode(cast('a ' as char(5))), + reverse(cast('a ' as char(5))), + quotename(cast('a[] b ' as char(6))), + patindex('a %', cast('a ' as char(5))), + rtrim(cast('a ' as char(5))) +; +go + +select + lower(cast('A ' as char(5))), + left(cast('a ' as char(5)), 2), + charindex(cast('a ' as char(5)), 'a '), + ascii(cast('a ' as char(5))), + datalength(cast('123 ' as char(5))), + length(cast('123 ' as char(5))), + len(cast('123 ' as char(5))) +; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1994-VARCHAR.sql b/contrib/test/JDBC/input/BABEL-1994-VARCHAR.sql new file mode 100644 index 00000000000..32ab99b1d2a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1994-VARCHAR.sql @@ -0,0 +1,130 @@ +drop table if exists person; +go +create table person( + name_vcnn varchar(10) not null primary key, + name_vcn varchar(10) null +); +go + +insert into person values ('smith', 'smith'); +go +insert into person values ('jones ', 'jones '); +go +insert into person values ('jones ', 'jones '); +go + +select count(*) from person where name_vcnn = 'smith '; +go +select count(*) from person where name_vcn = 'smith '; +go +select count(*) from person where name_vcnn = 'jones '; +go +select count(*) from person where name_vcn = 'jones '; +go +select count(*) from person where name_vcn = 'jones '; +go + +drop table person; +go + +-- Cast from VARCHAR +select + cast(cast('2021-08-15 ' as varchar(11)) as smalldatetime), + cast(cast('2021-08-15 ' as varchar(11)) as datetime2), + cast(cast('2021-08-15 ' as varchar(11)) as datetime) +; +go + +select + cast(cast('decimal ' as varchar(8)) as sql_variant), + cast(cast('abc ' as varchar(5)) as pg_catalog.varchar), + cast(cast('abc ' as varchar(5)) as pg_catalog.bpchar(4)), + cast(cast(' ' as varchar(12)) as xml), + cast(cast('abc ' as varchar(5)) as text), + cast(cast('abc ' as varchar(5)) as name), + cast(cast('abc ' as varchar(5)) as char(1)) +; +go + +-- Convert from VARCHAR +select + convert(sql_variant, cast('decimal ' as varchar(8))), + convert(smalldatetime, cast('2021-08-15 ' as varchar(11))), + convert(datetime2, cast('2021-08-15 ' as varchar(11))) +; +go + +select + convert(pg_catalog.varchar, cast('abc ' as varchar(5))), + convert(pg_catalog.bpchar(4), cast('abc ' as varchar(5))), + convert(xml, cast(' ' as varchar(12))), + convert(text, cast('abc ' as varchar(5))), + convert(name, cast('abc ' as varchar(5))), + convert(char(1), cast('abc ' as varchar(5))) +; +go + +-- CAST to VARCHAR +select + cast(cast('2021-08-15 ' as datetime) as varchar(20)), + cast(cast('2021-08-15 ' as datetime2) as varchar(20)), + cast(cast('2021-08-15 ' as smalldatetime) as varchar(20)), + cast(cast('decimal ' as sql_variant) as varchar(8)), + cast(FALSE as varchar(6)), + cast(cast('a' as char(1)) as varchar(3)) +; +go + +select + cast(cast('abc ' as name) as varchar(4)), + cast(cast('128' as cidr) as varchar(13)), + cast(cast('2001:4f8:3:ba::/64' as inet) as varchar(20)), + cast(cast('abc ' as text) as varchar(4)), + cast(cast(' ' as xml) as varchar(12)), + cast(cast('abc ' as pg_catalog.bpchar(5)) as varchar(4)), + cast(cast('abc ' as pg_catalog.varchar) as varchar(4)) +; +go + +-- Convert to VARCHAR +select + convert(varchar(20), cast('2021-08-15 ' as datetime)), + convert(varchar(20), cast('2021-08-15 ' as datetime2)), + convert(varchar(20), cast('2021-08-15 ' as smalldatetime)), + convert(varchar(8), cast('decimal ' as sql_variant)), + convert(varchar(6), FALSE), + convert(varchar(3), cast('a' as pg_catalog.char(1))) +; +go + +select + convert(varchar(4), cast('abc ' as name)), + convert(varchar(13), cast('128' as cidr)), + convert(varchar(20), cast('2001:4f8:3:ba::/64' as inet)), + convert(varchar(4), cast('abc ' as text)), + convert(varchar(12), cast(' ' as xml)), + convert(varchar(4), cast('abc ' as pg_catalog.bpchar(5))), + convert(varchar(4), cast('abc ' as pg_catalog.varchar)) +; +go + +-- String functions +select + unicode(cast('a ' as varchar(5))), + reverse(cast('a ' as varchar(5))), + quotename(cast('a[] b ' as varchar(6))), + patindex('a %', cast('a ' as varchar(5))), + rtrim(cast('a ' as varchar(5))) +; +go + +select + lower(cast('A ' as varchar(5))), + left(cast('a ' as varchar(5)), 2), + charindex(cast('a ' as varchar(5)), 'a '), + ascii(cast('a ' as varchar(5))), + datalength(cast('123 ' as varchar(5))), + length(cast('123 ' as varchar(5))), + len(cast('123 ' as varchar(5))) +; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-1997.sql b/contrib/test/JDBC/input/BABEL-1997.sql new file mode 100644 index 00000000000..20d79a1a56a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-1997.sql @@ -0,0 +1,25 @@ +--create +CREATE TABLE t1997_1(c1 int primary key, c2 int) +GO +CREATE TABLE t1997_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t1997_1(c1)) +GO +INSERT INTO t1997_1 VALUES(1, 10) +INSERT INTO t1997_2 VALUES(100, 1) +GO +--generate error +begin tran + begin try + TRUNCATE TABLE t1997_1; + end try + begin catch + select xact_state(); + DROP TABLE t1997_2 + DROP TABLE t1997_1 + end catch +go + +--clean +DROP TABLE t1997_2 +GO +DROP TABLE t1997_1 +GO diff --git a/contrib/test/JDBC/input/BABEL-2010.sql b/contrib/test/JDBC/input/BABEL-2010.sql new file mode 100644 index 00000000000..57e6c0c7108 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2010.sql @@ -0,0 +1,31 @@ +DECLARE @a VARCHAR(50); +SELECT @a = 'SELECT ''hello world'''; +EXEC (@a); +go + +-- BABEL-1388, BABEL-2010 +DECLARE @a VARCHAR(50); +SELECT @a = 'DROP PROCEDURE myproc'; +EXEC @a; +go + +-- Error, procedure called does not exist +DECLARE @a VARCHAR(50) = 'babel_2010_proc'; +EXEC @a; +go + +CREATE PROC babel_2010_proc AS +SELECT 'hello'; +go + +-- Need support for: +-- EXEC @module_name_var +-- where @module_name_var is a variable +-- whose value is a proc/func name. +-- Should pass after BABEL-341 is fixed. +DECLARE @a VARCHAR(50) = 'babel_2010_proc'; +EXEC @a; +go + +DROP PROC babel_2010_proc +go diff --git a/contrib/test/JDBC/input/BABEL-2011.sql b/contrib/test/JDBC/input/BABEL-2011.sql new file mode 100644 index 00000000000..c919fc5b9db --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2011.sql @@ -0,0 +1,29 @@ +USE master; +GO + +-- test volatility of functions in multi-row insert +-- newsequentialid() +CREATE TABLE myTable (ColumnA uniqueidentifier DEFAULT NEWSEQUENTIALID(), a int); +go +insert myTable (a) values (1), (2) +go +-- should be equal to 2 +select count(distinct a) from myTable +go + +-- newid() +CREATE TABLE t1_2011 (a int); +CREATE TABLE myTable2 (ColumnA uniqueidentifier, a int); +go +insert t1_2011 (a) values (1), (2) +go +insert myTable2 select newid(), a from t1_2011 +go +-- should be equal to 2 +select count(distinct a) from myTable2 +go + +drop table myTable2; +drop table myTable; +drop table t1_2011; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-2020-DELETE.sql b/contrib/test/JDBC/input/BABEL-2020-DELETE.sql new file mode 100644 index 00000000000..2d5e1084511 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2020-DELETE.sql @@ -0,0 +1,138 @@ +drop procedure if exists babel_2020_delete_ct; +go + +create procedure babel_2020_delete_ct as +begin + drop table if exists babel_2020_delete_t1 + create table babel_2020_delete_t1 (a int) + insert into babel_2020_delete_t1 values (1), (2), (NULL) + drop table if exists babel_2020_delete_t2 + create table babel_2020_delete_t2 (a int) + insert into babel_2020_delete_t2 values (2), (3), (NULL) +end +go + +-- single tables in FROM clause +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x where x.a = 2; +go + +-- multiple tables in FROM clause +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x, babel_2020_delete_t2 y; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x, babel_2020_delete_t2 y where x.a = 2; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x, babel_2020_delete_t2 y where y.a = 2; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x, babel_2020_delete_t2 y where x.a = y.a; +go + +-- JOIN clause +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x join babel_2020_delete_t2 y on 1 = 1; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x join babel_2020_delete_t2 y on x.a = 2; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x join babel_2020_delete_t2 y on y.a = 2; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x join babel_2020_delete_t2 y on x.a = y.a; +go + +-- subqueries +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from (select * from babel_2020_delete_t1) x; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x, (select * from babel_2020_delete_t1) y; +go + +-- self join +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x, (select * from babel_2020_delete_t1) y where x.a + 1 = y.a; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 y, (select * from babel_2020_delete_t1) x where x.a + 1 = y.a; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x join babel_2020_delete_t1 on babel_2020_delete_t1.a + 1 = x.a; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 join babel_2020_delete_t1 x on babel_2020_delete_t1.a + 1 = x.a; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x, babel_2020_delete_t1 y where x.a + 1 = y.a; +go + +-- outer joins +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x left outer join babel_2020_delete_t2 on babel_2020_delete_t2.a = x.a; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t2 left outer join babel_2020_delete_t1 x on babel_2020_delete_t2.a = x.a; +go + +-- null filters +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x where x.a is null; +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t2 left outer join babel_2020_delete_t1 x on x.a is null; +go + +-- updatable views +drop view if exists babel_2020_delete_v1; +go + +exec babel_2020_delete_ct; +go + +create view babel_2020_delete_v1 as select * from babel_2020_delete_t1 where babel_2020_delete_t1.a is not null; +go + +delete babel_2020_delete_v1 from babel_2020_delete_v1 x where x.a = 2; +go + +drop view if exists babel_2020_delete_v1; +go + +-- semi joins +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x where x.a in (select a from babel_2020_delete_t1 where babel_2020_delete_t1.a = x.a); +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x where not exists (select a from babel_2020_delete_t1 y where y.a + 1 = x.a); +go + +exec babel_2020_delete_ct; +delete babel_2020_delete_t1 from babel_2020_delete_t1 x where exists (select a from babel_2020_delete_t1 y where y.a + 1 = x.a); +go + +drop procedure if exists babel_2020_delete_ct; +drop table if exists babel_2020_delete_t1; +drop table if exists babel_2020_delete_t2; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-2020-UPDATE.sql b/contrib/test/JDBC/input/BABEL-2020-UPDATE.sql new file mode 100644 index 00000000000..a3124fd52f2 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2020-UPDATE.sql @@ -0,0 +1,138 @@ +drop procedure if exists babel_2020_update_ct; +go + +create procedure babel_2020_update_ct as +begin + drop table if exists babel_2020_update_t1 + create table babel_2020_update_t1 (a int) + insert into babel_2020_update_t1 values (1), (2), (NULL) + drop table if exists babel_2020_update_t2 + create table babel_2020_update_t2 (a int) + insert into babel_2020_update_t2 values (2), (3), (NULL) +end +go + +-- single tables in FROM clause +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x where x.a = 2; +go + +-- multiple tables in FROM clause +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x, babel_2020_update_t2 y; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x, babel_2020_update_t2 y where x.a = 2; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x, babel_2020_update_t2 y where y.a = 2; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x, babel_2020_update_t2 y where x.a = y.a; +go + +-- JOIN clause +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x join babel_2020_update_t2 y on 1 = 1; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x join babel_2020_update_t2 y on x.a = 2; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x join babel_2020_update_t2 y on y.a = 2; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x join babel_2020_update_t2 y on x.a = y.a; +go + +-- subqueries +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from (select * from babel_2020_update_t1) x; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x, (select * from babel_2020_update_t1) y; +go + +-- self join +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x, (select * from babel_2020_update_t1) y where x.a + 1 = y.a; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 y, (select * from babel_2020_update_t1) x where x.a + 1 = y.a; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x join babel_2020_update_t1 on babel_2020_update_t1.a + 1 = x.a; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 join babel_2020_update_t1 x on babel_2020_update_t1.a + 1 = x.a; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x, babel_2020_update_t1 y where x.a + 1 = y.a; +go + +-- outer joins +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x left outer join babel_2020_update_t2 on babel_2020_update_t2.a = x.a; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t2 left outer join babel_2020_update_t1 x on babel_2020_update_t2.a = x.a; +go + +-- null filters +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x where x.a is null; +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t2 left outer join babel_2020_update_t1 x on x.a is null; +go + +-- updatable views +drop view if exists babel_2020_update_v1; +go + +exec babel_2020_update_ct; +go + +create view babel_2020_update_v1 as select * from babel_2020_update_t1 where babel_2020_update_t1.a is not null; +go + +update babel_2020_update_v1 set a = 100 from babel_2020_update_v1 x where x.a = 2; +go + +drop view if exists babel_2020_update_v1; +go + +-- semi joins +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x where x.a in (select a from babel_2020_update_t1 where babel_2020_update_t1.a = x.a); +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x where not exists (select a from babel_2020_update_t1 y where y.a + 1 = x.a); +go + +exec babel_2020_update_ct; +update babel_2020_update_t1 set a = 100 from babel_2020_update_t1 x where exists (select a from babel_2020_update_t1 y where y.a + 1 = x.a); +go + +drop procedure if exists babel_2020_update_ct; +drop table if exists babel_2020_update_t1; +drop table if exists babel_2020_update_t2; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-2034.sql b/contrib/test/JDBC/input/BABEL-2034.sql new file mode 100644 index 00000000000..214ab5eb80d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2034.sql @@ -0,0 +1,79 @@ +CREATE TABLE EasDateTime (EasDateTime pg_catalog.timestamp, LastUpdDateTime pg_catalog.timestamp, LastCompressionMaxDate pg_catalog.timestamp, CompressionRate real); +GO + +-- Test ITVF - column references such as "easdatetime" in the query collides +-- with the column names of the returned rows - should not throw error +CREATE FUNCTION CalculateEasDateTime ( @InputDate DATETIME = NULL ) RETURNS TABLE AS RETURN ( +WITH +RawValues AS (SELECT EasDateTime, LastUpdDateTime, LastCompressionMaxDate, ISNULL(CompressionRate, 1.0) AS compressionrate, ISNULL(NULL, CURRENT_TIMESTAMP) AS currdatetime FROM EasDateTime), + +RawValues2 AS (SELECT ISNULL(EasDateTime, currdatetime) AS easdatetime, ISNULL(LastUpdDateTime, currdatetime) AS lastupddatetime, LastCompressionMaxDate, currdatetime, compressionrate FROM RawValues), + +Calcs AS (SELECT easdatetime, lastupddatetime, LastCompressionMaxDate, compressionrate, currdatetime, CASE WHEN easdatetime IS NULL THEN currdatetime ELSE DATEADD(s, DATEDIFF(s, lastupddatetime, currdatetime) * compressionrate, easdatetime) END AS adjeasdatetime FROM RawValues2), + +UnionedWithDefaults AS (SELECT easdatetime, lastupddatetime, LastCompressionMaxDate, compressionrate, currdatetime, adjeasdatetime AdjDatetimeWithoutCap, 2 WeightToForceDefault FROM Calcs UNION SELECT GETDATE() easdatetime, GETDATE() lastupddatetime, GETDATE() LastCompressionMaxDate, 1.0 compressionrate, GETDATE() currdatetime, GETDATE() AdjDatetimeWithoutCap, 1 WeightToForceDefault) + +SELECT TOP 1 easdatetime, lastupddatetime, LastCompressionMaxDate, compressionrate, currdatetime, AdjDatetimeWithoutCap FROM UnionedWithDefaults ORDER BY WeightToForceDefault DESC +); +GO + +SELECT count(*) FROM CalculateEasDateTime(); +GO + +-- Test MSTVF - the return parameter @tableVar should be added to the namespace +-- so that it can prevent duplicate parameter/variable names +-- Correct case - no duplicate +create function mstvf(@i int) returns @tableVar table +( + a text not null, + b int primary key, + c int +) +as +begin + insert into @tableVar values('hello1', 1, 100); + insert into @tableVar values('hello2', 2, 200); + return; +end; +GO +select * from mstvf(1); +GO + +-- Duplicate parameter name - should throw error +create function mstvf_dup_input_arg(@tableVar int) returns @tableVar table +( + a text not null, + b int primary key, + c int +) +as +begin + insert into @tableVar values('hello1', 1, 100); + insert into @tableVar values('hello2', 2, 200); + return; +end; +GO + +-- Duplicate variable name - should throw error +create function mstvf_dup_local_arg(@i int) returns @tableVar table +( + a text not null, + b int primary key, + c int +) +as +begin + declare @tableVar int; + insert into @tableVar values('hello1', 1, 100); + insert into @tableVar values('hello2', 2, 200); + return; +end; +GO + +-- cleanup +DROP FUNCTION CalculateEasDateTime; +GO +DROP TABLE EasDateTime; +GO +drop function mstvf; +go diff --git a/contrib/test/JDBC/input/BABEL-2049.sql b/contrib/test/JDBC/input/BABEL-2049.sql new file mode 100644 index 00000000000..43d23d17935 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2049.sql @@ -0,0 +1,62 @@ +create table t_babel_2049 (a int, b int); +insert into t_babel_2049 values (10, 1); +go + +declare @v int=1; +set @v+=10 +select @v +go + +declare @v int=11; +set @v-=10; +select @v; +go + +declare @v int=2; +set @v*=10; +select @v; +go + +declare @v int=20; +set @v/=10; +select @v; +go + +declare @v int=24; +set @v%=10; +select @v; +go + +declare @v int=63; +set @v&=10; +select @v; +go + +declare @v int=7; +set @v|=10; +select @v; +go + +declare @v int=7; +set @v^=10; +select @v; +go + +declare @a int=-10; +declare @v int=1; +set @v+=abs(@a) +select @v +go + +declare @v int=1; +set @v+=-10; +select @v +go + +declare @v int=1; +set @v+=+10; +select @v +go + +drop table t_babel_2049; +go diff --git a/contrib/test/JDBC/input/BABEL-2051.sql b/contrib/test/JDBC/input/BABEL-2051.sql new file mode 100644 index 00000000000..a4ed0ab00c5 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2051.sql @@ -0,0 +1,12 @@ +USE MASTER +go + +USE master +GO + +-- test square brackets +USE [master] +GO + +USE [maSter] +GO diff --git a/contrib/test/JDBC/input/BABEL-2079.sql b/contrib/test/JDBC/input/BABEL-2079.sql new file mode 100644 index 00000000000..24a8917ee1a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2079.sql @@ -0,0 +1,44 @@ +create schema error_mapping; +GO + +CREATE TABLE t3616(id int); +GO +CREATE TRIGGER t3616Trigger +ON t3616 +AFTER INSERT +AS +BEGIN + BEGIN TRY + BEGIN TRAN + INSERT INTO t3616 VALUES (2) + COMMIT TRAN + END TRY + BEGIN CATCH + END CATCH +END +GO + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3616 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +SET NOCOUNT ON +GO + +exec error_mapping.ErrorHandling1; +GO + +select * from t3616; +GO + +drop table t3616; +GO + +drop procedure error_mapping.ErrorHandling1; +GO + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/BABEL-2086.sql b/contrib/test/JDBC/input/BABEL-2086.sql new file mode 100644 index 00000000000..e437810dd16 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2086.sql @@ -0,0 +1,286 @@ +CREATE TABLE testing(col varchar(20) COLLATE latin1_general_90_bin2); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE latin1_general_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1250_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1251_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1253_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1254_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1255_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1256_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1257_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1258_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp1_cs_ai); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE sql_latin1_general_cp874_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE arabic_ci_ai); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_bin2); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1250_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1251_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1253_ci_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1254_ci_ai); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1255_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_pref_cp1256_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1257_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1258_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp1_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_cp874_cs_as); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE bbf_unicode_general_cs_ai); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Chinese_PRC_CI_AI); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Cyrillic_General_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Estonian_CI_AI); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Finnish_Swedish_CI_AI); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE French_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Greek_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Hebrew_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Korean_Wansung_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Modern_Spanish_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Mongolian_CI_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Polish_CI_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Thai_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Traditional_Spanish_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Turkish_CI_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Ukrainian_CI_AI); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO + +CREATE TABLE testing(col varchar(20) COLLATE Vietnamese_CS_AS); +INSERT INTO testing VALUES ('COLLATION'); +SELECT * FROM testing; +GO +DROP TABLE testing; +GO diff --git a/contrib/test/JDBC/input/BABEL-2203.sql b/contrib/test/JDBC/input/BABEL-2203.sql new file mode 100644 index 00000000000..8cb665cf9cc --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2203.sql @@ -0,0 +1,122 @@ +-- procedure in function +CREATE PROCEDURE p2203_inner AS + SELECT 1 +GO + +CREATE FUNCTION f2203() RETURNS INT AS +BEGIN + DECLARE @return INT + SET @return = 42 + EXEC p2203_inner -- should throw runtime error + RETURN @return +END +GO + +EXEC f2203 +GO + +-- EXEC function in function -> should be allowed +CREATE FUNCTION f2203_inner() RETURNS INT AS +BEGIN + RETURN 42; +END +GO + +CREATE FUNCTION f2203_2() RETURNS INT AS +BEGIN + DECLARE @return INT + EXEC @return = f2203_inner + RETURN @return +END +GO + +DECLARE @ret INT +EXEC @ret = f2203_2 +SELECT @ret +GO + +CREATE TABLE t2203(a int); +INSERT INTO t2203 VALUES (1); +GO + +CREATE FUNCTION f2203_ie() RETURNS INT AS +BEGIN + INSERT INTO t2203 EXEC p2203_inner; + RETURN 0; +END +GO + +CREATE FUNCTION f2203_i() RETURNS INT AS +BEGIN + INSERT INTO t2203 VALUES (2); + RETURN 0; +END +GO + +CREATE FUNCTION f2203_u() RETURNS INT AS +BEGIN + UPDATE t2203 SET a = 2; + RETURN 0; +END +GO + +CREATE FUNCTION f2203_d() RETURNS INT AS +BEGIN + DELETE FROM t2203; + RETURN 0; +END +GO + +CREATE FUNCTION f2203_cv() RETURNS INT AS +BEGIN + CREATE INDEX i2203 on t2203(a); + RETURN 0; +END +GO + +CREATE FUNCTION f2203_dt() RETURNS INT AS +BEGIN + DROP TABLE t2203; + RETURN 0; +END +GO + +-- exec in trigger should be allowed +CREATE TABLE t2203_inserted_by_proc(a int); +GO + +CREATE PROCEDURE p2203_2_inner AS + INSERT INTO t2203_inserted_by_proc VALUES (42); +GO + +CREATE TABLE t2203_2(a int); +INSERT INTO t2203_2 VALUES (1); +GO + +CREATE TRIGGER tr2203_2 on t2203_2 FOR INSERT AS +BEGIN + exec p2203_2_inner; +END +GO + +INSERT INTO t2203_2 VALUES (2); +GO + +SELECT * FROM t2203_2; +GO + +-- value should be inserted by proc triggered by trigger. +SELECT * FROM t2203_inserted_by_proc; +GO + +DROP PROCEDURE p2203_inner, p2203_2_inner; +GO + +DROP FUNCTION f2203, f2203_inner, f2203_2; +GO + +DROP TRIGGER tr2203_2; +GO + +DROP TABLE t2203, t2203_2, t2203_inserted_by_proc; +GO diff --git a/contrib/test/JDBC/input/BABEL-2225.sql b/contrib/test/JDBC/input/BABEL-2225.sql new file mode 100644 index 00000000000..bdc67068529 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2225.sql @@ -0,0 +1,24 @@ +CREATE SEQUENCE seq_2225 INCREMENT BY 6 MINVALUE 5 MAXVALUE 10; +GO + +CREATE SEQUENCE seq_2225 INCREMENT BY 6 MINVALUE 5 MAXVALUE 11; +GO + +ALTER SEQUENCE seq_2225 MAXVALUE 10; +GO + +ALTER SEQUENCE seq_2225 INCREMENT BY 7; +GO + +DROP SEQUENCE seq_2225; +GO + +-- Test with extreme values +CREATE SEQUENCE seq_2225 INCREMENT BY -9223372036854775808 MINVALUE 0 MAXVALUE 9223372036854775807; +GO + +CREATE SEQUENCE seq_2225 INCREMENT BY -9223372036854775808 MINVALUE -9223372036854775808 MAXVALUE 9223372036854775807; +GO + +DROP SEQUENCE seq_2225; +GO diff --git a/contrib/test/JDBC/input/BABEL-2234.sql b/contrib/test/JDBC/input/BABEL-2234.sql new file mode 100644 index 00000000000..597c036e5b9 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2234.sql @@ -0,0 +1,21 @@ +use master; +go + +-- sql batch +SELECT NULLIF(NULL, 2); +GO + +-- create procedure -> should throw compile-time error +CREATE PROCEDURE p_2234 AS + SELECT NULLIF(NULL, 2); +GO + +-- NULL variable should be allowed +declare @a int; +set @a = NULL; +select nullif(@a, 2); +go + +-- mixed case nullif +SELECT NuLlIf(nULL, 2); +GO diff --git a/contrib/test/JDBC/input/BABEL-2258.sql b/contrib/test/JDBC/input/BABEL-2258.sql new file mode 100644 index 00000000000..42b4275ff66 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2258.sql @@ -0,0 +1,11 @@ +select 'foo' where 'bar ' = 'bar'; +go + +select 'foo' where 0xAE = 0xAE00; +go + +select 'foo' where 101.5E5 = 1015E4; +go + +select 'foo' where (select 'bar ') = (select 'bar'); +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-2262.sql b/contrib/test/JDBC/input/BABEL-2262.sql new file mode 100644 index 00000000000..c8043b00a98 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2262.sql @@ -0,0 +1,41 @@ +CREATE TABLE t_2262(c1 int); +INSERT INTO t_2262 VALUES (42); +GO + +CREATE TABLE t_2262_2(c2 int); +GO + +CREATE PROCEDURE p_2262 + @pCursor CURSOR VARYING OUTPUT +AS +BEGIN + DECLARE @c1 INT; + FETCH NEXT FROM @pCursor INTO @c1 + INSERT INTO t_2262_2 VALUES (@c1); +END +GO + +DECLARE @cur CURSOR FOR SELECT c1 FROM t_2262; +OPEN @cur; +EXEC p_2262 @cur OUTPUT; +CLOSE @cur; +DEALLOCATE @cur; +GO + +SELECT c2 from t_2262_2; +GO + +CREATE FUNCTION f_2262 (@pCursor CURSOR VARYING OUTPUT) +RETURNS INT +AS +BEGIN + DECLARE @c1 INT; + FETCH NEXT FROM @pCursor INTO @c1 + RETURN @c1 + 1; +END; +GO + +DROP PROC p_2262; +DROP TABLE t_2262; +DROP TABLE t_2262_2; +GO diff --git a/contrib/test/JDBC/input/BABEL-2303.sql b/contrib/test/JDBC/input/BABEL-2303.sql new file mode 100644 index 00000000000..454ee99ddde --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2303.sql @@ -0,0 +1,67 @@ +-- Test multiplication between int types and money types +DECLARE @tinyint tinyint = 5 +DECLARE @smallint smallint = 5 +DECLARE @int bigint = 5 +DECLARE @smallmoney smallmoney = 2 +DECLARE @money money = 2 +SELECT + @tinyint * @smallmoney AS should_be_10 +,@tinyint * @money AS should_be_10 +,@smallint * @smallmoney AS should_be_10 +,@smallint * @money AS should_be_10 +,@int * @smallmoney AS should_be_10 +,@int * @money AS should_be_10 +,@smallmoney * @tinyint AS should_be_10 +,@money * @tinyint AS should_be_10 +,@smallmoney * @smallint AS should_be_10 +,@money * @smallint AS should_be_10 +,@smallmoney * @int AS should_be_10 +,@money * @int AS should_be_10 +GO + +CREATE TABLE t1 +( + id int PRIMARY KEY IDENTITY +,c_tinyint tinyint +,c_smallint smallint +,c_smallmoney smallmoney +,c_money money +,c_tinyint_m_smallmoney AS c_tinyint * c_smallmoney +,c_tinyint_m_money AS c_tinyint * c_money +,c_smallint_m_smallmoney AS c_smallint * c_smallmoney +,c_smallint_m_money AS c_smallint * c_money +,c_smallmoney_m_tinyint AS c_smallmoney * c_tinyint +,c_money_m_tinyint AS c_money * c_tinyint +,c_smallmoney_m_smallint AS c_smallmoney * c_smallint +,c_money_m_smallint AS c_money * c_smallint +) +GO +INSERT INTO t1(c_tinyint, c_smallint, c_smallmoney, c_money) VALUES(5,5,2,2) +GO +SELECT c_tinyint_m_smallmoney, c_tinyint_m_money, c_smallint_m_smallmoney, c_smallint_m_money, c_smallmoney_m_tinyint, c_money_m_tinyint, c_smallmoney_m_smallint, c_money_m_smallint FROM t1 +GO + +-- Test division between int types and money types +DECLARE @tinyint tinyint = 5 +DECLARE @smallint smallint = 5 +DECLARE @int bigint = 5 +DECLARE @smallmoney smallmoney = 2 +DECLARE @money money = 2 +SELECT + @tinyint / @smallmoney AS ts +,@tinyint / @money AS tm +,@smallint / @smallmoney AS ss +,@smallint / @money AS sm +,@int / @smallmoney AS ids +,@int / @money AS im +,@smallmoney / @tinyint AS st +,@money / @tinyint AS mt +,@smallmoney / @smallint AS ss +,@money / @smallint AS ms +,@smallmoney / @int AS si +,@money / @int AS mi +GO + +-- clean up +DROP TABLe t1; +GO diff --git a/contrib/test/JDBC/input/BABEL-2317.sql b/contrib/test/JDBC/input/BABEL-2317.sql new file mode 100644 index 00000000000..60b9df10f07 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2317.sql @@ -0,0 +1,22 @@ +CREATE TABLE t_2317 (c1 int IDENTITY PRIMARY KEY, c2 int default 42); +INSERT INTO t_2317 DEFAULT VALUES; +INSERT t_2317 DEFAULT VALUES; +INSERT INTO t_2317 with (dummy_hint) DEFAULT VALUES; +GO + +SELECT * FROM t_2317; +GO + +-- not yet supported since conflict at backend parser +INSERT INTO t_2317 output inserted.* DEFAULT VALUES; +GO +CREATE TABLE t_2317_2 (d1 int, d2 int); +GO +INSERT INTO t_2317 output inserted.c1, inserted.c2 INTO t_2317_2 DEFAULT VALUES; +GO +INSERT INTO t_2317 output inserted.c1, inserted.c2 INTO t_2317_2(d1, d2) DEFAULT VALUES; +GO + +DROP TABLE t_2317; +DROP TABLE t_2317_2; +GO diff --git a/contrib/test/JDBC/input/BABEL-2325.sql b/contrib/test/JDBC/input/BABEL-2325.sql new file mode 100644 index 00000000000..44799249d09 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2325.sql @@ -0,0 +1,39 @@ +SET QUOTED_IDENTIFIER OFF +GO + +-- should return literal +SELECT 'literal' +GO + +-- should report that column "ident" does not exist +SELECT [ident] +GO + +-- should return string +SELECT "string" +GO + +-- should report error (double-quoted string literals cannot contain single-quotes while QUOTED_IDENTIFIER=OFF) +SELECT "f'oo" +GO + +-------------------------------------------------------------------------------- + +SET QUOTED_IDENTIFIER ON +GO + +-- should return literal +SELECT 'literal' +GO + +-- should report that column "ident" does not exist +SELECT [ident] +GO + +-- should report that column "ident" does not exist +SELECT "ident" +GO + +-- should report that column "f'oo" does not exist +SELECT "f'oo" +GO diff --git a/contrib/test/JDBC/input/BABEL-2328.sql b/contrib/test/JDBC/input/BABEL-2328.sql new file mode 100644 index 00000000000..1fa135e6045 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2328.sql @@ -0,0 +1,58 @@ +CREATE PROCEDURE babel_2328_proc_varchar( + @nvcwl nvarchar(32), + @nvcwol nvarchar, + @vcwl varchar(32), + @vcwol varchar + ) as + select @nvcwl, @nvcwol, @vcwl, @vcwol; +; +go + +exec babel_2328_proc_varchar + N'nvarchar with length', + N'nvarchar without length', + N'varchar with length', + N'varchar without length' +; +go + +exec babel_2328_proc_varchar + 'nvarchar with length', + 'nvarchar without length', + 'varchar with length', + 'varchar without length' +; +go + +drop procedure babel_2328_proc_varchar; +go + + +CREATE PROCEDURE babel_2328_proc_char( + @ncwl nchar(32), + @ncwol nchar, + @cwl char(32), + @cwol char +) as +select @ncwl, @ncwol, @cwl, @cwol; + ; +go + +exec babel_2328_proc_char + N'nchar with length', + N'nchar without length', + N'char with length', + N'char without length' +; +go + +exec babel_2328_proc_char + 'nchar with length', + 'nchar without length', + 'char with length', + 'char without length' +; +go + +drop procedure babel_2328_proc_char; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-2337.sql b/contrib/test/JDBC/input/BABEL-2337.sql new file mode 100644 index 00000000000..87f8b39ce6f --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2337.sql @@ -0,0 +1,5 @@ +USE master; +GO + +SELECT nspname FROM sys.babelfish_namespace_ext where dbid in (1,2) and nspname like '%dbo' order by 1; +GO diff --git a/contrib/test/JDBC/input/BABEL-2347.sql b/contrib/test/JDBC/input/BABEL-2347.sql new file mode 100644 index 00000000000..ddde2fb50fc --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2347.sql @@ -0,0 +1,162 @@ +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(month, @date1, @date2) as momth_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(mm, @date1, @date2) as mm_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(day, @date1, @date2) as day_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(dd, @date1, @date2) as dd_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(year, @date1, @date2) as year_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(yyyy, @date1, @date2) as yyyy_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(yy, @date1, @date2) as yy_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(dayofyear, @date1, @date2) as yy_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(dy, @date1, @date2) as yy_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(y, @date1, @date2) as yy_diff -- BABEL-1063 +SELECT DATEADD(y, 1, @date1) as yy_add -- BABEL-1063 +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(quarter, @date1, @date2) as quarter_diff +go + +declare @date1 DATE; +declare @date2 DATE; +set @date1 = '2012-12-31' +set @date2 = '2013-01-01' +SELECT DATEDIFF(qq, @date1, @date2) as qq_diff +go + +-- BABEL-1626 +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(day, @date1, @date2) as day_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(dd, @date1, @date2) as dd_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(month, @date1, @date2) as momth_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(mm, @date1, @date2) as mm_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(year, @date1, @date2) as year_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(yyyy, @date1, @date2) as yyyy_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(yy, @date1, @date2) as yy_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(quarter, @date1, @date2) as quarter_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32 +10:0' +set @date2 = '1913-01-01 12:25:32 +10:0' +SELECT DATEDIFF(qq, @date1, @date2) as qq_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32.001 +10:0' +set @date2 = '1912-12-31 12:24:32.002 +10:0' +SELECT DATEDIFF(nanosecond, @date1, @date2) as nanosecond_diff +go + +declare @date1 datetimeoffset(6) +declare @date2 datetimeoffset(6) +set @date1 = '1912-12-31 12:24:32.001 +10:0' +set @date2 = '1912-12-31 12:24:32.002 +10:0' +SELECT DATEDIFF(ns, @date1, @date2) as ns_diff +go diff --git a/contrib/test/JDBC/input/BABEL-235.sql b/contrib/test/JDBC/input/BABEL-235.sql new file mode 100644 index 00000000000..73458178fdc --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-235.sql @@ -0,0 +1,59 @@ +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +GO + +SET ANSI_DEFAULTS ON; +GO + +-- Test invalid setting +SET ANSI_DEFAULTS OFF; +GO + +-- Test ANSI_DEFAULTS can be set to OFF when ESCAPE_HATCH_SESSION_SETTINGS = 'ignore' +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO +SET ANSI_DEFAULTS OFF; +GO + +-- expect OFF +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_nulls', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_warnings', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_null_dflt_on', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_padding', true); +GO +-- expect OFF +SELECT CURRENT_SETTING('babelfishpg_tsql.implicit_transactions', true); +GO +-- expect OFF +SELECT CURRENT_SETTING('babelfishpg_tsql.quoted_identifier', true); +GO + +SET ANSI_DEFAULTS ON; +GO + +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_nulls', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_warnings', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_null_dflt_on', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.ansi_padding', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.implicit_transactions', true); +GO +-- expect ON +SELECT CURRENT_SETTING('babelfishpg_tsql.quoted_identifier', true); +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO diff --git a/contrib/test/JDBC/input/BABEL-2350.sql b/contrib/test/JDBC/input/BABEL-2350.sql new file mode 100644 index 00000000000..9b5b660eb0e --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2350.sql @@ -0,0 +1,44 @@ +CREATE TABLE t1 (c1 int) +GO +INSERT INTO t1(c1) VALUES(1), (3), (4), (257) +GO + +CREATE TABLE t2 (c1 int) +GO + +CREATE PROC p1 +@limit int +AS + SELECT * FROM t1 WHERE c1 <= @limit +GO + +INSERT INTO t2(c1) +EXEC('EXEC p1 1000000') +GO + +SELECT * FROM t2; +GO + +-- Test more than one level of nested EXEC +CREATE PROC p2 +@limit int +AS + EXEC p1 @limit +GO + +INSERT INTO t2(c1) +EXEC('EXEC p2 1000000') +GO + +SELECT * from t2; +GO + +-- Cleanup +DROP TABLE t1 +GO +DROP TABLE t2 +GO +DROP PROC p1 +GO +DROP PROC p2 +GO diff --git a/contrib/test/JDBC/input/BABEL-2354.sql b/contrib/test/JDBC/input/BABEL-2354.sql new file mode 100644 index 00000000000..cb2a081203a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2354.sql @@ -0,0 +1,15 @@ +CREATE TABLE employeeData( ID INT IDENTITY (1,1) PRIMARY KEY,Emp_First_name VARCHAR (50),Emp_Last_name VARCHAR (50),Emp_Salary INT) +GO + +CREATE TRIGGER updEmployeeData ON employeeData AFTER UPDATE AS + IF (COLUMNS_UPDATED() & 14) > 0 + BEGIN + PRINT 'Columns 3, 5 and 9 updated'; + END; +GO + +drop trigger updEmployeeData +GO + +drop table employeeData +GO diff --git a/contrib/test/JDBC/input/BABEL-2355.sql b/contrib/test/JDBC/input/BABEL-2355.sql new file mode 100644 index 00000000000..7dc19e7f9ac --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2355.sql @@ -0,0 +1,15 @@ +-- All following procedures should report error. +-- Before the fix for BABEL-2355, all these +-- procedures lead to crash. + +CREATE PROC babel_2355_proc1 AS +DECLARE @a DECIMAL(38, 39) +go + +CREATE PROC babel_2355_proc2 AS +DECLARE @a INTA +go + +DROP PROC babel_2355_proc1 +DROP PROC babel_2355_proc2 +go diff --git a/contrib/test/JDBC/input/BABEL-2357.sql b/contrib/test/JDBC/input/BABEL-2357.sql new file mode 100644 index 00000000000..48e83409ffd --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2357.sql @@ -0,0 +1,9 @@ +-- Tests that ANSI_PADDING can be set to ON +SET ANSI_PADDING ON; +GO + +DECLARE @v VARCHAR(20); +SET ANSI_PADDING ON; +SET @v = 'SHOULD BE SHOWN'; +SELECT @v; +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-2371.sql b/contrib/test/JDBC/input/BABEL-2371.sql new file mode 100644 index 00000000000..761a32efea0 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2371.sql @@ -0,0 +1,53 @@ +-- simple case +create table t_2371(AbCd int, EfGh varchar(10)); +GO +select attname, attoptions from pg_class C, pg_attribute A where C.oid = A.attrelid and C.relname like 't_2371' and A.attnum > 0 and attisdropped = 'f' order by attname; +GO + +-- alter table add column +alter table t_2371 add IjKl int; +select attname, attoptions from pg_class C, pg_attribute A where C.oid = A.attrelid and C.relname like 't_2371' and A.attnum > 0 and attisdropped = 'f' order by attname; +GO +alter table t_2371 drop column EfGh; +GO +select attname, attoptions from pg_class C, pg_attribute A where C.oid = A.attrelid and C.relname like 't_2371' and A.attnum > 0 and attisdropped = 'f' order by attname; +GO +drop table t_2371 +GO + +-- identifier longer than 64 characters +create table t_2371_2(A123456789B123456789C123456789D123456789E123456789F123456789G123456789H123456789I123456789J123456789K123456789 int); +GO +select attname, attoptions from pg_class C, pg_attribute A where C.oid = A.attrelid and C.relname like 't_2371_2' and A.attname like 'a123456789%'; +GO +drop table t_2371_2 +GO + +-- non-reserved keyword (level is non-reserved keyword in PG) +create table t_2371_3 (LeVeL int); +GO +select attname, attoptions from pg_class C, pg_attribute A where C.oid = A.attrelid and C.relname like 't_2371_3' and A.attname like 'level'; +GO +drop table t_2371_3 +GO + +-- delimited identifier +SET QUOTED_IDENTIFIER ON; +GO +create table t_2371_4 ([Abcd] int, "Efgh" int, Ijhl int, [K)@m($[^&] int, "x'AbC""e" int, """""""" int) +GO +select attname, attoptions from pg_class C, pg_attribute A where C.oid = A.attrelid and C.relname like 't_2371_4' and A.attnum > 0 and attisdropped = 'f' order by attname; +GO +drop table t_2371_4 +GO + +-- CREATE TYPE +CREATE TYPE ty_2371_5 as table ("a""B""c" int); +GO +select attname, attoptions from pg_class C, pg_attribute A where C.oid = A.attrelid and C.relname like 'ty_2371_5' and A.attnum > 0 and attisdropped = 'f' order by attname; +GO +drop type ty_2371_5 +GO + +SET QUOTED_IDENTIFIER OFF; -- default +GO diff --git a/contrib/test/JDBC/input/BABEL-2372.sql b/contrib/test/JDBC/input/BABEL-2372.sql new file mode 100644 index 00000000000..ab47344e541 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2372.sql @@ -0,0 +1,18 @@ +-- Setting language to anything other than "us_english" will throw an error in strict mode +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +GO + +SET LANGUAGE Italian +GO + +SET LANGUAGE us_english +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO + +SET LANGUAGE Italian +GO + +SET LANGUAGE us_english +GO diff --git a/contrib/test/JDBC/input/BABEL-2390.sql b/contrib/test/JDBC/input/BABEL-2390.sql new file mode 100644 index 00000000000..635a208c697 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2390.sql @@ -0,0 +1,24 @@ +create table t_2390(a int); +insert into t_2390 values (1), (2); +GO + +-- query hint option should be ignored +DECLARE @a int +DECLARE c_byname CURSOR STATIC LOCAL FOR select a from t_2390 order by a option (maxdop 1) +open c_byname +fetch c_byname into @a +select @a +close c_byname +GO + +-- table hint shoudl be ignored +DECLARE @a int +DECLARE c_byname CURSOR STATIC LOCAL FOR select a from t_2390 with (dummy_hint) order by a +open c_byname +fetch c_byname into @a +select @a +close c_byname +GO + +drop table t_2390; +GO diff --git a/contrib/test/JDBC/input/BABEL-2392.sql b/contrib/test/JDBC/input/BABEL-2392.sql new file mode 100644 index 00000000000..bfe1234ba93 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2392.sql @@ -0,0 +1,49 @@ +USE master; +go + +create procedure sp1 (@a int = 0, @b varchar(10)) as begin select @a, @b; end; +go + +-- Test out of order arguments +exec sp1 @b = "abcd", @a = 3; +go + +-- Test normal order arguments +exec sp1 @a = 3, @b = "abcd"; +go + +-- Test missing argument +exec sp1 @b = "abcd"; +go + +-- Test truncation on varchar/varchar(1) with out of order arguments +create procedure sp2 (@a int, @b varchar) as begin select @a, @b; end; +go + +-- Test out of order arguments +exec sp2 @b = "abcd", @a = 3; +go + +-- Test normal order arguments +exec sp2 @a = 3, @b = "abcd"; +go + +-- Test OUTPUT param and missing param +create proc sp3 (@a int, @b varchar(10) = NULL, @c varchar(8) OUTPUT) as begin select @a, @b, @c; end +go + +-- Test missing param @b +exec sp3 @c = 'abcdefghijklmn', @a = 1; +go + +-- Test out of order arguments +exec sp3 @b = 'abcdefghijklmn', @c = 'abcdefghijklmn', @a = 1; +go + +-- Test normal order arguments +exec sp3 @a = 1, @b = 'abcdefghijklmn', @c = 'abcdefghijklmn'; +go + +-- Clean up +drop procedure sp1, sp2, sp3; +go diff --git a/contrib/test/JDBC/input/BABEL-2403.mix b/contrib/test/JDBC/input/BABEL-2403.mix new file mode 100644 index 00000000000..202e621280a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2403.mix @@ -0,0 +1,3 @@ +--psql +SELECT * FROM sys.babelfish_inconsistent_metadata(); +GO diff --git a/contrib/test/JDBC/input/BABEL-2409.mix b/contrib/test/JDBC/input/BABEL-2409.mix new file mode 100644 index 00000000000..d7e6cddaa79 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2409.mix @@ -0,0 +1,31 @@ +-- tsql +USE master +go + +-- tsql +CREATE SCHEMA abc; +GO + +-- psql +DROP SCHEMA master_abc; +GO + +-- psql +ALTER SCHEMA master_abc RENAME TO abc; +GO + +-- psql +CREATE SCHEMA master_xyz; +GO + +-- tsql +DROP SCHEMA xyz; +GO + +-- psql +DROP SCHEMA master_xyz; +GO + +-- tsql +DROP SCHEMA abc; +GO diff --git a/contrib/test/JDBC/input/BABEL-2410.sql b/contrib/test/JDBC/input/BABEL-2410.sql new file mode 100644 index 00000000000..fbc41a5668b --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2410.sql @@ -0,0 +1,27 @@ +USE master +go + +-- Should be blocked +CREATE USER babel_2410_user +go + +-- Should be blocked +CREATE ROLE babel_2410_role +go + +CREATE PROC babel_2410_proc AS +SELECT 123 +go + +-- Should be blocked +GRANT ALL ON babel_2410_proc TO jdbc_user +go + +DROP PROC babel_2410_proc +go + +DROP USER babel_2410_user +go + +DROP ROLE babel_2410_role +go diff --git a/contrib/test/JDBC/input/BABEL-2412.sql b/contrib/test/JDBC/input/BABEL-2412.sql new file mode 100644 index 00000000000..4c7ef9d0809 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2412.sql @@ -0,0 +1,5 @@ +create procedure empty_proc as ; +go + +drop procedure empty_proc; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-2416.sql b/contrib/test/JDBC/input/BABEL-2416.sql new file mode 100644 index 00000000000..65ad98f4a49 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2416.sql @@ -0,0 +1,15 @@ +-- schema id is not a stable value, test nullability instead +USE master +GO + +SELECT CASE WHEN schema_id('dbo') IS NULL then 'is null' ELSE 'not null' END; +GO + +USE tempdb +GO + +SELECT CASE WHEN schema_id('dbo') IS NULL then 'is null' ELSE 'not null' END; +GO + +USE master +GO diff --git a/contrib/test/JDBC/input/BABEL-2417.sql b/contrib/test/JDBC/input/BABEL-2417.sql new file mode 100644 index 00000000000..161460ef85a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2417.sql @@ -0,0 +1,5 @@ +-- Only need to scan the entire table and ensure no error is raised +DECLARE @a INT +SELECT @a = COUNT(*) FROM sys.objects +SELECT (CASE WHEN @a > 1 THEN 'true' ELSE 'false' END) AS result +go diff --git a/contrib/test/JDBC/input/BABEL-2418.sql b/contrib/test/JDBC/input/BABEL-2418.sql new file mode 100644 index 00000000000..5b548144a7c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2418.sql @@ -0,0 +1,26 @@ +USE master +go + +CREATE DATABASE babel_2418_db +go + +USE babel_2418_db +go + +CREATE SCHEMA babel_2418_schema1 +go + +CREATE SCHEMA babel_2418_schema2 +go + +SELECT nspname FROM sys.babelfish_namespace_ext; +go + +USE master +go + +DROP DATABASE babel_2418_db +go + +SELECT nspname FROM sys.babelfish_namespace_ext; +go diff --git a/contrib/test/JDBC/input/BABEL-2419.sql b/contrib/test/JDBC/input/BABEL-2419.sql new file mode 100644 index 00000000000..04622bd3449 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2419.sql @@ -0,0 +1,20 @@ +create table t_2419 (a varchar(10)); +insert into t_2419 values ('correct'); + +select a 'alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij' +from t_2419 +order by alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij +go + +select a 'alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij' +from t_2419 +order by [alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] +go + +select a 'alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij' +from t_2419 +order by [ALIAS_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] +go + +drop table t_2419; +go diff --git a/contrib/test/JDBC/input/BABEL-2432.sql b/contrib/test/JDBC/input/BABEL-2432.sql new file mode 100644 index 00000000000..f8c6b6b2657 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2432.sql @@ -0,0 +1,27 @@ +-- mixed-case column name. select via lowercase name +create table t2432 ([COL_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] int); +insert into t2432 values (1); +GO + +select [col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] from t2432; +GO + +select col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij from t2432; +GO + +drop table t2432; +GO + +-- lowercase column name. select via mixed-case name +create table t2432_2 ([col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] int); +insert into t2432_2 values (1); +GO + +select [COL_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] from t2432_2; +GO + +select COL_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij from t2432_2; +GO + +drop table t2432_2; +GO diff --git a/contrib/test/JDBC/input/BABEL-2433.sql b/contrib/test/JDBC/input/BABEL-2433.sql new file mode 100644 index 00000000000..f91835c386d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2433.sql @@ -0,0 +1,34 @@ +USE master; +GO + +create table obytt (a int); +insert into obytt values (1); +go + +select a as B from obytt order by b; +go + +select a as B from obytt order by B; +go + +-- ORDER BY b+3 is not allowed in either the tsql or the postgres dialect +-- error is: column "b" does not exist +select a as B from obytt order by b+3; +go + +-- However, order by a+3 is allowed because a is a column name +select a+3 as B from obytt order by a+3; +go + +-- and it is done case-insensitively if the db collation is CI +select a+3 as B from obytt order by a+3; +go + +select a+3 as B from obytt order by a; +go + +select a+3 as b from obytt order by B; +go + +drop table obytt; +go diff --git a/contrib/test/JDBC/input/BABEL-2437.sql b/contrib/test/JDBC/input/BABEL-2437.sql new file mode 100644 index 00000000000..372340d7124 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2437.sql @@ -0,0 +1,16 @@ +use master; +go + +create schema babel_2437_schema; +go + +create table babel_2437_schema.t1 (a int primary key); +go + +create table babel_2437_schema.t2 (b int, FOREIGN KEY (b) REFERENCES babel_2437_schema.t1 (a) ON DELETE CASCADE ON UPDATE CASCADE); +go + +drop table if exists babel_2437_schema.t2; +drop table if exists babel_2437_schema.t1; +drop schema babel_2437_schema; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-244.sql b/contrib/test/JDBC/input/BABEL-244.sql new file mode 100644 index 00000000000..dc47e2c515c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-244.sql @@ -0,0 +1,43 @@ +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (a INT); +GO + +INSERT INTO t1 +VALUES (1), (2); +GO + +-- Test by default cursor can be used after commit tran +-- i.e. default setting of cursor_close_on_commit is off + +BEGIN TRAN; +DECLARE testcursor CURSOR FOR + SELECT a FROM t1; +OPEN testcursor; +COMMIT TRAN; +FETCH NEXT FROM testcursor; +CLOSE testcursor; +DEALLOCATE testcursor; +GO + +-- test cursor can not be used after close +FETCH NEXT FROM testcursor; +GO + +-- Test invalid setting. +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +SET CURSOR_CLOSE_ON_COMMIT ON; +GO + +-- Test CURSOR_CLOSE_ON_COMMIT can be set when escape_hatch_session_settings is ignore +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +SET CURSOR_CLOSE_ON_COMMIT ON; +GO + +SET CURSOR_CLOSE_ON_COMMIT OFF; +GO + +-- Clean up +DROP TABLE t1; +GO +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO diff --git a/contrib/test/JDBC/input/BABEL-2440.mix b/contrib/test/JDBC/input/BABEL-2440.mix new file mode 100644 index 00000000000..a60f2fc3dc6 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2440.mix @@ -0,0 +1,38 @@ +-- tsql +SELECT session_user, current_user, db_name(); +GO + +CREATE LOGIN r1 WITH PASSWORD = '123'; +GO + +-- tsql user=r1 password=123 +SELECT session_user, current_user, db_name(); +GO + +ALTER LOGIN r1 WITH PASSWORD = 'abc'; +GO + +SELECT session_user, current_user, db_name(); +GO + +ALTER LOGIN r1 WITH PASSWORD = '123abc' OLD_PASSWORD = 'abc'; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_login_old_password', 'ignore', 'false') +GO + +ALTER LOGIN r1 WITH PASSWORD = '123abc' OLD_PASSWORD = 'abc'; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_login_old_password', 'strict', 'false') +GO + +SELECT session_user, current_user, db_name(); +GO + +-- tsql +ALTER LOGIN r1 WITH PASSWORD = 'abc'; +GO + +DROP LOGIN r1; +GO diff --git a/contrib/test/JDBC/input/BABEL-2458.mix b/contrib/test/JDBC/input/BABEL-2458.mix new file mode 100644 index 00000000000..7e1131a1840 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2458.mix @@ -0,0 +1,82 @@ +-- psql +SHOW "babelfishpg_tsql.migration_mode"; -- make sure it is single-db mode +GO + +-- tsql +USE master -- 1. test master DB +GO + +CREATE SCHEMA test2458; +GO +CREATE SCHEMA master; +GO +CREATE SCHEMA xyz_sfab; +GO +-- test very long schema name +CREATE SCHEMA wqreqwreqwerqwereqwrqreqwrqwrqwerqewreqwreqwrewqreerweqrqwreqwreqreqewrqwereqrrereqwerqwereqwrw; +GO + +-- test sys.schemas return logical schema name +-- test schema_id and schema_name to handle logical name +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'test2458'; +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'master'; +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'xyz_sfab'; +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'dbo'; +GO + +SELECT COUNT(*) FROM sys.schemas where schema_name(schema_id(name)) = 'wqreqwreqwerqwereqwrqreqwrqwrqwerqewreqwreqwrewqreerweqrqwreqwreqreqewrqwereqrrereqwerqwereqwrw'; +GO + +-- cleanup +DROP SCHEMA test2458; +GO +DROP SCHEMA master; +GO +DROP SCHEMA xyz_sfab; +GO +DROP SCHEMA wqreqwreqwerqwereqwrqreqwrqwrqwerqewreqwreqwrewqreerweqrqwreqwreqreqewrqwereqrrereqwerqwereqwrw; +GO + + +CREATE DATABASE db_a; -- 2. test single-db mode user DB +GO + +USE db_a; +GO + +CREATE SCHEMA test2458; +GO +CREATE SCHEMA master; +GO +CREATE SCHEMA xyz_sfab; +GO +-- test very long schema name +CREATE SCHEMA wqreqwreqwerqwereqwrqreqwrqwrqwerqewreqwreqwrewqreerweqrqwreqwreqreqewrqwereqrrereqwerqwereqwrw; +GO + +-- test sys.schemas return logical schema name +-- test schema_id and schema_name to handle logical name +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'test2458'; +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'master'; +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'xyz_sfab'; +SELECT name, schema_name(schema_id(name)) FROM sys.schemas where name = 'dbo'; +GO + +SELECT COUNT(*) FROM sys.schemas where schema_name(schema_id(name)) = 'wqreqwreqwerqwereqwrqreqwrqwrqwerqewreqwreqwrewqreerweqrqwreqwreqreqewrqwereqrrereqwerqwereqwrw'; +GO + +-- cleanup +DROP SCHEMA test2458; +GO +DROP SCHEMA master; +GO +DROP SCHEMA xyz_sfab; +GO +DROP SCHEMA wqreqwreqwerqwereqwrqreqwrqwrqwerqewreqwreqwrewqreerweqrqwreqwreqreqewrqwereqrrereqwerqwereqwrw; +GO + +USE master; +GO + +DROP DATABASE db_a; +GO diff --git a/contrib/test/JDBC/input/BABEL-2470.sql b/contrib/test/JDBC/input/BABEL-2470.sql new file mode 100644 index 00000000000..dc198d7bc57 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2470.sql @@ -0,0 +1,22 @@ +-- verify sys.databases.owner_sid and sys.sysdatabases.sid are the same +-- inner select should return all 1's (e.g. columns are the same) +select owner_sid from +sys.sysdatabases as sdb +inner join sys.databases as d on sdb.name = d.name where owner_sid != [sid] +go + +-- verify value of owner_sid/sid from sys.databases/sys.sysdatabases +-- exists in principal_id from server_principals +select [sid] from sys.sysdatabases + where cast(cast([sid] as int) as oid) not in + ( + select principal_id from sys.server_principals + ) +go + +select owner_sid from sys.databases + where cast(cast(owner_sid as int) as oid) not in + ( + select principal_id from sys.server_principals + ) +go diff --git a/contrib/test/JDBC/input/BABEL-2482.sql b/contrib/test/JDBC/input/BABEL-2482.sql new file mode 100644 index 00000000000..e2cb0e1f05f --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2482.sql @@ -0,0 +1,28 @@ +USE master +go + +-- create user defined type +create type dbo.inttype from int; +GO + +-- create stored procedure with arg & default referencing UDT +create proc p4 @v dbo.inttype = cast (1 as dbo.inttype) as SELECT @v; +GO + +-- execute default value +EXEC p4 +GO + +-- execute non-default value +EXEC p4 10 +GO + +-- test select case +select cast (1 as dbo.inttype); +GO + +DROP PROCEDURE p4 +GO + +DROP TYPE dbo.inttype; +GO diff --git a/contrib/test/JDBC/input/BABEL-2513.sql b/contrib/test/JDBC/input/BABEL-2513.sql new file mode 100644 index 00000000000..68d14eeeb0d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2513.sql @@ -0,0 +1,354 @@ +use master; +go + +CREATE TABLE t2513( + dto datetimeoffset, + dt2 datetime2, + dt datetime, + sdt smalldatetime, + d date, + t time); + +INSERT INTO t2513 values ('1900-01-01 00:00:00', '1900-01-01 00:00:00', '1900-01-01 00:00:00', '1900-01-01 00:00:00', '1900-01-01', '00:00:00'); -- all same +INSERT INTO t2513 values ('1900-01-01 00:00:00', '1900-01-02 00:00:02', '1900-01-03 00:00:03', '1900-01-04 00:00:04', '1900-01-05', '00:00:06'); -- ascending +INSERT INTO t2513 values ('1900-01-06 00:00:00', '1900-01-05 00:00:05', '1900-01-04 00:00:04', '1900-01-03 00:00:03', '1900-01-02', '00:00:01'); -- descending +GO + +-- start of dto + +-- dto vs dto +select * from t2513 where dto = dto; +select * from t2513 where dto != dto; +select * from t2513 where dto < dto; +select * from t2513 where dto <= dto; +select * from t2513 where dto > dto; +select * from t2513 where dto >= dto; +GO + +-- dto vs dt2 +select * from t2513 where dto = dt2; +select * from t2513 where dto != dt2; +select * from t2513 where dto < dt2; +select * from t2513 where dto <= dt2; +select * from t2513 where dto > dt2; +select * from t2513 where dto >= dt2; +GO + +-- dto vs dt +select * from t2513 where dto = dt; +select * from t2513 where dto != dt; +select * from t2513 where dto < dt; +select * from t2513 where dto <= dt; +select * from t2513 where dto > dt; +select * from t2513 where dto >= dt; +GO + +-- dto vs sdt +select * from t2513 where dto = sdt; +select * from t2513 where dto != sdt; +select * from t2513 where dto < sdt; +select * from t2513 where dto <= sdt; +select * from t2513 where dto > sdt; +select * from t2513 where dto >= sdt; +GO + +-- dto vs d +select * from t2513 where dto = d; +select * from t2513 where dto != d; +select * from t2513 where dto < d; +select * from t2513 where dto <= d; +select * from t2513 where dto > d; +select * from t2513 where dto >= d; +GO + +-- dto vs t +select * from t2513 where dto = t; +select * from t2513 where dto != t; +select * from t2513 where dto < t; +select * from t2513 where dto <= t; +select * from t2513 where dto > t; +select * from t2513 where dto >= t; +GO + +-- start of dt2 + +-- dt2 vs dto +select * from t2513 where dt2 = dto; +select * from t2513 where dt2 != dto; +select * from t2513 where dt2 < dto; +select * from t2513 where dt2 <= dto; +select * from t2513 where dt2 > dto; +select * from t2513 where dt2 >= dto; +GO + +-- dt2 vs dt2 +select * from t2513 where dt2 = dt2; +select * from t2513 where dt2 != dt2; +select * from t2513 where dt2 < dt2; +select * from t2513 where dt2 <= dt2; +select * from t2513 where dt2 > dt2; +select * from t2513 where dt2 >= dt2; +GO + +-- dt2 vs dt +select * from t2513 where dt2 = dt; +select * from t2513 where dt2 != dt; +select * from t2513 where dt2 < dt; +select * from t2513 where dt2 <= dt; +select * from t2513 where dt2 > dt; +select * from t2513 where dt2 >= dt; +GO + +-- dt2 vs sdt +select * from t2513 where dt2 = sdt; +select * from t2513 where dt2 != sdt; +select * from t2513 where dt2 < sdt; +select * from t2513 where dt2 <= sdt; +select * from t2513 where dt2 > sdt; +select * from t2513 where dt2 >= sdt; +GO + +-- dt2 vs d +select * from t2513 where dt2 = d; +select * from t2513 where dt2 != d; +select * from t2513 where dt2 < d; +select * from t2513 where dt2 <= d; +select * from t2513 where dt2 > d; +select * from t2513 where dt2 >= d; +GO + +-- dt2 vs t +select * from t2513 where dt2 = t; +select * from t2513 where dt2 != t; +select * from t2513 where dt2 < t; +select * from t2513 where dt2 <= t; +select * from t2513 where dt2 > t; +select * from t2513 where dt2 >= t; +GO + +-- start of dt + +-- dt vs dto +select * from t2513 where dt = dto; +select * from t2513 where dt != dto; +select * from t2513 where dt < dto; +select * from t2513 where dt <= dto; +select * from t2513 where dt > dto; +select * from t2513 where dt >= dto; +GO + +-- dt vs dt2 +select * from t2513 where dt = dt2; +select * from t2513 where dt != dt2; +select * from t2513 where dt < dt2; +select * from t2513 where dt <= dt2; +select * from t2513 where dt > dt2; +select * from t2513 where dt >= dt2; +GO + +-- dt vs dt +select * from t2513 where dt = dt; +select * from t2513 where dt != dt; +select * from t2513 where dt < dt; +select * from t2513 where dt <= dt; +select * from t2513 where dt > dt; +select * from t2513 where dt >= dt; +GO + +-- dt vs sdt +select * from t2513 where dt = sdt; +select * from t2513 where dt != sdt; +select * from t2513 where dt < sdt; +select * from t2513 where dt <= sdt; +select * from t2513 where dt > sdt; +select * from t2513 where dt >= sdt; +GO + +-- dt vs d +select * from t2513 where dt = d; +select * from t2513 where dt != d; +select * from t2513 where dt < d; +select * from t2513 where dt <= d; +select * from t2513 where dt > d; +select * from t2513 where dt >= d; +GO + +-- dt vs t +select * from t2513 where dt = t; +select * from t2513 where dt != t; +select * from t2513 where dt < t; +select * from t2513 where dt <= t; +select * from t2513 where dt > t; +select * from t2513 where dt >= t; +GO + +-- start of sdt + +-- sdt vs dto +select * from t2513 where sdt = dto; +select * from t2513 where sdt != dto; +select * from t2513 where sdt < dto; +select * from t2513 where sdt <= dto; +select * from t2513 where sdt > dto; +select * from t2513 where sdt >= dto; +GO + +-- sdt vs dt2 +select * from t2513 where sdt = dt2; +select * from t2513 where sdt != dt2; +select * from t2513 where sdt < dt2; +select * from t2513 where sdt <= dt2; +select * from t2513 where sdt > dt2; +select * from t2513 where sdt >= dt2; +GO + +-- sdt vs dt +select * from t2513 where sdt = dt; +select * from t2513 where sdt != dt; +select * from t2513 where sdt < dt; +select * from t2513 where sdt <= dt; +select * from t2513 where sdt > dt; +select * from t2513 where sdt >= dt; +GO + +-- sdt vs sdt +select * from t2513 where sdt = sdt; +select * from t2513 where sdt != sdt; +select * from t2513 where sdt < sdt; +select * from t2513 where sdt <= sdt; +select * from t2513 where sdt > sdt; +select * from t2513 where sdt >= sdt; +GO + +-- sdt vs d +select * from t2513 where sdt = d; +select * from t2513 where sdt != d; +select * from t2513 where sdt < d; +select * from t2513 where sdt <= d; +select * from t2513 where sdt > d; +select * from t2513 where sdt >= d; +GO + +-- sdt vs t +select * from t2513 where sdt = t; +select * from t2513 where sdt != t; +select * from t2513 where sdt < t; +select * from t2513 where sdt <= t; +select * from t2513 where sdt > t; +select * from t2513 where sdt >= t; +GO + +-- start of d + +-- d vs dto +select * from t2513 where d = dto; +select * from t2513 where d != dto; +select * from t2513 where d < dto; +select * from t2513 where d <= dto; +select * from t2513 where d > dto; +select * from t2513 where d >= dto; +GO + +-- d vs dt2 +select * from t2513 where d = dt2; +select * from t2513 where d != dt2; +select * from t2513 where d < dt2; +select * from t2513 where d <= dt2; +select * from t2513 where d > dt2; +select * from t2513 where d >= dt2; +GO + +-- d vs dt +select * from t2513 where d = dt; +select * from t2513 where d != dt; +select * from t2513 where d < dt; +select * from t2513 where d <= dt; +select * from t2513 where d > dt; +select * from t2513 where d >= dt; +GO + +-- d vs sdt +select * from t2513 where d = sdt; +select * from t2513 where d != sdt; +select * from t2513 where d < sdt; +select * from t2513 where d <= sdt; +select * from t2513 where d > sdt; +select * from t2513 where d >= sdt; +GO + +-- d vs d +select * from t2513 where d = d; +select * from t2513 where d != d; +select * from t2513 where d < d; +select * from t2513 where d <= d; +select * from t2513 where d > d; +select * from t2513 where d >= d; +GO + +-- d vs t +select * from t2513 where d = t; +select * from t2513 where d != t; +select * from t2513 where d < t; +select * from t2513 where d <= t; +select * from t2513 where d > t; +select * from t2513 where d >= t; +GO + +-- start of t + +-- t vs dto +select * from t2513 where t = dto; +select * from t2513 where t != dto; +select * from t2513 where t < dto; +select * from t2513 where t <= dto; +select * from t2513 where t > dto; +select * from t2513 where t >= dto; +GO + +-- t vs dt2 +select * from t2513 where t = dt2; +select * from t2513 where t != dt2; +select * from t2513 where t < dt2; +select * from t2513 where t <= dt2; +select * from t2513 where t > dt2; +select * from t2513 where t >= dt2; +GO + +-- t vs dt +select * from t2513 where t = dt; +select * from t2513 where t != dt; +select * from t2513 where t < dt; +select * from t2513 where t <= dt; +select * from t2513 where t > dt; +select * from t2513 where t >= dt; +GO + +-- t vs sdt +select * from t2513 where t = sdt; +select * from t2513 where t != sdt; +select * from t2513 where t < sdt; +select * from t2513 where t <= sdt; +select * from t2513 where t > sdt; +select * from t2513 where t >= sdt; +GO + +-- t vs d +select * from t2513 where t = d; +select * from t2513 where t != d; +select * from t2513 where t < d; +select * from t2513 where t <= d; +select * from t2513 where t > d; +select * from t2513 where t >= d; +GO + +-- t vs t +select * from t2513 where t = t; +select * from t2513 where t != t; +select * from t2513 where t < t; +select * from t2513 where t <= t; +select * from t2513 where t > t; +select * from t2513 where t >= t; +GO + +drop table t2513 +GO diff --git a/contrib/test/JDBC/input/BABEL-2515.sql b/contrib/test/JDBC/input/BABEL-2515.sql new file mode 100644 index 00000000000..512a0710d32 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2515.sql @@ -0,0 +1,32 @@ +use master; +GO + +CREATE TABLE [BABEL-2515] +( + [PartitionId] [smallint] NOT NULL, + CONSTRAINT [PK_DataRecord2056] PRIMARY KEY CLUSTERED ( [PartitionId] ASC ) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) + ON [XFPS_DataRecord2056]([PartitionId]) +) +ON [XFPS_DataRecord2056]([PartitionId]); +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_storage_on_partition', 'ignore'; +GO + +CREATE TABLE [BABEL-2515] +( + [PartitionId] [smallint] NOT NULL, + CONSTRAINT [PK_DataRecord2056] PRIMARY KEY CLUSTERED ( [PartitionId] ASC ) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) + ON [XFPS_DataRecord2056]([PartitionId]) +) +ON [XFPS_DataRecord2056]([PartitionId]); +GO + +DROP TABLE [BABEL-2515] +GO + +-- reset to default +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_storage_on_partition', 'strict'; +GO diff --git a/contrib/test/JDBC/input/BABEL-2535.sql b/contrib/test/JDBC/input/BABEL-2535.sql new file mode 100644 index 00000000000..d4d784dfd65 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2535.sql @@ -0,0 +1,177 @@ +CREATE TABLE t8134 (a INT) +GO +CREATE TABLE tmp (a INT) +GO + +CREATE TRIGGER tr1 ON t8134 +AFTER INSERT AS + BEGIN TRY + INSERT INTO tmp VALUES (1); + SELECT 1/0; + INSERT INTO tmp VALUES (2); + END TRY + BEGIN CATCH + SELECT XACT_STATE() AS "XACT_STATE" + END CATCH +GO + +INSERT INTO t8134 VALUES (1); +GO + +SELECT * FROM tmp; +GO + +CREATE TABLE t8134_1 (a INT) +GO +CREATE TABLE tmp_1 (a INT) +GO + +CREATE TRIGGER tr2 ON t8134_1 +AFTER INSERT AS + INSERT INTO tmp_1 VALUES (555); +GO + +INSERT INTO t8134_1 VALUES (1); +INSERT INTO t8134 VALUES (1); +GO + +SELECT * FROM tmp_1; +GO + +SELECT * FROM tmp; +GO + +SELECT * FROM t8134_1; +GO + +TRUNCATE TABLE tmp_1; +GO + +BEGIN TRAN; +GO + +INSERT INTO t8134_1 VALUES (1); +INSERT INTO t8134 VALUES (1); +GO + +SELECT * FROM tmp_1; +GO + +SELECT * FROM tmp; +GO + +SELECT @@trancount; +GO + +SELECT * FROM t8134_1; +GO + +TRUNCATE TABLE tmp; +GO + +DROP TRIGGER tr1; +GO + +CREATE TRIGGER tr1 ON t8134 +AFTER INSERT AS + SELECT 1/0; +GO + +INSERT INTO tmp VALUES (666); +INSERT INTO t8134 VALUES (1); +INSERT INTO tmp VALUES (777); +GO + +SELECT * FROM tmp; +GO + +DROP TRIGGER tr1; +GO + +DROP TRIGGER tr2; +GO + +CREATE TABLE t1 (a INT); +GO + +CREATE TABLE t2 (a INT); +GO + +create trigger tr2 on t2 +after insert as +begin try + insert into t1 values(1); +end try +begin catch + print 'tr2'; + select @@trancount; +end catch; +GO + +create trigger tr1 on t1 +after insert as +begin try + select 1/0; +end try +begin catch + print 'tr1'; + select @@trancount; + rollback tran; +end catch +GO + +INSERT INTO t2 VALUES (1); +GO + +DROP TRIGGER tr1; +GO + +CREATE TRIGGER tr1 ON t1 +AFTER INSERT AS + BEGIN TRY + SELECT 1/0 + END TRY + BEGIN CATCH + SELECT XACT_STATE() AS "XACT_STATE" --Should be -1 + COMMIT + END CATCH +go +INSERT INTO t1 VALUES(1) +go +SELECT @@TRANCOUNT AS "TRANCOUNT" --Should be 0 +go + +DROP TRIGGER tr1; +GO + +CREATE TRIGGER tr1 ON t1 +AFTER INSERT AS + BEGIN TRY + SELECT 1/0 + END TRY + BEGIN CATCH + SELECT XACT_STATE() AS "XACT_STATE" --Should be -1 + ROLLBACK + END CATCH +go +INSERT INTO t1 VALUES(1) +go +SELECT @@TRANCOUNT AS "TRANCOUNT" --Should be 0 +go + +DROP TRIGGER tr1; +GO +DROP TRIGGER tr2; +GO +DROP TABLE t2; +GO +DROP TABLE t1; +GO +DROP TABLE t8134; +GO +DROP TABLE t8134_1; +GO +DROP TABLE tmp; +GO +DROP TABLE tmp_1; +GO diff --git a/contrib/test/JDBC/input/BABEL-2585.sql b/contrib/test/JDBC/input/BABEL-2585.sql new file mode 100644 index 00000000000..774312e822d --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2585.sql @@ -0,0 +1,178 @@ +USE master; +go + +CREATE DATABASE [babelfish-1234]; +go + +DROP DATABASE [babelfish-1234]; +go + +CREATE DATABASE babel_recursive_cte; +go + +DROP DATABASE babel_recursive_cte; +go + +CREATE SCHEMA [babelfish-1234]; +go + +DROP SCHEMA [babelfish-1234]; +go + +CREATE SCHEMA babel_recursive_cte; +go + +DROP SCHEMA babel_recursive_cte; +go + +-- Test for BABEL-2589 +-- Check the stability of CREATE/DROP LOGIN with different names +CREATE LOGIN babel_2589_login_1 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_1 +go + +CREATE LOGIN babel_2589_login_2 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_2 +go + +CREATE LOGIN babel_2589_login_3 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_3 +go + +CREATE LOGIN babel_2589_login_4 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_4 +go + +CREATE LOGIN babel_2589_login_5 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_5 +go + +CREATE LOGIN babel_2589_login_6 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_6 +go + +CREATE LOGIN babel_2589_login_7 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_7 +go + +CREATE LOGIN babel_2589_login_8 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_8 +go + +CREATE LOGIN babel_2589_login_9 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_9 +go + +CREATE LOGIN babel_2589_login_10 WITH PASSWORD = '123' +go +DROP LOGIN babel_2589_login_10 +go + +-- Test for BABEL-2600 +-- Check stability of CREATE/DROP DATABASE with different names +CREATE DATABASE babel_2600_db_1 +go +USE babel_2600_db_1 +go +USE master +go +DROP DATABASE babel_2600_db_1 +go + +CREATE DATABASE babel_2600_db_2 +go +USE babel_2600_db_2 +go +USE master +go +DROP DATABASE babel_2600_db_2 +go + +CREATE DATABASE babel_2600_db_3 +go +USE babel_2600_db_3 +go +USE master +go +DROP DATABASE babel_2600_db_3 +go + +CREATE DATABASE babel_2600_db_4 +go +USE babel_2600_db_4 +go +USE master +go +DROP DATABASE babel_2600_db_4 +go + +CREATE DATABASE babel_2600_db_5 +go +USE babel_2600_db_5 +go +USE master +go +DROP DATABASE babel_2600_db_5 +go + +-- Check stability of CREATE/DROP DATABASE with the same name +CREATE DATABASE babel_2600_db +go +USE babel_2600_db +go +USE master +go +DROP DATABASE babel_2600_db +go + +CREATE DATABASE babel_2600_db +go +USE babel_2600_db +go +USE master +go +DROP DATABASE babel_2600_db +go + +CREATE DATABASE babel_2600_db +go +USE babel_2600_db +go +USE master +go +DROP DATABASE babel_2600_db +go + +CREATE DATABASE babel_2600_db +go +USE babel_2600_db +go +USE master +go +DROP DATABASE babel_2600_db +go + +CREATE DATABASE babel_2600_db +go +USE babel_2600_db +go +USE master +go +DROP DATABASE babel_2600_db +go + +-- Test for BABEL-2586 +-- Check if we can get the correct default db after multiple operations +DECLARE @default_db TEXT +SET @default_db = sys.babelfish_get_login_default_db('jdbc_user'); +SELECT CASE WHEN @default_db = 'master' THEN 'correct' ELSE 'error' END; +go diff --git a/contrib/test/JDBC/input/BABEL-2593.sql b/contrib/test/JDBC/input/BABEL-2593.sql new file mode 100644 index 00000000000..5412cc2e047 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2593.sql @@ -0,0 +1,48 @@ +use master +go + +create schema testschema +go + +create procedure testschema.proc1 as select 1 +go + +create table testschema.table1 (a int primary key); +go + +create view testschema.view1 as select a from testschema.table1; +go + +create procedure dbo.proc2 as select 1 +go + +create table dbo.table2 (a int primary key); +go + +create view dbo.view2 as select a from dbo.table2; +go + +-- test procedure, table, constraint and view are visible for testschema +select name from sys.objects where name ='proc1' or name = 'table1' or name = 'view1' or name = 'table1_pkey'; +go + +-- test procedure, table, constraint and view are visible for dbo schema +select name from sys.objects where name ='proc2' or name = 'table2' or name = 'view2' or name = 'table2_pkey'; +go + +-- test proc1 and proc2 are visible in sys.procedures +select name from sys.procedures where name ='proc1' +go + +select name from sys.procedures where name ='proc2' +go + +--Clean up +drop procedure testschema.proc1; +drop view testschema.view1; +drop table testschema.table1; +drop schema testschema; +drop procedure dbo.proc2; +drop view dbo.view2; +drop table dbo.table2; +go diff --git a/contrib/test/JDBC/input/BABEL-2607.sql b/contrib/test/JDBC/input/BABEL-2607.sql new file mode 100644 index 00000000000..cf79dae38fd --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-2607.sql @@ -0,0 +1,35 @@ +USE master +go + +CREATE DATABASE babel_2607_db +go + +-- This is to test if sp_helpdb can be executed successfully +-- Output are not fixed values, and sp_helpdb is a procedure which we cannot +-- control the output, so we run the underlying function instead +SELECT + CASE WHEN name IS NOT NULL + AND owner IS NOT NULL + AND dbid > 0 + AND created IS NOT NULL + THEN 'correct' ELSE 'error' END +FROM babelfish_helpdb(); +go + +SELECT name, dbid +FROM babelfish_helpdb('master') +WHERE name = 'master' +go + +SELECT name, dbid +FROM babelfish_helpdb('tempdb') +WHERE name = 'tempdb' +go + +SELECT name +FROM babelfish_helpdb('babel_2607_db') +WHERE name = 'babel_2607_db'; +go + +DROP DATABASE babel_2607_db +go diff --git a/contrib/test/JDBC/input/BABEL-265.sql b/contrib/test/JDBC/input/BABEL-265.sql new file mode 100644 index 00000000000..b24b5f3dad6 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-265.sql @@ -0,0 +1,56 @@ +-- When ANSI_NULLS is ON, the conditions exp = NULL, exp <> NULL and exp != NULL are never true: +CREATE TABLE t_nulls1 (c1 CHAR(1), c2 INT); +INSERT INTO t_nulls1 VALUES ('A', 1); +INSERT INTO t_nulls1 VALUES ('B', NULL); +GO + +-- Expect 0 rows +SELECT c1 FROM t_nulls1 WHERE c2 = NULL; +GO + +-- Expect 0 rows +SELECT c1 FROM t_nulls1 WHERE c2 <> NULL; +GO + +SELECT c1 FROM t_nulls1 WHERE c2 != NULL; +GO + +-- Only IS NULL and IS NOT NULL can be used. Result: A +SELECT c1 FROM t_nulls1 WHERE c2 IS NOT NULL; +GO + +SET ANSI_NULLS OFF +GO + +-- Expect A +SELECT c1 FROM t_nulls1 WHERE c2 <> NULL; +GO + +SELECT c1 FROM t_nulls1 WHERE c2 != NULL; +GO + +-- Expect B +SELECT c1 FROM t_nulls1 WHERE c2 = NULL; +GO + +-- IS NULL and IS NOT NULL still can be used when ANSI_NULLS is OFF +SELECT c1 FROM t_nulls1 WHERE c2 IS NOT NULL; +GO + +-- RESET ANSI_NULLS AND test behavior is back to normal +SET ANSI_NULLS ON + +-- Expect 0 rows +SELECT c1 FROM t_nulls1 WHERE c2 = NULL; +GO + +-- Expect 0 rows +SELECT c1 FROM t_nulls1 WHERE c2 <> NULL; +GO + +SELECT c1 FROM t_nulls1 WHERE c2 != NULL; +GO + +-- Clean up +DROP TABLE t_nulls1; +GO diff --git a/contrib/test/JDBC/input/BABEL-338.sql b/contrib/test/JDBC/input/BABEL-338.sql new file mode 100644 index 00000000000..c0747f3d395 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-338.sql @@ -0,0 +1,126 @@ +create table t (a int, b int) +go + +insert t values(1,1) +insert t values(2,2) +insert t values(3,3) +insert t values(4,4) +insert t values(5,5) +go + +select * from t +go + +update t set b = b*-1 where b > 0 +go + +select * from t +go + +update top(2) t set b = b*-1 where b < 0 +go + +select count(*) from t where b > 0 +go + +delete top(2) from t where b < 0 +go + +select count(*) from t +go + +declare @a int +set @a = 1 +delete top (@a) from t +go + +select count(*) from t +go + +-- test TOP clause in UPDATE together with JOIN +create table t1 (a int, b int) +go + +insert t1 values(1,1) +insert t1 values(2,2) +insert t1 values(3,3) +insert t1 values(4,4) +insert t1 values(5,5) +go + +create table t2 (a int, b int) +go + +insert t2 values(1,-1) +insert t2 values(2,2) +insert t2 values(3,3) +insert t2 values(400,4) +insert t2 values(500,5) +go + +update top(2) t1 set a = a*-1 from t1 alias1 inner join t2 alias2 on alias1.a = alias2.a +go + +select count(*) from t1 where a < 0 +go + +-- test TOP clause in DELETE together with JOIN +create table t3 (a int, b int) +go + +insert t3 values(1,1) +insert t3 values(2,2) +insert t3 values(-3,-3) +go + +create table t4 (a int, b int) +go + +insert t4 values(1,-1) +insert t4 values(2,2) +insert t4 values(3,3) +insert t4 values(400,4) +insert t4 values(500,5) +go + +delete top(1) t3 from t3 alias3 inner join t4 alias4 on alias3.b = alias4.b +go + +select count(*) from t3 +go + +-- test error message on TOP n PERCENT +-- TOP 100 PERCENT should be supported +update top (100) percent t3 set b = 100 +go + +select count(*) from t3 where b = 100 +go + +delete top (100) percent from t3 +go + +select count(*) from t3 +go + +-- other percentage should report unsupported error +update top (10) percent t4 set b = 100 +go + +delete top (10) percent from t4 +go + +drop table t +go + +drop table t1 +go + +drop table t2 +go + +drop table t3 +go + +drop table t4 +go diff --git a/contrib/test/JDBC/input/BABEL-388.sql b/contrib/test/JDBC/input/BABEL-388.sql new file mode 100644 index 00000000000..9b55559c921 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-388.sql @@ -0,0 +1,65 @@ +-- note: this test only can run on babel since it relies on pg_typeof + +create view babel_388_v1 as select null a; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v1; +GO + +create view babel_388_v2 as select null a union select null; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v2; +GO + +create view babel_388_v3 as select null a union select null union select 1; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v3; +GO + +create view babel_388_v4 as select 1 a; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v4; +GO + +create view babel_388_v5 as select 'string' a; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v5; +GO + +create view babel_388_v6 as select 1 a UNION select NULL; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v6; +GO + +create view babel_388_v7 as select NULL a UNION select 1; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v7; +GO + +create view babel_388_v8 as select 'string' a UNION select NULL; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v8; +GO + +create view babel_388_v9 as select NULL a UNION select 'string'; +GO +select cast(pg_typeof(a) as varchar(10)) as typname from babel_388_v9; +GO + +DROP view babel_388_v1; +GO +DROP view babel_388_v2; +GO +DROP view babel_388_v3; +GO +DROP view babel_388_v4; +GO +DROP view babel_388_v5; +GO +DROP view babel_388_v6; +GO +DROP view babel_388_v7; +GO +DROP view babel_388_v8; +GO +DROP view babel_388_v9; +GO diff --git a/contrib/test/JDBC/input/BABEL-405.sql b/contrib/test/JDBC/input/BABEL-405.sql new file mode 100644 index 00000000000..3435f190568 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-405.sql @@ -0,0 +1,200 @@ +create table t_405_insert1(x int); +create table t_405_insert2(x int); +insert into t_405_insert1 values(1); +insert into t_405_insert1 select * from t_405_insert1; +insert into t_405_insert1 select * from t_405_insert1; +go + +create trigger tr1 on t_405_insert2 instead of insert +as +begin +select * from t_405_insert1; +end +go + +create trigger tr2 on t_405_insert2 instead of insert +as +begin +select * from t_405_insert1; +end +go + +insert into t_405_insert2 values(2); +go + +select * from t_405_insert2; +go + +drop trigger tr1; +go + +create trigger tr1 on t_405_insert2 instead of insert +as +begin +end +go + +insert into t_405_insert2 values(2); +select * from t_405_insert2; +go + +drop trigger tr1; +go + +insert into t_405_insert2 values(2); +select * from t_405_insert2; +go + +drop table t_405_insert1; +drop table t_405_insert2; +go + +create table t_405_delete1(x int); +create table t_405_delete2(x int); +insert into t_405_delete1 values(1); +insert into t_405_delete1 select * from t_405_delete1; +insert into t_405_delete1 select * from t_405_delete1; +insert into t_405_delete2 values(2); +go + +create trigger tr1 on t_405_delete2 instead of delete +as +begin +select * from t_405_delete1; +end +go + +create trigger tr2 on t_405_delete2 instead of insert +as +begin +select '1'; +end +go + +delete from t_405_delete2 where 1=1; +go + +select * from t_405_delete2; +go + +drop trigger tr1; +go + +create trigger tr1 on t_405_delete2 instead of delete +as +begin +end +go + +delete from t_405_delete2; +select * from t_405_delete2; +go + +drop trigger tr1; +go + +insert into t_405_delete2 values(2); +select * from t_405_delete2; +go + +drop table t_405_delete1; +drop table t_405_delete2; +go + +create table t_405_update1(x int); +create table t_405_update2(x int); +insert into t_405_update1 values(1); +insert into t_405_update1 select * from t_405_update1; +insert into t_405_update1 select * from t_405_update1; +insert into t_405_update2 values(1); +go + +create trigger tr1 on t_405_update2 instead of update +as +begin +select * from t_405_update1; +end +go + +create trigger tr2 on t_405_update2 instead of update +as +begin +select 'hello'; +end +go + +update t_405_update2 set x = 2 where x=1; +go + +select * from t_405_update2; +go + +drop trigger tr1; +go + +create trigger tr1 on t_405_update2 instead of update +as +begin +end +go + +update t_405_update2 set x = 3 where 1 = 1; +select * from t_405_update2; +go + +drop trigger tr1; +go + +update t_405_update2 set x=2 where x=1; +select * from t_405_update2; +go + +drop table t_405_update1; +drop table t_405_update2; +go + +create table t_405_insert1(x int); +create table t_405_insert2(x int); +insert into t_405_insert1 values(1); +insert into t_405_insert1 select * from t_405_insert1; +insert into t_405_insert1 select * from t_405_insert1; +go + +create trigger tr1 on t_405_insert2 instead of insert +as +begin +select * from t_405_insert1 +end; +go + +create trigger after_trig on t_405_insert2 after insert +as +begin +select 'hello' +end; +go + +insert into t_405_insert2 values(3); +go + +drop trigger tr1; +go + +insert into t_405_insert2 values(3); +go + +select * from t_405_insert2; +go + +drop trigger after_trig; + +insert into t_405_insert2 values(3); +go + +select * from t_405_insert2; +go + +drop table t_405_insert1; +drop table t_405_insert2; +go + diff --git a/contrib/test/JDBC/input/BABEL-480.sql b/contrib/test/JDBC/input/BABEL-480.sql new file mode 100644 index 00000000000..c167dbef8d1 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-480.sql @@ -0,0 +1,17 @@ +create procedure p_rcv3 as begin select 123 end +go + +create procedure p_rcv12a +as +begin -- begin + end lines are optional +select 'hello' +select 'world' +select '!!!' +end -- begin + end lines are optional +go + +drop procedure p_rcv3 +GO + +drop procedure p_rcv12a +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-493.sql b/contrib/test/JDBC/input/BABEL-493.sql new file mode 100644 index 00000000000..f25b0856b25 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-493.sql @@ -0,0 +1,70 @@ +-- please note that this test conatins PG-specific feature to check the plan (to confirm index is hit) + +create table babel_493_t1(i int, b bit); +GO +create index babel_493_t1_full_idx_a on babel_493_t1(i); +GO + +-- FULL index +create index babel_493_t1_full_idx_b on babel_493_t1(b); +GO + +insert into babel_493_t1 values (1, 0), (2, 0), (3, 0), (4, 0); +insert into babel_493_t1 select i+4, b from babel_493_t1; +insert into babel_493_t1 select i+8, b from babel_493_t1; +insert into babel_493_t1 select i+16, b from babel_493_t1; +insert into babel_493_t1 select i+32, b from babel_493_t1; +insert into babel_493_t1 select i+64, b from babel_493_t1; +insert into babel_493_t1 select i+128, b from babel_493_t1; +insert into babel_493_t1 select i+256, b from babel_493_t1; +insert into babel_493_t1 select i+512, b from babel_493_t1; +insert into babel_493_t1 select i+1024, b from babel_493_t1; +insert into babel_493_t1 select i+2048, b from babel_493_t1; +-- make a few rows have bit value 1 +update babel_493_t1 set b = 1 where i = 3; +update babel_493_t1 set b = 1 where i = 1111; +update babel_493_t1 set b = 1 where i = 4093; +-- make a few rows have bit value NULL +update babel_493_t1 set b = NULL where i = 7; +update babel_493_t1 set b = NULL where i = 2222; +GO + +SELECT count(*) from babel_493_t1 where b = 0; +GO + +SELECT count(*) from babel_493_t1 where b = 1; +GO + +drop index babel_493_t1_full_idx_b on babel_493_t1; +GO + +-- PARTIAL index +create index babel_493_t1_partial_idx_b on babel_493_t1(b) where b = 1; +GO + +-- Bitmap Index Scan +SELECT count(*) from babel_493_t1 where b = 1; +GO + +-- Bitmap Index Scan + BitMap And +SELECT count(*) from babel_493_t1 where b = 1 and i = 4093; +GO + +drop index babel_493_t1_partial_idx_b on babel_493_t1; +GO + +-- composite index and check matching of boolean index columns to WHERE conditions and sort keys +create index babel_493_composite_idx_a_b on babel_493_t1(b,i); +GO + +select top(10) * from babel_493_t1 order by b, i; +GO + +select top(10) * from babel_493_t1 where b = 1 order by i; +GO + +select top(10) * from babel_493_t1 where not (b = 1) order by i; +GO + +DROP table babel_493_t1; +GO diff --git a/contrib/test/JDBC/input/BABEL-559.sql b/contrib/test/JDBC/input/BABEL-559.sql new file mode 100644 index 00000000000..7c60f7811ef --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-559.sql @@ -0,0 +1,35 @@ +create table babel_559_t1 (a int); +go + +alter table babel_559_t1 add primary key (a asc); +go + +create table babel_559_t2 (a int, b int, c int, d int); +go + +alter table babel_559_t2 add primary key (a asc, b desc , c); +go + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +go + +alter table babel_559_t2 add unique (a asc); +go + +alter table babel_559_t2 add unique (a asc, c desc); +go + +create table babel_559_t3 (a int, primary key(a asc)); +go + +create table babel_559_t4 (a int, b int, primary key(a asc, b desc)); +go + +create table babel_559_t5 (a int, b int, c varchar(20), unique(a asc, b, c desc)); +go + +create table babel_559_t6 (a int); +go + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; +go diff --git a/contrib/test/JDBC/input/BABEL-565.sql b/contrib/test/JDBC/input/BABEL-565.sql new file mode 100644 index 00000000000..998c525d482 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-565.sql @@ -0,0 +1,37 @@ +CREATE TABLE AccountsReceivable ( + Id int IDENTITY(1,1) NOT NULL, + ApplicationId int NOT NULL +) ON [PRIMARY] +GO + +drop table AccountsReceivable +GO + +-- test TEXTIMAGE_ON +CREATE TABLE AccountsReceivable ( + Id int IDENTITY(1,1) NOT NULL, + ApplicationId int NOT NULL +) TEXTIMAGE_ON [PRIMARY] +GO + +drop table AccountsReceivable +GO + +-- test on primary with TEXTIMAGE_ON +CREATE TABLE AccountsReceivable ( + Id int IDENTITY(1,1) NOT NULL, + ApplicationId int NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +drop table AccountsReceivable +GO + +CREATE TABLE AccountsReceivable ( + Id int IDENTITY(1,1) NOT NULL, + ApplicationId int NOT NULL +) TEXTIMAGE_ON [PRIMARY] ON [PRIMARY] +GO + +drop table AccountsReceivable +GO diff --git a/contrib/test/JDBC/input/BABEL-586.sql b/contrib/test/JDBC/input/BABEL-586.sql new file mode 100644 index 00000000000..dbc4fae0a85 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-586.sql @@ -0,0 +1,99 @@ +CREATE PROCEDURE test_proc1 (@p0 int, @p1 int OUTPUT, @p2 smallint, @p3 smallint OUTPUT) +AS BEGIN +SELECT @p1=@p0, @p3=@p2; +RETURN; +END +GO + +CREATE PROCEDURE test_proc2 (@p0 int, @p1 int OUTPUT, @p2 smallint, @p3 smallint OUTPUT) +AS BEGIN +SELECT @p1=@p0, @p3= @p2; +RETURN; +END +GO + +CREATE PROCEDURE test_proc3 (@p0 int, @p1 int OUTPUT, @p2 smallint, @p3 smallint OUTPUT) +AS BEGIN +SELECT @p1= @p0, @p3=@p2; +RETURN; +END +GO + +CREATE PROCEDURE test_proc4 (@p0 int, @p1 int OUTPUT, @p2 smallint, @p3 smallint OUTPUT) +AS BEGIN +SELECT @p1=@p0+@p0, @p3= @p2+@p2; +RETURN; +END +GO + +CREATE PROCEDURE test_proc5 (@p0 int, @p1 int OUTPUT, @p2 smallint, @p3 smallint OUTPUT) +AS BEGIN +SELECT @p1=CAST(@p0 as INT), @p3= @p2+@p2; +RETURN; +END +GO + +CREATE PROCEDURE test_proc6 (@p0 int, @p1 int OUTPUT, @p2 smallint, @p3 smallint OUTPUT) +AS BEGIN +SELECT @p1=1234, @p3= 5678; +RETURN; +END +GO + + +CREATE PROCEDURE test_proc0 +AS BEGIN +DECLARE @a int; +DECLARE @b smallint; + +EXEC test_proc1 1, @a OUT, 2, @b OUT; +PRINT @a +PRINT @b + +EXEC test_proc2 3, @a OUT, 4, @b OUT; +PRINT @a +PRINT @b + +EXEC test_proc3 5, @a OUT, 6, @b OUT; +PRINT @a +PRINT @b + +EXEC test_proc4 7, @a OUT, 9, @b OUT; +PRINT @a +PRINT @b + +EXEC test_proc5 11, @a OUT, 13, @b OUT; +PRINT @a +PRINT @b + +EXEC test_proc6 17, @a OUT, 19, @b OUT; +PRINT @a +PRINT @b + +END +GO + +EXEC test_proc0 +GO + +DROP PROCEDURE test_proc1 +GO + +DROP PROCEDURE test_proc2 +GO + +DROP PROCEDURE test_proc3 +GO + +DROP PROCEDURE test_proc4 +GO + +DROP PROCEDURE test_proc5 +GO + +DROP PROCEDURE test_proc6 +GO + +DROP PROCEDURE test_proc0 +GO + diff --git a/contrib/test/JDBC/input/BABEL-588.sql b/contrib/test/JDBC/input/BABEL-588.sql new file mode 100644 index 00000000000..29b039af10a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-588.sql @@ -0,0 +1,1026 @@ +-- Tests for OUTPUT with INSERT statement -- +create table t1(num integer, word varchar(10)); +go + +insert into t1 output inserted.num values(1, 'one'); +go + +insert into t1 output inserted.num, inserted.word values(2, 'two'); +go + +insert into t1 output inserted.* values(3, 'three'); +go + +select * from t1; +go + +-- Test conflict case with table name +create table inserted(num integer); +go + +insert into inserted output inserted.* values(10); +go + +-- Tests for OUTPUT with DELETE statement -- +delete t1 output deleted.num where num=1; +go + +delete t1 output deleted.num, deleted.word where word='two'; +go + +delete t1 output deleted.* where num=3; +go + +select * from t1; +go + +-- Test conflict cases with table name +create table deleted(num integer, nextnum integer); +go + +insert into deleted values(10, 11), (12, 13), (14,15); +go + +insert into t1 values(10, 'ten'), (12, 'twelve'), (14, 'fourteen'); +go + +delete deleted +output deleted.num +from deleted +inner join t1 + on deleted.num=t1.num +where t1.num=10; +go + +delete deleted +output deleted.nextnum +from deleted +inner join t1 + on deleted.num=t1.num +where t1.num=12; +go + +delete t1 +output t1.word +from t1 +inner join deleted + on t1.num=deleted.num +where t1.num=14; +go + +select * from deleted; +go + +select * from t1; +go + +-- Cleanup +drop table t1; +go + +drop table inserted; +go + +drop table deleted; +go + +-- Tests for OUTPUT INTO with INSERT statement -- +create table t1(num integer, word varchar(10)); +go + +create table t2(num integer, word varchar(10)); +go + +insert into t1(num, word) + output inserted.num, inserted.word into t2 +values(1, 'one'); +go + +select * from t1; +go + +select * from t2; +go + +create table t3(num integer, word varchar(10)); +go + +with cte(num, word) as( + select num, word from t1 +) +insert into t2(num, word) + output inserted.num, inserted.word into t3 +select num, word from cte; +go + +select * from t3; +go + +-- Test recursive CTE case +create table t4(num integer); +go + +create table t5(num integer); +go + +with Numbers as +( + select 1 as n + union all + select n + 1 from Numbers where n + 1 <= 10 +) +insert into t4(num) +output num into t5 +select n from Numbers; +go + +select * from t4; +go + +-- Cleanup +drop table t1; +go + +drop table t2; +go + +drop table t3; +go + +drop table t4; +go + +drop table t5; +go + +-- Tests for OUTPUT INTO with DELETE statement -- +create table t1(num integer, word varchar(10)); +go + +insert into t1 values(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'); +go + +create table t1_insert(num integer, word varchar(10)); +go + +delete t1 +output deleted.* into t1_insert +where num < 4; +go + +create table t2(num integer, word varchar(10)); +go + +insert into t1 values(1, 'one'), (2, 'two'), (3, 'three'); +go + +select * from t1; +go + +select * from t1_insert; +go + +delete from t1_insert +output deleted.num, deleted.word into t2 +where num in ( + select num from t1); +go + +select * from t1_insert; +go + +select * from t2; +go + +create table inserted(num integer); +go + +insert into inserted output inserted.* values(10); +go + +create table deleted(num integer, nextnum integer); +go + +insert into deleted values(10, 11); +go + +insert into t1 values(10, 'ten'); +go + +delete deleted +output deleted.num into inserted +from deleted +inner join t1 + on deleted.num=t1.num +where t1.num=10; +go + +select * from deleted; +go + +select * from t1; +go + +-- Cleanup +drop table t1; +go + +drop table t1_insert; +go + +drop table t2; +go + +drop table inserted; +go + +drop table deleted; +go + +-- Tests for OUTPUT with UPDATE statement -- +create table t1(a integer); +go + +insert into t1(a) values (1),(2); +go + +update t1 set a=20 +output deleted.a, inserted.a +where a>1; +go + +update t1 set a=30 +output deleted.a, inserted.a; +go + +-- Test that order of execution of AND and OR in where clause is preserved +create table t2(a integer, b integer, c integer, d integer); +go + +insert into t2 values(1,2,3,4), (5,6,7,8), (4,2,6,1), (8,9,0,3); +go + +update t2 set a=25 output deleted.* +where a>2 and b<20 or c>5 and d>0; +go + +create table table1 (age integer, fname varchar(100), year integer); + insert into table1 (age, fname, year) values (10, 'albert', 30); + insert into table1 (age, fname, year) values (100, 'isaac', 40); + insert into table1 (age, fname, year) values (30, 'marie', 70); + select * from table1; +go + +create table table2 (age integer, fname varchar(100), lastname varchar(100)); + insert into table2 (age, fname, lastname) values (10, 'albert', 'einstein'); + insert into table2 (age, fname, lastname) values (100, 'isaac', 'newton'); + insert into table2 (age, fname, lastname) values (30, 'mary', 'kom'); + select * from table2; +go + +update table1 set age=1 output deleted.age +from table1 t1 +left join table2 t2 +on t1.fname=t2.fname where year>50 and lastname!='smith'; +go + +update table1 set age=1 output deleted.age +from table1 t1 +left join table2 t2 +on t1.fname=t2.fname where year>50; +go + +update table1 set year=1990 output deleted.*, inserted.* +from table1 t1 +left join table2 t2 +on t1.fname=t2.fname where t1.fname='isaac'; + +update table1 set year=2020 output deleted.* +from table2 t2 +where table1.fname=t2.fname and lastname='einstein'; +go + +-- Cleanup +drop table t1; +go + +drop table t2; +go + +drop table table1; +go + +drop table table2; +go + +-- Tests for OUTPUT INTO with UPDATE statement -- +create table t1(a integer); +go + +create table t1_insert(a integer); +go + +insert into t1(a) values (1),(2); +go + +update t1 set a=20 +output deleted.a into t1_insert +where a>1; +go + +select * from t1_insert; +go + +update t1 set a=30 +output inserted.a into t1_insert; +go + +select * from t1_insert; +go + +-- Test that order of execution of AND and OR in where clause is preserved +create table t2(a integer, b integer, c integer, d integer); +go + +create table t2_insert(a integer, b integer, c integer, d integer); +go + +insert into t2 values(1,2,3,4), (5,6,7,8), (4,2,6,1), (8,9,0,3); +go + +update t2 set a=25 output deleted.* into t2_insert +where a>2 and b<20 or c>5 and d>0; +go + +select * from t2_insert; +go + +create table table1 (age integer, fname varchar(100), year integer); + insert into table1 (age, fname, year) values (10, 'albert', 30); + insert into table1 (age, fname, year) values (100, 'isaac', 40); + insert into table1 (age, fname, year) values (30, 'marie', 70); + select * from table1; +go + +create table table2 (age integer, fname varchar(100), lastname varchar(100)); + insert into table2 (age, fname, lastname) values (10, 'albert', 'einstein'); + insert into table2 (age, fname, lastname) values (100, 'isaac', 'newton'); + insert into table2 (age, fname, lastname) values (30, 'mary', 'kom'); + select * from table2; +go + +create table table_insert (age integer, fname varchar(100), year integer); +go + +update table1 set age=1 +output deleted.age, inserted.fname, inserted.year into table_insert +from table1 t1 +left join table2 t2 +on t1.fname=t2.fname where year>50 and lastname!='smith'; +go + +select * from table_insert; +go + +update table1 set age=1 +output deleted.age, inserted.fname, inserted.year into table_insert +from table1 t1 +left join table2 t2 +on t1.fname=t2.fname where year>50; +go + +select * from table_insert; +go + +update table1 set year=1990 output deleted.*, inserted.* +from table1 t1 +left join table2 t2 +on t1.fname=t2.fname where t1.fname='isaac'; + +select * from table_insert; +go + +update table1 set year=2020 output deleted.* +from table2 t2 +where table1.fname=t2.fname and lastname='einstein'; +go + +select * from table_insert; + +-- Cleanup +drop table t1; +go + +drop table t1_insert; +go + +drop table t2; +go + +drop table t2_insert; +go + +drop table table1; +go + +drop table table2; +go + +drop table table_insert; +go + +-- Tests for order of execution of OUTPUT clause -- +create table t1 (age integer, fname varchar(20), year integer); +go + +create trigger t1_insert_trig on t1 for insert as +begin + update t1 set age = 99; +end; +go + +insert into t1 output inserted.* values (21, 'Amanda', 2000); +go + +select * from t1; +go + +drop trigger t1_insert_trig; +go + +create trigger t1_update_trig on t1 for update as +begin + delete t1; +end; +go + +update t1 set fname = 'Lucy' +output deleted.fname, inserted.fname +where fname = 'Amanda'; +go + +select * from t1; +go + +insert into t1 values (21, 'Amanda', 2000); +go + +drop trigger t1_update_trig; +go + +create trigger t1_delete_trig on t1 for delete as +begin + insert into t1 values (22, 'Tracy', 1998) +end; +go + +delete t1 output deleted.year; +go + +select * from t1; +go + +drop trigger t1_delete_trig +go + +-- Cleanup +drop table t1; +go + +-- Tests for NULL in output target list (BABEL-1768) -- +create table t1(age integer, fname varchar(100)); +create table t2(age integer, fname varchar(100), lname varchar(100)); +go + +insert into t1 + output inserted.age, inserted.fname, null into t2 +values(10, 'albert'); +go + +select * from t1; +go + +select * from t2; +go + +update t2 set age=20 + output deleted.age, null into t1 +where age=10; +go + +select * from t2; +go + +select * from t1; +go + +delete t1 +output deleted.age, deleted.fname, null; +go + +select * from t1; +go + +-- Cleanup +drop table t1; +go + +drop table t2; +go + +-- Tests for column names for target table in OUTPUT INTO (BABEL-1769) -- +create table t1(num integer, word varchar(10)); +go + +create table t2(num integer, word varchar(10), next_num integer); +go + +create table t3(prev_word varchar(10), random_number integer); +go + +insert into t1 +output inserted.num, inserted.word into t2(num, word) +values(1, 'one'); +go + +select * from t1; +go + +select * from t2; +go + +update t1 set word='one unit' +output deleted.word into t3(prev_word) +where num=1; +go + +select * from t1; +go + +select * from t3; +go + +delete t2 +output deleted.num into t1(num); +go + +select * from t2; +go + +select * from t1; +go + +-- Cleanup +drop table t1; +go + +drop table t2; +go + +drop table t3; +go + + +-- Test OUTPUT with temp tables -- +create table non_temp_tbl(fname varchar(10), lname varchar(10), age integer, score decimal); +create table #temp_tbl(fname varchar(10), lname varchar(10), age integer, score decimal); +go + +insert into non_temp_tbl +output inserted.* into #temp_tbl +values ('kelly', 'slater', 40, 100), ('john', 'cena', 50, 78); +go + +select * from non_temp_tbl; +go + +select * from #temp_tbl; +go + +update #temp_tbl set score=0 +output inserted.* into non_temp_tbl +where age=40; +go + +select * from #temp_tbl; +go + +select * from non_temp_tbl; +go + +-- Test OUTPUT with triggers -- +delete non_temp_tbl; +go + +delete #temp_tbl; +go + +create trigger insert_output_trig on non_temp_tbl for insert +as +begin +update non_temp_tbl set age=-1 +output inserted.*, deleted.* +end; +go + +insert into non_temp_tbl values ('joey', 'tribbiani', 45, 99); +go + +drop trigger insert_output_trig; +go + +create trigger update_output_trig on non_temp_tbl for update +as +begin +insert into non_temp_tbl +output inserted.* into #temp_tbl +values ('joni', 'mitchell', 80, 0) +end; +go + +update non_temp_tbl set lname='morgan' +where age=-1; +go + +select * from non_temp_tbl; +go + +select * from #temp_tbl; +go + +-- Cleanup +drop table #temp_tbl; +go + +drop table non_temp_tbl; +go + +-- Test OUTPUT with procedures -- +create table t1(num integer, word varchar(10)); +create table t2(num integer, word varchar(10)); +go + +create procedure output_insert_proc as +begin +insert into t1 output inserted.* into t2 values(1, 'one'); +end; +go + +exec output_insert_proc; +go + +select * from t1; +go + +select * from t2; +go + +create procedure output_update_proc as +begin +update t1 set num=100 output inserted.*, deleted.* where num=1; +end; +go + +exec output_update_proc; +go + +select * from t1; +go + +create procedure output_delete_proc as +begin +delete t1 output deleted.*; +end; +go + +exec output_delete_proc; +go + +select * from t1; +go + +-- Cleanup +drop procedure output_insert_proc; +go + +drop procedure output_update_proc; +go + +drop procedure output_delete_proc; +go + +drop table t1; +go + +drop table t2; +go + +-- [BABEL-1921] Test OUTPUT with functions and expressions -- +create table t1(num integer, word varchar(10)); +go + +create table t2(num integer, word varchar(10)); +go + +create table t3(str varchar(20)); +go + +insert into t1 output inserted.num+2 values(1, 'one'); +go + +select * from t1; +go + +insert into t1 output round(inserted.num)+1, 'sum' into t2 values(2, 'two'); +go + +select * from t1; +go + +select * from t2; +go + +insert into t1 values (3, 'three'), (4, 'four'), (5, 'five'); +go + +select * from t1; +go + +update t1 set word = 'one unit' output concat(inserted.word, '_old') where num = 1; +go + +select * from t1; +go + +update t1 set word = 'two units' + output concat(inserted.word, '_old') into t3 +where num = 2 and word = 'two'; +go + +select * from t1; +go + +select * from t3; +go + +delete t1 output round(deleted.num)+5 where num = 3; +go + +select * from t1; +go + +delete t1 output concat(deleted.word, '_old') into t3 where num = 4; +go + +select * from t1; +go + +select * from t3; +go + +-- Test nested functions +insert into t1 output round(floor(inserted.num)) values (6, 'six'), (7, 'seven'), (8, 'eight'), (9, 'nine'); +go + +select * from t1; +go + +-- Cleanup +drop table t1; +go + +drop table t2; +go + +drop table t3; +go + +-- Test that order by is working -- +create table t1(num integer, word varchar(10)); +go + +create table t2(num integer, word varchar(10)); +go + +insert into t1 values(2, 'two'), (1, 'one'), (3, 'three'); +go + +insert into t2 select * from t1 order by num; +go + +select * from t2; +go + +select * from t1; +go + +-- Cleanup +drop table t1; +go + +drop table t2; +go + +-- [BABEL-1901] Test specific cases that trigger ambiguous column errors +create table t1(num integer, word varchar(10)); +go + +create table t2(prev_word varchar(10), new_word varchar(10)); +go + +insert into t1 values(1, 'one'); +go + +-- output deleted, inserted of same column into table +update t1 set word='one unit' +output deleted.word, inserted.word into t2(prev_word, new_word) +where num=1; +go + +select * from t1; +go + +select * from t2; +go + +delete t1; +go + +drop table t2; +go + +create table t2(num integer, word varchar(10)); +go + +insert into t1 values(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5, 'five'); +go + +-- delete with top +delete top 2 t1 +output deleted.* into t2 +where num<5; +go + +select * from t1; +go + +select * from t2; +go + +-- delete with top in subquery +delete t1 +output deleted.num, deleted.word into t2 +from (select top 2 * from t1 order by num asc) as x +where t1.num = x.num and t1.num<5; +go + +select * from t1; +go + +select * from t2; +go + +drop table t1; +go + +drop table t2; +go + +CREATE TABLE t1( + c1PK INT PRIMARY KEY + , c2INT INT NOT NULL + , c3STR VARCHAR(50) NOT NULL + , c4COMMENT VARCHAR(100) NOT NULL +) +go +CREATE TABLE HISTORY( + c4BEFORE VARCHAR(100) NOT NULL + , c4AFTER VARCHAR(100) NOT NULL +) +go +INSERT INTO t1 VALUES( 1, 10, 'filler1', 'vanilla insert' ) +go + +UPDATE t1 +SET c4COMMENT = 'updated: output to table' +OUTPUT DELETED.c4COMMENT, INSERTED.c4COMMENT INTO HISTORY +WHERE c1PK = 1 +go + +SELECT * FROM t1; +go + +SELECT * FROM HISTORY; +go + +DROP TABLE t1 +go +DROP TABLE HISTORY +go + +CREATE TABLE t1( + c1PK INT PRIMARY KEY + , c2INT INT NOT NULL + , c3STR VARCHAR(50) NOT NULL + , c4COMMENT VARCHAR(100) NOT NULL +); +go +CREATE TABLE t2( + c1PK INT PRIMARY KEY + , c2INT INT NOT NULL + , c3STR VARCHAR(50) NOT NULL + , c4COMMENT VARCHAR(100) NOT NULL +); +go +CREATE TABLE HISTORY( + c1PK INT PRIMARY KEY + , c2INT INT NOT NULL + , c3BEFORE VARCHAR(100) NOT NULL + , c4AFTER VARCHAR(100) NOT NULL +); +go +INSERT INTO t1 VALUES( 1, 10, 'filler1', 'vanilla insert' ); +go +INSERT INTO t1 VALUES( 2, 20, 'filler2', 'vanilla insert' ); +go +INSERT INTO t2 VALUES( 1, 10, 'filler1', 'vanilla insert' ); +go +INSERT INTO t2 VALUES( 2, 20, 'filler2', 'vanilla insert' ); +go + +DELETE FROM t1 +OUTPUT DELETED.c1PK, DELETED.c2INT, DELETED.c3STR, DELETED.c4COMMENT INTO HISTORY +FROM t2 table2 +WHERE t1.c1PK = 2 +AND t1.c2INT = table2.c2INT; +go + +SELECT * FROM t1 +go + +SELECT * FROM HISTORY +go + +DROP TABLE t1; +go +DROP TABLE t2; +go +DROP TABLE HISTORY; +go + +CREATE TABLE t1( + c1PK INT PRIMARY KEY + , c2INT INT NOT NULL + , c3STR VARCHAR(50) NOT NULL + , c4COMMENT VARCHAR(100) NOT NULL +) +go +CREATE TABLE t2( + c1PK INT PRIMARY KEY + , c2INT INT NOT NULL + , c3STR VARCHAR(50) NOT NULL + , c4COMMENT VARCHAR(100) NOT NULL +) +go +CREATE TABLE trigger_history( + c1OPS CHAR(3) NOT NULL + , c2PK INT NOT NULL + , c3INT INT NOT NULL + , c4COMMENT VARCHAR(100) NOT NULL + , c5ROWS INT NOT NULL + , c6SRCTABLE VARCHAR(20) NOT NULL +) +go + +CREATE TRIGGER t1_ins +ON t1 +AFTER INSERT +AS +DECLARE @rows INT = @@rowcount +PRINT '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> INSERT TRIGGER ON t1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<' +INSERT INTO t2 +OUTPUT 'INS', INSERTED.c1PK, INSERTED.c2INT, INSERTED.c4COMMENT, @rows, 't1' INTO trigger_history( c1OPS, c2PK, c3INT, c4COMMENT, c5ROWS, c6SRCTABLE ) +SELECT INSERTED.c1PK, INSERTED.c2INT, INSERTED.c3STR, INSERTED.c4COMMENT FROM INSERTED +PRINT '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TRIGGER DONE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<' +RETURN +go + +INSERT INTO t1 VALUES( 1, 10, 'filler1', 'vanilla insert' ) +go + +SELECT * from t2 +go + +-- The value of c5ROWS needs to be changed when BABEL-2208 is fixed +SELECT * from trigger_history +go + +-- Cleanup +DROP TRIGGER t1_ins +go +DROP TABLE trigger_history +go +DROP TABLE t1 +go +DROP TABLE t2 +go diff --git a/contrib/test/JDBC/input/BABEL-597.sql b/contrib/test/JDBC/input/BABEL-597.sql new file mode 100644 index 00000000000..86bd5e03734 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-597.sql @@ -0,0 +1,6 @@ +-- create procedure with incomplete definition +create procedure [p_employee_insert] +@person_id int, @fname varchar(20), @lname varchar(30), @sal money +as +begin +go diff --git a/contrib/test/JDBC/input/BABEL-604.sql b/contrib/test/JDBC/input/BABEL-604.sql new file mode 100644 index 00000000000..e301373a45c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-604.sql @@ -0,0 +1,20 @@ +create table employees(@pers_id int, @fname nvarchar(20), @lname nvarchar(30), sal money) +GO + +create procedure p_employee_select as begin select * from employees end +GO + +call p_employee_select +GO + +call p_employee_select +GO + +create procedure p as select 2; select 1; +go +execute p; +go + +drop procedure p_employee_select, p; +drop table employees; +go diff --git a/contrib/test/JDBC/input/BABEL-625.sql b/contrib/test/JDBC/input/BABEL-625.sql new file mode 100644 index 00000000000..e1a12818e6a --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-625.sql @@ -0,0 +1,9 @@ +-- create procedure with incomplete definition +create procedure hf_proc AS +GO + +exec hf_proc +GO + +DROP PROCEDURE hf_proc +GO diff --git a/contrib/test/JDBC/input/BABEL-627.sql b/contrib/test/JDBC/input/BABEL-627.sql new file mode 100644 index 00000000000..45c3eda3ff4 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-627.sql @@ -0,0 +1,10 @@ +USE master; +GO + +-- run built-in functions with master dbo +select dateadd(year, 2, CAST('20060830' AS datetime)); +GO +select datediff(year, CAST('2037-03-01 23:30:05.523' AS datetime), CAST('2036-02-28 23:30:05.523' AS datetime)); +GO +select datepart(week, CAST('2007-04-21' AS date)), datepart(weekday, CAST('2007-04-21' AS date)); +GO diff --git a/contrib/test/JDBC/input/BABEL-646.sql b/contrib/test/JDBC/input/BABEL-646.sql new file mode 100644 index 00000000000..83fecaf2c46 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-646.sql @@ -0,0 +1,77 @@ +create table t1 ( a int ) +GO + +create procedure my_proc as +PRINT 'OK' +GO + +create procedure test_proc1 as +execute my_proc +execute [my_proc] +GO + +create procedure test_proc2 as +execute my_proc +execute [my_proc] +GO + +create procedure test_proc3 as +execute my_proc +execute [my_proc] +select 123 +GO + +create procedure test_proc4 as +execute my_proc +execute [my_proc] +insert into t1 values (1) +GO + +create procedure test_proc5 as +execute my_proc +execute [my_proc] +update t1 set a = 2 +GO + +EXEC test_proc1; +GO + +EXEC test_proc2; +GO + +EXEC test_proc3; +GO + +EXEC test_proc4; +GO + +select * from t1; +GO + +EXEC test_proc5; +GO + +select * from t1; +GO + +drop procedure test_proc1 +GO + +drop procedure test_proc2 +GO + +drop procedure test_proc3 +GO + +drop procedure test_proc4 +GO + +drop procedure test_proc5 +GO + +drop procedure my_proc +GO + +drop table t1 +GO + diff --git a/contrib/test/JDBC/input/BABEL-662.sql b/contrib/test/JDBC/input/BABEL-662.sql new file mode 100644 index 00000000000..995a73859f5 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-662.sql @@ -0,0 +1,71 @@ +-- BABEL-662 +CREATE TABLE babel_662_table (x INT, y FLOAT, z VARCHAR(10)); +go + +CREATE PROC babel_662_proc_1 AS SELECT 'hi' +go + +CREATE PROC babel_662_proc_2 @p VARCHAR(50) AS SELECT @p +go + +CREATE PROC babel_662_proc_3 +( + @a INT, + @b FLOAT, + @c VARCHAR(10) +) +AS INSERT INTO babel_662_table VALUES (@a, @b, @c); +go + +babel_662_proc_1 +go + +babel_662_proc_2 'hello' +go + +babel_662_proc_2 'hello again' +go + +babel_662_proc_3 1, 1.1, 'one'; +go + +babel_662_proc_3 @a = 2, @b = 2.2, @c = 'two'; +go + +babel_662_proc_3 3, @b = 3.3, @c = 'three'; +go + +babel_662_proc_3 4, @b = 4.4, 'four'; -- invalid +go + +SELECT * FROM babel_662_table; +go + +-- Invalid syntax +begin babel_662_proc_2 end +go + +babel_662_proc_2 +go + +-- BABEL-1995 +ABC +go + +-- BABEL-2052 +us emaster +go + +-- BABEL-2067 +seelect 1; +go + +-- Test stored procedure +sp_executesql N'SELECT ''hello world''' +go + +DROP TABLE babel_662_table +DROP PROC babel_662_proc_1 +DROP PROC babel_662_proc_2 +DROP PROC babel_662_proc_3 +go diff --git a/contrib/test/JDBC/input/BABEL-672.sql b/contrib/test/JDBC/input/BABEL-672.sql new file mode 100644 index 00000000000..c345ca90a61 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-672.sql @@ -0,0 +1,5 @@ +CREATE TABLE babel_672_table ( a int, constraint ab primary key nonclustered (a) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] +go + +DROP TABLE babel_672_table +go diff --git a/contrib/test/JDBC/input/BABEL-694.sql b/contrib/test/JDBC/input/BABEL-694.sql new file mode 100644 index 00000000000..f672f8bdefc --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-694.sql @@ -0,0 +1,24 @@ +CREATE PROCEDURE my_test @a int, @b int +AS +PRINT @a + @b +GO + +CREATE PROCEDURE caller @a int, @b int +AS +EXEC my_test(@a,@b) +GO + +EXEC my_test(1,2) +GO + +EXEC my_test 1, (1,2) +GO + +EXECUTE my_test(1,2) +GO + +EXECUTE my_test 1, (1,2) +GO + +drop procedure my_test +GO diff --git a/contrib/test/JDBC/input/BABEL-701.sql b/contrib/test/JDBC/input/BABEL-701.sql new file mode 100644 index 00000000000..f7f9d342e03 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-701.sql @@ -0,0 +1,12 @@ +CREATE PROCEDURE babel_701_sp (@a varbinary OUTPUT) AS +BEGIN + SET @a=0x121; + Select @a as a; +END; +GO + +EXEC babel_701_sp 0x122 +GO + +DROP PROCEDURE babel_701_sp +GO diff --git a/contrib/test/JDBC/input/BABEL-708.sql b/contrib/test/JDBC/input/BABEL-708.sql new file mode 100644 index 00000000000..b369c9c2896 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-708.sql @@ -0,0 +1,190 @@ +CREATE PROCEDURE babel_708_proc_1 AS +DECLARE @one varchar(2) +BEGIN + SET @one = 'abcdef'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_1; +GO + +CREATE PROCEDURE babel_708_proc_2(@one AS VARCHAR(2)) AS +BEGIN + SET @one = 'abcdef'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_2 ''; +GO + +EXEC babel_708_proc_2 'zyxwvut'; +GO + +CREATE PROCEDURE babel_708_proc_3 AS +DECLARE @one numeric(10,4) +BEGIN + SET @one = 12.3456789; + SELECT @one; +END; +GO + +EXEC babel_708_proc_3; +GO + +CREATE PROCEDURE babel_708_proc_4(@one AS numeric(10,4)) AS +BEGIN + SET @one = 12.3456789; + SELECT @one; +END; +GO + +EXEC babel_708_proc_4 0; +GO + +EXEC babel_708_proc_4 99.9999999; +GO + +CREATE PROCEDURE babel_708_proc_5 AS +DECLARE @one varbinary(2) +BEGIN + SET @one = 0x0123456789; + SELECT @one; +END; +GO + +EXEC babel_708_proc_5; +GO + +CREATE PROCEDURE babel_708_proc_6(@one AS VARBINARY(2)) AS +BEGIN + SET @one = 0x0123456789; + SELECT @one; +END; +GO + +EXEC babel_708_proc_6 ''; +GO + +EXEC babel_708_proc_6 0xabcdefabcedf; +GO + +CREATE PROCEDURE babel_708_proc_7 AS +DECLARE @one char(2) +BEGIN + SET @one = 'abcede'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_7; +GO + +CREATE PROCEDURE babel_708_proc_8(@one AS char(2)) AS +BEGIN + SET @one = 'abcede'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_8 ''; +GO + +EXEC babel_708_proc_8 'abcedef'; +GO + +CREATE PROCEDURE babel_708_proc_9 AS +DECLARE @one binary(2) +BEGIN + SET @one = 0x0123456789; + SELECT @one; +END; +GO + +EXEC babel_708_proc_9; +GO + +CREATE PROCEDURE babel_708_proc_10(@one AS binary(2)) AS +BEGIN + SET @one = 0x0123456789; + SELECT @one; +END; +GO + +EXEC babel_708_proc_10 ''; +GO + +EXEC babel_708_proc_10 0xabcdefabcdef; +GO + +CREATE PROCEDURE babel_708_proc_11 AS +DECLARE @one datetimeoffset(2) +BEGIN + SET @one = '2030-05-06 13:59:29.123456 -8:00'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_11; +GO + +CREATE PROCEDURE babel_708_proc_12(@one AS datetimeoffset(2)) AS +BEGIN + SET @one = '2030-05-06 13:59:29.123456 -8:00'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_12 '2040-05-06 13:59:29.567891 -8:00'; +GO + +CREATE PROCEDURE babel_708_proc_13 AS +DECLARE @one datetime2(2) +BEGIN + SET @one = '2030-05-06 13:59:29.123456'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_13; +GO + +CREATE PROCEDURE babel_708_proc_14(@one AS datetime2(2)) AS +BEGIN + SET @one = '2030-05-06 13:59:29.123456'; + SELECT @one; +END; +GO + +EXEC babel_708_proc_14 '2040-05-06 13:59:29.567891'; +GO + +DROP procedure babel_708_proc_1; +GO +DROP procedure babel_708_proc_2; +GO +DROP procedure babel_708_proc_3; +GO +DROP procedure babel_708_proc_4; +GO +DROP procedure babel_708_proc_5; +GO +DROP procedure babel_708_proc_6; +GO +DROP procedure babel_708_proc_7; +GO +DROP procedure babel_708_proc_8; +GO +DROP procedure babel_708_proc_9; +GO +DROP procedure babel_708_proc_10; +GO +DROP procedure babel_708_proc_11; +GO +DROP procedure babel_708_proc_12; +GO +DROP procedure babel_708_proc_13; +GO +DROP procedure babel_708_proc_14; +GO diff --git a/contrib/test/JDBC/input/BABEL-719.sql b/contrib/test/JDBC/input/BABEL-719.sql new file mode 100644 index 00000000000..f1306347269 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-719.sql @@ -0,0 +1,54 @@ +use master; +go + +select $12.4524 +go + +select +$12.4524 +go + +select -$12.4524 +go + +select $+12.4524 +go + +select $-12.4524 +go + +select abs($12.4524) +go + +select abs(+$12.4524) +go + +select abs(-$12.4524) +go + +select abs($+12.4524) +go + +select abs($-12.4524) +go + +-- udf accepting float +create function f719(@a float) returns int as begin return floor(@a) end +go + +select f719($12.4524) +go + +select f719(+$12.4524) +go + +select f719(-$12.4524) +go + +select f719($+12.4524) +go + +select f719($-12.4524) +go + +drop function f719 +go diff --git a/contrib/test/JDBC/input/BABEL-741.sql b/contrib/test/JDBC/input/BABEL-741.sql new file mode 100644 index 00000000000..e88d9ceb47e --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-741.sql @@ -0,0 +1,39 @@ +SELECT schema_id('public') +GO + +SELECT schema_id('pg_toast') +GO + +SELECT schema_id(NULL) +GO + +SELECT schema_id(1) +GO + +SELECT schema_id('fake_schemaname') +GO + +SELECT schema_name(99) +GO + +SELECT schema_name(2200) +GO + +SELECT schema_name(-1) +GO + +SELECT schema_name(0) +GO + +SELECT schema_name(1) +GO + +SELECT schema_name(123412341234) +GO + +SELECT schema_name(NULL) +GO + +SELECT schema_name('asdf') +GO + diff --git a/contrib/test/JDBC/input/BABEL-768.sql b/contrib/test/JDBC/input/BABEL-768.sql new file mode 100644 index 00000000000..6a8c9ec062f --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-768.sql @@ -0,0 +1,88 @@ +-- simple case +CREATE TABLE t1 ( a INT PRIMARY KEY) +GO + +CREATE TABLE t2 ( a INT REFERENCES t1(a)) +GO + + +INSERT INTO t1 VALUES(1) +GO + +INSERT INTO t2 VALUES(1) +GO + + +-- test TSQL specific types + +CREATE TABLE t3 ( a UNIQUEIDENTIFIER PRIMARY KEY) +GO + +CREATE TABLE t4 ( a UNIQUEIDENTIFIER REFERENCES t3(a)) +GO + +INSERT INTO t3 VALUES('123e4567-e89b-12d3-a456-426614174000') +GO + +INSERT INTO t4 VALUES('123e4567-e89b-12d3-a456-426614174000') +GO + + + +CREATE TABLE t5 (a SQL_VARIANT PRIMARY KEY) +GO + +CREATE TABLE t6 (a SQL_VARIANT REFERENCES t5(a)) +GO + +INSERT INTO t5 VALUES('123') +GO + +INSERT INTO t6 VALUES('123') +GO + + +-- other scenarios may trigger modified codes + +CREATE TABLE t7 ( a INT PRIMARY KEY) +GO + +CREATE TABLE t8 ( a INT REFERENCES t7(a) ON DELETE RESTRICT) +GO + + +INSERT INTO t7 VALUES(1) +GO + +INSERT INTO t8 VALUES(1) +GO + +DELETE FROM t7 WHERE a = 1 +GO + + +-- drop tables (dependent table first) +DROP TABLE t2 +GO + +DROP TABLE t1 +GO + +DROP TABLE t4 +GO + +DROP TABLE t3 +GO + +DROP TABLE t6 +GO + +DROP TABLE t5 +GO + +DROP TABLE t8 +GO + +DROP TABLE t7 +GO + diff --git a/contrib/test/JDBC/input/BABEL-775.sql b/contrib/test/JDBC/input/BABEL-775.sql new file mode 100644 index 00000000000..1dc596b7c28 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-775.sql @@ -0,0 +1,42 @@ +USE master +go + +create schema babel_775_logging +go + +create schema babel_775_efm +go + +CREATE TABLE [babel_775_logging].[EFMIntegrationMessage]( +[EFMIntegrationLogId] int IDENTITY(1, 1) NOT NULL +) +ON [PRIMARY]; +GO + +ALTER TABLE [babel_775_logging].[EFMIntegrationMessage] +ADD CONSTRAINT [PK_logging.EFMIntegrationMessage] PRIMARY KEY ([EFMIntegrationLogId]); +GO + +CREATE TABLE [babel_775_efm].[FilingCompletion]( +[FilingCompletionId] int IDENTITY(1, 1) NOT NULL, +[EfmIntegrationLogId] int NULL +) +ON [PRIMARY]; +GO + +ALTER TABLE [babel_775_efm].[FilingCompletion] +ADD CONSTRAINT [FK_efm.FilingCompletion_logging.EFMIntegrationMessage_EfmIntegrationLogId] FOREIGN KEY ([EfmIntegrationLogId]) +REFERENCES [babel_775_logging].[EFMIntegrationMessage] ([EFMIntegrationLogId]); +GO + +DROP TABLE [babel_775_efm].[FilingCompletion] +GO + +DROP SCHEMA babel_775_efm +go + +DROP TABLE [babel_775_logging].[EFMIntegrationMessage] +GO + +DROP SCHEMA babel_775_logging +go diff --git a/contrib/test/JDBC/input/BABEL-776.sql b/contrib/test/JDBC/input/BABEL-776.sql new file mode 100644 index 00000000000..4b5863006ee --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-776.sql @@ -0,0 +1,74 @@ +USE master; +GO + +SELECT FORMATMESSAGE('%s', 'Hi') +GO + +SELECT FORMATMESSAGE('Hello %s', CAST('World' as VARCHAR(10))) +GO + +SELECT FORMATMESSAGE('Testing %s, %s, %s', CAST('one' AS VARCHAR(10)), CAST('two' AS VARCHAR(10)), CAST('three' AS VARCHAR(10))) +GO + +SELECT formatmessage('Testing adjacent %d%s%d%s', 1, CAST('two' as VARCHAR(10)), 3, CAST('four' AS VARCHAR(10))) +GO + +SELECT formatmessage('d: %d %d %d', 1, 2, 3) +GO + +SELECT formatmessage('i: %i, %i, %i', 1, 2, 3) +GO + +SELECT formatmessage('Extra params with no format', CAST('asdf' AS VARCHAR(10)), CAST('asdf' AS VARCHAR(10)), CAST('asdf' AS VARCHAR(10))) +GO + +select formatmessage('Not enough parameters: %s, %s, %s, %s', CAST('1' AS VARCHAR(10)), CAST('2' AS VARCHAR(10))) +GO + +SELECT formatmessage('More parameters than %s', CAST('placeholders' AS VARCHAR(12)), CAST('to' AS VARCHAR(10)), CAST('handle' AS VARCHAR(10))) +GO + +SELECT formatmessage('Testing no inputs with placeholder %s') +GO + +SELECT FORMATMESSAGE('Unsigned hexadecimal %x, %X, %X, %X, %x', 11, 11, -11, 50, -50) +GO + +SELECT FORMATMESSAGE('Unsigned int %u, %u', 50, -50) +GO + +SELECT FORMATMESSAGE('Unsigned octal %o, %o', 50, -50) +GO + +SELECT FORMATMESSAGE('Nonlatin: 亚马%s', CAST('逊' AS VARCHAR(10))) +GO + +SELECT FORMATMESSAGE('Long string tests: \n\n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla consequat metus sit amet lectus vulputate lacinia. Nam vel auctor enim, quis finibus lorem. Quisque maximus egestas est, et vehicula lectus. Curabitur sodales efficitur nibh, quis varius metus semper vel. Aenean quis rhoncus tortor. Nullam eleifend blandit imperdiet. Donec a gravida ex. Cras ac nulla orci. Fusce ut est ac odio consequat rutrum. Morbi diam erat, blandit ut nunc efficitur, venenatis placerat nunc. Sed laoreet aliquam leo. Quisque vestibulum hendrerit mi, at cursus eros aliquet vitae. Mauris a sem blandit, interdum ligula et, feugiat nulla. Sed quis ipsum at dolor congue vulputate at elementum ex. Integer iaculis, libero nec pulvinar molestie, neque mi vestibulum nisl, in volutpat metus arcu in purus. Praesent ullamcorper mi sit amet consectetur mattis. Sed quis ex placerat, finibus purus dignissim, porttitor nibh. Maecenas dignissim tristique tempus. Proin venenatis sem a orci sagittis, vel consequat sem vestibulum. Donec consectetur cursus tortor, eget volutpat lacus posuere a. Pellentesque eu orci hendrerit sapien dictum fringilla. Morbi rutrum mollis ipsum sit amet hendrerit. Nullam nec felis tortor. Mauris augue turpis, volutpat ac odio in, faucibus venenatis purus. Curabitur scelerisque pharetra nunc non mattis. Nunc sagittis euismod mi sit amet gravida. Integer malesuada pretium nibh. Maecenas sagittis facilisis enim, et hendrerit nulla fringilla in. Nam pulvinar, nibh non mattis hendrerit, arcu velit molestie sem, sed rutrum arcu odio a risus. Pellentesque lorem sem, dictum nec sem nec, congue ornare purus. Mauris id efficitur mi. Phasellus nec tortor lacus. Integer vitae tempus lorem. Suspendisse augue orci, volutpat vitae magna id, venenatis viverra nunc. Sed lobortis faucibus ante, ac porttitor risus. Proin viverra vestibulum enim, sed dignissim erat lobortis scelerisque. Aliquam et diam ut lacus dictum tristique. Sed a sapien quis nulla semper elementum. Ut fringilla laoreet luctus. Vivamus mi ipsum. %s', CAST('Ellipsis' AS VARCHAR(3000))) +GO + +SELECT FORMATMESSAGE('Long string test parameter: %s', CAST('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla consequat metus sit amet lectus vulputate lacinia. Nam vel auctor enim, quis finibus lorem. Quisque maximus egestas est, et vehicula lectus. Curabitur sodales efficitur nibh, quis varius metus semper vel. Aenean quis rhoncus tortor. Nullam eleifend blandit imperdiet. Donec a gravida ex. Cras ac nulla orci. Fusce ut est ac odio consequat rutrum. Morbi diam erat, blandit ut nunc efficitur, venenatis placerat nunc. Sed laoreet aliquam leo. Quisque vestibulum hendrerit mi, at cursus eros aliquet vitae. Mauris a sem blandit, interdum ligula et, feugiat nulla. Sed quis ipsum at dolor congue vulputate at elementum ex. Integer iaculis, libero nec pulvinar molestie, neque mi vestibulum nisl, in volutpat metus arcu in purus. Praesent ullamcorper mi sit amet consectetur mattis. Sed quis ex placerat, finibus purus dignissim, porttitor nibh. Maecenas dignissim tristique tempus. Proin venenatis sem a orci sagittis, vel consequat sem vestibulum. Donec consectetur cursus tortor, eget volutpat lacus posuere a. Pellentesque eu orci hendrerit sapien dictum fringilla. Morbi rutrum mollis ipsum sit amet hendrerit. Nullam nec felis tortor. Mauris augue turpis, volutpat ac odio in, faucibus venenatis purus. Curabitur scelerisque pharetra nunc non mattis. Nunc sagittis euismod mi sit amet gravida. Integer malesuada pretium nibh. Maecenas sagittis facilisis enim, et hendrerit nulla fringilla in. Nam pulvinar, nibh non mattis hendrerit, arcu velit molestie sem, sed rutrum arcu odio a risus. Pellentesque lorem sem, dictum nec sem nec, congue ornare purus. Mauris id efficitur mi. Phasellus nec tortor lacus. Integer vitae tempus lorem. Suspendisse augue orci, volutpat vitae magna id, venenatis viverra nunc. Sed lobortis faucibus ante, ac porttitor risus. Proin viverra vestibulum enim, sed dignissim erat lobortis scelerisque. Aliquam et diam ut lacus dictum tristique. Sed a sapien quis nulla semper elementum. Ut fringilla laoreet luctus. Vivamus mi ipsum.i' AS VARCHAR(3000))) +GO + +SELECT FORMATMESSAGE('21 args: %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s', CAST('one' AS VARCHAR(10)), CAST('two' AS VARCHAR(10)),CAST('three' AS VARCHAR(10)),CAST('four' AS VARCHAR(10)),CAST('five' AS VARCHAR(10)),CAST('six' AS VARCHAR(10)),CAST('seven' AS VARCHAR(10)),CAST('eight' AS VARCHAR(10)),CAST('nine' AS VARCHAR(10)),CAST('ten' AS VARCHAR(10)),CAST('eleven' AS VARCHAR(10)),CAST('twelve' AS VARCHAR(10)),CAST('thirteen' AS VARCHAR(10)),CAST('fourteen' AS VARCHAR(10)),CAST('fifteen' AS VARCHAR(10)),CAST('sixteen' AS VARCHAR(10)),CAST('seventeen' AS VARCHAR(10)),CAST('eighteen' AS VARCHAR(10)),CAST('nineteen' AS VARCHAR(10)),CAST('twenty' AS VARCHAR(10)),CAST('twenty-one' AS VARCHAR(10))) +GO + +SELECT FORMATMESSAGE('Invalid parameter example: %s', TRUE) +GO + +SELECT FORMATMESSAGE('Invalid placeholder example: %m', 0) +GO + +SELECT FORMATMESSAGE(NULL, 0) +GO + +SELECT FORMATMESSAGE('Placeholder null: %s', NULL) +GO + +SELECT FORMATMESSAGE('Mismatch datatype: %d', 'string') +GO + +SELECT FORMATMESSAGE('Mismatch datatype: %o', CAST('string' AS VARCHAR(10))) +GO + +SELECT FORMATMESSAGE('Mismatch datatype: %s', 123); +GO diff --git a/contrib/test/JDBC/input/BABEL-785.sql b/contrib/test/JDBC/input/BABEL-785.sql new file mode 100644 index 00000000000..5303c5c5ddb --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-785.sql @@ -0,0 +1,115 @@ +-- Verify correct value of ANSI_NULLS +select sessionproperty('ANSI_NULLS'); +go + +create table testTbl(fname varchar(10)); +insert into testTbl values (null), ('Jenny'); +select * from testTbl +go + +select * from testTbl where fname=null; +go + +select * from testTbl where fname<>null; +go + +drop table testTbl; +go + +-- Verify correct value of ANSI_PADDING +select sessionproperty('ANSI_PADDING'); +go + +create table testTbl ( + is_char_null char(10) null, + is_char_notnull char(10) not null, + is_varchar_null varchar(10) null, + is_varchar_notnull varchar(10) not null +); +go + +insert into testTbl values + ('aaa','aaa','aaa','aaa'), + ('aaa ','aaa ','aaa ','aaa '); +go + +select datalength(is_char_null) as is_char_null_len, + datalength(is_char_notnull) as is_char_notnull_len, + datalength(is_varchar_null) as is_varchar_null_len, + datalength(is_varchar_notnull) as is_varchar_notnull_len +from testTbl; +go + +SELECT + is_char_null+'|' as is_char_null, + is_char_notnull+'|' as is_char_notnull, + is_varchar_null+'|' as is_varchar_null, + is_varchar_notnull+'|' as is_varchar_notnull +from testTbl; +go + +drop table testTbl; +go + +-- Verify correct value of ANSI_WARNINGS +select sessionproperty('ANSI_WARNINGS'); +go + +create table testTbl(fname varchar(10)); +go + +insert into testTbl values (null), ('Jenny Matthews'); +go + +drop table testTbl; +go + +-- Verify correct value of ARITHABORT +select sessionproperty('ARITHABORT'); +go + +select 25/0; +go + +-- Verify correct value of CONCAT_NULL_YIELDS_NULL +select sessionproperty('CONCAT_NULL_YIELDS_NULL'); +go + +select 'test'+null; +go + +select concat('test', null); +go + +select concat(null, 'test'); +go + +-- Verify correct value of NUMERIC_ROUNDABORT +select sessionproperty('NUMERIC_ROUNDABORT'); +go + +create table testTbl(size int) +go + +insert into testTbl values (707072), (1024000); +go + +select (100 / SUM((((size) * 8.00) / 1024))) from testTbl as T; +go + +drop table testTbl; +go + +-- Verify correct value of QUOTED_IDENTIFIER +select sessionproperty('QUOTED_IDENTIFIER'); +go + +select 'Hello, world!'; +go + +select "Hello, world!"; +go + +-- Test invalid property +select sessionproperty('nonexistent_property'); +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-788.sql b/contrib/test/JDBC/input/BABEL-788.sql new file mode 100644 index 00000000000..416e3449808 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-788.sql @@ -0,0 +1,91 @@ + +drop table if exists babel_788_int; +GO + +create table babel_788_int(a int null, b int not null); +GO + +insert babel_788_int values (1, 10); +insert babel_788_int values (3, 7); +insert babel_788_int values (null, 8); +GO + +select * from babel_788_int order by a; +GO +select * from babel_788_int order by a asc; +GO +select * from babel_788_int order by a desc; +GO + +select * from babel_788_int order by b; +GO +select * from babel_788_int order by b asc; +GO +select * from babel_788_int order by b desc; +GO + +select * from babel_788_int order by a + b; +GO +select * from babel_788_int order by a + b asc; +GO +select * from babel_788_int order by a + b desc; +GO + +select * from (select top(2) a from babel_788_int order by 1) s; +GO + +drop table if exists babel_788_select_into; +GO +select top(2) a into babel_788_select_into from babel_788_int order by 1; +GO +select * from babel_788_select_into; +GO + +drop table if exists babel_788_subquery_select_into; +GO +select * into babel_788_subquery_select_into from (select top(2) a from babel_788_int order by 1) s; +GO +select * from babel_788_subquery_select_into; +GO + +drop table if exists babel_788_int; +GO +drop table if exists babel_788_select_into; +GO +drop table if exists babel_788_subquery_select_into; +GO + +drop table if exists babel_788_varchar; +GO + +create table babel_788_varchar(a varchar(2) null, b varchar(2) not null); +GO + +insert babel_788_varchar values ('1', '10'); +insert babel_788_varchar values ('3', '7'); +insert babel_788_varchar values ('', ' '); +insert babel_788_varchar values (null, '8'); +GO + +select * from babel_788_varchar order by a; +GO +select * from babel_788_varchar order by a asc; +GO +select * from babel_788_varchar order by a desc; +GO + +select * from babel_788_varchar order by b; +GO +select * from babel_788_varchar order by b asc; +GO +select * from babel_788_varchar order by b desc; +GO + +select * from babel_788_varchar order by a + b; +GO +select * from babel_788_varchar order by a + b asc; +GO +select * from babel_788_varchar order by a + b desc; +GO +drop table if exists babel_788_varchar; +GO diff --git a/contrib/test/JDBC/input/BABEL-798.sql b/contrib/test/JDBC/input/BABEL-798.sql new file mode 100644 index 00000000000..c2c1bf74739 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-798.sql @@ -0,0 +1,9 @@ +-- Without any transaction +SELECT XACT_STATE() +GO + +-- Inside a transaction +BEGIN TRANSACTION + SELECT XACT_STATE() +COMMIT TRANSACTION +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-807.sql b/contrib/test/JDBC/input/BABEL-807.sql new file mode 100644 index 00000000000..cf3ca5d9870 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-807.sql @@ -0,0 +1,9 @@ +-- supported collation +SELECT COLLATIONPROPERTY('Traditional_Spanish_CS_AS', 'CodePage'), COLLATIONPROPERTY('Traditional_Spanish_CS_AS', 'ComparisonStyle'), COLLATIONPROPERTY('Traditional_Spanish_CS_AS', 'Version'), COLLATIONPROPERTY('Traditional_Spanish_CS_AS', 'LCID'); +go + +SELECT COLLATIONPROPERTY('Traditional_Spanish_CS_AS', 'INVALID'); +go + +SELECT COLLATIONPROPERTY('INVALID', 'LCID'); +go diff --git a/contrib/test/JDBC/input/BABEL-820.sql b/contrib/test/JDBC/input/BABEL-820.sql new file mode 100644 index 00000000000..c38b8c65dd1 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-820.sql @@ -0,0 +1,21 @@ +SELECT 42 = CAST(42 AS numeric(38,0)) +GO + +SELECT CAST(42 AS numeric(38,0)) = 42 +GO + + +CREATE TABLE babel_820_test_table1(test_id INT IDENTITY, test_col1 INT) +GO + +INSERT INTO babel_820_test_table1 (test_col1) VALUES (10), (20), (30) +GO + + +SELECT test_col1 FROM babel_820_test_table1 WHERE test_id = @@IDENTITY +GO + + +DROP TABLE babel_820_test_table1 +GO + diff --git a/contrib/test/JDBC/input/BABEL-872.sql b/contrib/test/JDBC/input/BABEL-872.sql new file mode 100644 index 00000000000..c38b8c65dd1 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-872.sql @@ -0,0 +1,21 @@ +SELECT 42 = CAST(42 AS numeric(38,0)) +GO + +SELECT CAST(42 AS numeric(38,0)) = 42 +GO + + +CREATE TABLE babel_820_test_table1(test_id INT IDENTITY, test_col1 INT) +GO + +INSERT INTO babel_820_test_table1 (test_col1) VALUES (10), (20), (30) +GO + + +SELECT test_col1 FROM babel_820_test_table1 WHERE test_id = @@IDENTITY +GO + + +DROP TABLE babel_820_test_table1 +GO + diff --git a/contrib/test/JDBC/input/BABEL-909.sql b/contrib/test/JDBC/input/BABEL-909.sql new file mode 100644 index 00000000000..61a82019cc0 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-909.sql @@ -0,0 +1,30 @@ +-- Test NEWSEQUENTIALID() as column default constraint +CREATE TABLE new_sequential_id_table_1 (ColumnA uniqueidentifier DEFAULT NEWSEQUENTIALID()); +go +-- Test NEWSEQUENTIALID() in alter table +ALTER TABLE new_sequential_id_table_1 ADD ColumnB uniqueidentifier DEFAULT NEWSEQUENTIALID(); +go +-- Test NEWSEQUENTIALID() as column default constraint with wrong type (shoudl fail) +CREATE TABLE new_sequential_id_table_2 (ColumnA varchar(60) DEFAULT NEWSEQUENTIALID()); +go +-- Test NEWSEQUENTIALID() in SELECT statement (shoudl fail) +SELECT pg_typeof(newsequentialid()); +go +-- Test NEWSEQUENTIALID() as scalar function (should fail) +CREATE FUNCTION foo(@i uniqueidentifier) +RETURNS uniqueidentifier +AS +BEGIN + RETURN @i +END; +go +CREATE TABLE new_sequential_id_table_3 (ColumnA uniqueidentifier DEFAULT foo(NEWSEQUENTIALID())); +go +DROP TABLE new_sequential_id_table_1; +go +DROP TABLE new_sequential_id_table_2; +go +DROP TABLE new_sequential_id_table_3; +go +DROP FUNCTION foo; +go diff --git a/contrib/test/JDBC/input/BABEL-931.sql b/contrib/test/JDBC/input/BABEL-931.sql new file mode 100644 index 00000000000..f9d0eec5345 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-931.sql @@ -0,0 +1,51 @@ +CREATE PROCEDURE babel_931_proc (@a INT OUTPUT, @b INT OUTPUT) AS +BEGIN + SET @a=100; PRINT '@a (inner): ' + cast(@a as varchar(10)); + SET @b=200; PRINT '@b (inner): ' + cast(@b as varchar(10)); + select @a+@b as ret; END; +GO + +CREATE PROCEDURE babel_931_caller AS +BEGIN + EXEC babel_931_proc 2, 3; +END +GO + +EXEC babel_931_caller +GO + +CREATE PROCEDURE babel_931_caller_2 AS +BEGIN + DECLARE @b INT; + EXEC babel_931_proc 2, @b OUT; + PRINT '@b (outer): ' + cast(@b as varchar(10)); +END +GO + +EXEC babel_931_caller_2 +GO + +CREATE PROCEDURE babel_931_caller_3 AS +BEGIN + DECLARE @a INT; + DECLARE @b INT; + EXEC babel_931_proc @a OUT, @b OUT; + PRINT '@a (outer): ' + cast(@a as varchar(10)); + PRINT '@b (outer): ' + cast(@b as varchar(10)); +END +GO + +EXEC babel_931_caller_3 +GO + +DROP PROCEDURE babel_931_proc +GO + +DROP PROCEDURE babel_931_caller +GO + +DROP PROCEDURE babel_931_caller_2 +GO + +DROP PROCEDURE babel_931_caller_3 +GO diff --git a/contrib/test/JDBC/input/BABEL-941.sql b/contrib/test/JDBC/input/BABEL-941.sql new file mode 100644 index 00000000000..7293c3e2594 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-941.sql @@ -0,0 +1,43 @@ +CREATE PROCEDURE p1 as +BEGIN +DECLARE @a int; +SELECT @a = 123; +PRINT @a; +END +GO + +CREATE TABLE t1 ( b int) ; +GO + +INSERT INTO t1 VALUES (1) +GO + +INSERT INTO t1 VALUES (2) +GO + +INSERT INTO t1 VALUES (3) +GO + +CREATE PROCEDURE p2 as +BEGIN +DECLARE @a int; +SELECT @a = b from t1 where (b = 1); +PRINT @a; +END +GO + +EXEC p1; +GO + +EXEC p2; +GO + +DROP TABLE t1 +GO + +DROP PROCEDURE p1 +GO + +DROP PROCEDURE p2 +GO + diff --git a/contrib/test/JDBC/input/BABEL-APPLOCK.sql b/contrib/test/JDBC/input/BABEL-APPLOCK.sql new file mode 100644 index 00000000000..ab5e3bdc3bd --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-APPLOCK.sql @@ -0,0 +1,109 @@ +-- Part #1: single-session tests + +-- default params +BEGIN TRAN; +exec sp_getapplock @Resource = 'lock1', @LockMode = 'SHARED'; +COMMIT; +GO + +-- given params +exec sp_getapplock @Resource = 'lock1', @LockMode = 'EXCLUSIVE', @LockOwner = 'SESSION', @LockTimeout = 10, @DbPrincipal = 'dbo'; +GO + +exec sp_releaseapplock @Resource = 'lock1', @LockOwner = 'SESSION'; +GO + +-- null params not allowed +exec sp_getapplock @Resource = null, @LockMode = 'shared'; +GO + +exec sp_getapplock @Resource = 'lock1', @LockMode = null; +GO + +-- parameters are case-insensitive except for the lock name + +exec sp_getapplock @Resource = 'lock1', @LockMode = 'Shared', @LockOwner = 'session'; +GO + +exec sp_getapplock @Resource = 'LOCK1', @LockMode = 'Exclusive', @LockOwner = 'SESSION'; +GO + +exec sp_releaseapplock @Resource = 'lock1', @LockOwner = 'SESSION'; +GO + +exec sp_releaseapplock @Resource = 'LOCK1', @LockOwner = 'session'; +GO + +-- implicit unlocking, lock will be gone after commit +BEGIN TRAN; +exec sp_getapplock @Resource = 'lock1', @LockMode = 'SHARED', @LockOwner = 'TRANSACTION'; +GO +COMMIT; +GO + +-- explicit unlocking (trx & session) +BEGIN TRAN; +exec sp_getapplock @Resource = 'lock1', @LockMode = 'SHARED', @LockOwner = 'TRANSACTION'; +GO +exec sp_releaseapplock @Resource = 'lock1'; +GO +COMMIT; +GO + +exec sp_getapplock @Resource = 'lock1', @LockMode = 'SHARED', @LockOwner = 'SESSION'; +GO + +exec sp_releaseapplock @Resource = 'lock1', @LockOwner = 'SESSION'; +GO + +-- aquire lock multiple times, and need to unlock same number of times too + +exec sp_getapplock @Resource = 'lock1', @LockMode = 'SHARED', @LockOwner = 'SESSION'; +GO + +exec sp_getapplock @Resource = 'lock1', @LockMode = 'SHARED', @LockOwner = 'SESSION'; +GO + +exec sp_releaseapplock @Resource = 'lock1', @LockOwner = 'SESSION'; +GO + +exec sp_releaseapplock @Resource = 'lock1', @LockOwner = 'SESSION'; +GO + +-- APPLOCK_MODE tests +exec sp_getapplock @Resource = 'test1', @LockMode = 'shared', @LockOwner = 'session'; +GO + +exec sp_getapplock @Resource = 'test1', @LockMode = 'update', @LockOwner = 'session'; +GO + +select APPLOCK_MODE ('dbo', 'test1', 'session'); -- should show 'Update' +go + +exec sp_releaseapplock @Resource = 'test1', @LockOwner = 'session'; +GO + +select APPLOCK_MODE ('dbo', 'test1', 'session'); -- should show 'Shared' +go + +exec sp_releaseapplock @Resource = 'test1', @LockOwner = 'session'; +GO + +select APPLOCK_MODE ('dbo', 'test1', 'session'); -- should show "NoLock" +go + +-- APPLOCK_TEST tests +SELECT APPLOCK_TEST('dbo', 'lock1', 'Exclusive', 'Session'); +GO + +BEGIN TRAN +SELECT APPLOCK_TEST('dbo', 'lock1', 'Shared', 'Transaction'); +COMMIT +GO + +BEGIN TRAN +exec sp_getapplock @Resource = 'lock1', @LockMode = 'exclusive', @LockOwner = 'Transaction' +SELECT APPLOCK_TEST('dbo', 'lock1', 'Shared', 'Transaction'); +COMMIT +GO + diff --git a/contrib/test/JDBC/input/BABEL-COLUMN-ALIAS.sql b/contrib/test/JDBC/input/BABEL-COLUMN-ALIAS.sql new file mode 100644 index 00000000000..a75cc5a6515 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-COLUMN-ALIAS.sql @@ -0,0 +1,114 @@ +SELECT 1 a +go + +SELECT 1 'a' +go + +SELECT 1 "a" +go + +SELECT a=1 +go + +SELECT 'a'=1 +go + +SELECT "a"=1 +go + +-- column alias using PG reserved keyword but non-reserved keyword in T-SQL +SELECT 1 year +go + +SELECT 1 'year' +go + +SELECT 1 "year" +go + +SELECT year=1 +go + +SELECT 'year'=1 +go + +SELECT "year"=1 +go + +CREATE TABLE t_567 (a int); +INSERT INTO t_567 VALUES (42); +GO +SELECT a year from t_567; +GO +SELECT a 'year' from t_567; +GO +SELECT a "year" from t_567; +GO +SELECT year=a from t_567; +GO +SELECT 'year'=a from t_567; +GO +SELECT "year"=a from t_567; +GO +DROP TABLE t_567; +GO + +-- double quoted alias having escapted charcter +SELECT 1 """col""" +go + +SELECT """col"""=1 +go + +-- column alias with squred bracket +SELECT 1 ["col"] +go + +SELECT ["col"]=1 +go + +-- BABEL-2116: regression in a complex query. Plase note column alias LastRefreshed is specificed and expression refers to a local tsql variable +CREATE PROC [GetLandingPageDetails] AS +BEGIN + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; + DECLARE @MaxDateHeapUsage DATE; + DECLARE @MaxDateAuditorSummary DATE + DECLARE @MaxDateBadCname DATE + DECLARE @MaxDateCPUSummary DATE + DECLARE @MaxDDLAudit DATE + DECLARE @MaxDateLinkServerSecurity DATE; + SELECT @MaxDateLinkServerSecurity = CAST(MAX(LinkServerCodeLastCheck) AS DATE) FROM MCPBI.dbo.LinkServerRemediation (NOLOCK); + SELECT @MaxDateHeapUsage = MAX(ForDate) FROM dbo.HeapUsageSummary (NOLOCK); + SELECT @MaxDateAuditorSummary = MAX(ExecutionDate) FROM dbo.AuditorSummary (NOLOCK); + SELECT @MaxDateBadCname = d.Date FROM MCPData.Dimension.Date AS d (NOLOCK) WHERE d.DateID = (SELECT MAX(b.DateID) FROM dbo.BadCNameUsage AS b (NOLOCK)); + SELECT @MaxDateCPUSummary = MAX(EndDate) FROM CPUSummary_TeamNames_Monthly (NOLOCK); + SELECT @MaxDDLAudit = CAST(MAX(LastCheckUTC) AS DATE) FROM MCPInventory.Metric.DBA_tblAuditDDL (NOLOCK); + WITH rptUsage AS (SELECT rud.Path, rud.Name, COUNT(*) AS RunCount FROM ReportServer.dbo.vwReportUsageDetails AS rud JOIN MCPInventory.Pub.vw_vw_WD_HRIS_Model AS hrd ON ('AQR\' + hrd.DomainId) COLLATE Latin1_General_100_CI_AS_KS_WS = rud.UserName WHERE hrd.GroupName != 'DBA' AND rud.Name IS NOT NULL AND rud.Path <> '/HeapUsageSummary' GROUP BY rud.Path, rud.Name) + SELECT bil.Name, bil.Description, bil.rptLink, bil.DataRefreshCycle, LastRefreshed = CASE WHEN bil.DependsOnTable = 'AuditorSummary' THEN @MaxDateAuditorSummary WHEN bil.DependsOnTable = 'HeapUsageSummary' THEN @MaxDateHeapUsage WHEN bil.DependsOnTable = 'BadCNameUsage' THEN @MaxDateBadCname WHEN bil.DependsOnTable = 'CPUSummary_TeamNames_Monthly' THEN @MaxDateCPUSummary WHEN bil.DependsOnTable = 'Metric.DBA_tblAuditDDL' THEN @MaxDDLAudit WHEN bil.DependsOnTable = 'MCPBI.dbo.LinkServerRemediation' THEN @MaxDateLinkServerSecurity END, ISNULL(ru.RunCount, 0) AS UsageInLast60Days, bil.isProd FROM SSRS.BIReportsList AS bil LEFT JOIN rptUsage AS ru ON bil.rptName COLLATE Latin1_General_100_CI_AS_KS_WS = ru.Name WHERE bil.isProd = 1; +END; +GO + +CREATE PROC [GetLandingPageDetails_2] AS +BEGIN + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; + DECLARE @MaxDateHeapUsage DATE; + DECLARE @MaxDateAuditorSummary DATE + DECLARE @MaxDateBadCname DATE + DECLARE @MaxDateCPUSummary DATE + DECLARE @MaxDDLAudit DATE + DECLARE @MaxDateLinkServerSecurity DATE; + SELECT @MaxDateLinkServerSecurity = CAST(MAX(LinkServerCodeLastCheck) AS DATE) FROM MCPBI.dbo.LinkServerRemediation (NOLOCK); + SELECT @MaxDateHeapUsage = MAX(ForDate) FROM dbo.HeapUsageSummary (NOLOCK); + SELECT @MaxDateAuditorSummary = MAX(ExecutionDate) FROM dbo.AuditorSummary (NOLOCK); + SELECT @MaxDateBadCname = d.Date FROM MCPData.Dimension.Date AS d (NOLOCK) WHERE d.DateID = (SELECT MAX(b.DateID) FROM dbo.BadCNameUsage AS b (NOLOCK)); + SELECT @MaxDateCPUSummary = MAX(EndDate) FROM CPUSummary_TeamNames_Monthly (NOLOCK); + SELECT @MaxDDLAudit = CAST(MAX(LastCheckUTC) AS DATE) FROM MCPInventory.Metric.DBA_tblAuditDDL (NOLOCK); + WITH rptUsage AS (SELECT rud.Path, rud.Name, COUNT(*) AS RunCount FROM ReportServer.dbo.vwReportUsageDetails AS rud JOIN MCPInventory.Pub.vw_vw_WD_HRIS_Model AS hrd ON ('AQR\' + hrd.DomainId) COLLATE Latin1_General_100_CI_AS_KS_WS = rud.UserName WHERE hrd.GroupName != 'DBA' AND rud.Name IS NOT NULL AND rud.Path <> '/HeapUsageSummary' GROUP BY rud.Path, rud.Name) + SELECT bil.Name, bil.Description, bil.rptLink, bil.DataRefreshCycle, CASE WHEN bil.DependsOnTable = 'AuditorSummary' THEN @MaxDateAuditorSummary WHEN bil.DependsOnTable = 'HeapUsageSummary' THEN @MaxDateHeapUsage WHEN bil.DependsOnTable = 'BadCNameUsage' THEN @MaxDateBadCname WHEN bil.DependsOnTable = 'CPUSummary_TeamNames_Monthly' THEN @MaxDateCPUSummary WHEN bil.DependsOnTable = 'Metric.DBA_tblAuditDDL' THEN @MaxDDLAudit WHEN bil.DependsOnTable = 'MCPBI.dbo.LinkServerRemediation' THEN @MaxDateLinkServerSecurity END LastRefereshed, ISNULL(ru.RunCount, 0) AS UsageInLast60Days, bil.isProd FROM SSRS.BIReportsList AS bil LEFT JOIN rptUsage AS ru ON bil.rptName COLLATE Latin1_General_100_CI_AS_KS_WS = ru.Name WHERE bil.isProd = 1; +END; +GO + +DROP PROC [GetLandingPageDetails] +GO +DROP PROC [GetLandingPageDetails_2] +GO diff --git a/contrib/test/JDBC/input/BABEL-DATABASEPROPERTYEX.sql b/contrib/test/JDBC/input/BABEL-DATABASEPROPERTYEX.sql new file mode 100644 index 00000000000..efe0f414271 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-DATABASEPROPERTYEX.sql @@ -0,0 +1,21 @@ +-- test databasepropertyex() function +select databasepropertyex(N'template1',N'Collation') +GO +select databasepropertyex(N'template1',N'IsInStandBy') +GO +select databasepropertyex(N'template1',N'IsAutoClose') +GO +select databasepropertyex(N'template1',N'IsAutoCreateStatistics') +GO +select 'true' where databasepropertyex(N'template1',N'IsTornPageDetectionEnabled') >= 0 +GO +select databasepropertyex(N'template1',N'Updateability') +GO +select databasepropertyex(N'template1',N'Status') +GO +SELECT (case when charindex(cast(databasepropertyex(N'template1',N'Version') as nvarchar), version()) > 0 then 'true' else 'false' end) result +GO +select databasepropertyex(N'template1',N'IsArithmeticAbortEnabled') +GO +select databasepropertyex(N'template1',N'IsAutoShrink') +GO diff --git a/contrib/test/JDBC/input/BABEL-EXTENDEDPROPERTY.sql b/contrib/test/JDBC/input/BABEL-EXTENDEDPROPERTY.sql new file mode 100644 index 00000000000..3230ae17ac2 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-EXTENDEDPROPERTY.sql @@ -0,0 +1,17 @@ +create table t1 (a int) +go + +-- For now, will always return empty result set because sys.extended_properties +-- is always empty before the support of sp_[add/drop/update]extendedproperty (BABEL-280) +select * FROM fn_listextendedproperty('COLUMN', 'schema', N'dbo', 'table', N't1', 'column', N'a'); +go + +select * FROM fn_listextendedproperty(NULL, 'schema', N'dbo', 'table', N't1', NULL, NULL); +go + +-- Failed query in BABEL-1784 +exec [sys].sp_columns_100 N't23',N'dbo',NULL,NULL,@ODBCVer=3,@fUsePattern=1; +go + +drop table t1 +go diff --git a/contrib/test/JDBC/input/BABEL-GUC-PLAN.sql b/contrib/test/JDBC/input/BABEL-GUC-PLAN.sql new file mode 100644 index 00000000000..bf46c12ed51 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-GUC-PLAN.sql @@ -0,0 +1,213 @@ +CREATE TABLE test_table1(test_id INT IDENTITY, test_col1 INT); +go + +CREATE TABLE test_table2(test_id INT IDENTITY, test_col1 INT); +go + +CREATE PROCEDURE insert_test_table1_stmt1 +AS BEGIN +INSERT INTO test_table1 (test_id, test_col1) VALUES (5, 1); +END; +go + +CREATE PROCEDURE insert_test_table1_stmt1_outer +AS BEGIN +EXEC insert_test_table1_stmt1; +END; +go + +CREATE PROCEDURE insert_test_table1_stmt2 +AS BEGIN +INSERT INTO test_table1 (test_id, test_col1) VALUES (8, 2); +END; +go + +CREATE PROCEDURE insert_test_table1_stmt2_outer +AS BEGIN +EXEC insert_test_table1_stmt2; +END; +go + +CREATE PROCEDURE insert_test_table1_stmt3 +AS BEGIN +INSERT INTO test_table1 (test_id, test_col1) VALUES (10, 3); +END; +go + +CREATE PROCEDURE insert_test_table1_stmt4 +AS BEGIN +INSERT INTO test_table1 (test_id, test_col1) VALUES (64, 4); +END; +go + +CREATE PROCEDURE insert_test_table1_stmt5 +AS BEGIN +INSERT INTO test_table1 (test_id, test_col1) VALUES (128, 5); +END; +go + +CREATE PROCEDURE insert_test_table1_stmt6 +AS BEGIN +INSERT INTO test_table1 (test_id, test_col1) VALUES (-2, 6); +END; +go + +CREATE PROCEDURE insert_test_table1_test_multi_stmts +AS BEGIN +EXEC insert_test_table1_stmt1 +EXEC insert_test_table1_stmt2 +EXEC insert_test_table1_stmt3 +EXEC insert_test_table1_stmt4 +EXEC insert_test_table1_stmt5 +EXEC insert_test_table1_stmt6 + +EXEC insert_test_table1_stmt3 +EXEC insert_test_table1_stmt5 +EXEC insert_test_table1_stmt4 +EXEC insert_test_table1_stmt6 +EXEC insert_test_table1_stmt1 +EXEC insert_test_table1_stmt2 +END; +go + +CREATE PROCEDURE insert_test_table1_val +@val INT +AS BEGIN +INSERT INTO test_table1 VALUES (@val); +END; +go + +-- Test re-planning based on GUC state +SET IDENTITY_INSERT test_table1 ON; +go + +EXEC insert_test_table1_stmt1_outer; +go + +EXEC insert_test_table1_stmt2_outer; +go + +SET IDENTITY_INSERT test_table1 OFF; +go + +EXEC insert_test_table1_stmt1_outer; +go + +EXEC insert_test_table1_stmt2_outer; +go + +SET IDENTITY_INSERT test_table1 ON; + +EXEC insert_test_table1_test_multi_stmts; +go + +SET IDENTITY_INSERT test_table1 OFF; +go + +-- Test a follow-up regular INSERT +EXEC insert_test_table1_val 7; +go + +SELECT * FROM test_table1; +go + +-- Test procedure drops +SET IDENTITY_INSERT test_table1 ON; +go + +EXEC insert_test_table1_stmt1_outer; +go + +EXEC insert_test_table1_stmt2_outer; +go + +DROP PROC insert_test_table1_stmt1_outer; +go + +EXEC insert_test_table1_stmt2_outer; +go + +SET IDENTITY_INSERT test_table1 OFF; +go + +EXEC insert_test_table1_stmt2_outer; +go + +SELECT * FROM test_table1; +go + +-- Test after arbitrarily changing state +SET IDENTITY_INSERT test_table2 ON; +go + +EXEC insert_test_table1_stmt1; +go + +EXEC insert_test_table1_stmt2; +go + +SET IDENTITY_INSERT test_table2 ON; +go + +SET IDENTITY_INSERT test_table2 OFF; +go + +SET IDENTITY_INSERT test_table2 OFF; +go + +EXEC insert_test_table1_stmt1; +go + +EXEC insert_test_table1_stmt2; +go + +SET IDENTITY_INSERT test_table1 ON; +go + +SET IDENTITY_INSERT test_table1 OFF; +go + +EXEC insert_test_table1_stmt1; +go + +EXEC insert_test_table1_stmt2; +go + +SET IDENTITY_INSERT test_table1 ON; +go + +EXEC insert_test_table1_stmt1; +go + +SET IDENTITY_INSERT test_table1 ON; +go + +EXEC insert_test_table1_stmt2; +go + +SET IDENTITY_INSERT test_table1 OFF; +go + +EXEC insert_test_table1_stmt1; +go + +EXEC insert_test_table1_stmt2; +go + +SELECT * FROM test_table1; +go + +-- Clean up +DROP PROC insert_test_table1_stmt1, +insert_test_table1_stmt2, +insert_test_table1_stmt2_outer, +insert_test_table1_stmt3, +insert_test_table1_stmt4, +insert_test_table1_stmt5, +insert_test_table1_stmt6, +insert_test_table1_test_multi_stmts, +insert_test_table1_val; +go +DROP TABLE test_table1, +test_table2; +go diff --git a/contrib/test/JDBC/input/BABEL-IDENTITY-BIFS.sql b/contrib/test/JDBC/input/BABEL-IDENTITY-BIFS.sql new file mode 100644 index 00000000000..2b94ac949b5 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-IDENTITY-BIFS.sql @@ -0,0 +1,346 @@ +CREATE SCHEMA ident_bifs; +GO + +-- Test standard INSERTs +SELECT IDENT_SEED('ident_bifs.t1'); +go +SELECT IDENT_INCR('ident_bifs.t1'); +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +CREATE TABLE ident_bifs.t1(id INT IDENTITY, c1 INT); +go +SELECT @@IDENTITY; +go +SELECT IDENT_SEED('ident_bifs.t1'); +go +SELECT IDENT_INCR('ident_bifs.t1'); +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go + +-- Test IDENTITY_INSERT +SET IDENTITY_INSERT ident_bifs.t1 ON; +go +INSERT INTO ident_bifs.t1 (id, c1) VALUES (10, 42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +INSERT INTO ident_bifs.t1 (id, c1) VALUES (-5, 42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +SET IDENTITY_INSERT ident_bifs.t1 OFF; +go + +-- Test follow-up INSERTs +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go + +-- Test new table with IDENTITY_INSERT +CREATE TABLE ident_bifs.t2(id INT IDENTITY, c1 INT); +go +SET IDENTITY_INSERT ident_bifs.t2 ON; +go +INSERT INTO ident_bifs.t2 (id, c1) VALUES (10, 42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t2'); +go +SET IDENTITY_INSERT ident_bifs.t2 OFF; +go + +-- Test follow-up INSERTs +INSERT INTO ident_bifs.t2 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t2'); +go +INSERT INTO ident_bifs.t2 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t2'); +go + +-- Test follow-up INSERTs to the previous table +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +INSERT INTO ident_bifs.t1 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go + +-- Test standard INSERTs with decrementing values +CREATE TABLE ident_bifs.t3(id INT IDENTITY(5, -25), c1 INT); +go +SELECT @@IDENTITY; +go +SELECT IDENT_SEED('ident_bifs.t3'); +go +SELECT IDENT_INCR('ident_bifs.t3'); +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go + +-- Test IDENTITY_INSERT +SET IDENTITY_INSERT ident_bifs.t3 ON; +go +INSERT INTO ident_bifs.t3 (id, c1) VALUES (-500, 42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go +INSERT INTO ident_bifs.t3 (id, c1) VALUES (10, 42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go +SET IDENTITY_INSERT ident_bifs.t3 OFF; +go + +-- Test follow-up INSERTs +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go + +-- Test new table with IDENTITY_INSERT +CREATE TABLE ident_bifs.t4(id INT IDENTITY(-10, -1), c1 INT); +go +SELECT IDENT_SEED('ident_bifs.t4'); +go +SELECT IDENT_INCR('ident_bifs.t4'); +go +SELECT IDENT_CURRENT('ident_bifs.t4'); +go +SET IDENTITY_INSERT ident_bifs.t4 ON; +go +INSERT INTO ident_bifs.t4 (id, c1) VALUES (-50, 42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t4'); +go +SET IDENTITY_INSERT ident_bifs.t4 OFF; +go + +-- Test follow-up INSERTs +INSERT INTO ident_bifs.t4 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t4'); +go +INSERT INTO ident_bifs.t4 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t4'); +go + +-- Test follow-up INSERTs to the previous table +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go +INSERT INTO ident_bifs.t3 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go + +-- Test multi-inserts +SET IDENTITY_INSERT ident_bifs.t1 ON; +go +INSERT INTO ident_bifs.t1 (id, c1) VALUES (10, 42), (9999,42), (0, 42), (-100, 42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +SET IDENTITY_INSERT ident_bifs.t1 OFF; +go + +CREATE PROCEDURE ident_bifs.insertLoopT1 +AS BEGIN +DECLARE @N INT +SET @N = 1 +SET IDENTITY_INSERT ident_bifs.t1 ON +WHILE (@N < 10) +BEGIN + INSERT INTO ident_bifs.t1 (id, c1) VALUES (@N*42, 42) + SET @N = @N + 1 +END +SET IDENTITY_INSERT ident_bifs.t1 OFF +END; +go + +EXEC ident_bifs.insertLoopT1; +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t1'); +go + +-- Update the last identity value as a reference +INSERT INTO ident_bifs.t2 (c1) VALUES (42); +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t2'); +go + +-- Insert select and check that the last value changed to what is expected +SET IDENTITY_INSERT ident_bifs.t2 ON; +go +INSERT INTO ident_bifs.t2 (id, c1) SELECT * FROM ident_bifs.t1; +go +SELECT @@IDENTITY; +go +SELECT IDENT_CURRENT('ident_bifs.t2'); +go +SET IDENTITY_INSERT ident_bifs.t2 OFF; +go + +-- Check value of each table in the session so far +SELECT IDENT_CURRENT('ident_bifs.t1'); +go +SELECT IDENT_CURRENT('ident_bifs.t2'); +go +SELECT IDENT_CURRENT('ident_bifs.t3'); +go +SELECT IDENT_CURRENT('ident_bifs.t4'); +go + +-- Test with table in default schema +CREATE TABLE id_bifs_t1(id INT IDENTITY(64, 32), c1 INT); +go +SELECT IDENT_SEED('id_bifs_t1'); +go +SELECT IDENT_INCR('id_bifs_t1'); +go +SELECT IDENT_CURRENT('id_bifs_t1'); +go +INSERT INTO id_bifs_t1 (c1) VALUES (8); +go +SELECT IDENT_CURRENT('id_bifs_t1'); +go +SELECT @@IDENTITY; +go + +-- Test camel case +CREATE TABLE [ident_bifs].[ID_BIFs_T2](id INT IDENTITY(0, -128), c1 INT); +go +SELECT IDENT_SEED('[ident_bifs].[ID_BIFs_T2]'); +go +SELECT IDENT_INCR('[ident_bifs].[ID_BIFs_T2]'); +go +SELECT IDENT_CURRENT('[ident_bifs].[ID_BIFs_T2]'); +go +INSERT INTO [ident_bifs].[ID_BIFs_T2] (c1) VALUES (8); +go +SELECT IDENT_CURRENT('[ident_bifs].[ID_BIFs_T2]'); +go +INSERT INTO [ident_bifs].[ID_BIFs_T2] (c1) VALUES (8); +go +SELECT IDENT_CURRENT('[ident_bifs].[ID_BIFs_T2]'); +go +SELECT @@IDENTITY; +go + +-- Test faulty input +SELECT IDENT_SEED('[ident_bifs].ID_BIFs_T2'); +go +SELECT IDENT_INCR('[ident_bifs].[ID_BIFs_T2'); +go +SELECT IDENT_CURRENT('[ident_bifs].ID_BIFs_T2]'); +go +SELECT IDENT_SEED('[ident_bifs].[[ID_BIFs_T2]]'); +go +SELECT IDENT_INCR('[ident_bifs].[[ID_BIFs_T2]'); +go +SELECT IDENT_CURRENT('[ident_bifs].ID_[BIFs]_T2'); +go +SELECT IDENT_SEED(''); +go +SELECT IDENT_INCR(''); +go +SELECT IDENT_CURRENT(''); +go +SELECT IDENT_SEED(NULL); +go +SELECT IDENT_INCR(NULL); +go +SELECT IDENT_CURRENT(NULL); +go + +DROP PROC ident_bifs.insertLoopT1; +go +DROP TABLE ident_bifs.t1, ident_bifs.t2, ident_bifs.t3, ident_bifs.t4, id_bifs_t1, ident_bifs.ID_BIFs_T2; +go +DROP SCHEMA ident_bifs; +GO diff --git a/contrib/test/JDBC/input/BABEL-IDENTITY-COLUMN.sql b/contrib/test/JDBC/input/BABEL-IDENTITY-COLUMN.sql new file mode 100644 index 00000000000..55c06ff9e87 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-IDENTITY-COLUMN.sql @@ -0,0 +1,123 @@ +USE MASTER; +GO + +-- Expect an error. Test nullable identity column creation. +CREATE TABLE dbo.t1(id INT IDENTITY NULL, col1 INT); +go + +CREATE TABLE dbo.t1(id INT IDENTITY NOT NULL, col1 INT); +go + +CREATE TABLE dbo.t2(col1 INT); +go + +-- Expect an error. +ALTER TABLE dbo.t2 ADD id INT IDENTITY NULL; +go + +ALTER TABLE dbo.t2 ADD id INT IDENTITY NOT NULL; +go + +CREATE TABLE dbo.test_table1(test_id INT IDENTITY(10, -1), test_col1 INT); +go + +INSERT INTO test_table1 VALUES (2), (4), (8), (16); +go + +SELECT * FROM test_table1; +go + +CREATE TABLE dbo.test_table2(test_id INT IDENTITY(100, -20), test_col1 INT); +go + +INSERT INTO test_table2 VALUES (2), (4), (8), (16); +go + +SELECT * FROM test_table2; +go + +CREATE TABLE dbo.test_table3(test_id INT IDENTITY(-10, 5), test_col1 INT); +go + +INSERT INTO test_table3 VALUES (2), (4), (8), (16); +go + +SELECT * FROM test_table3; +go + +-- Test if TRUNCATE resets identity +INSERT INTO dbo.t1 VALUES (2), (4), (8), (16); +go + +SELECT * FROM dbo.t1; +go + +TRUNCATE TABLE dbo.t1; +GO + +INSERT INTO dbo.t1 VALUES (2), (4), (8), (16); +go + +SELECT * FROM dbo.t1; +go + +TRUNCATE TABLE test_table1; +go + +SELECT * FROM test_table1; +go + +INSERT INTO test_table1 VALUES (2), (4), (8), (16); +go + +SELECT * FROM test_table1; +go + +-- Test preventing multiple identity columns +-- Expect error +CREATE TABLE dbo.t3(id1 INT IDENTITY, id2 INT IDENTITY); +GO + +-- Expect error +CREATE TABLE dbo.t3(id1 NUMERIC IDENTITY, id2 INT IDENTITY, id3 DECIMAL IDENTITY); +GO + +CREATE TABLE dbo.t3(id1 INT IDENTITY, c1 INT); +GO + +-- Expect error +ALTER TABLE dbo.t3 ADD id2 INT IDENTITY; +GO + +ALTER TABLE dbo.t3 DROP COLUMN id1; +GO + +ALTER TABLE dbo.t3 ADD id1 INT IDENTITY; +GO + +-- Expect error +ALTER TABLE dbo.t3 ADD id2 INT IDENTITY; +GO + +CREATE TABLE dbo.t4(c1 INT); +GO + +ALTER TABLE dbo.t4 ADD id2 SMALLINT IDENTITY; +GO + +-- Expect error +ALTER TABLE dbo.t4 ADD id3 BIGINT IDENTITY; +GO + +ALTER TABLE dbo.t4 DROP COLUMN id2; +GO + +ALTER TABLE dbo.t4 ADD id2 SMALLINT IDENTITY; +GO + +-- Expect error +ALTER TABLE dbo.t4 ADD id3 BIGINT IDENTITY; +GO + +DROP TABLE dbo.t1, dbo.t2, dbo.t3, dbo.t4, dbo.test_table1, dbo.test_table2, dbo.test_table3; +GO diff --git a/contrib/test/JDBC/input/BABEL-IDENTITY.sql b/contrib/test/JDBC/input/BABEL-IDENTITY.sql new file mode 100644 index 00000000000..bb05f5bc965 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-IDENTITY.sql @@ -0,0 +1,270 @@ +USE MASTER; +GO + +CREATE TABLE dbo.test_table1 (test_id INT IDENTITY, test_col1 INT); +go + +CREATE PROCEDURE insert_test_table1 + @id INT, + @val INT +AS + INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (@id, @val); +go + +SELECT @@IDENTITY; +go +SELECT SCOPE_IDENTITY(); +go +INSERT INTO dbo.test_table1 (test_col1) VALUES (10); +go +SELECT @@IDENTITY; +go +SELECT SCOPE_IDENTITY(); +go +SELECT @@SERVERNAME; +go +-- Expect an error +INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (2, 10); +go + +SET IDENTITY_INSERT dbo.test_table1 ON; +go + +-- Test custom insert +EXECUTE insert_test_table1 2, 10; +go + +-- Insert a non-sequential max identity value +EXECUTE insert_test_table1 10, 10; +go + +-- Insert a lesser identity value +EXECUTE insert_test_table1 5, 10; +go + +-- Set to off. Notice we're not specifying the schema this time +SET IDENTITY_INSERT test_table1 OFF; +go + +-- Verify the identity sequence value is updated to the max value +INSERT INTO dbo.test_table1 (test_col1) VALUES (11); +go +INSERT INTO dbo.test_table1 (test_col1) VALUES (12); +go + +SELECT * FROM dbo.test_table1; +go + +-- Expect an error. Verify IDENTITY_INSERT set off +INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (2, 10); +go + +-- Set to table then drop it. Should implicitly turn IDENTITY_INSERT off +SET IDENTITY_INSERT dbo.test_table1 ON; +go +DROP TABLE test_table1; +go + +-- Create a table with the same name +CREATE TABLE dbo.test_table1 (test_id INT IDENTITY, test_col1 INT); +go + +-- Try to insert. Expect an error. Same name but different OID +INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (2, 10); +go + +-- Expect errors +SET IDENTITY_INSERT test_table2 ON; +go +SET IDENTITY_INSERT fake_schema.test_table1 ON; +go +SET IDENTITY_INSERT fake_db.dbo.test_table1 ON; +go + +CREATE TABLE dbo.test_table2 (test_id INT IDENTITY(7,2), test_col1 INT); +go + +-- Expect error. Set IDENTITY_INSERT to a table then try setting it to another +SET IDENTITY_INSERT dbo.test_table1 ON; +go +SET IDENTITY_INSERT dbo.test_table2 ON; +go +SET IDENTITY_INSERT dbo.test_table1 OFF; +go +INSERT INTO dbo.test_table2 (test_col1) VALUES (13); +go +INSERT INTO dbo.test_table2 (test_col1) VALUES (108); +go +SELECT @@IDENTITY; +go +SELECT SCOPE_IDENTITY(); +go + +SELECT * FROM dbo.test_table2; +go + +-- Expect error. Cannot set IDENTITY_INSERT to table without identity property +CREATE TABLE dbo.test_table3 (test_id INT, test_col1 INT); +go + +SET IDENTITY_INSERT dbo.test_table3 ON; +go + +-- Test INSERT with default target list that omits identity columns +CREATE TABLE dbo.employees +(person_id int IDENTITY PRIMARY KEY, firstname nvarchar(20), lastname nvarchar(30), salary money); +go + +INSERT INTO employees VALUES (N'Neil', N'Armstrong', 11236.9898); +go + +SELECT * FROM dbo.employees; +go + +-- Test identity insert with multiple columns +SET IDENTITY_INSERT dbo.employees ON; +go + +CREATE PROCEDURE insert_employees + @id INT, + @first TEXT, + @last TEXT, + @salary NUMERIC(18,4) +AS + INSERT INTO dbo.employees (person_id, firstname, lastname, salary) VALUES (@id, @first, @last, @salary); +go + +EXEC insert_employees 5, N'Buzz', N'Aldrin', 11236.9898; +go + +SELECT @@IDENTITY; +go + +-- Expect Errors. Cannot insert without explicit identity column value +INSERT INTO employees VALUES (N'Michael', N'Collins', 11236.9898); +go + +INSERT INTO employees (firstname, lastname, salary) VALUES (N'Michael', N'Collins', 11236.9898); +go + +SET IDENTITY_INSERT dbo.employees OFF; +go + +INSERT INTO employees VALUES (N'Michael', N'Collins', 11236.9898); +go + +SELECT * FROM dbo.employees; +go + +-- Test Camel Case +CREATE TABLE [dbo].[Test_Table1]([Test_Id] INT IDENTITY, test_col1 INT); +go + +SET IDENTITY_INSERT [Test_Table1] ON; +go + +CREATE PROCEDURE insert_test_table_c + @id INT, + @val INT +AS + INSERT INTO [dbo].[Test_Table1] ([Test_Id], test_col1) VALUES (@id, @val); +go + +CREATE PROCEDURE insert_test_table_c_default + @val INT +AS + INSERT INTO [dbo].[Test_Table1] (test_col1) VALUES (@val); +go + +EXEC insert_test_table_c 1, 10; +go + +EXEC insert_test_table_c 5, 20; +go + +-- Expect error. Insert restriction +EXEC insert_test_table_c_default 30; +go +-- Expect errors. Not matching case +SET IDENTITY_INSERT Test_Table1 ON; +go +SET IDENTITY_INSERT [tEst_tAble1] ON; +go +SET IDENTITY_INSERT [dbo].[Test_Table1] ON; +go +INSERT INTO [dbo].[Test_Table1] (test_id, test_col1) VALUES (10, 30); +go + +-- Set to off and verify table +SET IDENTITY_INSERT [dbo].[Test_Table1] OFF; +go + +EXEC insert_test_table_c_default 30; +go + +SELECT * FROM [Test_Table1]; +go + +-- Test updating negative increment +CREATE TABLE dbo.t_neg_inc_1(id INT IDENTITY(1, -1), col1 INT); +go + +CREATE PROCEDURE insert_default_neg_inc_1 + @val INT +AS BEGIN + INSERT INTO dbo.t_neg_inc_1(col1) VALUES (@val); +END; +go + +CREATE PROCEDURE insert_id_neg_inc_1 + @id INT, + @val INT +AS BEGIN + SET IDENTITY_INSERT t_neg_inc_1 ON; + INSERT INTO dbo.t_neg_inc_1(id, col1) VALUES (@id, @val); + SET IDENTITY_INSERT t_neg_inc_1 OFF; +END; +go + +EXEC insert_default_neg_inc_1 10; +go + +EXEC insert_default_neg_inc_1 20; +go + +EXEC insert_id_neg_inc_1 -5, 30; +go + +EXEC insert_default_neg_inc_1 40; +go + +EXEC insert_id_neg_inc_1 5, 50; +go + +EXEC insert_default_neg_inc_1 60; +go + +SELECT * FROM t_neg_inc_1; +go + +-- Test get id max/min helper functions +SELECT sys.get_min_id_from_table(';drop table master_dbo.test_table1;', 'master_dbo', 'test_table1'); +GO + +SELECT sys.get_max_id_from_table('test_id', ';drop table master_dbo', 'test_table1;'); +GO + +-- Clean up +DROP PROCEDURE insert_test_table1, +insert_employees, +insert_test_table_c, +insert_test_table_c_default, +insert_default_neg_inc_1, +insert_id_neg_inc_1; +go +DROP TABLE dbo.test_table1, +dbo.test_table2, +dbo.test_table3, +dbo.employees, +dbo.t_neg_inc_1; +go diff --git a/contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN-PREPEXEC.txt b/contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN-PREPEXEC.txt new file mode 100644 index 00000000000..d01c429277e --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN-PREPEXEC.txt @@ -0,0 +1,40 @@ +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +CREATE TABLE impl_txn_prepexec_table (a varchar(15) UNIQUE, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) + +prepst#!#INSERT INTO impl_txn_prepexec_table VALUES (@a, @b, @c, @d, @e, @f, @g)#!#varchar|-|a|-|Apple#!#nvarchar|-|b|-|red#!#int|-|c|-|1#!#char|-|d|-|Delhi#!#nchar|-|e|-|Sad😞#!#datetime|-|f|-|2000-12-13 12:58:23.123#!#numeric|-|g|-|123.1|-|4|-|1 + +SET IMPLICIT_TRANSACTIONS ON + +SELECT @@TRANCOUNT +prepst#!#SELECT * FROM impl_txn_prepexec_table WHERE a = @p1 AND b = @p2 AND c = @p3#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|1 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|green#!#int|-|p3|-|1 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +prepst#!#SELECT 12, 34, 56 FROM (SELECT * FROM impl_txn_prepexec_table WHERE a = @p1 AND b = @p2 AND c = @p3) AS dummy_table#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|1 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|green#!#int|-|p3|-|1 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT + +SET IMPLICIT_TRANSACTIONS OFF +DROP TABLE impl_txn_prepexec_table +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; diff --git a/contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN.sql b/contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN.sql new file mode 100644 index 00000000000..3f56ac6daeb --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN.sql @@ -0,0 +1,232 @@ +-- Setup +CREATE TABLE implicit_tran_table (a int) +GO + +INSERT INTO implicit_tran_table VALUES (10) +GO + +SET IMPLICIT_TRANSACTIONS ON +GO + +-- Select from table should start implicit transaction +SELECT @@TRANCOUNT +SELECT * FROM implicit_tran_table +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +SELECT @@TRANCOUNT +DECLARE @implicit_tran_table_var int +SELECT @implicit_tran_table_var = a FROM implicit_tran_table +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Select from table variable should start implicit transaction +SELECT @@TRANCOUNT +DECLARE @implicit_tran_table_var TABLE (col1 VARCHAR(10)); +SELECT * FROM @implicit_tran_table_var +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Select not from a table should not start implicit transaction +SELECT @@TRANCOUNT +SELECT 123 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +SELECT @@TRANCOUNT +DECLARE @implicit_tran_table_var int +SELECT @implicit_tran_table_var = 1234 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- BABEL-1869 +-- 2-Column select should not start implicit transaction +SELECT @@TRANCOUNT +SELECT 1, 2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +SELECT @@TRANCOUNT +SELECT 1, 2, 3 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Select from table in subquery should start implicit transaction +SELECT @@TRANCOUNT +SELECT (select count(*) from implicit_tran_table) +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +SELECT @@TRANCOUNT +SELECT 1, 2 FROM (SELECT * FROM implicit_tran_table) as dummy_table +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Select to call a function should not start implicit transaction +SELECT @@TRANCOUNT +SELECT @@ERROR +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +/* + * DMLs should start implicit transaction + * Note: Did not add test for MERGE since + * we do not support it (BABEL-877) + */ +SELECT @@TRANCOUNT +INSERT INTO implicit_tran_table VALUES (11) +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +SELECT @@TRANCOUNT +UPDATE implicit_tran_table SET a = 100 WHERE a = 10 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +SELECT @@TRANCOUNT +DELETE FROM implicit_tran_table WHERE a = 100 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Create table should start implicit transaction +SELECT @@TRANCOUNT +CREATE TABLE implicit_tran_table2 (c smallint) +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- BABEL-1870 +-- SELECT ... INTO should start implicit transaction +-- Note: We internally convert this to CREATE TABLE AS +SELECT @@TRANCOUNT +SELECT * INTO dummy_table FROM implicit_tran_table2 +SELECT @@TRANCOUNT +DROP TABLE dummy_table +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Alter table should start implicit transaction +SELECT @@TRANCOUNT; +ALTER TABLE implicit_tran_table2 ADD CONSTRAINT default_c DEFAULT 99 FOR c +SELECT @@TRANCOUNT; +IF @@TRANCOUNT > 0 COMMIT +GO + +-- truncate table should start implicit transaction +SELECT @@TRANCOUNT +TRUNCATE TABLE implicit_tran_table2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Drop table should start implicit transaction +SELECT @@TRANCOUNT +DROP TABLE implicit_tran_table2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Create procedure should start implicit transaction +SELECT @@TRANCOUNT +GO +CREATE PROCEDURE implicit_tran_proc + AS + BEGIN + SELECT 'Select inside a procedure' + END +GO +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +/* + * Alter procedure should start implicit transaction + * Note: Did not add test for ALTER PROCEDURE since + * we do not support it (BABEL-442) + */ + + +-- Drop procedure should start implicit transaction +SELECT @@TRANCOUNT +DROP PROCEDURE implicit_tran_proc +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Begin transaction should start implicit transaction +SELECT @@TRANCOUNT +BEGIN TRANSACTION +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Create database should not start implicit transaction +SELECT @@TRANCOUNT +CREATE DATABASE implicit_tran_db +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +-- Drop database should not start implicit transaction +SELECT @@TRANCOUNT +DROP DATABASE implicit_tran_db +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO + +/* + * Declare cursor should start an implicit transaction + */ +SELECT @@TRANCOUNT; +DECLARE implicit_tran_cursor CURSOR FOR SELECT * FROM implicit_tran_table; +SELECT @@TRANCOUNT; +DEALLOCATE implicit_tran_cursor; +IF @@TRANCOUNT > 0 COMMIT; +GO + +SELECT @@TRANCOUNT; +DECLARE implicit_tran_cursor CURSOR FOR SELECT 9876; +SELECT @@TRANCOUNT; +DEALLOCATE implicit_tran_cursor; +GO + +-- Open and fetch should start implicit transaction +-- Close and deallocate should not start implicit transaction +DECLARE implicit_tran_cursor CURSOR FOR SELECT * FROM implicit_tran_table; +DECLARE @val INT; +IF @@TRANCOUNT > 0 COMMIT; +SELECT @@TRANCOUNT; +OPEN implicit_tran_cursor; +SELECT @@TRANCOUNT; +IF @@TRANCOUNT > 0 COMMIT; +SELECT @@TRANCOUNT; +FETCH FROM implicit_tran_cursor INTO @val; +SELECT @@TRANCOUNT; +IF @@TRANCOUNT > 0 COMMIT; +SELECT @@TRANCOUNT; +CLOSE implicit_tran_cursor; +SELECT @@TRANCOUNT; +DEALLOCATE implicit_tran_cursor; +SELECT @@TRANCOUNT; +GO + +-- Cleanup +SET IMPLICIT_TRANSACTIONS OFF +GO + +DROP TABLE implicit_tran_table +GO diff --git a/contrib/test/JDBC/input/BABEL-INSERT-EXECUTE.sql b/contrib/test/JDBC/input/BABEL-INSERT-EXECUTE.sql new file mode 100644 index 00000000000..dce94d8d521 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-INSERT-EXECUTE.sql @@ -0,0 +1,272 @@ +create table t1 (a int); +GO +insert into t1 values (1); +GO +create table t2 (a int); +GO + +-- procedure with SELECT +create procedure sp_multi_selects as +select * from t1; +select a from t1; +go +-- normal execute +execute sp_multi_selects; +go +-- insert execute +select * from t2; +go +insert into t2 execute sp_multi_selects; +go +select * from t2; +go +-- insert execute a second time +insert into t2 execute sp_multi_selects; +go +select * from t2; +go + +-- column mismatch +create table t3(a int, b int, c int); +GO +insert into t3 execute sp_multi_selects; +GO +select * from t3; +GO +-- INSERT with matching column list +insert into t3 (a) execute sp_multi_selects; +GO +select * from t3; +GO + +-- DML statements in procedure +create procedure sp_dml_select as +insert into t1 values(2); +update t1 set a = 3 where a = 2; +delete t1 where a = 3; +select * from t1; +GO +select * from t1; +GO +-- normal EXECUTE - each DML should send result to client +execute sp_dml_select; +GO +-- INSERT EXECUTE - only final INSERT should send result to client +insert into t2 execute sp_dml_select; +GO + +-- DDL statements in procedure +create procedure sp_ddl_select as +create table sp_ddl_select_table(c int); +select * from sp_ddl_select_table; +drop table sp_ddl_select_table; +GO +-- normal EXECUTE +execute sp_ddl_select; +GO +-- INSERT EXECUTE +insert into t2 execute sp_ddl_select; +GO + +-- test using OUTPUT clause in INSERT EXECUTE +insert into t2 output inserted.* into t3.a exec sp_multi_selects; +GO + +-- COMMIT with no BEGIN TRAN +create procedure sp_commit_no_begin as +insert into t1 values(3); +commit; +select * from t1; +GO +-- normal EXECUTE - should insert into t1 +select * from t1; +GO +execute sp_commit_no_begin; +GO +select * from t1; +GO +-- INSERT EXECUTE - should not insert into t1 or t2 +select * from t2; +GO +insert into t2 execute sp_commit_no_begin; +GO +select * from t1; +GO +select * from t2; +GO +-- more COMMIT than BEGIN TRAN +create procedure sp_commits_begin as +begin tran +insert into t1 values(3); +commit; +commit; +select * from t1; +go +-- normal EXECUTE - should insert into t1 +execute sp_commits_begin; +GO +select * from t1; +GO +-- INSERT EXECUTE - should not insert into t1 or t2 +insert into t2 execute sp_commits_begin; +GO +select * from t1; +GO +select * from t2; +GO + +-- ROLLBACK stmt is not allowed in INSERT EXEC, whether there is BEGIN TRAN or +-- not. +-- ROLLBACK with no BEGIN TRAN +create procedure sp_rollback_no_begin as +insert into t1 values(4); +rollback; +select * from t1; +GO +-- normal EXECUTE - should insert into t1 +execute sp_rollback_no_begin +GO +select * from t1; +GO +-- INSERT EXECUTE - should not insert into t1 or t2 +select * from t2; +GO +insert into t2 execute sp_rollback_no_begin; +GO +select * from t1; +GO +select * from t2; +GO +-- ROLLBACK with BEGIN TRAN +create procedure sp_rollback_with_begin as +begin tran; +insert into t1 values(4); +rollback; +select * from t1; +go +-- normal EXECUTE - should insert into t1 and rollback +execute sp_rollback_with_begin +GO +select * from t1; +GO +-- INSERT EXECUTE - should not insert into t1 or t2 +select * from t2; +GO +insert into t2 execute sp_rollback_with_begin; +GO +select * from t1; +GO +select * from t2; +GO + +-- column mismatch with previous DML - should not insert into t1 or t2 +create procedure sp_select_mismatch_with_dml as +insert into t1 values(5); +select a, a from t1; +GO +select * from t1; +GO +select * from t2; +GO +insert into t2 execute sp_select_mismatch_with_dml +go +select * from t1; +GO +select * from t2; +GO + +-- column mismatch with previous DML in subtransaction - should not insert into +-- t1 or t2 +create procedure sp_select_mismatch_after_subtran as +begin tran; +insert into t1 values(6); +commit; +select a, a from t1; +GO +select * from t1; +GO +select * from t2; +GO +insert into t2 execute sp_select_mismatch_after_subtran; +GO +select * from t1; +GO +select * from t2; +GO + +-- procedure with parameter +create procedure sp_select_param (@a int) as +select * from t1 where a = @a; +GO +insert into t1 values (2); +GO +select * from t1; +GO +-- normal EXECUTE +execute sp_select_param 1; +go +select * from t2; +GO +-- INSERT EXECUTE +insert into t2 execute sp_select_param 1; +GO +select * from t2; +GO + +-- test if PL parser correctly recognizes whether EXECUTE starts a new statement +-- or not +-- INSERT has VALUES - EXEC should start a new statement +insert into t1 values(7) +exec sp_multi_selects +go +-- INSERT has SELECT - EXEC should start a new statement +insert into t2 values(8) +insert into t1 select * from t2 where a = 8 +exec sp_multi_selects +go +-- INSERT has EXEC - SELECT should start a new statement +insert into t2 exec sp_multi_selects +select * from t1 +go + +-- test INSERT EXEC with inline code blocks +delete t1; +go +insert into t1 exec('select 1; select 2'); +go +select * from t1; +go +-- test INSERT EXEC with inline code blocks on table variable +declare @a table (a int); +insert into @a execute('select * from t1; select 3'); +select * from @a; +go + +-- clean up +drop table t1 +go +drop table t2 +go +drop table t3 +go +drop procedure sp_multi_selects +go +drop procedure sp_dml_select +go +drop procedure sp_ddl_select +go +drop procedure sp_commit_no_begin +go +drop procedure sp_commits_begin +go +drop procedure sp_rollback_no_begin +go +drop procedure sp_rollback_with_begin +go +drop procedure sp_select_mismatch_with_dml +go +-- drop savepoint +drop procedure sp_select_mismatch_after_subtran +go +drop procedure sp_select_param +go diff --git a/contrib/test/JDBC/input/BABEL-LIKE2ILIKE.sql b/contrib/test/JDBC/input/BABEL-LIKE2ILIKE.sql new file mode 100644 index 00000000000..697a6ada2d2 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-LIKE2ILIKE.sql @@ -0,0 +1,96 @@ +-- test LIKE to ILIKE transformation +create table like_tesing1 (c1 varchar(20), c2 char(20), c3 nvarchar(20)) +GO +insert into like_tesing1 values ('JONES','JONES','JONES') +GO +insert into like_tesing1 values ('JoneS','JoneS','JoneS') +GO +insert into like_tesing1 values ('jOnes','jOnes','jOnes') +GO +insert into like_tesing1 values ('abcD','AbcD','ABCd') +GO +insert into like_tesing1 values ('äbĆD','äḃcD','äƀCd') +GO +-- test that like is case-insenstive +select c1 from like_tesing1 where c1 LIKE 'jones' +GO +select c1 from like_tesing1 where c1 LIKE 'Jon%' +GO +select c1 from like_tesing1 where c1 LIKE 'jone_' +GO +select c1 from like_tesing1 where c1 LIKE '_one_' +GO +-- test that like is accent-senstive for CI_AS collation +select c1 from like_tesing1 where c1 LIKE 'ab%' +GO +select c1 from like_tesing1 where c1 LIKE 'äb%' +GO +select c1 from like_tesing1 where c1 LIKE 'äḃĆ_' +GO +-- test not like +select c1 from like_tesing1 where c1 NOT LIKE 'jones' +GO +select c1 from like_tesing1 where c1 NOT LIKE 'jone%' +GO +-- wild card literals are transformed to equal +select c1 from like_tesing1 where c1 LIKE '\%ones' +GO +select c1 from like_tesing1 where c1 LIKE '\_ones' +GO +-- test combining with other string functions +select c1 from like_tesing1 where c1 LIKE lower('_ones') +GO +select c1 from like_tesing1 where c1 LIKE upper('_ones') +GO +select c1 from like_tesing1 where c1 LIKE concat('_on','_s') +GO +select c1 from like_tesing1 where c1 LIKE concat('a','%d') +GO +select c1 from like_tesing1 where c1 NOT LIKE lower('%s') +GO +-- test sub-queries +Select count(*) from like_tesing1 where c1 LIKE (select c1 from like_tesing1 where c1 LIKE 'AbcD') +GO +Select count(*) from like_tesing1 where c2 NOT LIKE (select c2 from like_tesing1 where c2 NOT LIKE 'jo%' AND c2 NOT LIKE 'ä%') +GO +Select count(*) from like_tesing1 where c3 LIKE (select c3 from like_tesing1 where c3 NOT LIKE'jo%' AND c3 NOT LIKE 'ä%') +GO +with p1 as (select c1 from like_tesing1 where c1 LIKE '__Ć_'), +p2 as (select c3 from like_tesing1 where c3 LIKE 'äƀ__') +select * from p1 union all select * from p2 +GO +-- test case expression +select c1,(case when c1 LIKE 'j%' then 1 when c1 NOT LIKE 'j%' then 2 end) from like_tesing1 +GO +-- test that LIKE transformation is only applied for SQL_LATIN1_GENERAL_CI_AS column +create table like_tesing2(c1 varchar(20) COLLATE SQL_Latin1_General_CP1_CS_AS) +GO +insert into like_tesing2 values ('JONES') +GO +insert into like_tesing2 values ('JoneS') +GO +insert into like_tesing2 values ('abcD') +GO +insert into like_tesing2 values ('äbĆD') +GO +select * from like_tesing2 where c1 LIKE 'jo%' +GO +select * from like_tesing2 where c1 NOT LIKE 'j%' +GO +select * from like_tesing2 where c1 LIKE 'AB%' +GO +-- test eplicitly specify collation as CI_AS, like transformation is also applied. +SELECT CASE WHEN 'JONES' like 'jo%' THEN 1 ELSE 0 END +GO +SELECT CASE WHEN 'JONES' COLLATE SQL_Latin1_General_CP1_CI_AS like 'jo%' THEN 1 ELSE 0 END +GO +-- test when pattern is NULL +SELECT CASE WHEN 'JONES' like '' THEN 1 ELSE 0 END +GO +SELECT * from like_tesing1 where c1 like '' +GO + +drop table like_tesing1 +GO +drop table like_tesing2 +GO diff --git a/contrib/test/JDBC/input/BABEL-LOGIN-USER-EXT.mix b/contrib/test/JDBC/input/BABEL-LOGIN-USER-EXT.mix new file mode 100644 index 00000000000..428da9a82b5 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-LOGIN-USER-EXT.mix @@ -0,0 +1,267 @@ +-- psql +ALTER SYSTEM SET babelfishpg_tsql.allow_antlr_to_unsupported_grammar_for_testing = true; +SELECT pg_reload_conf(); +GO + +-- tsql +-- Test initialization +SELECT COUNT(*) FROM sys.babelfish_authid_login_ext; +go + +CREATE LOGIN r1 WITH PASSWORD = 'abc'; +go + +SELECT COUNT(*) FROM sys.babelfish_authid_login_ext; +go + +-- Test initialization with password +CREATE LOGIN r2 WITH PASSWORD = '123' +go + +SELECT COUNT(*) FROM sys.babelfish_authid_login_ext; +go + +-- Test altering the login ability +SELECT rolcanlogin FROM pg_catalog.pg_roles WHERE rolname = 'r1'; +go +SELECT name, is_disabled FROM sys.server_principals WHERE name = 'r1'; +go + +ALTER LOGIN r1 disable; +go + +SELECT rolcanlogin FROM pg_catalog.pg_roles WHERE rolname = 'r1'; +go +SELECT name, is_disabled FROM sys.server_principals WHERE name = 'r1'; +go + +ALTER LOGIN r1 enable; +go + +SELECT rolcanlogin FROM pg_catalog.pg_roles WHERE rolname = 'r1'; +go +SELECT name, is_disabled FROM sys.server_principals WHERE name = 'r1'; +go + +-- Test altering the password +ALTER LOGIN r2 WITH PASSWORD = '456' +go + +-- Test dropping +DROP LOGIN r1; +go + +SELECT COUNT(*) FROM sys.babelfish_authid_login_ext; +go + +DROP LOGIN r2; +go + +SELECT COUNT(*) FROM sys.babelfish_authid_login_ext; +go + +-- Test membership +CREATE LOGIN r3 WITH PASSWORD = '789'; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'sysadmin') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'r3'); +GO + +ALTER SERVER ROLE sysadmin ADD MEMBER r3; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'sysadmin') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'r3'); +GO + +ALTER SERVER ROLE sysadmin DROP MEMBER r3; +GO + +SELECT COUNT(*) FROM pg_auth_members +WHERE roleid = (SELECT oid FROM pg_roles WHERE rolname = 'sysadmin') +AND "member" = (SELECT oid FROM pg_roles WHERE rolname = 'r3'); +GO + +DROP LOGIN r3; +GO + +-- Test error cases +ALTER SERVER ROLE db_owner ADD MEMBER dummy; +GO + +ALTER SERVER ROLE db_owner DROP MEMBER dummy; +GO + +ALTER SERVER ROLE sysadmin ADD MEMBER jdbc_user; +GO + +ALTER SERVER ROLE sysadmin DROP MEMBER jdbc_user; +GO + +-- Test CREATE User +CREATE USER user1; +GO + +-- Test User functions +USE MASTER; +GO + +SELECT user_name(); +GO + +SELECT user_name(user_id()); +GO + +SELECT user_name(user_id('guest')); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('dbo'); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('guest'); +GO + +USE TEMPDB; +GO + +SELECT user_name(); +GO + +SELECT user_name(user_id()); +GO + +SELECT user_name(user_id('guest')); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('dbo'); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('guest'); +GO + +CREATE DATABASE db1; +GO + +USE db1; +GO + +SELECT user_name(); +GO + +SELECT user_name(user_id()); +GO + +SELECT user_name(user_id('guest')); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('dbo'); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('guest'); +GO + +-- Expect NULL +SELECT user_name(-1); +GO + +SELECT user_id('master_dbo'); +GO + +-- Test Login functions +CREATE LOGIN login1 WITH PASSWORD = '456'; +GO + +SELECT name FROM sys.server_principals WHERE principal_id = suser_id('login1'); +GO + +SELECT name FROM sys.server_principals WHERE name = suser_name(suser_id('login1')); +GO + +-- Expect NULL +SELECT suser_name(-1); +GO + +USE MASTER; +GO + +DROP DATABASE db1; +GO + +DROP LOGIN login1; +GO + +-- psql +ALTER SYSTEM SET babelfishpg_tsql.migration_mode = 'multi-db'; +SELECT pg_reload_conf(); +GO + +-- tsql +-- Test multi-db mode +USE master; +GO + +CREATE DATABASE db1; +GO + +CREATE DATABASE db2; +GO + +USE db1; +GO + +SELECT user_name(user_id('dbo')); +GO + +SELECT user_name(user_id('guest')); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('dbo'); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('guest'); +GO + +USE db2; +GO + +SELECT user_name(user_id('dbo')); +GO + +SELECT user_name(user_id('guest')); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('dbo'); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('guest'); +GO + +USE MASTER; +GO + +SELECT user_name(user_id('dbo')); +GO + +SELECT user_name(user_id('guest')); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('dbo'); +GO + +SELECT rolname FROM pg_roles WHERE oid = user_id('guest'); +GO + +DROP DATABASE db1; +GO + +DROP DATABASE db2; +GO + +-- psql +ALTER SYSTEM SET babelfishpg_tsql.migration_mode = 'single-db'; +ALTER SYSTEM SET babelfishpg_tsql.allow_antlr_to_unsupported_grammar_for_testing = false; +SELECT pg_reload_conf(); +GO diff --git a/contrib/test/JDBC/input/BABEL-OBJECT-FUNCTIONS.sql b/contrib/test/JDBC/input/BABEL-OBJECT-FUNCTIONS.sql new file mode 100644 index 00000000000..4818499e54b --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-OBJECT-FUNCTIONS.sql @@ -0,0 +1,25 @@ +CREATE SCHEMA obj_funcs; +GO + +CREATE TABLE obj_funcs.t1(id INT, c1 NVARCHAR); +GO + +SELECT (CASE WHEN OBJECT_NAME(OBJECT_ID(N't1 ', N'U')) = 't1' THEN 'true' ELSE 'false' END) result; +GO + +SELECT (CASE WHEN OBJECT_NAME(OBJECT_ID(N' t1', N'U')) = 't1' THEN 'true' ELSE 'false' END) result; +GO + +SELECT (CASE WHEN OBJECT_NAME(OBJECT_ID(N' t1 ')) = 't1' THEN 'true' ELSE 'false' END) result; +GO + +SELECT (CASE WHEN OBJECT_NAME(OBJECT_ID(N' [t1] ', N'U')) = 't1' THEN 'true' ELSE 'false' END) result; +GO + +SELECT (CASE WHEN OBJECT_NAME(OBJECT_ID(N' [obj_funcs].[t1] ', N'U')) = 't1' THEN 'true' ELSE 'false' END) result; +GO + +DROP TABLE obj_funcs.t1; +GO +DROP SCHEMA obj_funcs; +GO diff --git a/contrib/test/JDBC/input/BABEL-RAND.sql b/contrib/test/JDBC/input/BABEL-RAND.sql new file mode 100644 index 00000000000..d3ce35e13b0 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-RAND.sql @@ -0,0 +1,29 @@ +USE master; +GO + +SELECT rand(2147483647) +GO +SELECT rand() +GO +SELECT rand() +GO +SELECT rand() +GO + +SELECT rand(0) +GO +SELECT rand() +GO +SELECT rand() +GO +SELECT rand() +GO + +SELECT rand(-2147483648) +GO +SELECT rand() +GO +SELECT rand() +GO +SELECT rand() +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-RECURSIVE-CTE.sql b/contrib/test/JDBC/input/BABEL-RECURSIVE-CTE.sql new file mode 100644 index 00000000000..3441e307e45 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-RECURSIVE-CTE.sql @@ -0,0 +1,85 @@ +USE master; +GO + +CREATE SCHEMA babel_recursive_cte; +GO + +CREATE TABLE babel_recursive_cte.numbers (c1 int); +GO + +INSERT INTO babel_recursive_cte.numbers VALUES (3) +GO + +-- basic positive case +WITH numbers(c1) +AS ( + SELECT 1 + UNION ALL + SELECT c1 + 1 FROM numbers WHERE c1 <= 5 +) +SELECT c1 FROM numbers ORDER BY c1 +GO + +-- basic negative case (not recursive) +WITH numbers(c1) +AS ( + SELECT 1 + UNION ALL + SELECT c1 + 1 FROM babel_recursive_cte.numbers WHERE c1 <= 5 -- referring physical table +) +SELECT c1 FROM numbers ORDER BY c1 +GO + +-- invalid recursive cte 1 +WITH numbers(c1) +AS ( + SELECT 1 FROM numbers + UNION ALL + SELECT c1 + 1 FROM numbers WHERE c1 <= 5 +) +SELECT c1 FROM numbers ORDER BY c1 +GO + +-- invalid recursive cte 2 +WITH numbers(c1) +AS ( + SELECT c1 + 1 FROM numbers WHERE c1 <= 5 +) +SELECT c1 FROM numbers ORDER BY c1 +GO + +-- recursive + join +WITH numbers(c1) +AS ( + SELECT 1 + UNION ALL + SELECT Y.c1 + 1 FROM babel_recursive_cte.numbers X INNER JOIN numbers Y on 1 = 1 WHERE Y.c1 <= 5 +) +SELECT c1 FROM numbers ORDER BY c1 +GO + +-- recursive + subquery +WITH numbers(c1) +AS ( + SELECT 1 + UNION ALL + SELECT c1 + 1 FROM (SELECT * FROM numbers) X WHERE c1 <= 5 +) +SELECT c1 FROM numbers ORDER BY c1 +GO + +-- recursive + expr-subquery (unsupported) +WITH numbers(c1) +AS ( + SELECT 1 + UNION ALL + SELECT (SELECT c1 FROM numbers) + 1 FROM babel_recursive_cte.numbers X WHERE c1 <= 5 +) +SELECT c1 FROM numbers ORDER BY c1 +GO + +DROP TABLE babel_recursive_cte.numbers; +GO + +DROP SCHEMA babel_recursive_cte; +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-ROWCOUNT.sql b/contrib/test/JDBC/input/BABEL-ROWCOUNT.sql new file mode 100644 index 00000000000..d3b9f547eba --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-ROWCOUNT.sql @@ -0,0 +1,112 @@ +USE master; +GO + +CREATE TABLE babel_rowcount(test_id INT IDENTITY, test_col1 INT); +GO + +CREATE PROCEDURE babel_rowcount_insert +AS BEGIN + INSERT INTO babel_rowcount (test_col1) VALUES (10), (10), (10) + + IF @@ROWCOUNT <> 3 PRINT @@ROWCOUNT + + INSERT INTO babel_rowcount (test_col1) VALUES (20) + INSERT INTO babel_rowcount (test_col1) VALUES (20) + + IF @@ROWCOUNT <> 1 PRINT @@ROWCOUNT +END; +GO + +CREATE PROCEDURE babel_rowcount_select +AS BEGIN + SELECT * FROM babel_rowcount + IF @@ROWCOUNT <> 5 PRINT @@ROWCOUNT +END; +GO + +CREATE PROCEDURE babel_rowcount_update +AS BEGIN + UPDATE babel_rowcount SET test_col1 = 1 WHERE test_col1 = 10 + + IF @@ROWCOUNT <> 3 PRINT @@ROWCOUNT + + UPDATE babel_rowcount SET test_col1 = 2 WHERE test_col1 = 20 + + IF @@ROWCOUNT <> 2 PRINT @@ROWCOUNT +END; +GO + +CREATE PROCEDURE babel_rowcount_select_set +AS BEGIN + DECLARE @v int + SELECT @v = test_col1 FROM babel_rowcount + IF @@ROWCOUNT <> 5 PRINT @@ROWCOUNT +END; +GO + +CREATE PROCEDURE babel_rowcount_delete +AS BEGIN + DELETE FROM babel_rowcount WHERE test_col1 = 1 + + IF @@ROWCOUNT <> 3 PRINT @@ROWCOUNT + + DELETE FROM babel_rowcount WHERE test_col1 = 2 + + IF @@ROWCOUNT <> 2 PRINT @@ROWCOUNT +END; +GO + +CREATE PROCEDURE babel_rowcount_basic_statements +AS BEGIN + DECLARE @v int + SET @v = 42 + IF @@ROWCOUNT <> 1 raiserror('ROWCOUNT should be 1', 11, 1) + print @v + IF @@ROWCOUNT <> 0 raiserror('ROWCOUNT should be 0', 11, 1) +END; +GO + +CREATE PROCEDURE babel_rowcount_return +AS BEGIN + RETURN 1 +END; +GO + +EXEC babel_rowcount_insert; +GO +EXEC babel_rowcount_select; +GO +EXEC babel_rowcount_update; +GO +-- Expect 2. Implicit return does not affect value +SELECT @@ROWCOUNT; +GO +EXEC babel_rowcount_select_set; +GO +EXEC babel_rowcount_return; +GO +-- Expect 1. Explicit return resets to 1 +SELECT @@ROWCOUNT; +GO +EXEC babel_rowcount_delete; +GO +EXEC babel_rowcount_basic_statements; +GO + +-- Clean up +DROP TABLE babel_rowcount; +GO +DROP PROCEDURE babel_rowcount_insert; +GO +DROP PROCEDURE babel_rowcount_select; +GO +DROP PROCEDURE babel_rowcount_update; +GO +DROP PROCEDURE babel_rowcount_select_set; +GO +DROP PROCEDURE babel_rowcount_return; +GO +DROP PROCEDURE babel_rowcount_delete; +GO +DROP PROCEDURE babel_rowcount_basic_statements; +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-SCHEMABINDING.sql b/contrib/test/JDBC/input/BABEL-SCHEMABINDING.sql new file mode 100644 index 00000000000..a13b9dc6bfc --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SCHEMABINDING.sql @@ -0,0 +1,64 @@ +CREATE TABLE t_babel_1201 (a int); +INSERT INTO t_babel_1201 values (1); +GO + +-- create view with schemabinding +CREATE VIEW v_babel_1201 WITH SCHEMABINDING AS SELECT * FROM t_babel_1201; +GO +SELECT * FROM v_babel_1201; +GO + +-- create trigger with schemabinding +CREATE TABLE t_babel_1201_2 (c varchar(20)); +GO +CREATE TRIGGER tr_babel_1201 on t_babel_1201 AFTER INSERT AS INSERT INTO t_babel_1201_2 values ('triggered'); +GO +INSERT INTO t_babel_1201 values (2); +GO +SELECT * FROM t_babel_1201_2; +GO + +-- create procedure with schemabinding +CREATE PROCEDURE p_babel_1201 (@v int) WITH SCHEMABINDING AS BEGIN PRINT CAST(@v AS VARCHAR(10)) END; +GO +EXEC p_babel_1201 1 +GO + +-- create function with schemabinding +CREATE FUNCTION f_babel_1201_1 (@v int) RETURNS INT WITH SCHEMABINDING AS BEGIN RETURN @v+1 END; +GO +SELECT f_babel_1201_1(1) +GO + +CREATE FUNCTION f_babel_1201_2 (@v int) RETURNS TABLE WITH SCHEMABINDING AS RETURN select @v+1 as a; +GO +SELECT f_babel_1201_2(2) +GO + +-- create function with other function options +CREATE FUNCTION f_babel_1201_3 (@v int) RETURNS INT WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT AS BEGIN RETURN @v+1 END; +GO +SELECT f_babel_1201_3(3) +GO + +CREATE FUNCTION f_babel_1201_4 (@v int) RETURNS INT WITH RETURNS NULL ON NULL INPUT, SCHEMABINDING AS BEGIN RETURN @v+1 END; +GO +SELECT f_babel_1201_4(4) +GO + +-- create function with duplicate schemabinding +CREATE FUNCTION f_babel_1201_5 (@v int) RETURNS INT WITH SCHEMABINDING, SCHEMABINDING AS BEGIN RETURN @v+1 END; +GO +SELECT f_babel_1201_5(5) +GO + +DROP FUNCTION f_babel_1201_1; +DROP FUNCTION f_babel_1201_2; +DROP FUNCTION f_babel_1201_3; +DROP FUNCTION f_babel_1201_4; +DROP FUNCTION f_babel_1201_5; +DROP PROCEDURE p_babel_1201; +DROP VIEW v_babel_1201; +DROP TRIGGER tr_babel_1201; +DROP TABLE t_babel_1201; +GO diff --git a/contrib/test/JDBC/input/BABEL-SEQUENCE.sql b/contrib/test/JDBC/input/BABEL-SEQUENCE.sql new file mode 100644 index 00000000000..2422144dfd7 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SEQUENCE.sql @@ -0,0 +1,300 @@ +USE master; +GO + +CREATE TABLE babel_sequence_tinyint (id [tinyint] IDENTITY, col1 [tinyint]); +go +CREATE PROCEDURE insert_babel_sequence_tinyint_id +@id tinyint, @val tinyint +AS BEGIN + SET IDENTITY_INSERT babel_sequence_tinyint ON; + INSERT INTO babel_sequence_tinyint (id, col1) VALUES (@id, @val); + SET IDENTITY_INSERT babel_sequence_tinyint OFF; +END; +go +EXEC insert_babel_sequence_tinyint_id 2, 1; +go +EXEC insert_babel_sequence_tinyint_id 8, 2; +go +INSERT INTO babel_sequence_tinyint (col1) VALUES (10), (20), (30); +go +EXEC insert_babel_sequence_tinyint_id 16, 3; +go +EXEC insert_babel_sequence_tinyint_id 255, 4; +go +INSERT INTO babel_sequence_tinyint (col1) VALUES (40); +go +SELECT * FROM babel_sequence_tinyint; +go + +CREATE TABLE babel_sequence_tinyint_dec (id [tinyint] IDENTITY(1,-1), col1 [tinyint]); +go +INSERT INTO babel_sequence_tinyint_dec (col1) VALUES (10); +go +INSERT INTO babel_sequence_tinyint_dec (col1) VALUES (20); +go +INSERT INTO babel_sequence_tinyint_dec (col1) VALUES (30); +go +SELECT * FROM babel_sequence_tinyint_dec; +go + +CREATE TABLE babel_sequence_smallint (id [smallint] IDENTITY, col1 [int]); +go +INSERT INTO babel_sequence_smallint (col1) VALUES (10), (20), (30); +go +SELECT * FROM babel_sequence_smallint; +go + +CREATE TABLE babel_sequence_int (id [int] IDENTITY, col1 [int]); +go +CREATE PROCEDURE insert_babel_sequence_int_id +@id INT, @val INT +AS BEGIN + SET IDENTITY_INSERT babel_sequence_int ON; + INSERT INTO babel_sequence_int (id, col1) VALUES (@id, @val); + SET IDENTITY_INSERT babel_sequence_int OFF; +END; +go +EXEC insert_babel_sequence_int_id 2, 1; +go +EXEC insert_babel_sequence_int_id 8, 2; +go +INSERT INTO babel_sequence_int (col1) VALUES (10), (20), (30); +go +EXEC insert_babel_sequence_int_id 16, 3; +go +EXEC insert_babel_sequence_int_id 32, 4; +go +SELECT * FROM babel_sequence_int; +go + +CREATE TABLE babel_sequence_bigint (id [bigint] IDENTITY, col1 [int]); +go +INSERT INTO babel_sequence_bigint (col1) VALUES (10), (20), (30); +go +SELECT * FROM babel_sequence_bigint; +go + +CREATE TABLE babel_sequence_numeric (id numeric(18,0) IDENTITY, col1 int); +go +CREATE PROCEDURE insert_babel_sequence_numeric_id +@id NUMERIC, @val NUMERIC +AS BEGIN + SET IDENTITY_INSERT babel_sequence_numeric ON; + INSERT INTO babel_sequence_numeric (id, col1) VALUES (@id, @val); + SET IDENTITY_INSERT babel_sequence_numeric OFF; +END; +go +EXEC insert_babel_sequence_numeric_id 2, 1; +go +EXEC insert_babel_sequence_numeric_id 8, 2; +go +INSERT INTO babel_sequence_numeric (col1) VALUES (10), (20), (30); +go +EXEC insert_babel_sequence_numeric_id 16, 3; +go +EXEC insert_babel_sequence_numeric_id 32, 4; +go +SELECT * FROM babel_sequence_numeric; +go + +CREATE TABLE babel_sequence_decimal (id decimal(18,0) IDENTITY, col1 int); +go +CREATE PROCEDURE insert_babel_sequence_decimal_id +@id DECIMAL, @val DECIMAL +AS BEGIN + SET IDENTITY_INSERT babel_sequence_decimal ON; + INSERT INTO babel_sequence_decimal (id, col1) VALUES (@id, @val); + SET IDENTITY_INSERT babel_sequence_decimal OFF; +END; +go +EXEC insert_babel_sequence_decimal_id 2, 1; +go +EXEC insert_babel_sequence_decimal_id 8, 2; +go +INSERT INTO babel_sequence_decimal (col1) VALUES (10), (20), (30); +go +EXEC insert_babel_sequence_decimal_id 16, 3; +go +EXEC insert_babel_sequence_decimal_id 32, 4; +go +SELECT * FROM babel_sequence_decimal; +go + +-- Test faulty table creation +CREATE TABLE babel_sequence_numeric_faulty_scale (id numeric(18,6) IDENTITY, col1 int); +go +CREATE TABLE babel_sequence_numeric_faulty_precision (id numeric(20,0) IDENTITY, col1 int); +go + +-- Test ALTER on identity property +CREATE TABLE babel_sequence_alter (col1 [int]); +go +INSERT INTO babel_sequence_alter VALUES (-5), (10), (42); +go + +ALTER TABLE babel_sequence_alter ADD id_tinyint [tinyint] IDENTITY(1,1); +go +SELECT * FROM babel_sequence_alter; +go +ALTER TABLE babel_sequence_alter DROP COLUMN id_tinyint; +go + +ALTER TABLE babel_sequence_alter ADD id_smallint [smallint] IDENTITY(1,1); +go +SELECT * FROM babel_sequence_alter; +go +ALTER TABLE babel_sequence_alter DROP COLUMN id_smallint; +go + +ALTER TABLE babel_sequence_alter ADD id_int [int] IDENTITY; +go +SELECT * FROM babel_sequence_alter; +go +ALTER TABLE babel_sequence_alter DROP COLUMN id_int; +go + +ALTER TABLE babel_sequence_alter ADD id_bigint [bigint] IDENTITY(32,8); +go +INSERT INTO babel_sequence_alter VALUES (-5), (10), (42); +go +SELECT * FROM babel_sequence_alter; +go +ALTER TABLE babel_sequence_alter DROP COLUMN id_bigint; +go + +ALTER TABLE babel_sequence_alter ADD id_numeric numeric(18,0) IDENTITY(32,8); +go +INSERT INTO babel_sequence_alter VALUES (-5), (10), (42); +go +SELECT * FROM babel_sequence_alter; +go +ALTER TABLE babel_sequence_alter DROP COLUMN id_numeric; +go + +ALTER TABLE babel_sequence_alter ADD id_decimal decimal(18,0) IDENTITY(32,8); +go +INSERT INTO babel_sequence_alter VALUES (-5), (10), (42); +go +SELECT * FROM babel_sequence_alter; +go +ALTER TABLE babel_sequence_alter DROP COLUMN id_decimal; +go + +-- Test sequences +CREATE SEQUENCE seq_tinyint +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go +SELECT nextval('seq_tinyint'); +go +SELECT setval('seq_tinyint', 255); +go +SELECT nextval('seq_tinyint'); +go + +CREATE SEQUENCE seq_smallint +AS [smallint] +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go +SELECT nextval('seq_smallint'); +go +SELECT setval('seq_smallint', 32767); +go +SELECT nextval('seq_smallint'); +go + +CREATE SEQUENCE seq_int +AS [int] +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go +SELECT nextval('seq_int'); +go +SELECT setval('seq_int', 2147483647); +go +SELECT nextval('seq_int'); +go + +CREATE SEQUENCE seq_bigint +AS [bigint] +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go +SELECT nextval('seq_bigint'); +go +SELECT setval('seq_bigint', 9223372036854775807); +go +SELECT nextval('seq_bigint'); +go + +CREATE SEQUENCE seq_numeric +AS numeric(18,0) +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go +SELECT nextval('seq_numeric'); +go +SELECT setval('seq_numeric', 9223372036854775807); +go +SELECT nextval('seq_numeric'); +go + +CREATE SEQUENCE seq_decimal +AS decimal(18,0) +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go +SELECT nextval('seq_decimal'); +go +SELECT setval('seq_decimal', 9223372036854775807); +go +SELECT nextval('seq_decimal'); +go + +-- Test faulty sequence creation +CREATE SEQUENCE seq_tinyint_faulty_min +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE -1 +MAXVALUE 255 +CACHE 50 +go + +CREATE SEQUENCE seq_tinyint_faulty_max +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 0 +MAXVALUE 256 +CACHE 50 +go + +CREATE SEQUENCE seq_numeric_faulty_scale +AS numeric(10,1) +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go + +CREATE SEQUENCE seq_numeric_faulty_precision +AS numeric(21,0) +START WITH 1 +INCREMENT BY 1 +CACHE 50 +go + +DROP PROC insert_babel_sequence_tinyint_id, insert_babel_sequence_int_id, insert_babel_sequence_numeric_id, insert_babel_sequence_decimal_id; +go +DROP TABLE babel_sequence_tinyint, babel_sequence_tinyint_dec, babel_sequence_smallint, babel_sequence_int, babel_sequence_bigint, babel_sequence_numeric, babel_sequence_decimal, babel_sequence_alter; +go +DROP SEQUENCE seq_tinyint, seq_smallint, seq_int, seq_bigint, seq_numeric, seq_decimal; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-SERVERPROPERTY.sql b/contrib/test/JDBC/input/BABEL-SERVERPROPERTY.sql new file mode 100644 index 00000000000..e3c7a6d50d2 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SERVERPROPERTY.sql @@ -0,0 +1,17 @@ +-- test serverproperty() function +-- invalid property name, should reutnr NULL +select serverproperty('invalid property'); +go +-- valid supported properties +select serverproperty('collation'); +go +select 'true' where serverproperty('collationId') >= 0; +go +select serverproperty('IsSingleUser'); +go +select serverproperty('ServerName'); +go + +-- BABEL-1286 +SELECT SERVERPROPERTY('babelfish'); +go diff --git a/contrib/test/JDBC/input/BABEL-SET-COMMAND.sql b/contrib/test/JDBC/input/BABEL-SET-COMMAND.sql new file mode 100644 index 00000000000..a360cac4f0c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SET-COMMAND.sql @@ -0,0 +1,52 @@ +-- Simple SET +SET XACT_ABORT ON; +SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +GO +SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +SET XACT_ABORT OFF; +GO + +-- Inside transaction with commit +BEGIN TRANSACTION + SET XACT_ABORT ON; +COMMIT TRANSACTION +GO +SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +SET XACT_ABORT OFF; +GO + +-- Inside transaction with rollback +BEGIN TRANSACTION + SET XACT_ABORT ON; +ROLLBACK TRANSACTION +GO +SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +SET XACT_ABORT OFF; +GO + +-- Inside transaction with rollback to savepoint +BEGIN TRANSACTION; + SET XACT_ABORT OFF; + SAVE TRAN SP1; + SET XACT_ABORT ON; + SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; + ROLLBACK TRAN SP1; + SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +ROLLBACK TRANSACTION +GO +SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +SET XACT_ABORT OFF; +GO + +-- Inside procedure +CREATE PROCEDURE xact_proc +AS +BEGIN + SET XACT_ABORT ON; + SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +END +GO +EXEC xact_proc; +SELECT name, setting FROM pg_settings WHERE name = 'babelfishpg_tsql.xact_abort'; +SET XACT_ABORT OFF; +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-SPCOLUMNS.sql b/contrib/test/JDBC/input/BABEL-SPCOLUMNS.sql new file mode 100644 index 00000000000..dd585d38e1c --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SPCOLUMNS.sql @@ -0,0 +1,56 @@ +USE master +GO +CREATE DATABASE mydb1 +GO +USE mydb1 +GO + +-- Error: have to provide table name +EXEC sp_columns +GO + +-- Testing a few different types +CREATE table t_time (a time) +GO +EXEC sp_columns @table_name = 't_time' +GO + +CREATE table t_text(a text) +GO +exec sp_columns @table_name = 't_text' +GO + +CREATE table t_int (a int) +GO +exec sp_columns @table_name = 't_int' +GO + +CREATE table t_money(a money) +GO +exec sp_columns @table_name = 't_money' +GO + +-- Testing all parameters +EXEC sp_columns @table_name = 't_int', @table_owner = 'dbo', @table_qualifier = 'mydb1', @column_name = 'a' +GO +EXEC sp_columns 't_int', 'dbo', 'mydb1', 'a' +GO + +-- sp_columns_100, wild card matching enabled +EXEC sp_columns_100 '%_money', 'dbo', NULL, NULL, 0, 2, 1 +GO + +-- no wild card matching +EXEC sp_columns_100 '%_money', 'dbo', NULL, NULL, 0, 2, 0 +GO + +drop table t_int +drop table t_text +drop table t_time +drop table t_money +GO + +USE master +GO +DROP DATABASE mydb1 +GO diff --git a/contrib/test/JDBC/input/BABEL-SPCURSOR.sql b/contrib/test/JDBC/input/BABEL-SPCURSOR.sql new file mode 100644 index 00000000000..8193955b437 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SPCURSOR.sql @@ -0,0 +1,105 @@ +CREATE TABLE babel_cursor_t1 (i INT, d double precision, c varchar(10), u uniqueidentifier, v sql_variant); +INSERT INTO babel_cursor_t1 VALUES (1, 1.1, 'a', '1E984725-C51C-4BF4-9960-E1C80E27ABA0', 1); +INSERT INTO babel_cursor_t1 VALUES (2, 22.22, 'bb', '2E984725-C51C-4BF4-9960-E1C80E27ABA0', 22.22); +INSERT INTO babel_cursor_t1 VALUES (3, 333.333, 'cccc', '3E984725-C51C-4BF4-9960-E1C80E27ABA0', 'cccc'); +INSERT INTO babel_cursor_t1 VALUES (4, 4444.4444, 'dddddd', '4E984725-C51C-4BF4-9960-E1C80E27ABA0', cast('4E984725-C51C-4BF4-9960-E1C80E27ABA0' as uniqueidentifier)); +INSERT INTO babel_cursor_t1 VALUES (NULL, NULL, NULL, NULL, NULL); +GO + +-- simple happy case +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select i, d, c, u from babel_cursor_t1', 2, 8193; +-- NEXT 1 +EXEC sp_cursorfetch @cursor_handle, 2, 0, 1; +-- NEXT 1 +EXEC sp_cursorfetch @cursor_handle, 2, 0, 1; +-- NEXT 1 +EXEC sp_cursorfetch @cursor_handle, 2, 0, 1; +-- PREV 1 +EXEC sp_cursorfetch @cursor_handle, 4, 0, 1; +-- FIRST 2 +EXEC sp_cursorfetch @cursor_handle, 1, 0, 2; +-- LAST 3 +EXEC sp_cursorfetch @cursor_handle, 8, 0, 3; +-- ABSOLUTE 2 2 +EXEC sp_cursorfetch @cursor_handle, 16, 2, 2; +EXEC sp_cursorclose @cursor_handle; +GO + +-- sp_cursor auto-close +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select i, d, c, u from babel_cursor_t1', 16400, 8193; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 100; +DECLARE @num_opened_cursor int; +SELECT @num_opened_cursor = count(*) FROM pg_catalog.pg_cursors where statement not like '%num_opened_cursor%'; +PRINT 'num_opened_cursor: ' + cast(@num_opened_cursor as varchar(10)); +GO + +-- sp_cursor auto-close (no fast-forward) +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select i, d, c, u from babel_cursor_t1', 16384, 8193; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 100; +DECLARE @num_opened_cursor int; +SELECT @num_opened_cursor = count(*) FROM pg_catalog.pg_cursors where statement not like '%num_opened_cursor%'; +PRINT 'num_opened_cursor: ' + cast(@num_opened_cursor as varchar(10)); +GO + +-- sp_cursor auto-close (BABEL-1812) +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select i, d, c, u from babel_cursor_t1', 16388, 8193; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 100; +DECLARE @num_opened_cursor int; +SELECT @num_opened_cursor = count(*) FROM pg_catalog.pg_cursors where statement not like '%num_opened_cursor%'; +PRINT 'num_opened_cursor: ' + cast(@num_opened_cursor as varchar(10)); +GO + + +-- sp_cursoroption and sp_cursor (not meaningful without TDS implemenation) +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select i, d, c, u from babel_cursor_t1', 2, 1; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 2; +-- TEXTPTR_ONLY 2 +EXEC sp_cursoroption @cursor_handle, 1, 2; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +-- TEXTPTR_ONLY 4 +EXEC sp_cursoroption @cursor_handle, 1, 4; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +-- TEXTPTR_ONLY 0 +EXEC sp_cursoroption @cursor_handle, 1, 0; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +-- TEXTDATA 3 +EXEC sp_cursoroption @cursor_handle, 3, 3; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +-- TEXTDATA 0 +EXEC sp_cursoroption @cursor_handle, 3, 0; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +EXEC sp_cursorclose @cursor_handle; +GO + +-- cursor prep/exec test +DECLARE @stmt_handle int; +DECLARE @cursor_handle int; +DECLARE @cursor_handle2 int; +EXEC sp_cursorprepare @stmt_handle OUTPUT, N'', 'select i, d, c, u from babel_cursor_t1', 0, 2, 1; +EXEC sp_cursorexecute @stmt_handle, @cursor_handle OUTPUT; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 1; +EXEC sp_cursorclose @cursor_handle; +EXEC sp_cursorexecute @stmt_handle, @cursor_handle2 OUTPUT; +EXEC sp_cursorfetch @cursor_handle2, 2, 0, 4; +EXEC sp_cursorclose @cursor_handle2; +EXEC sp_cursorunprepare @stmt_handle; +GO + +-- cursor prepexec test +DECLARE @stmt_handle int; +DECLARE @cursor_handle int; +DECLARE @cursor_handle2 int; +EXEC sp_cursorprepexec @stmt_handle OUTPUT, @cursor_handle OUTPUT, N'', 'select i+100 from babel_cursor_t1', 0, 16400, 1; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 1; +EXEC sp_cursorclose @cursor_handle; +EXEC sp_cursorexecute @stmt_handle, @cursor_handle2 OUTPUT; +EXEC sp_cursorfetch @cursor_handle2, 2, 0, 4; +EXEC sp_cursorclose @cursor_handle2; +EXEC sp_cursorunprepare @stmt_handle; +GO + diff --git a/contrib/test/JDBC/input/BABEL-SP_DATATYPE_INFO.sql b/contrib/test/JDBC/input/BABEL-SP_DATATYPE_INFO.sql new file mode 100644 index 00000000000..dadd8763b90 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SP_DATATYPE_INFO.sql @@ -0,0 +1,9 @@ +exec sp_datatype_info_100 @data_type = 1 +go + +exec sp_datatype_info @data_type = 2 +go + +-- Failed query in BABEL-2448 +EXEC sys.sp_datatype_info_100 1, @odbcver = 2 +go diff --git a/contrib/test/JDBC/input/BABEL-SP_DESCRIBE_FRIST_RESULT_SET.sql b/contrib/test/JDBC/input/BABEL-SP_DESCRIBE_FRIST_RESULT_SET.sql new file mode 100644 index 00000000000..83fbbd04617 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SP_DESCRIBE_FRIST_RESULT_SET.sql @@ -0,0 +1,17 @@ +create table t1(a int) +go + +-- no result +exec sys.sp_describe_first_result_set 'insert into t1 values(1)', NULL, 0 +go + +-- shows column info of 'a' +exec sp_describe_first_result_set 'select * from t1' +go + +-- should be empty because the queries above are not executed +select * from t1 +go + +drop table t1 +go diff --git a/contrib/test/JDBC/input/BABEL-SP_TABLES.sql b/contrib/test/JDBC/input/BABEL-SP_TABLES.sql new file mode 100644 index 00000000000..c4700d25303 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SP_TABLES.sql @@ -0,0 +1,55 @@ +create database db1 +go +use db1 +go +create table t_sptables(a int) +go + +-- syntax error: @table_name is required +exec sys.sp_tables +go + +exec sp_tables @table_name = 't_sptables' +go + +exec sp_tables @table_name = 't_sptables', @table_owner = 'dbo' +go + +exec sp_tables @table_name = 't_sptables', @table_qualifier = 'db1' +go + +exec sp_tables @table_name = 't_sptables', @table_type = "'TABLE'" +go + +exec sp_tables @table_name = 't_sptables', @table_type = "'TABLE','VIEW'" +go + +-- pattern matching is default to be ON +exec sp_tables @table_name = 't_spt%' +go + +-- pattern matching set to OFF +exec sp_tables @table_name = 't_spt%', @fUsePattern = '0' +go + +exec sp_tables @table_name = 't_sptables_nonexist' +go + +-- unnamed invocation +exec sp_tables 't_sptables', 'dbo', 'db1' +go + +-- case-insensative invocation +EXEC SP_TABLES @TABLE_NAME = 't_sptables', @TABLE_OWNER = 'dbo', @TABLE_QUALIFIER = 'db1' +GO + +-- failed query in BABEL-1782 +exec [sys].sp_tables N't23',N'dbo',NULL,N'''TABLE''',@fUsePattern=1; +go + +drop table t_sptables +go +use master +go +drop database db1 +go diff --git a/contrib/test/JDBC/input/BABEL-SQL-CONFIG-FUNCTIONS.sql b/contrib/test/JDBC/input/BABEL-SQL-CONFIG-FUNCTIONS.sql new file mode 100644 index 00000000000..7e75e1e5a5e --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SQL-CONFIG-FUNCTIONS.sql @@ -0,0 +1,2 @@ +SELECT count(*) FROM (SELECT @@VERSION) a where version like 'Babelfish for PostgreSQL with SQL Server Compatibility%' +GO diff --git a/contrib/test/JDBC/input/BABEL-SYS-DATABASES.mix b/contrib/test/JDBC/input/BABEL-SYS-DATABASES.mix new file mode 100644 index 00000000000..f9f5b18d5ba --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SYS-DATABASES.mix @@ -0,0 +1,26 @@ +-- psql +ALTER SYSTEM SET babelfishpg_tsql.migration_mode = 'multi-db'; +SELECT pg_reload_conf(); +GO + +-- tsql +CREATE DATABASE db1; +GO + +CREATE DATABASE db2; +GO + +SELECT name, compatibility_level, collation_name FROM sys.databases ORDER BY name; +GO + +SELECT name, snapshot_isolation_state, catalog_collation_type_desc FROM sys.databases ORDER BY name; +GO + +DROP DATABASE db1; +DROP DATABASE db2; +GO + +-- psql +ALTER SYSTEM SET babelfishpg_tsql.migration_mode = 'single-db'; +SELECT pg_reload_conf(); +GO diff --git a/contrib/test/JDBC/input/BABEL-SYSCHARSETS.sql b/contrib/test/JDBC/input/BABEL-SYSCHARSETS.sql new file mode 100644 index 00000000000..bcacaf425d9 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-SYSCHARSETS.sql @@ -0,0 +1,5 @@ +USE master +go + +select * from sys.syscharsets; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/BABEL-TABLEHINT.sql b/contrib/test/JDBC/input/BABEL-TABLEHINT.sql new file mode 100644 index 00000000000..1bbcd0732df --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-TABLEHINT.sql @@ -0,0 +1,84 @@ +create sequence t1_sec start with 1; +go +create table t1 (id int default nextval('t1_sec'), a int); +go + +-- Only checking the syntax. +-- INSERT +insert into t1 with() (a) values (1); -- syntax error +go +insert into t1 with((nowait)) (a) values (2); -- syntax error +go +insert into t1 with('nowait') (a) values (3); -- syntax error +go +insert into t1 with(123nowait) (a) values (4); -- syntax error +go +insert into t1 with($nowait) (a) values (5); -- syntax error +go +insert into t1 with(nowait.serializable) (a) values (6); -- syntax error +go +insert into t1 with(nowait) (a) values (7); +go +insert into t1 with(nowait serializable) (a) values (8); +go +insert into t1 with(nowait, serializable) (a) values (9); +go +select * from t1 order by id; +go + +-- DELETE +delete from t1 with (nowait) where a = 7; +go +delete from t1 with (nowait serializable) where a = 8; +go +delete from t1 with (nowait, serializable) where a = 9; +go +select * from t1 order by id; +go + +-- UPDATE +insert into t1 (a) values (1), (2), (3); +go +select * from t1 order by id; +go +update t1 with (nowait) +set a = 11 where a = 1; +go +update t1 with (nowait serializable) +set a = 22 where a = 2; +go +update t1 with (nowait, serializable) +set a = 33 where a = 3; +go +select * from t1 order by id; +go + +-- SELECT +select * from t1 with (nowait) order by id; +go +select * from t1 with (nowait serializable) order by id; +go +select * from t1 with (nowait, serializable) order by id; +go + +select * from t1 with (index=i1) order by id; +go +select * from t1 with (index(i1)) order by id; +go +select * from t1 with (index(i1, i2)) order by id; +go +select count(*) from t1 s1 with (index(i1,i2)) join t1 s2 with (index=i3) on s1.a=s2.a; +go + +-- BABEL-1148: Use table hints w/o WITH keyword +select * from t1 (tablock) order by id; -- success +go +select * from t1 (tablock, index(i1)) order by id; -- syntax error +go +-- BABEL-1263: syntax "FROM [table] ([table_hint]) [alias]" should be supported +Select * FROM t1 n1 (Nolock) WHERE (Select Count(*) FROM t1 (Nolock) n2) <= 0; +go + +-- Clean up +drop table t1; +go diff --git a/contrib/test/JDBC/input/BABEL-TARGETLIST.sql b/contrib/test/JDBC/input/BABEL-TARGETLIST.sql new file mode 100644 index 00000000000..2708d7cece5 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-TARGETLIST.sql @@ -0,0 +1,16 @@ +CREATE SCHEMA ts; +CREATE TABLE ts.t1 (ABC text, b varchar(20), c char(4), Xyz int, [Delimited] int, [Square Brackets] bigint); +GO + +SELECT * from ts.t1; +SELECT xyz, XYZ, xYz, xyz ColName, xYz AS ColName, [Square Brackets], [Delimited], [DeLIMITed] from ts.t1; +GO + +SELECT xyz AS [WOW! This is a very very long identifier that will get truncated with a uniquifying suffix by Babelfish] from ts.t1; +GO +SELECT xyz AS [WOW! This is a very very long identifier that will get truncated with a uniquifying suffix by Babelfish - with extra stuff at the end] from ts.t1; +GO + +DROP TABLE ts.t1; +DROP SCHEMA ts; +GO diff --git a/contrib/test/JDBC/input/BABEL-UNSUPPORTED.sql b/contrib/test/JDBC/input/BABEL-UNSUPPORTED.sql new file mode 100644 index 00000000000..629361d3b25 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL-UNSUPPORTED.sql @@ -0,0 +1,1512 @@ +-- default value was changed from 'strict' to 'ignore'. +-- to minimize touching test, test 'strict' first. +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +GO + +-- simple unsupported query but accepted in backend parser. should throw an error. +SET ANSI_PADDING OFF; +GO + +SET ANSI_WARNINGS OFF; +GO + +SET ARITHABORT OFF; +GO + +SET ARITHIGNORE ON; +GO + +SET NUMERIC_ROUNDABORT ON; +GO + +SET NOEXEC ON; +GO + +SET SHOWPLAN_ALL ON; +GO + +SET SHOWPLAN_TEXT ON; +GO + +SET SHOWPLAN_XML ON; +GO + +SET STATISTICS IO ON; +GO + +SET OFFSETS SELECT ON; +GO + +SET DATEFORMAT dmy; +GO + +SET DEADLOCK_PRIORITY 0; +GO + +SET LOCK_TIMEOUT 10; +GO + +SET CONTEXT_INFO 0; +GO + +SET LANGUAGE 'english' +GO + +-- one supported + one unsupported +SET ANSI_NULLS, ANSI_PADDING OFF; +GO +select current_setting('babelfishpg_tsql.ansi_nulls'); +GO +SET ANSI_NULLS ON; +GO + +SET ANSI_NULLS, STATISTICS IO OFF; +GO +select current_setting('babelfishpg_tsql.ansi_nulls'); -- should not be chagned +GO + +-- two unsupported +SET ANSI_PADDING, FORCEPLAN ON; +GO + +SET STATISTICS IO, OFFSETS SELECT ON; +GO + +-- escape_hatch_session_settings +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +GO + +SET ANSI_PADDING OFF; +GO +select current_setting('babelfishpg_tsql.ansi_padding'); +GO + +SET ANSI_WARNINGS OFF; +GO +select current_setting('babelfishpg_tsql.ansi_warnings'); +GO + +SET ARITHABORT OFF; +GO +select current_setting('babelfishpg_tsql.arithabort'); +GO + +SET ARITHIGNORE ON; +GO +select current_setting('babelfishpg_tsql.arithignore'); +GO + +SET NUMERIC_ROUNDABORT ON; +GO +select current_setting('babelfishpg_tsql.numeric_roundabort'); +GO + +SET NOEXEC ON; +GO +select current_setting('babelfishpg_tsql.noexec'); +GO + +SET SHOWPLAN_ALL ON; +GO +select current_setting('babelfishpg_tsql.showplan_all'); +GO + +SET SHOWPLAN_TEXT ON; +GO +select current_setting('babelfishpg_tsql.showplan_text'); +GO + +SET SHOWPLAN_XML ON; +GO +select current_setting('babelfishpg_tsql.showplan_xml'); +GO + +-- these statement will be ignored silently +SET STATISTICS IO ON; +GO +SET OFFSETS SELECT ON; +GO +SET DATEFORMAT dmy; +GO +SET DEADLOCK_PRIORITY 0; +GO +SET LOCK_TIMEOUT 10; +GO +SET CONTEXT_INFO 0; +GO +SET LANGUAGE 'english'; +GO + +-- one supported + one unsupported +SET ANSI_NULLS, ANSI_PADDING OFF; +GO +select current_setting('babelfishpg_tsql.ansi_nulls'); -- should be changed +GO +SET ANSI_NULLS ON; +GO + +SET ANSI_NULLS, STATISTICS IO OFF; +GO +select current_setting('babelfishpg_tsql.ansi_nulls'); -- should not be chagned +GO +SET ANSI_NULLS ON; +GO + +-- two unsupported +SET ANSI_PADDING, FORCEPLAN ON; +GO + +SET STATISTICS IO, OFFSETS SELECT ON; +GO + + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; +GO + +-- simple unsupported query which backend parser can't understand. should throw an error with nice error message +ALTER DATABASE blah SET ANSI_PADDING OFF; +GO + +-- unsupported query in a batch. execution should be aborted. +DECLARE @v varchar(20); +SET ANSI_PADDING OFF; -- error +SET @v = 'SHOULD NOT BE SHOWN'; +SELECT @v; +GO + +DECLARE @v varchar(20); +ALTER DATABASE blah SET ANSI_PADDING OFF; -- error +SET @v = 'SHOULD NOT BE SHOWN'; +SELECT @v; +GO + +-- escape hatch: storage_options +-- 'ignore' is default + +CREATE TABLE t_unsupported_fg1(a int) ON [primary]; +GO +DROP TABLE t_unsupported_fg1 +GO + +CREATE TABLE t_unsupported_fg2(a int) TEXTIMAGE_ON [primary]; +GO +DROP TABLE t_unsupported_fg2 +GO + +CREATE TABLE t_unsupported_fg3(a int) FILESTREAM_ON [primary]; +GO +DROP TABLE t_unsupported_fg3 +GO + +CREATE TABLE t_unsupported_fg4(a int) ON [primary] TEXTIMAGE_ON [primary]; +GO +DROP TABLE t_unsupported_fg4 +GO + +CREATE TABLE t_unsupported_fg5(a int); +GO +CREATE INDEX t_unsupported_fg5_i1 ON t_unsupported_fg5(a) ON [primary]; +GO +DROP TABLE t_unsupported_fg5; +GO + +CREATE TABLE t_unsupported_fg6(a int); +GO +ALTER TABLE t_unsupported_fg6 SET (FILESTREAM_ON = [primary]); +GO +DROP TABLE t_unsupported_fg6; +GO + +CREATE TABLE t_unsupported_fg7(a int) ON "default"; +GO +DROP TABLE t_unsupported_fg7; +GO + +CREATE TABLE t_unsupported_fg8(a int) FILESTREAM_ON [primary]; +GO +DROP TABLE t_unsupported_fg8 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_fg1(a int) ON [primary]; +GO + +CREATE TABLE t_unsupported_fg2(a int) TEXTIMAGE_ON [primary]; +GO + +CREATE TABLE t_unsupported_fg3(a int) FILESTREAM_ON [primary]; +GO + +CREATE TABLE t_unsupported_fg4(a int) ON [primary] TEXTIMAGE_ON [primary]; +GO + +CREATE TABLE t_unsupported_fg5(a int); +GO +CREATE INDEX t_unsupported_fg5_i1 ON t_unsupported_fg5(a) ON [primary]; +GO +DROP TABLE t_unsupported_fg5; +GO + +CREATE TABLE t_unsupported_fg6(a int); +GO +ALTER TABLE t_unsupported_fg6 SET (FILESTREAM_ON = [primary]); +GO +DROP TABLE t_unsupported_fg6; +GO + +CREATE TABLE t_unsupported_fg7(a int) ON "default"; +GO + +CREATE TABLE t_unsupported_fg8(a int) FILESTREAM_ON [primary]; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'ignore', 'false') +GO + +-- escape hatch: storage_on_partition. +-- 'strict' is default + +CREATE TABLE t_unsupported_sop1(a int) ON partition(a); +GO + +CREATE TABLE t_unsupported_sop2(a int) FILESTREAM_ON partition(a); +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_on_partition', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported_sop1(a int) ON partition(a); +GO +DROP TABLE t_unsupported_sop1; +GO + +CREATE TABLE t_unsupported_sop2(a int) FILESTREAM_ON partition(a); +GO +DROP TABLE t_unsupported_sop2 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_on_partition', 'strict', 'false') +GO + + +-- escape hatch: database_misc_options +-- 'ignore is default + +CREATE DATABASE db_unsupported1 CONTAINMENT = NONE; +GO +DROP DATABASE db_unsupported1; +GO + +CREATE DATABASE db_unsupported2 CONTAINMENT = PARTIAL; +GO +DROP DATABASE db_unsupported2; +GO + +CREATE DATABASE db_unsupported3 WITH DB_CHAINING ON; +GO +DROP DATABASE db_unsupported3; +GO + +CREATE DATABASE db_unsupported4 WITH TRUSTWORTHY OFF; +GO +DROP DATABASE db_unsupported4; +GO + +CREATE DATABASE db_unsupported5 CONTAINMENT = NONE WITH DB_CHAINING ON, DEFAULT_LANGUAGE = us_english, TRUSTWORTHY OFF; +GO +DROP DATABASE db_unsupported5; +GO + +CREATE DATABASE db_unsupported6 WITH PERSISTENT_LOG_BUFFER = ON (DIRECTORY_NAME = '/tmp'); +GO +DROP DATABASE db_unsupported6; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_database_misc_options', 'strict', 'false') +GO + +CREATE DATABASE db_unsupported1 CONTAINMENT = NONE; +GO + +CREATE DATABASE db_unsupported2 CONTAINMENT = PARTIAL; +GO + +CREATE DATABASE db_unsupported3 WITH DB_CHAINING ON; +GO + +CREATE DATABASE db_unsupported4 WITH TRUSTWORTHY OFF; +GO + +CREATE DATABASE db_unsupported5 CONTAINMENT = NONE WITH DB_CHAINING ON, DEFAULT_LANGUAGE = us_english, TRUSTWORTHY OFF; +GO + +CREATE DATABASE db_unsupported6 WITH PERSISTENT_LOG_BUFFER = ON (DIRECTORY_NAME = '/tmp'); +GO +DROP DATABASE db_unsupported6; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_database_misc_options', 'ignore', 'false') +GO + + +-- escape hatch: language_non_english +-- default is 'strict' + +CREATE DATABASE db_unsupported_l1 WITH DEFAULT_LANGUAGE = us_english; +GO +DROP DATABASE db_unsupported_l1; +GO + +CREATE DATABASE db_unsupported_l2 WITH DEFAULT_LANGUAGE = English; +GO +DROP DATABASE db_unsupported_l2; +GO + +CREATE DATABASE db_unsupported_l3 WITH DEFAULT_LANGUAGE = Deutsch; +GO + +CREATE LOGIN u_unsupported with password='12345678', default_language=english; +GO + +ALTER LOGIN u_unsupported with default_language=english; +GO + +ALTER LOGIN u_unsupported with default_language=spanish; +GO + +DROP LOGIN u_unsupported; +GO + +CREATE LOGIN u_unsupported_2 with password='12345678', default_language=spanish; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_language_non_english', 'ignore', 'false') +GO + +CREATE DATABASE db_unsupported_l1 WITH DEFAULT_LANGUAGE = us_english; +GO +DROP DATABASE db_unsupported_l1; +GO + +CREATE DATABASE db_unsupported_l2 WITH DEFAULT_LANGUAGE = English; +GO +DROP DATABASE db_unsupported_l2; +GO + +CREATE DATABASE db_unsupported_l3 WITH DEFAULT_LANGUAGE = Deutsch; +GO +DROP DATABASE db_unsupported_l3; +GO + +CREATE LOGIN u_unsupported with password='12345678', default_language=english; +GO + +ALTER LOGIN u_unsupported with default_language=english; +GO + +ALTER LOGIN u_unsupported with default_language=spanish; +GO + +DROP LOGIN u_unsupported; +GO + +CREATE LOGIN u_unsupported_2 with password='12345678', default_language=spanish; +GO + +DROP LOGIN u_unsupported_2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_language_non_english', 'strict', 'false') +GO + + +-- escape hatch: fulltext +-- 'strict' is default + +CREATE TABLE t_unsupported_ft (a text); +GO + +CREATE FULLTEXT INDEX ON t_unsupported_ft(a) KEY INDEX ix_unsupported_ft; +GO + +DROP TABLE t_unsupported_ft; +GO + +CREATE DATABASE db_unsupported_ft WITH DEFAULT_FULLTEXT_LANGUAGE = English; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_fulltext', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported_ft (a text); +GO + +CREATE FULLTEXT INDEX ON t_unsupported_ft(a) KEY INDEX ix_unsupported_ft; +GO + +DROP TABLE t_unsupported_ft; +GO + +CREATE DATABASE db_unsupported_ft WITH DEFAULT_FULLTEXT_LANGUAGE = English; +GO +DROP DATABASE db_unsupported_ft; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_fulltext', 'strict', 'false') +GO + + +-- escape hatch: schemabinding. +-- 'ignore' is by default. test if an error is thrown in strict mode if it is not explicitly given +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_function', 'strict', 'false') +GO +CREATE FUNCTION f_unsupported_1 (@v int) RETURNS INT AS BEGIN RETURN @v+1 END; +GO +CREATE FUNCTION f_unsupported_2 (@v int) RETURNS TABLE AS RETURN select @v+1 as a; +GO +CREATE FUNCTION f_unsupported_3 (@v int) RETURNS INT WITH RETURNS NULL ON NULL INPUT AS BEGIN RETURN @v+1 END; +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_function', 'ignore', 'false') +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_procedure', 'strict', 'false') +GO +CREATE PROCEDURE p_unsupported_1 (@v int) AS BEGIN PRINT CAST(@v AS VARCHAR(10)) END; +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_procedure', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported (a int); +INSERT INTO t_unsupported values (1); +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_view', 'strict', 'false') +GO +CREATE VIEW v_unsupported AS SELECT * FROM t_unsupported; +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_view', 'ignore', 'false') +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_trigger', 'strict', 'false') +GO +CREATE TRIGGER tr_unsupported on t_unsupported AFTER INSERT AS print 'triggered'; +GO +SELECT set_config('babelfishpg_tsql.escape_hatch_schemabinding_trigger', 'ignore', 'false') +GO + +DROP table t_unsupported; +GO + + +-- escape hatch escape_hatch_index_clustering +-- 'ignore' is default + +CREATE TABLE t_unsupported_ic1(a int, b int); +GO +CREATE CLUSTERED INDEX i_unsupported_ic11 on t_unsupported_ic1(a); +GO +CREATE NONCLUSTERED INDEX i_unsupported_ic12 on t_unsupported_ic1(b); +GO +DROP TABLE t_unsupported_ic1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_index_clustering', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_ic1(a int, b int); +GO +CREATE CLUSTERED INDEX i_unsupported_ic11 on t_unsupported_ic1(a); +GO +CREATE NONCLUSTERED INDEX i_unsupported_ic12 on t_unsupported_ic1(b); +GO +DROP TABLE t_unsupported_ic1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_index_clustering', 'ignore', 'false') +GO + +-- BABEL-1484 +-- escape hatch escape_hatch_unique_constraint +-- 'strict' is default +-- Test UNIQUE CONSTRAINT is not allowed on nullable column +-- this includes: create unique index, alter table add constraint unique +-- and create table with column constraint +CREATE TABLE t_unsupported_uc1(a int, b int NOT NULL, c int NOT NULL); +GO +CREATE UNIQUE INDEX i_unsupported_uc1 on t_unsupported_uc1(a); +GO +ALTER TABLE t_unsupported_uc1 ADD CONSTRAINT UQ_a UNIQUE (a); +GO +CREATE TABLE t_unsupported_uc2(a int UNIQUE, b int); +GO + +-- Test UNIQUE CONSTRAINT is allowed on NOT NULL column +CREATE UNIQUE INDEX i_unsupported_uc1 on t_unsupported_uc1(b); +GO +ALTER TABLE t_unsupported_uc1 ADD CONSTRAINT UQ_c UNIQUE (c); +GO +CREATE TABLE t_unsupported_uc2(a int UNIQUE NOT NULL, b int NOT NULL UNIQUE); +GO + +DROP TABLE t_unsupported_uc1; +DROP TABLE t_unsupported_uc2; +GO + +-- test UNIQUE INDEX/CONSTRAINT is allowed on nullable column +-- if escap_hatch_unique_constraint is set to ignore +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +GO + +CREATE TABLE t_unsupported_uc1(a int, b varchar(10)); +GO + +CREATE UNIQUE INDEX i_unsupported_uc1 on t_unsupported_uc1(b); +GO + +ALTER TABLE t_unsupported_uc1 ADD CONSTRAINT UQ_a UNIQUE (a); +GO + +CREATE TABLE t_unsupported_uc2(a int UNIQUE, b varchar(10) UNIQUE); +GO + +DROP TABLE t_unsupported_uc1 +DROP TABLE t_unsupported_uc2 +GO + +-- escape hatch escape_hatch_index_columnstore +-- 'strict' is default + +CREATE TABLE t_unsupported_cs1(a int, b int); +GO +CREATE COLUMNSTORE INDEX i_unsupported_cs1 on t_unsupported_cs1(a); +GO +DROP TABLE t_unsupported_cs1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_index_columnstore', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported_cs1(a int, b int); +GO +CREATE COLUMNSTORE INDEX i_unsupported_cs1 on t_unsupported_cs1(a); +GO +DROP TABLE t_unsupported_cs1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_index_columnstore', 'strict', 'false') +GO + + +-- escape hatch escape_hatch_for_replication +-- 'strict' is default + +CREATE TABLE t_unsupported_fr1(a int FOR REPLICATION); +GO + +CREATE TABLE t_unsupported_fr2(a int); +GO +ALTER TABLE t_unsupported_fr2 ADD b int NOT FOR REPLICATION; +GO +DROP TABLE t_unsupported_fr2; +GO + +CREATE PROCEDURE p_unsupported_fr1 (@v int) FOR REPLICATION AS BEGIN PRINT CAST(@v AS VARCHAR(10)) END; +GO + +CREATE TABLE t_unsupported_fr3(a int); +GO +CREATE TRIGGER tr_unsupported_fr3 on t_unsupported_fr3 AFTER INSERT NOT FOR REPLICATION AS print 'triggered'; +GO +DROP TABLE t_unsupported_fr3; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_for_replication', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported_fr1(a int FOR REPLICATION); +GO +DROP TABLE t_unsupported_fr1; +GO + +CREATE TABLE t_unsupported_fr2(a int); +GO +ALTER TABLE t_unsupported_fr2 ADD b int NOT FOR REPLICATION; +GO +DROP TABLE t_unsupported_fr2; +GO + +CREATE PROCEDURE p_unsupported_fr1 (@v int) FOR REPLICATION AS BEGIN PRINT CAST(@v AS VARCHAR(10)) END; +GO +DROP PROCEDURE p_unsupported_fr1; +GO + +CREATE TABLE t_unsupported_fr3(a int); +GO +CREATE TRIGGER tr_unsupported_fr3 on t_unsupported_fr3 AFTER INSERT NOT FOR REPLICATION AS print 'triggered'; +GO +DROP TABLE t_unsupported_fr3; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_for_replication', 'strict', 'false') +GO + + +-- escape hatch escape_hatch_rowguidcol_column +-- 'ignore' is default + +CREATE TABLE t_unsupported_gc1(a int ROWGUIDCOL); +GO +DROP TABLE t_unsupported_gc1; +GO + +CREATE TABLE t_unsupported_gc2(a int); +GO +ALTER TABLE t_unsupported_gc2 ADD b int ROWGUIDCOL; +GO +DROP TABLE t_unsupported_gc2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_rowguidcol_column', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_gc1(a int ROWGUIDCOL); +GO + +CREATE TABLE t_unsupported_gc2(a int); +GO +ALTER TABLE t_unsupported_gc2 ADD b int ROWGUIDCOL; +GO +DROP TABLE t_unsupported_gc2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_rowguidcol_column', 'ignore', 'false') +GO + + +-- escape hatch escape_hatch_sparse_column (incorporated with storage_options) +-- 'ignore' is default + +CREATE TABLE t_unsupported_sc1(a int SPARSE, b int); +GO +DROP TABLE t_unsupported_sc1 +GO + +CREATE TABLE t_unsupported_sc2(a int); +GO +ALTER TABLE t_unsupported_sc2 ADD b int SPARSE; +GO +DROP TABLE t_unsupported_sc2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_sc1(a int SPARSE, b int); +GO + +CREATE TABLE t_unsupported_sc2(a int); +GO +ALTER TABLE t_unsupported_sc2 ADD b int SPARSE; +GO +DROP TABLE t_unsupported_sc2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'ignore', 'false') +GO + + +-- escape hatch: filestream. (incorporated into storage_options) +-- 'ignore' is default + +CREATE TABLE t_unsupported_fs1(a int FILESTREAM, b int); +GO +DROP TABLE t_unsupported_fs1 +GO + +CREATE TABLE t_unsupported_fs2(a int); +GO +ALTER TABLE t_unsupported_fs2 ADD b int FILESTREAM; +GO +DROP TABLE t_unsupported_fs2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_fs1(a int FILESTREAM, b int); +GO + +CREATE TABLE t_unsupported_fs2(a int); +GO +ALTER TABLE t_unsupported_fs2 ADD b int FILESTREAM; +GO +DROP TABLE t_unsupported_fs2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'ignore', 'false') +GO + + +-- escape hatch: escape_hatch_fillfactor. (incorporated with storage_options) +-- 'ignore' is default + +CREATE TABLE t_unsupported_ff1(a int, primary key(a) with fillfactor=50); +GO +DROP TABLE t_unsupported_ff1 +GO + +CREATE TABLE t_unsupported_ff2(a int primary key with fillfactor=50); +GO +DROP TABLE t_unsupported_ff2 +GO + +CREATE TABLE t_unsupported_ff3(a int); +GO +ALTER TABLE t_unsupported_ff3 ADD PRIMARY KEY(a) with fillfactor=50; +GO +DROP TABLE t_unsupported_ff3; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_ff1(a int, primary key(a) with fillfactor=50); +GO + +CREATE TABLE t_unsupported_ff2(a int primary key with fillfactor=50); +GO + +CREATE TABLE t_unsupported_ff3(a int); +GO +ALTER TABLE t_unsupported_ff3 ADD PRIMARY KEY(a) with fillfactor=50; +GO +DROP TABLE t_unsupported_ff3; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'ignore', 'false') +GO + + +-- escape hatch: escape_hatch_storage_options (especially index option). +-- 'ignore' is default + +CREATE TABLE t_unsupported_so1(a int, primary key(a) with data_compression=none); +GO +DROP TABLE t_unsupported_so1 +GO + +CREATE TABLE t_unsupported_so2(a int, primary key(a) with pad_index=on); +GO +DROP TABLE t_unsupported_so2 +GO + +CREATE TABLE t_unsupported_so3(a int, primary key(a) with ignore_dup_key=on); +GO +DROP TABLE t_unsupported_so3 +GO + +CREATE TABLE t_unsupported_so4(a int, primary key(a) with STATISTICS_NORECOMPUTE=on); +GO +DROP TABLE t_unsupported_so4 +GO + +CREATE TABLE t_unsupported_so5(a int, primary key(a) with STATISTICS_INCREMENTAL=on); +GO +DROP TABLE t_unsupported_so5 +GO + +CREATE TABLE t_unsupported_so6(a int, primary key(a) with DROP_EXISTING=on); +GO + +CREATE TABLE t_unsupported_so7(a int, primary key(a) with ONLINE=on); +GO + +CREATE TABLE t_unsupported_so8(a int, primary key(a) with RESUMABLE=on, ONLINE=on); +GO + +CREATE TABLE t_unsupported_so9(a int, primary key(a) with (MAX_DURATION=60, ONLINE=on)); +GO + +CREATE TABLE t_unsupported_so10(a int, primary key(a) with (ALLOW_ROW_LOCKS=on)); +GO +DROP TABLE t_unsupported_so10; +GO + +CREATE TABLE t_unsupported_so11(a int, primary key(a) with (ALLOW_PAGE_LOCKS=on)); +GO +DROP TABLE t_unsupported_so11; +GO + +CREATE TABLE t_unsupported_so12(a int, primary key(a) with (OPTIMIZE_FOR_SEQUENTIAL_KEY=on)); +GO +DROP TABLE t_unsupported_so12; +GO + +CREATE TABLE t_unsupported_so13(a int, primary key(a) with (MAXDOP=40)); +GO +DROP TABLE t_unsupported_so13; +GO + +-- multiple options +CREATE TABLE t_unsupported_so14(a int, primary key(a) with data_compression=none, fillfactor=50); +GO +DROP TABLE t_unsupported_so14 +GO + +-- create index +CREATE TABLE t_unsupported_so15(a int); +GO +CREATE INDEX i_unsupported_so15 on t_unsupported_so15(a) with data_compression=none; +GO +DROP TABLE t_unsupported_so15 +GO + +-- create database with filestream +CREATE DATABASE db_unsupported1 WITH FILESTREAM (DIRECTORY_NAME = '/tmp') +GO +DROP DATABASE db_unsupported1; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_so1(a int, primary key(a) with data_compression=none); +GO + +CREATE TABLE t_unsupported_so2(a int, primary key(a) with pad_index=on); +GO + +CREATE TABLE t_unsupported_so3(a int, primary key(a) with ignore_dup_key=on); +GO + +CREATE TABLE t_unsupported_so4(a int, primary key(a) with STATISTICS_NORECOMPUTE=on); +GO + +CREATE TABLE t_unsupported_so5(a int, primary key(a) with STATISTICS_INCREMENTAL=on); +GO + +CREATE TABLE t_unsupported_so6(a int, primary key(a) with DROP_EXISTING=on); +GO + +CREATE TABLE t_unsupported_so7(a int, primary key(a) with ONLINE=on); +GO + +CREATE TABLE t_unsupported_so8(a int, primary key(a) with RESUMABLE=on, ONLINE=on); +GO + +CREATE TABLE t_unsupported_so9(a int, primary key(a) with (MAX_DURATION=60, ONLINE=on)); +GO + +CREATE TABLE t_unsupported_so10(a int, primary key(a) with (ALLOW_ROW_LOCKS=on)); +GO + +CREATE TABLE t_unsupported_so11(a int, primary key(a) with (ALLOW_PAGE_LOCKS=on)); +GO + +CREATE TABLE t_unsupported_so12(a int, primary key(a) with (OPTIMIZE_FOR_SEQUENTIAL_KEY=on)); +GO + +CREATE TABLE t_unsupported_so13(a int, primary key(a) with (MAXDOP=40)); +GO + +-- multiple options +CREATE TABLE t_unsupported_so14(a int, primary key(a) with data_compression=none, fillfactor=50); +GO + +-- create index +CREATE TABLE t_unsupported_so15(a int); +GO +CREATE INDEX i_unsupported_so15 on t_unsupported_so15(a) with data_compression=none; +GO +DROP TABLE t_unsupported_so15 +GO + +-- create database with filestream +CREATE DATABASE db_unsupported1 WITH FILESTREAM (DIRECTORY_NAME = '/tmp') +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'ignore', 'false') +GO + + +-- escape hatch: escape_hatch_nocheck_add_constraint. +-- 'strict' is default + +CREATE TABLE t_unsupported_cac1(a int, b int); +GO +ALTER TABLE t_unsupported_cac1 WITH CHECK ADD constraint chk1 check (a > 0) +GO +ALTER TABLE t_unsupported_cac1 WITH NOCHECK ADD constraint chk2 check (b < 0) +GO +DROP TABLE t_unsupported_cac1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_nocheck_add_constraint', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported_cac1(a int, b int); + + + + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_so1(a int, primary key(a) with data_compression=none); +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_storage_options', 'ignore', 'false') +GO + + +-- escape hatch: escape_hatch_nocheck_add_constraint. +-- 'strict' is default + +CREATE TABLE t_unsupported_cac1(a int, b int); +GO +ALTER TABLE t_unsupported_cac1 WITH CHECK ADD constraint chk1 check (a > 0) +GO +ALTER TABLE t_unsupported_cac1 WITH NOCHECK ADD constraint chk2 check (b < 0) +GO +DROP TABLE t_unsupported_cac1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_nocheck_add_constraint', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported_cac1(a int, b int); +GO +ALTER TABLE t_unsupported_cac1 WITH CHECK ADD constraint chk1 check (a > 0) +GO +ALTER TABLE t_unsupported_cac1 WITH NOCHECK ADD constraint chk2 check (b < 0) +GO +INSERT INTO t_unsupported_cac1 VALUES (0, 0); +GO +DROP TABLE t_unsupported_cac1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_nocheck_add_constraint', 'strict', 'false') +GO + + +-- escape hatch: escape_hatch_nocheck_existing_constraint. +-- 'strict' is default + +CREATE TABLE t_unsupported_cec1(a int, b int); +GO +INSERT INTO t_unsupported_cec1 values (0, 0); +GO +ALTER TABLE t_unsupported_cec1 ADD constraint chk1 check (a > 0) +GO +ALTER TABLE t_unsupported_cec1 CHECK constraint chk1 +GO +DROP TABLE t_unsupported_cec1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_nocheck_existing_constraint', 'ignore', 'false') +GO + +CREATE TABLE t_unsupported_cec1(a int, b int); +GO +INSERT INTO t_unsupported_cec1 values (0, 0); +GO +ALTER TABLE t_unsupported_cec1 ADD constraint chk1 check (a > 0) +GO +ALTER TABLE t_unsupported_cec1 CHECK constraint chk1 +GO +DROP TABLE t_unsupported_cec1 +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_nocheck_existing_constraint', 'strict', 'false') +GO + +-- escape hatch: escape_hatch_constraint_name_for_default. +-- 'ignore' is deafult + +CREATE TABLE t_unsupported_cd1(a int, b int); +GO +ALTER TABLE t_unsupported_cd1 ADD CONSTRAINT d1 DEFAULT 99 FOR a; +GO +INSERT INTO t_unsupported_cd1(b) VALUES (1); +GO +SELECT * FROM t_unsupported_cd1; +GO +DROP TABLE t_unsupported_cd1; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_constraint_name_for_default', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_cd1(a int, b int); +GO +ALTER TABLE t_unsupported_cd1 ADD CONSTRAINT d1 DEFAULT 99 FOR a; +GO +INSERT INTO t_unsupported_cd1(b) VALUES (1); +GO +SELECT * FROM t_unsupported_cd1; +GO +DROP TABLE t_unsupported_cd1; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_constraint_name_for_default', 'ignore', 'false') +GO + +-- escape hatch: table_hints +-- 'ignore' is default. +-- we have separate test already so briefly check simple cases here. + +CREATE TABLE t_unsupported_th1(a int); +GO +INSERT INTO t_unsupported_th1 WITH (INDEX=i1) VALUES (1), (2); +GO +UPDATE t_unsupported_th1 WITH (INDEX=i1) SET a = 3 WHERE a=2; +GO +DELETE FROM t_unsupported_th1 WITH (INDEX=i1) WHERE a = 1; +GO +SELECT * FROM t_unsupported_th1 WITH (INDEX=i1); +GO +DROP TABLE t_unsupported_th1; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_table_hints', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_th1(a int); +GO +INSERT INTO t_unsupported_th1 WITH (INDEX=i1) VALUES (1), (2); +GO +UPDATE t_unsupported_th1 WITH (INDEX=i1) SET a = 3 WHERE a=2; +GO +DELETE FROM t_unsupported_th1 WITH (INDEX=i1) WHERE a = 1; +GO +SELECT * FROM t_unsupported_th1 WITH (INDEX=i1); +GO +DROP TABLE t_unsupported_th1; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_table_hints', 'ignore', 'false') +GO + + +-- escape hatch: query_hints +-- 'ignore' is default. + +CREATE TABLE t_unsupported_qh1(a int); +GO +INSERT INTO t_unsupported_qh1 VALUES (1), (2) OPTION (MERGE JOIN) +GO +UPDATE t_unsupported_qh1 SET a = 3 WHERE a=2 OPTION (HASH GROUP); +GO +DELETE FROM t_unsupported_qh1 WHERE a = 1 OPTION (CONCAT UNION); +GO +SELECT * FROM t_unsupported_qh1 OPTION (FORCE ORDER); +GO +DROP TABLE t_unsupported_qh1; +GO + + +SELECT set_config('babelfishpg_tsql.escape_hatch_query_hints', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_qh1(a int); +GO +INSERT INTO t_unsupported_qh1 VALUES (1), (2) OPTION (MERGE JOIN) +GO +UPDATE t_unsupported_qh1 SET a = 3 WHERE a=2 OPTION (HASH GROUP); +GO +DELETE FROM t_unsupported_qh1 WHERE a = 1 OPTION (CONCAT UNION); +GO +SELECT * FROM t_unsupported_qh1 OPTION (FORCE ORDER); +GO +DROP TABLE t_unsupported_qh1; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_query_hints', 'ignore', 'false') +GO + + +-- escape hatch: join_hints +-- 'ignore' is default. + +CREATE TABLE t_unsupported_jh1(a int); +CREATE TABLE t_unsupported_jh2(a int); +GO +INSERT INTO t_unsupported_jh1 values (1), (2); +INSERT INTO t_unsupported_jh2 values (1), (3); +GO +SELECT * FROM t_unsupported_jh1 t1 INNER HASH JOIN t_unsupported_jh2 t2 ON t1.a=t2.a; +GO +DROP TABLE t_unsupported_jh1; +DROP TABLE t_unsupported_jh2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_join_hints', 'strict', 'false') +GO + +CREATE TABLE t_unsupported_jh1(a int); +CREATE TABLE t_unsupported_jh2(a int); +GO +INSERT INTO t_unsupported_jh1 values (1), (2); +INSERT INTO t_unsupported_jh2 values (1), (3); +GO +SELECT * FROM t_unsupported_jh1 t1 INNER HASH JOIN t_unsupported_jh2 t2 ON t1.a=t2.a; +GO +DROP TABLE t_unsupported_jh1; +DROP TABLE t_unsupported_jh2; +GO + +SELECT set_config('babelfishpg_tsql.escape_hatch_join_hints', 'ignore', 'false') +GO + + +-- test of sp_babelfish_configure + +EXEC sp_babelfish_configure; +GO + +-- short name +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_function'; +GO + +-- full name +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_function'; +GO + +-- with wildcard +EXEC sp_babelfish_configure '%'; +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.%'; +GO + +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%'; +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_%'; +GO + +-- set +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_function', 'strict'; +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_function'; +GO + +-- now should throw an error +CREATE FUNCTION f_unsupported_1 (@v int) RETURNS INT AS BEGIN RETURN @v+1 END; +GO + +-- set with wildcard +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_%', 'strict'; +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_%'; +GO + +-- should throw an error +CREATE PROCEDURE p_unsupported_1 (@v int) AS BEGIN PRINT CAST(@v AS VARCHAR(10)) END; +GO + +-- reset +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_%', 'ignore'; +GO + +-- same tests with no prefix +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_function', 'strict'; +GO + +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_function'; +GO + +-- now should throw an error +CREATE FUNCTION f_unsupported_1 (@v int) RETURNS INT AS BEGIN RETURN @v+1 END; +GO + +-- set with wildcard +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%', 'strict'; +GO + +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%'; +GO + +-- should throw an error +CREATE PROCEDURE p_unsupported_1 (@v int) AS BEGIN PRINT CAST(@v AS VARCHAR(10)) END; +GO + +-- reset +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%', 'ignore'; +GO + +-- server option +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%', 'strict', 'server'; +GO + +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%'; +GO +select config_value from (select unnest(setconfig) config_value from pg_db_role_setting) t where config_value like '%escape_hatch_schemabinding_%' order by config_value; +GO + +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_function', 'ignore', 'server'; +GO + +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%'; +GO +select config_value from (select unnest(setconfig) config_value from pg_db_role_setting) t where config_value like '%escape_hatch_schemabinding_%' order by config_value; +GO + +-- reset +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%', 'ignore', 'server'; +GO + +EXEC sp_babelfish_configure 'escape_hatch_schemabinding_%'; +GO +select config_value from (select unnest(setconfig) config_value from pg_db_role_setting) t where config_value like '%escape_hatch_schemabinding_%' order by config_value; +GO + +-- non-existing guc +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_XX', 'ignore'; +GO + +-- invalid server option +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_schemabinding_%', 'ignore', 'invalid'; +GO + +-- test automatically generated message for unsupported DDL +CREATE PARTITION FUNCTION f_unsupported_1(datetime) AS RANGE RIGHT FOR VALUES (N'2017-07-11T00:00:00.000', N'2017-07-12T00:00:00.000', N'2017-07-13T00:00:00.000'); +GO + +ALTER AUTHORIZATION ON a1 TO SCHEMA OWNER; +GO + +-- kill (BABEL-2159) +KILL 1; +GO + +-- apply +SELECT * FROM t1 CROSS APPLY (SELECT * FROM Employee E WHERE E.DepartmentID = D.DepartmentID) A +GO + +-- alter view (BABEL-2017) +CREATE TABLE t_babel_2017(a int, b int); +GO +CREATE VIEW v_babel_2017 AS SELECT * FROM t_babel_2017; +GO +ALTER VIEW v_babel_2017 AS SELECT a FROM t_babel_2017; +GO +CREATE OR ALTER VIEW v_babel_2017 AS SELECT b FROM t_babel_2017; +GO +DROP VIEW v_babel_2017; +GO +DROP TABLE t_babel_2017; +GO + + +-- alter trigger (2110) +CREATE TABLE t2110a(c int); +CREATE TABLE t2110b(c int); +GO +CREATE TRIGGER trigger2110 ON t2110a FOR INSERT AS SELECT * FROM t2110a; +GO +ALTER TRIGGER trigger2110 ON t2110b FOR INSERT AS SELECT * FROM t2110a; +GO +DROP TABLE t2110a, t2110b; +GO + + +-- alter schema transfer (33144) +CREATE SCHEMA s33144; +GO +CREATE TABLE t33144(a int); +GO +ALTER SCHEMA s33144 TRANSFER t33144; +GO +DROP TABLE t33144; +GO +DROP SCHEMA s33144; +GO + + +-- alter table add PERSISTED (4919) +CREATE TABLE t4919(c1 int, c2 varchar(1)); +GO +ALTER TABLE t4919 ALTER COLUMN c2 varchar(1) ADD PERSISTED; +GO +DROP TABLE t4919; +GO + + +-- PIVOT (265, 488) +CREATE TABLE t265 (c1 VARCHAR(50), c2 VARCHAR(50)); +GO +SELECT * FROM (SELECT c1, c2 FROM t265) AS p PIVOT (COUNT(c1) FOR c2 IN (c1)) AS pv; +GO +DROP TABLE t265; +GO + +CREATE TABLE t488(id text); +GO +SELECT * FROM (SELECT id from t488) p PIVOT( sum(id)FOR id in (["HI"])) s; +GO +DROP TABLE t488; +GO + + +-- CREATE DATBASE COLLATE (448) +CREATE DATABASE t448 COLLATE NOT_VALID_COLLATION; +GO + + +-- unsupported index option in CREATE INDEX (1070) +CREATE TABLE t1070(c1 int, c2 int); +GO +CREATE INDEX i1070 on t1070 (c1,c2) with allow_dup_row; +GO +DROP TABLE t1070 +GO + +-- XML (8113) +CREATE TABLE t8113_x(a XML); +GO +CREATE TABLE t8113_a(a int); +GO +UPDATE t8113_a SET a.query('.'); +GO +DROP TABLE t8113_x; +GO +DROP TABLE t8113_a; +GO + +-- NEXT VALUE FORa (11738) +CREATE SEQUENCE s11738 AS INT START WITH 5 INCREMENT BY 1; +GO +PRINT RIGHT('000000' + CAST(NEXT VALUE FOR s11738 AS NVARCHAR), 6); +GO +DROP SEQUENCE s11738; +GO + +-- APPLY (4101) +CREATE TABLE t4101(c1 VARCHAR(60), c2 INT) +GO +CREATE FUNCTION f4101(@n1 INT, @n2 INT) RETURNS TABLE AS RETURN (SELECT 1 AS ret); +GO +SELECT t.c1, tmp.t2 FROM t4101 t CROSS APPLY (SELECT SUM(c2) AS t2)tmp +GO +DROP FUNCTION f4101; +GO +DROP TABLE t4101; +GO + +-- XMLNAMESPACES (6869, 6870, 6871) +WITH XMLNAMESPACES ('test.com' AS ns1, 'test2.com' AS ns1) SELECT 'test' AS 'TestAttr' FOR XML RAW +GO +WITH XMLNAMESPACES ('test.com' AS n@s1) SELECT 'test' AS 'TestAttr' FOR XML RAW +GO +WITH XMLNAMESPACES ('test.com' AS xmlns) SELECT 'test' AS 'TestAttr' FOR XML RAW +GO + +-- BEGIN ATOMIC (10782) +create proc p10782 AS begin atomic with (transaction isolation level = snapshot, language = N'us_english') SELECT 1; end +GO + +-- DBCC (12608) +CREATE DATABASE d12608; +GO +DBCC CLONEDATABASE (d12608, d12608_clone); +GO +DROP DATABASE d12608; +GO + +-- ALTER WORKLOAD GROUP (10915) +ALTER WORKLOAD GROUP internal WITH (REQUEST_MAX_MEMORY_GRANT_PERCENT = 45); +GO + +-- $IDENTITY and $ROWGUID +SELECT $IDENTITY; +GO + +SELECT $ROWGUID; +GO + +-- TIMESTAMP and ROWVERSION +CREATE TABLE t_ts(a timestamp); +GO +CREATE TABLE t_ts2(a pg_catalog.timestamp); -- it's fine +GO +DROP TABLE t_ts2; +GO +CREATE TABLE t_rv(a ROWVERSION); +GO +CREATE PROCEDURE p_t2 (@v timestamp) AS BEGIN PRINT CAST(@v AS VARCHAR(10)) END; +GO + +-- CREATE TYPE WITH (10788) +CREATE TYPE type10788 AS TABLE (c1 INT) WITH (DATA_COMPRESSION = NONE) +GO + +-- ALTER TABLE ALTER COLUMN +CREATE TABLE t5074(a SMALLINT NOT NULL IDENTITY) +GO +ALTER TABLE t5074 ADD CONSTRAINT PK_t5074 PRIMARY KEY (a) +GO +ALTER TABLE t5074 ALTER COLUMN a INT NOT NULL +GO +DROP TABLE t5074 +GO + +-- XMLDATA +SELECT 'abc' AS 'TestAttr' FOR XML RAW, XMLDATA, ROOT; +GO + +-- EXECUTE AS (487) +CREATE TABLE t487(c1 int) +GO +CREATE FUNCTION f487() RETURNS TABLE WITH EXECUTE AS 'user' AS +RETURN (SELECT * FROM t487) +GO +DROP TABLE t487 +GO + +-- DEFAULT arguemnt +select func1(DEFAULT); +GO + +EXEC proc1 DEFAULT; +GO + +-- NATIONAL/VARYING (BABEL-2360/2361) +declare @v char varying(10); +GO +declare @v char varying; +GO +declare @v character varying(10); +GO +declare @v character varying; +GO +declare @v nchar varying(10); +GO +declare @v nchar varying; +GO +declare @v national char varying(10); +GO +declare @v national char varying; +GO +declare @v national char(10); +GO +declare @v national char; +GO + +-- unsupported system procedures +sp_help 'a' +GO + +EXEC sp_help 'a' +GO + +CREATE TABLE t_unsupported_sp (a int); +GO +INSERT INTO t_unsupported_sp EXEC sp_help 'a' +GO +DROP TABLE t_unsupported_sp; +GO diff --git a/contrib/test/JDBC/input/BABEL_2429.sql b/contrib/test/JDBC/input/BABEL_2429.sql new file mode 100644 index 00000000000..30c32d26921 --- /dev/null +++ b/contrib/test/JDBC/input/BABEL_2429.sql @@ -0,0 +1,30 @@ +create login newlogin with password = '12345678' , default_database=master; +go + +select db_name(), user_name() +go + +create database zzz +go + +drop database zzz +go + +create login newlogin2 with password = '12345678' , default_database=master; +go + +create login newlogin3 with password = '12345678' , default_database=master; +go + +create database zzz +go + +drop database zzz +go + +drop login newlogin +go +drop login newlogin2 +go +drop login newlogin3 +go diff --git a/contrib/test/JDBC/input/ErrorMapping/102_1.sql b/contrib/test/JDBC/input/ErrorMapping/102_1.sql new file mode 100644 index 00000000000..370c6a84795 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/102_1.sql @@ -0,0 +1,316 @@ +USE master; +GO + +-- simple batch start + +CREATE TABLE t102__2(c1 int); +GO +SELuCT * FROM t +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +begin transaction +GO +CREATE TABLE t102__2(c1 int); +GO +SELuCT * FROM t +GO + +if (@@trancount > 0) select cast('Txn is not rolledback' as text) else select cast('Txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SELuCT * FROM t +end +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp2 values(1) +SELuCT * FROM t +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +SELuCT * FROM t +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELuCT * FROM t +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELuCT * FROM t +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +SELuCT * FROM t +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +SELuCT * FROM t +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +begin try +select 1 +SELuCT * FROM t +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/102_2.sql b/contrib/test/JDBC/input/ErrorMapping/102_2.sql new file mode 100644 index 00000000000..654228e0368 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/102_2.sql @@ -0,0 +1,326 @@ +USE master; +GO + +-- simple batch start + +CREATE TABLE t102__2(c1 int); +GO +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +begin transaction +GO +CREATE TABLE t102__2(c1 int); +GO +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +GO + +if (@@trancount > 0) select cast('Txn is not rolledback' as text) else select cast('Txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +end +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp2 values(1) +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +begin try +select 1 +--Windowing framing cannot have negative PRECEEDING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN -1 PRECEDING AND 3 FOLLOWING) FROM t102__2; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/102_3.sql b/contrib/test/JDBC/input/ErrorMapping/102_3.sql new file mode 100644 index 00000000000..1bb2b5bc960 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/102_3.sql @@ -0,0 +1,326 @@ +USE master; +GO + +-- simple batch start + +CREATE TABLE t102__2(c1 int); +GO +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +begin transaction +GO +CREATE TABLE t102__2(c1 int); +GO +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +GO + +if (@@trancount > 0) select cast('Txn is not rolledback' as text) else select cast('Txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +end +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp2 values(1) +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +begin try +select 1 +--Windowing framing cannot have negative FOLLOWING +SELECT *, SUM(c1) OVER (ORDER BY c1 ROWS BETWEEN 1 PRECEDING AND -3 FOLLOWING) FROM t102__2; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/102_6.sql b/contrib/test/JDBC/input/ErrorMapping/102_6.sql new file mode 100644 index 00000000000..6193c5586b1 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/102_6.sql @@ -0,0 +1,316 @@ +USE master; +GO + +-- simple batch start + +CREATE TABLE t102__2(c1 int); +GO +CREATE TABLE @t102(c1 int) +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +begin transaction +GO +CREATE TABLE t102__2(c1 int); +GO +CREATE TABLE @t102(c1 int) +GO + +if (@@trancount > 0) select cast('Txn is not rolledback' as text) else select cast('Txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE TABLE @t102(c1 int) +end +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp2 values(1) +CREATE TABLE @t102(c1 int) +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t102__2(c1 int); +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE @t102(c1 int) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE @t102(c1 int) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t102__2(c1 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE @t102(c1 int) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE @t102(c1 int) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE @t102(c1 int) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t102__2(c1 int); +GO + +begin try +select 1 +CREATE TABLE @t102(c1 int) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t102__2; +GO + +DROP TABLE @t102; +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/1034_1.sql b/contrib/test/JDBC/input/ErrorMapping/1034_1.sql new file mode 100644 index 00000000000..8aeb0ae25a5 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1034_1.sql @@ -0,0 +1,54 @@ +-- simple batch start + +CREATE TABLE t1034 +( + c1 int +,c2 int +) +GO + +GO +CREATE TRIGGER tr1034 +ON t1034 +FOR UPDATE, UPDATE +AS +SELECT 1 + +GO +DROP TABLE t1034 +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO +CREATE TABLE t1034 +( + c1 int +,c2 int +) +GO + +GO +CREATE TRIGGER tr1034 +ON t1034 +FOR UPDATE, UPDATE +AS +SELECT 1 + +GO + + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1034 +GO + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/input/ErrorMapping/1049_1.sql b/contrib/test/JDBC/input/ErrorMapping/1049_1.sql new file mode 100644 index 00000000000..0fb43878855 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1049_1.sql @@ -0,0 +1,323 @@ +-- simple batch start + +CREATE TABLE t1049 (c1 INT) +GO + +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +GO +DROP TABLE t1049 +GO + + +begin transaction +GO +CREATE TABLE t1049 (c1 INT) +GO + +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1049 +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t1049 (c1 INT) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +end +GO + +DROP TABLE t1049 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t1049 (c1 INT) +GO + +insert into error_mapping.temp2 values(1) +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +GO + +DROP TABLE t1049 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t1049 (c1 INT) +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t1049 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t1049 (c1 INT) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1049 +GO + + + +CREATE TABLE t1049 (c1 INT) +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1049 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1049 (c1 INT) +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1049 +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t1049 (c1 INT) +--GO +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +--FOR SELECT c1 FROM t1049 +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t1049 +--GO +-- +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t1049 (c1 INT) +--GO +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +--FOR SELECT c1 FROM t1049 +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t1049 +--GO +-- +-- +-- +-- +--set xact_abort OFF; +--GO + +-- Error classification is done -- + +CREATE TABLE t1049 (c1 INT) +GO + + +begin try +select 1 +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1049 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/1051_1.sql b/contrib/test/JDBC/input/ErrorMapping/1051_1.sql new file mode 100644 index 00000000000..aad51efe5e2 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1051_1.sql @@ -0,0 +1,58 @@ +-- simple batch start + +CREATE TABLE t1051 +( + c1 int +,c2 int NULL +) +GO + + +CREATE PROC p1051_1 + @a CURSOR OUTPUT VARYING + AS +DECLARE c CURSOR + READ_ONLY +FOR +SELECT * FROM t1051 + +GO +DROP TABLE t1051 +GO + + +SET XACT_ABORT ON; +GO + +begin transaction +GO +CREATE TABLE t1051 +( + c1 int +,c2 int NULL +) +GO + + +CREATE PROC p1051_1 + @a CURSOR OUTPUT VARYING + AS +DECLARE c CURSOR + READ_ONLY +FOR +SELECT * FROM t1051 + +GO + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1051 +GO + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/input/ErrorMapping/10610_1.sql b/contrib/test/JDBC/input/ErrorMapping/10610_1.sql new file mode 100644 index 00000000000..b0f24c913b6 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/10610_1.sql @@ -0,0 +1,267 @@ +# Executing test ErrorHandling1 +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + +begin try +select 1 +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/113_1.sql b/contrib/test/JDBC/input/ErrorMapping/113_1.sql new file mode 100644 index 00000000000..221b8c8502a --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/113_1.sql @@ -0,0 +1,207 @@ +# Executing test ErrorHandling1 +--113 Unclosed end comment +--Note that there's no post-section since that would be included in the comment + +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT 1; /* comment SELECT 1GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--113 Unclosed end comment +--Note that there's no post-section since that would be included in the comment + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT 1; /* comment SELECT 1GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--113 Unclosed end comment +--Note that there's no post-section since that would be included in the comment + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT 1; /* comment SELECT 1GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--113 Unclosed end comment +--Note that there's no post-section since that would be included in the comment + +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT 1; /* comment SELECT 1GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--113 Unclosed end comment +--Note that there's no post-section since that would be included in the comment + +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT 1; /* comment SELECT 1GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--113 Unclosed end comment +--Note that there's no post-section since that would be included in the comment + +--Pre +GO + +begin try +select 1 +--Generate the error +SELECT 1; /* comment SELECT 1GO +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/11700_1.sql b/contrib/test/JDBC/input/ErrorMapping/11700_1.sql new file mode 100644 index 00000000000..f3f308bb220 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11700_1.sql @@ -0,0 +1,189 @@ +# Executing test ErrorHandling1 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +GO + +begin try +select 1 +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/11701_1.sql b/contrib/test/JDBC/input/ErrorMapping/11701_1.sql new file mode 100644 index 00000000000..37c75917dba --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11701_1.sql @@ -0,0 +1,201 @@ +create schema error_mapping; +GO +-- Executing test error_mapping.ErrorHandling1 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + +begin try +select 1 +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/11702_1.sql b/contrib/test/JDBC/input/ErrorMapping/11702_1.sql new file mode 100644 index 00000000000..f53faf5f66f --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11702_1.sql @@ -0,0 +1,260 @@ +-- simple batch start + +GO +CREATE SEQUENCE seq11702 AS bit +GO +GO + +begin transaction +GO +GO +CREATE SEQUENCE seq11702 AS bit +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE SEQUENCE seq11702 AS bit +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO +insert into error_mapping.temp2 values(1) +CREATE SEQUENCE seq11702 AS bit +GO + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +begin try +select 1 +CREATE SEQUENCE seq11702 AS bit +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/11703_1.sql b/contrib/test/JDBC/input/ErrorMapping/11703_1.sql new file mode 100644 index 00000000000..cfe82ad1527 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11703_1.sql @@ -0,0 +1,300 @@ +-- simple batch start + +GO +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +GO +GO + +begin transaction +GO +GO +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO +insert into error_mapping.temp2 values(1) +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +GO + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +begin try +select 1 +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/11703_2.sql b/contrib/test/JDBC/input/ErrorMapping/11703_2.sql new file mode 100644 index 00000000000..b5d256eedb9 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11703_2.sql @@ -0,0 +1,300 @@ +-- simple batch start + +GO +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +GO +GO + +begin transaction +GO +GO +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO +insert into error_mapping.temp2 values(1) +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +GO + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +begin try +select 1 +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/11704_1.sql b/contrib/test/JDBC/input/ErrorMapping/11704_1.sql new file mode 100644 index 00000000000..8a11ffaeb4e --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11704_1.sql @@ -0,0 +1,201 @@ +# Executing test ErrorHandling1 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 6; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 6; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP SEQUENCE s11704 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 6; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP SEQUENCE s11704 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 6; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 6; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +begin try +select 1 +ALTER SEQUENCE s11704 + MINVALUE 6; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/11704_2.sql b/contrib/test/JDBC/input/ErrorMapping/11704_2.sql new file mode 100644 index 00000000000..cecc08d935e --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11704_2.sql @@ -0,0 +1,219 @@ +# Executing test ErrorHandling1 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 3; + +ALTER SEQUENCE s11704 + MAXVALUE 4; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 3; + +ALTER SEQUENCE s11704 + MAXVALUE 4; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP SEQUENCE s11704 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 3; + +ALTER SEQUENCE s11704 + MAXVALUE 4; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP SEQUENCE s11704 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 3; + +ALTER SEQUENCE s11704 + MAXVALUE 4; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +create procedure ErrorHandling1 as +begin +ALTER SEQUENCE s11704 + MINVALUE 3; + +ALTER SEQUENCE s11704 + MAXVALUE 4; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE SEQUENCE s11704 +start with 5 +increment by 1 +GO + +begin try +select 1 +ALTER SEQUENCE s11704 + MINVALUE 3; + +ALTER SEQUENCE s11704 + MAXVALUE 4; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP SEQUENCE s11704 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/11705_1.sql b/contrib/test/JDBC/input/ErrorMapping/11705_1.sql new file mode 100644 index 00000000000..9f813b8b5f3 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11705_1.sql @@ -0,0 +1,195 @@ +# Executing test ErrorHandling1 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +GO + +begin try +select 1 +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/11706_1.sql b/contrib/test/JDBC/input/ErrorMapping/11706_1.sql new file mode 100644 index 00000000000..e13a8d23303 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11706_1.sql @@ -0,0 +1,213 @@ +# Executing test ErrorHandling1 +GO + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +GO + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +GO + + +begin try +select 1 +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 + + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/11708_1.sql b/contrib/test/JDBC/input/ErrorMapping/11708_1.sql new file mode 100644 index 00000000000..a4e8e466b30 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/11708_1.sql @@ -0,0 +1,201 @@ +# Executing test ErrorHandling1 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +GO + +begin try +select 1 +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/129_1.sql b/contrib/test/JDBC/input/ErrorMapping/129_1.sql new file mode 100644 index 00000000000..f24f09001ef --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/129_1.sql @@ -0,0 +1,305 @@ +USE master; +GO + +-- simple batch start + +CREATE TABLE t129(id int) +GO +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +GO +DROP TABLE t129 +GO + +begin transaction +GO +CREATE TABLE t129(id int) +GO +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t129 +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t129(id int) +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +end +GO + +DROP TABLE t129 +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t129(id int) +GO +insert into error_mapping.temp2 values(1) +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +GO + +DROP TABLE t129 +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t129(id int) +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t129 +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t129(id int) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t129 +GO + + +CREATE TABLE t129(id int) +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t129 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t129(id int) +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t129 +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t129(id int) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t129 +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t129(id int) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t129 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t129(id int) +GO + +begin try +select 1 +CREATE INDEX i129 ON t129 (id) +WITH (FILLFACTOR = 1000) +GO +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t129 +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/132_1.sql b/contrib/test/JDBC/input/ErrorMapping/132_1.sql new file mode 100644 index 00000000000..70d48050d41 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/132_1.sql @@ -0,0 +1,376 @@ +-- simple batch start + +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +GO +--------------------------------------------------------------------------- +--Post +GO + +begin transaction +GO +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +--------------------------------------------------------------------------- +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +end +GO + +--------------------------------------------------------------------------- +--Post +GO + +create table error_mapping.temp2 (a int) +GO + +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp2 values(1) +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +GO + +--------------------------------------------------------------------------- +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +--------------------------------------------------------------------------- +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--------------------------------------------------------------------------- +--Post +GO + + +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--------------------------------------------------------------------------- +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--132 Label already declared + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--------------------------------------------------------------------------- +--Post +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +----132 Label already declared +-- +----Could not find this PG error in excel file +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--lbl: +--lbl: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----132 Label already declared +-- +----Could not find this PG error in excel file +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--lbl: +--lbl: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +--132 Label already declared + +--------------------------------------------------------------------------- +--Pre +GO + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/133_1.sql b/contrib/test/JDBC/input/ErrorMapping/133_1.sql new file mode 100644 index 00000000000..79292e52ce9 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/133_1.sql @@ -0,0 +1,378 @@ +-- simple batch start + +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +GO +--------------------------------------------------------------------------- +--Post +GO + +begin transaction +GO +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +--------------------------------------------------------------------------- +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +end +GO + +--------------------------------------------------------------------------- +--Post +GO + +create table error_mapping.temp2 (a int) +GO + +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp2 values(1) +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +GO + +--------------------------------------------------------------------------- +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +--------------------------------------------------------------------------- +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--------------------------------------------------------------------------- +--Post +GO + + +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--------------------------------------------------------------------------- +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--------------------------------------------------------------------------- +--Post +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--GOTO lbl2 +--blb3: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----133 Reference non-existing label in GOTO +-- +----Could not find this PG error in excel file +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--GOTO lbl2 +--blb3: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +--133 Reference non-existing label in GOTO + +--Could not find this PG error in excel file + +--------------------------------------------------------------------------- +--Pre +GO + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/134_1.sql b/contrib/test/JDBC/input/ErrorMapping/134_1.sql new file mode 100644 index 00000000000..da1bcb27682 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/134_1.sql @@ -0,0 +1,365 @@ +-- simple batch start + +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +GO +--Post +GO + +begin transaction +GO +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +end +GO + +--Post +GO + +create table error_mapping.temp2 (a int) +GO + +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp2 values(1) +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +GO + +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +-- +----CHECK constraint conflict, when adding constraint +--DECLARE @a int; +--DECLARE @a int; +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----134 Declare same variable name twice +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +-- +----CHECK constraint conflict, when adding constraint +--DECLARE @a int; +--DECLARE @a int; +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +--134 Declare same variable name twice + +--------------------------------------------------------------------------- +--Pre +GO + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error + +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/135_1.sql b/contrib/test/JDBC/input/ErrorMapping/135_1.sql new file mode 100644 index 00000000000..7660eda460a --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/135_1.sql @@ -0,0 +1,336 @@ +-- simple batch start + +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +--Generate the error +BREAK +GO +--Post +GO + +begin transaction +GO +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +--Generate the error +BREAK +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Generate the error +BREAK +end +GO + +--Post +GO + +create table error_mapping.temp2 (a int) +GO + +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +insert into error_mapping.temp2 values(1) +--Generate the error +BREAK +GO + +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +BREAK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +BREAK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +BREAK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--BREAK +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----135 BREAK when not in WHILE loop +-- +----Could not find this error in excel file +-- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--BREAK +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +--135 BREAK when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO + +begin try +select 1 +--Generate the error +BREAK +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/136_1.sql b/contrib/test/JDBC/input/ErrorMapping/136_1.sql new file mode 100644 index 00000000000..b679b1285b9 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/136_1.sql @@ -0,0 +1,336 @@ +-- simple batch start + +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +--Generate the error +CONTINUE +GO +--Post +GO + +begin transaction +GO +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +--Generate the error +CONTINUE +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Generate the error +CONTINUE +end +GO + +--Post +GO + +create table error_mapping.temp2 (a int) +GO + +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +insert into error_mapping.temp2 values(1) +--Generate the error +CONTINUE +GO + +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +CONTINUE +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +CONTINUE +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +CONTINUE +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--CONTINUE +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----136 CONTINUE when not in WHILE loop +-- +----Could not find this error in excel file +-- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--CONTINUE +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +--136 CONTINUE when not in WHILE loop + +--Could not find this error in excel file + +--Pre +GO + +begin try +select 1 +--Generate the error +CONTINUE +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/141_1.sql b/contrib/test/JDBC/input/ErrorMapping/141_1.sql new file mode 100644 index 00000000000..4cca64f0fce --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/141_1.sql @@ -0,0 +1,343 @@ +-- simple batch start + +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +GO +--Post +DROP TABLE t141; +GO + +begin transaction +GO +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +--Post +DROP TABLE t141; +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +end +GO + +--Post +DROP TABLE t141; +GO + +create table error_mapping.temp2 (a int) +GO + +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +insert into error_mapping.temp2 values(1) +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +GO + +--Post +DROP TABLE t141; +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +--Post +DROP TABLE t141; +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t141; +GO + + +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t141; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t141; +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +----141 Inline variable assignment for only some columns +-- +----Pre +--CREATE TABLE t141(c1 int, c2 int); +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--DECLARE @a int; +--SELECT @a = c1, c2 FROM t141 +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--DROP TABLE t141; +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----141 Inline variable assignment for only some columns +-- +----Pre +--CREATE TABLE t141(c1 int, c2 int); +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--DECLARE @a int; +--SELECT @a = c1, c2 FROM t141 +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--DROP TABLE t141; +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +--141 Inline variable assignment for only some columns + +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO + +begin try +select 1 +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +DROP TABLE t141; +GO + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/142_1.sql b/contrib/test/JDBC/input/ErrorMapping/142_1.sql new file mode 100644 index 00000000000..9b44c9f8a9b --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/142_1.sql @@ -0,0 +1,282 @@ +-- simple batch start + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +GO +DROP TABLE someTable +GO + +begin transaction +GO +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE someTable +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +end +GO + +DROP TABLE someTable +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +insert into error_mapping.temp2 values(1) +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +GO + +DROP TABLE someTable +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE someTable +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE someTable +GO + + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE someTable +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE someTable +GO +set xact_abort OFF; +GO + +---- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE someTable(c1 int PRIMARY KEY) +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE someTable +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE someTable(c1 int PRIMARY KEY) +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE someTable +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +---- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO + +begin try +select 1 +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE someTable +--GO +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/1505_1.sql b/contrib/test/JDBC/input/ErrorMapping/1505_1.sql new file mode 100644 index 00000000000..9ee0fa08ae4 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1505_1.sql @@ -0,0 +1,211 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t1505; +GO + + +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t1505; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t1505; +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t1505; +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t1505; +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO + +begin try +select 1 +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t1505; +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/16948_1.sql b/contrib/test/JDBC/input/ErrorMapping/16948_1.sql new file mode 100644 index 00000000000..e3e3c07411e --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/16948_1.sql @@ -0,0 +1,500 @@ +-- simple batch start + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +GO +DROP TABLE t16948 +GO + + +begin transaction +GO +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t16948 +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +end +GO + +DROP TABLE t16948 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + +insert into error_mapping.temp2 values(1) +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +GO + +DROP TABLE t16948 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t16948 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t16948 +GO + + + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t16948 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t16948 +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t16948(c1 int) +--GO +--INSERT INTO t16948 (c1) +--VALUES +--(10), +--(3), +--(8) +--GO +-- +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE @notCursor INT +-- +--DECLARE @c1 INT +--DECLARE tcursor CURSOR FOR +--SELECT c1 +--FROM t16948 +-- +--OPEN tcursor +--FETCH NEXT FROM @notCursor INTO @c1 +-- +--CLOSE tcursor +--DEALLOCATE tcursor +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t16948 +--GO +-- +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t16948(c1 int) +--GO +--INSERT INTO t16948 (c1) +--VALUES +--(10), +--(3), +--(8) +--GO +-- +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE @notCursor INT +-- +--DECLARE @c1 INT +--DECLARE tcursor CURSOR FOR +--SELECT c1 +--FROM t16948 +-- +--OPEN tcursor +--FETCH NEXT FROM @notCursor INTO @c1 +-- +--CLOSE tcursor +--DEALLOCATE tcursor +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t16948 +--GO +-- +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO + + + +begin try +select 1 +DECLARE @notCursor INT + +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 + +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 + +CLOSE tcursor +DEALLOCATE tcursor + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t16948 +--GO +-- +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/16950_1.sql b/contrib/test/JDBC/input/ErrorMapping/16950_1.sql new file mode 100644 index 00000000000..f8f70b05594 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/16950_1.sql @@ -0,0 +1,322 @@ +-- simple batch start + +GO + +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +GO +GO + + +begin transaction +GO +GO + +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +GO + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +begin try +select 1 +DECLARE @c1 INT +DECLARE @cursor1 CURSOR + +FETCH NEXT FROM @cursor1 INTO @c1 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/1752_1750_2.sql b/contrib/test/JDBC/input/ErrorMapping/1752_1750_2.sql new file mode 100644 index 00000000000..316e18288e3 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1752_1750_2.sql @@ -0,0 +1,201 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1752 +GO + + +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1752 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1752 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1752 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1752 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + +begin try +select 1 +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1752 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/1765_1750_1.sql b/contrib/test/JDBC/input/ErrorMapping/1765_1750_1.sql new file mode 100644 index 00000000000..458fa4ad778 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1765_1750_1.sql @@ -0,0 +1,303 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO + +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO + + +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO + +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO + +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO + +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO + +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + +begin try +select 1 +--SET QUOTED_IDENTIFIER ON +--GO + +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/1768_1750_1.sql b/contrib/test/JDBC/input/ErrorMapping/1768_1750_1.sql new file mode 100644 index 00000000000..8adf209b2db --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1768_1750_1.sql @@ -0,0 +1,291 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + +begin try +select 1 +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/1770_1750_1.sql b/contrib/test/JDBC/input/ErrorMapping/1770_1750_1.sql new file mode 100644 index 00000000000..a473dcee8d7 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1770_1750_1.sql @@ -0,0 +1,201 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1770_1750_1(c1 int); +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1770_1750_2( + c1 int PRIMARY KEY +,c2 int, CONSTRAINT fkt1770_1750_1 FOREIGN KEY (c2) REFERENCES t1770_1750_1(c3)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1770_1750_1 +GO + + + +CREATE TABLE t1770_1750_1(c1 int); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t1770_1750_2( + c1 int PRIMARY KEY +,c2 int, CONSTRAINT fkt1770_1750_1 FOREIGN KEY (c2) REFERENCES t1770_1750_1(c3)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1770_1750_1 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1770_1750_1(c1 int); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t1770_1750_2( + c1 int PRIMARY KEY +,c2 int, CONSTRAINT fkt1770_1750_1 FOREIGN KEY (c2) REFERENCES t1770_1750_1(c3)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1770_1750_1 +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1770_1750_1(c1 int); +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1770_1750_2( + c1 int PRIMARY KEY +,c2 int, CONSTRAINT fkt1770_1750_1 FOREIGN KEY (c2) REFERENCES t1770_1750_1(c3)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1770_1750_1 +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1770_1750_1(c1 int); +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1770_1750_2( + c1 int PRIMARY KEY +,c2 int, CONSTRAINT fkt1770_1750_1 FOREIGN KEY (c2) REFERENCES t1770_1750_1(c3)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1770_1750_1 +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1770_1750_1(c1 int); +GO + +begin try +select 1 +CREATE TABLE t1770_1750_2( + c1 int PRIMARY KEY +,c2 int, CONSTRAINT fkt1770_1750_1 FOREIGN KEY (c2) REFERENCES t1770_1750_1(c3)) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1770_1750_1 +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/1774_1750_1.sql b/contrib/test/JDBC/input/ErrorMapping/1774_1750_1.sql new file mode 100644 index 00000000000..a8392dfff51 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1774_1750_1.sql @@ -0,0 +1,249 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1774a( + c1 int, + c2 int, + primary key(c1,c2) + ) + +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1774b +( +c1 int, +foreign key(c1) references t1774a +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1774a +GO + + + +CREATE TABLE t1774a( + c1 int, + c2 int, + primary key(c1,c2) + ) + +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t1774b +( +c1 int, +foreign key(c1) references t1774a +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1774a +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1774a( + c1 int, + c2 int, + primary key(c1,c2) + ) + +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t1774b +( +c1 int, +foreign key(c1) references t1774a +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1774a +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1774a( + c1 int, + c2 int, + primary key(c1,c2) + ) + +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1774b +( +c1 int, +foreign key(c1) references t1774a +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1774a +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1774a( + c1 int, + c2 int, + primary key(c1,c2) + ) + +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1774b +( +c1 int, +foreign key(c1) references t1774a +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1774a +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t1774a( + c1 int, + c2 int, + primary key(c1,c2) + ) + +GO + + +begin try +select 1 +CREATE TABLE t1774b +( +c1 int, +foreign key(c1) references t1774a +) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1774a +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/180_1.sql b/contrib/test/JDBC/input/ErrorMapping/180_1.sql new file mode 100644 index 00000000000..dc7412dc953 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/180_1.sql @@ -0,0 +1,4246 @@ +-- simple batch start + + +GO +CREATE FUNCTION fn180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +RETURNS INT +AS +BEGIN + RETURN 100 +END + +GO + +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO + +GO +CREATE FUNCTION fn180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +RETURNS INT +AS +BEGIN + RETURN 100 +END + +GO + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/input/ErrorMapping/180_2.sql b/contrib/test/JDBC/input/ErrorMapping/180_2.sql new file mode 100644 index 00000000000..2ec467a6bd5 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/180_2.sql @@ -0,0 +1,4242 @@ +-- simple batch start + + +GO +CREATE PROC p180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +AS +BEGIN + RETURN 100 +END + +GO + +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO + +GO +CREATE PROC p180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +AS +BEGIN + RETURN 100 +END + +GO + +if @@trancount > 0 select cast('Does not respoect xact_abort flag' as TEXT) else select cast('Respects xact_abort flag' as text); +GO + +if @@trancount > 0 rollback tran +GO + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/input/ErrorMapping/1946_1.sql b/contrib/test/JDBC/input/ErrorMapping/1946_1.sql new file mode 100644 index 00000000000..96a1454aae9 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/1946_1.sql @@ -0,0 +1,523 @@ +-- simple batch start + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +GO +DROP TABLE t1946 +GO + +begin transaction +GO +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1946 +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +end +GO + +DROP TABLE t1946 +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +insert into error_mapping.temp2 values(1) +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +GO + +DROP TABLE t1946 +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t1946 +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1946 +GO + + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1946 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t1946 +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1946 +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1946 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + +begin try +select 1 +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) + + + + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1946 +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/208_1.sql b/contrib/test/JDBC/input/ErrorMapping/208_1.sql new file mode 100644 index 00000000000..6b706dbefbf --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/208_1.sql @@ -0,0 +1,189 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Table doesn't exist +SELECT * FROM t208; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Table doesn't exist +SELECT * FROM t208; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Table doesn't exist +SELECT * FROM t208; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Table doesn't exist +SELECT * FROM t208; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Table doesn't exist +SELECT * FROM t208; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +--Table doesn't exist +SELECT * FROM t208; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/217_1.sql b/contrib/test/JDBC/input/ErrorMapping/217_1.sql new file mode 100644 index 00000000000..03e723ad2d9 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/217_1.sql @@ -0,0 +1,285 @@ +# Executing test ErrorHandling1 +--217 NESTLEVEL exceeded + +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 + +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + +--217 NESTLEVEL exceeded + +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 + +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--217 NESTLEVEL exceeded + +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 + +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--217 NESTLEVEL exceeded + +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 + +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--217 NESTLEVEL exceeded + +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 + +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--217 NESTLEVEL exceeded + +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 + +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/218_1.sql b/contrib/test/JDBC/input/ErrorMapping/218_1.sql new file mode 100644 index 00000000000..a11e3cd534f --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/218_1.sql @@ -0,0 +1,263 @@ +USE master; +GO + +-- simple batch start + +GO +DROP TYPE type_218 +GO +GO + +begin transaction +GO +GO +DROP TYPE type_218 +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DROP TYPE type_218 +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO +insert into error_mapping.temp2 values(1) +DROP TYPE type_218 +GO + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +DROP TYPE type_218 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DROP TYPE type_218 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DROP TYPE type_218 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +DROP TYPE type_218 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +DROP TYPE type_218 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +begin try +select 1 +DROP TYPE type_218 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/219_1.sql b/contrib/test/JDBC/input/ErrorMapping/219_1.sql new file mode 100644 index 00000000000..d7893bf7564 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/219_1.sql @@ -0,0 +1,183 @@ +# Executing test ErrorHandling1 +CREATE TYPE type_218 from INT +GO + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TYPE type_218 +GO + + +CREATE TYPE type_218 from INT +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TYPE type_218 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TYPE type_218 from INT +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TYPE type_218 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TYPE type_218 from INT +GO + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TYPE type_218 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TYPE type_218 from INT +GO + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TYPE type_218 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TYPE type_218 from INT +GO + +begin try +select 1 +CREATE TYPE type_218 from INT +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TYPE type_218 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/220_1.sql b/contrib/test/JDBC/input/ErrorMapping/220_1.sql new file mode 100644 index 00000000000..65b0e244eb5 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/220_1.sql @@ -0,0 +1,219 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +begin try +select 1 +--Generate the error +DECLARE @a tinyint = 1000; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/220_2.sql b/contrib/test/JDBC/input/ErrorMapping/220_2.sql new file mode 100644 index 00000000000..984ec81627b --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/220_2.sql @@ -0,0 +1,213 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +begin try +select 1 +DECLARE @a smallint = 45000; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/220_3.sql b/contrib/test/JDBC/input/ErrorMapping/220_3.sql new file mode 100644 index 00000000000..382a5f2491f --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/220_3.sql @@ -0,0 +1,213 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +begin try +select 1 +INSERT INTO t220_1(c1) VALUES(1000); +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/220_4.sql b/contrib/test/JDBC/input/ErrorMapping/220_4.sql new file mode 100644 index 00000000000..da731983a52 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/220_4.sql @@ -0,0 +1,213 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + +begin try +select 1 +INSERT INTO t220_2(c1) VALUES(45000); +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/222_1.sql b/contrib/test/JDBC/input/ErrorMapping/222_1.sql new file mode 100644 index 00000000000..2feab3d4f5a --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/222_1.sql @@ -0,0 +1,195 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE TYPE t222_1 FROM t222_2 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE TYPE t222_1 FROM t222_2 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE TYPE t222_1 FROM t222_2 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE TYPE t222_1 FROM t222_2 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE TYPE t222_1 FROM t222_2 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +--Generate the error +CREATE TYPE t222_1 FROM t222_2 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/257_2.sql b/contrib/test/JDBC/input/ErrorMapping/257_2.sql new file mode 100644 index 00000000000..2a4f3422e7f --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/257_2.sql @@ -0,0 +1,195 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +create procedure ErrorHandling1 as +begin +DECLARE @a real; SET @a = CAST(GETDATE() AS smalldatetime); +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t257; +GO + + +--Pre +CREATE TABLE t257(c1 binary(10)); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +DECLARE @a real; SET @a = CAST(GETDATE() AS smalldatetime); +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t257; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t257(c1 binary(10)); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +DECLARE @a real; SET @a = CAST(GETDATE() AS smalldatetime); +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t257; +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +create procedure ErrorHandling1 as +begin +DECLARE @a real; SET @a = CAST(GETDATE() AS smalldatetime); +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t257; +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +create procedure ErrorHandling1 as +begin +DECLARE @a real; SET @a = CAST(GETDATE() AS smalldatetime); +GO +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t257; +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +begin try +select 1 +DECLARE @a real; SET @a = CAST(GETDATE() AS smalldatetime); +GO +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t257; +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/2732_1.sql b/contrib/test/JDBC/input/ErrorMapping/2732_1.sql new file mode 100644 index 00000000000..0b7af080bff --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/2732_1.sql @@ -0,0 +1,282 @@ +-- simple batch start + +GO + +RAISERROR(5005, 10, 1, N'ErrorMessage') +GO +GO + + +begin transaction +GO +GO + +RAISERROR(5005, 10, 1, N'ErrorMessage') +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +RAISERROR(5005, 10, 1, N'ErrorMessage') +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +RAISERROR(5005, 10, 1, N'ErrorMessage') +GO + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +begin try +select 1 +RAISERROR(5005, 10, 1, N'ErrorMessage') +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/2747_1.sql b/contrib/test/JDBC/input/ErrorMapping/2747_1.sql new file mode 100644 index 00000000000..4ed164f206b --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/2747_1.sql @@ -0,0 +1,300 @@ +-- simple batch start + +GO +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +GO +GO + +begin transaction +GO +GO +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO +insert into error_mapping.temp2 values(1) +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +GO + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +--,1, +--1, +--3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +--,1, +--1, +--3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + +begin try +select 1 +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--GO +-- +-- +-- +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/2760_1.sql b/contrib/test/JDBC/input/ErrorMapping/2760_1.sql new file mode 100644 index 00000000000..ff379afade7 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/2760_1.sql @@ -0,0 +1,186 @@ +USE master; +GO + +# Executing test ErrorHandling1 +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE N0nExistantSchema.t2760 (c1 INT) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE N0nExistantSchema.t2760 (c1 INT) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE N0nExistantSchema.t2760 (c1 INT) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE N0nExistantSchema.t2760 (c1 INT) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + +create procedure ErrorHandling1 as +begin +CREATE TABLE N0nExistantSchema.t2760 (c1 INT) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +GO + +begin try +select 1 +CREATE TABLE N0nExistantSchema.t2760 (c1 INT) + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/2787_1.sql b/contrib/test/JDBC/input/ErrorMapping/2787_1.sql new file mode 100644 index 00000000000..a8c5fef8d96 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/2787_1.sql @@ -0,0 +1,282 @@ +-- simple batch start + + +GO +RAISERROR('Hello %q', 16, 1, 'as') +GO + +GO + +begin transaction +GO + +GO +RAISERROR('Hello %q', 16, 1, 'as') +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +RAISERROR('Hello %q', 16, 1, 'as') +end +GO + + +GO + +create table error_mapping.temp2 (a int) +GO + + +GO +insert into error_mapping.temp2 values(1) +RAISERROR('Hello %q', 16, 1, 'as') +GO + + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +GO +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 + +GO + +begin try +select 1 +RAISERROR('Hello %q', 16, 1, 'as') +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/2812_1.sql b/contrib/test/JDBC/input/ErrorMapping/2812_1.sql new file mode 100644 index 00000000000..2d8bc27bad7 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/2812_1.sql @@ -0,0 +1,177 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +glarg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +glarg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +glarg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +glarg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +glarg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +glarg buh +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/2812_2.sql b/contrib/test/JDBC/input/ErrorMapping/2812_2.sql new file mode 100644 index 00000000000..58457e3dda6 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/2812_2.sql @@ -0,0 +1,177 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +EXEC gurka +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +EXEC gurka +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +EXEC gurka +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +EXEC gurka +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +EXEC gurka +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +EXEC gurka +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/2812_3.sql b/contrib/test/JDBC/input/ErrorMapping/2812_3.sql new file mode 100644 index 00000000000..a1d50102a32 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/2812_3.sql @@ -0,0 +1,177 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +glärg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +glärg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +glärg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +glärg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +glärg buh +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +glärg buh +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/293_1.sql b/contrib/test/JDBC/input/ErrorMapping/293_1.sql new file mode 100644 index 00000000000..482eb5a7208 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/293_1.sql @@ -0,0 +1,378 @@ +-- simple batch start + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +GO +DROP TABLE t293 +GO + + +begin transaction +GO +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t293 +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +end +GO + +DROP TABLE t293 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + +insert into error_mapping.temp2 values(1) +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +GO + +DROP TABLE t293 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t293 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t293 +GO + + + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t293 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t293 +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t293 +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t293 +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO + + +begin try +select 1 +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t293 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/3609_1.sql b/contrib/test/JDBC/input/ErrorMapping/3609_1.sql new file mode 100644 index 00000000000..ff8eb30595a --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3609_1.sql @@ -0,0 +1,403 @@ +-- simple batch start + +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + +INSERT INTO t3609 values (1) +GO +DROP TABLE t3609 +Go + +begin transaction +GO +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + +INSERT INTO t3609 values (1) +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t3609 +Go + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +INSERT INTO t3609 values (1) +end +GO + +DROP TABLE t3609 +Go + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + +insert into error_mapping.temp2 values(1) +INSERT INTO t3609 values (1) +GO + +DROP TABLE t3609 +Go + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t3609 +Go + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3609 +Go + + +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3609 +Go + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3609 +Go +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3609 +Go + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3609 +Go + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t3609(id int); +GO + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION + +END +GO + + +begin try +select 1 +INSERT INTO t3609 values (1) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3609 +Go + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/3701_3.sql b/contrib/test/JDBC/input/ErrorMapping/3701_3.sql new file mode 100644 index 00000000000..3356773f32c --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3701_3.sql @@ -0,0 +1,189 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +--Procedure doesn't exist +DROP PROC p3701; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/3701_4.sql b/contrib/test/JDBC/input/ErrorMapping/3701_4.sql new file mode 100644 index 00000000000..ac808023e8e --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3701_4.sql @@ -0,0 +1,189 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +--Trigger doesn't exist +DROP TRIGGER tr3701 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/3701_5.sql b/contrib/test/JDBC/input/ErrorMapping/3701_5.sql new file mode 100644 index 00000000000..980908ca7a7 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3701_5.sql @@ -0,0 +1,189 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +--Function doesn't exist +DROP FUNCTION fn3701; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/3728_3727_1.sql b/contrib/test/JDBC/input/ErrorMapping/3728_3727_1.sql new file mode 100644 index 00000000000..b463f352780 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3728_3727_1.sql @@ -0,0 +1,225 @@ +# Executing test ErrorHandling1 +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3728 +GO + + + +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3728 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3728 +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3728 +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3728 +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO + + +begin try +select 1 +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3728 +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/3729_1.sql b/contrib/test/JDBC/input/ErrorMapping/3729_1.sql new file mode 100644 index 00000000000..31ba3ca2e6b --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3729_1.sql @@ -0,0 +1,282 @@ +USE master +GO + +# Executing test ErrorHandling1 +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + +begin try +select 1 +DROP FUNCTION s3729.f3729 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/3902_1.sql b/contrib/test/JDBC/input/ErrorMapping/3902_1.sql new file mode 100644 index 00000000000..24180c29af7 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3902_1.sql @@ -0,0 +1,189 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +--Generate the error +COMMIT; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/3903_1.sql b/contrib/test/JDBC/input/ErrorMapping/3903_1.sql new file mode 100644 index 00000000000..055eeee72a2 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3903_1.sql @@ -0,0 +1,195 @@ +# Executing test ErrorHandling1 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + + +begin try +select 1 +--Generate the error +ROLLBACK +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/3930_1.sql b/contrib/test/JDBC/input/ErrorMapping/3930_1.sql new file mode 100644 index 00000000000..cfa82a2f7d9 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/3930_1.sql @@ -0,0 +1,271 @@ +# Executing test ErrorHandling1 +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + +begin try +select 1 +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/4708_1.sql b/contrib/test/JDBC/input/ErrorMapping/4708_1.sql new file mode 100644 index 00000000000..eb905be40dc --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4708_1.sql @@ -0,0 +1,249 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + +begin try +select 1 +TRUNCATE TABLE v4708 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/4712_1.sql b/contrib/test/JDBC/input/ErrorMapping/4712_1.sql new file mode 100644 index 00000000000..087f43520ca --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4712_1.sql @@ -0,0 +1,243 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO + +begin try +select 1 +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/4901_1.sql b/contrib/test/JDBC/input/ErrorMapping/4901_1.sql new file mode 100644 index 00000000000..974c8c12681 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4901_1.sql @@ -0,0 +1,326 @@ +-- simple batch start + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + +ALTER TABLE t4901 ADD c2 int NOT NULL +GO +DROP TABLE t4901 +GO + + +begin transaction +GO +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + +ALTER TABLE t4901 ADD c2 int NOT NULL +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t4901 +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +ALTER TABLE t4901 ADD c2 int NOT NULL +end +GO + +DROP TABLE t4901 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + +insert into error_mapping.temp2 values(1) +ALTER TABLE t4901 ADD c2 int NOT NULL +GO + +DROP TABLE t4901 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t4901 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4901 +GO + + + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4901 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4901 +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4901 +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4901 +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO + + +begin try +select 1 +ALTER TABLE t4901 ADD c2 int NOT NULL +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4901 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/4920_1.sql b/contrib/test/JDBC/input/ErrorMapping/4920_1.sql new file mode 100644 index 00000000000..fb4f2933df1 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4920_1.sql @@ -0,0 +1,243 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4920 +GO + + + + +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4920 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4920 +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4920 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4920 +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + +begin try +select 1 +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4920 +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/4924_1.sql b/contrib/test/JDBC/input/ErrorMapping/4924_1.sql new file mode 100644 index 00000000000..2fc0b449c56 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4924_1.sql @@ -0,0 +1,225 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 DROP COLUMN NonExisting + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + +CREATE TABLE t4924(c1 int, c2 int) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 DROP COLUMN NonExisting + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4924 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4924(c1 int, c2 int) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 DROP COLUMN NonExisting + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4924 +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 DROP COLUMN NonExisting + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 DROP COLUMN NonExisting + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +begin try +select 1 +ALTER TABLE t4924 DROP COLUMN NonExisting + + + + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/4924_2.sql b/contrib/test/JDBC/input/ErrorMapping/4924_2.sql new file mode 100644 index 00000000000..2a3eab1f6dd --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4924_2.sql @@ -0,0 +1,225 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 ALTER COLUMN c3 bigint + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + +CREATE TABLE t4924(c1 int, c2 int) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 ALTER COLUMN c3 bigint + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4924 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4924(c1 int, c2 int) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 ALTER COLUMN c3 bigint + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4924 +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 ALTER COLUMN c3 bigint + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4924 ALTER COLUMN c3 bigint + + + + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4924(c1 int, c2 int) +GO + + +begin try +select 1 +ALTER TABLE t4924 ALTER COLUMN c3 bigint + + + + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4924 +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/4936_1.sql b/contrib/test/JDBC/input/ErrorMapping/4936_1.sql new file mode 100644 index 00000000000..4e9b06e04a0 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4936_1.sql @@ -0,0 +1,195 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4936 ADD c3 AS CONVERT(DATETIME2,ISNULL(c1, DATEADD(SECOND, c2, CONVERT(DATE,'19700101'))),112) PERSISTED + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4936 ADD c3 AS CONVERT(DATETIME2,ISNULL(c1, DATEADD(SECOND, c2, CONVERT(DATE,'19700101'))),112) PERSISTED + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4936 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +ALTER TABLE t4936 ADD c3 AS CONVERT(DATETIME2,ISNULL(c1, DATEADD(SECOND, c2, CONVERT(DATE,'19700101'))),112) PERSISTED + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4936 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4936 ADD c3 AS CONVERT(DATETIME2,ISNULL(c1, DATEADD(SECOND, c2, CONVERT(DATE,'19700101'))),112) PERSISTED + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4936 ADD c3 AS CONVERT(DATETIME2,ISNULL(c1, DATEADD(SECOND, c2, CONVERT(DATE,'19700101'))),112) PERSISTED + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +begin try +select 1 +ALTER TABLE t4936 ADD c3 AS CONVERT(DATETIME2,ISNULL(c1, DATEADD(SECOND, c2, CONVERT(DATE,'19700101'))),112) PERSISTED + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/4936_2.sql b/contrib/test/JDBC/input/ErrorMapping/4936_2.sql new file mode 100644 index 00000000000..cccc03a4dd8 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/4936_2.sql @@ -0,0 +1,213 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t4936b +( + c1 INT, + c2 AS GETDATE() persisted +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t4936b +( + c1 INT, + c2 AS GETDATE() persisted +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4936 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +CREATE TABLE t4936b +( + c1 INT, + c2 AS GETDATE() persisted +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t4936 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t4936b +( + c1 INT, + c2 AS GETDATE() persisted +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t4936b +( + c1 INT, + c2 AS GETDATE() persisted +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t4936 (c1 DATETIME, c2 INT) +GO + + +begin try +select 1 +CREATE TABLE t4936b +( + c1 INT, + c2 AS GETDATE() persisted +) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4936 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/512_1.sql b/contrib/test/JDBC/input/ErrorMapping/512_1.sql new file mode 100644 index 00000000000..824c21a363d --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/512_1.sql @@ -0,0 +1,207 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t512; +GO + + +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t512; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t512; +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t512; +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t512; +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO + +begin try +select 1 +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t512; +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/515_1.sql b/contrib/test/JDBC/input/ErrorMapping/515_1.sql new file mode 100644 index 00000000000..79c8af57f18 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/515_1.sql @@ -0,0 +1,201 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t515(c1 int not null); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t515 +GO + + +--Pre +CREATE TABLE t515(c1 int not null); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t515 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t515(c1 int not null); +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +DROP TABLE t515 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t515(c1 int not null); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t515 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t515(c1 int not null); +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t515 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t515(c1 int not null); +GO + +begin try +select 1 +--Generate the error +INSERT INTO t515 VALUES(null); +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t515 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/517_1.sql b/contrib/test/JDBC/input/ErrorMapping/517_1.sql new file mode 100644 index 00000000000..6c19f4c2801 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/517_1.sql @@ -0,0 +1,282 @@ +-- simple batch start + +GO + +SELECT DATEADD(YY,-300,getdate()) +GO +GO + + +begin transaction +GO +GO + +SELECT DATEADD(YY,-300,getdate()) +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SELECT DATEADD(YY,-300,getdate()) +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +SELECT DATEADD(YY,-300,getdate()) +GO + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +begin try +select 1 +SELECT DATEADD(YY,-300,getdate()) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/545_1.sql b/contrib/test/JDBC/input/ErrorMapping/545_1.sql new file mode 100644 index 00000000000..f9f961e6dcc --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/545_1.sql @@ -0,0 +1,261 @@ +# Executing test ErrorHandling1 +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) + +GO + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON + +INSERT INTO t545 (c2) VALUES(2) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t545 + +GO + + + + +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON + +INSERT INTO t545 (c2) VALUES(2) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t545 + +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON + +INSERT INTO t545 (c2) VALUES(2) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t545 + +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) + +GO + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON + +INSERT INTO t545 (c2) VALUES(2) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t545 + +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) + +GO + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON + +INSERT INTO t545 (c2) VALUES(2) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t545 + +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) + +GO + + + +begin try +select 1 +SET IDENTITY_INSERT t545 ON + +INSERT INTO t545 (c2) VALUES(2) + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t545 + +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/550_1.sql b/contrib/test/JDBC/input/ErrorMapping/550_1.sql new file mode 100644 index 00000000000..a3245c3ef72 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/550_1.sql @@ -0,0 +1,279 @@ +# Executing test ErrorHandling1 +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + +begin try +select 1 +INSERT INTO v550 (c2) VALUES(20) +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/556_1.sql b/contrib/test/JDBC/input/ErrorMapping/556_1.sql new file mode 100644 index 00000000000..ffd3aa1aeec --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/556_1.sql @@ -0,0 +1,468 @@ +-- simple batch start + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +begin transaction +GO +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +INSERT INTO t556(id, results) +EXECUTE proc_556; +end +GO + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +insert into error_mapping.temp2 values(1) +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO + + +begin try +select 1 +INSERT INTO t556(id, results) +EXECUTE proc_556; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/6401_1.sql b/contrib/test/JDBC/input/ErrorMapping/6401_1.sql new file mode 100644 index 00000000000..0cf0f52c1e7 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/6401_1.sql @@ -0,0 +1,213 @@ +# Executing test ErrorHandling1 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +--Post +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +--Pre +GO + +begin try +select 1 +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/8106_1.sql b/contrib/test/JDBC/input/ErrorMapping/8106_1.sql new file mode 100644 index 00000000000..c7719b63f17 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8106_1.sql @@ -0,0 +1,314 @@ +-- simple batch start + +CREATE TABLE t8106(a int) +GO + +SET IDENTITY_INSERT t8106 ON + +GO +DROP TABLE t8106 +GO + + +begin transaction +GO +CREATE TABLE t8106(a int) +GO + +SET IDENTITY_INSERT t8106 ON + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t8106 +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t8106(a int) +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SET IDENTITY_INSERT t8106 ON + +end +GO + +DROP TABLE t8106 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t8106(a int) +GO + +insert into error_mapping.temp2 values(1) +SET IDENTITY_INSERT t8106 ON + +GO + +DROP TABLE t8106 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t8106(a int) +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +DROP TABLE t8106 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t8106(a int) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8106 +GO + + + +CREATE TABLE t8106(a int) +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t8106 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t8106(a int) +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t8106 +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t8106(a int) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8106 +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t8106(a int) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8106 +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t8106(a int) +GO + + +begin try +select 1 +SET IDENTITY_INSERT t8106 ON + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8106 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/8107_1.sql b/contrib/test/JDBC/input/ErrorMapping/8107_1.sql new file mode 100644 index 00000000000..ce13194e796 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8107_1.sql @@ -0,0 +1,291 @@ +# Executing test ErrorHandling1 +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO + + +begin try +select 1 +SET IDENTITY_INSERT t8107_2 ON + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/8143_1.sql b/contrib/test/JDBC/input/ErrorMapping/8143_1.sql new file mode 100644 index 00000000000..8cc8923259a --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8143_1.sql @@ -0,0 +1,201 @@ +# Executing test ErrorHandling1 + +GO + + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 + +GO + + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 + +GO + + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 + +GO + + + +begin try +select 1 +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/8143_2.sql b/contrib/test/JDBC/input/ErrorMapping/8143_2.sql new file mode 100644 index 00000000000..1ae4604bd29 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8143_2.sql @@ -0,0 +1,201 @@ +# Executing test ErrorHandling1 + +GO + + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 + +GO + + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 + +GO + + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 + +GO + + + +begin try +select 1 +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/8152_1.sql b/contrib/test/JDBC/input/ErrorMapping/8152_1.sql new file mode 100644 index 00000000000..9d85e93ca99 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8152_1.sql @@ -0,0 +1,207 @@ +# Executing test ErrorHandling1 +CREATE TABLE t8152(a CHAR(5)) +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8152 +GO + + + + +CREATE TABLE t8152(a CHAR(5)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t8152 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t8152(a CHAR(5)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t8152 +GO + + +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t8152(a CHAR(5)) +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8152 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t8152(a CHAR(5)) +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8152 +GO + + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t8152(a CHAR(5)) +GO + + +begin try +select 1 +INSERT INTO t8152(a) VALUES('123456') + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8152 +GO + + + + diff --git a/contrib/test/JDBC/input/ErrorMapping/8152_2.sql b/contrib/test/JDBC/input/ErrorMapping/8152_2.sql new file mode 100644 index 00000000000..6f27402c8c1 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8152_2.sql @@ -0,0 +1,195 @@ +# Executing test ErrorHandling1 +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t2628 +GO + + +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t2628 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +DROP TABLE t2628 +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +# Executing test ErrorHandling10000000 +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t2628 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t2628 +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +# Executing test ErrorHandling10000000 +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + +begin try +select 1 +INSERT INTO t2628 VALUES('1234') + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t2628 +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/8159_1.sql b/contrib/test/JDBC/input/ErrorMapping/8159_1.sql new file mode 100644 index 00000000000..11a54f38e06 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8159_1.sql @@ -0,0 +1,40 @@ +-- simple batch start + +GO + + +CREATE VIEW v8159(c1, c2) +AS +SELECT + 'a' AS c1 +GO +DROP VIEW v8159 +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO +GO + + +CREATE VIEW v8159(c1, c2) +AS +SELECT + 'a' AS c1 +GO + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP VIEW v8159 +GO + +SET XACT_ABORT OFF; +GO + + diff --git a/contrib/test/JDBC/input/ErrorMapping/8179_1.sql b/contrib/test/JDBC/input/ErrorMapping/8179_1.sql new file mode 100644 index 00000000000..7920e29ea43 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/8179_1.sql @@ -0,0 +1,302 @@ +-- simple batch start + +GO + +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +GO +GO + + +begin transaction +GO +GO + +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +GO + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +begin try +select 1 +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/9809_1.sql b/contrib/test/JDBC/input/ErrorMapping/9809_1.sql new file mode 100644 index 00000000000..474b30fbcdb --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/9809_1.sql @@ -0,0 +1,292 @@ +-- simple batch start + +GO + +SELECT CONVERT(DATE, '10-10-10', 140) + +GO +GO + + +begin transaction +GO +GO + +SELECT CONVERT(DATE, '10-10-10', 140) + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SELECT CONVERT(DATE, '10-10-10', 140) + +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +SELECT CONVERT(DATE, '10-10-10', 140) + +GO + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + +-- compile time error portion + +-- Executing test error_mapping.ErrorHandling1 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + +-- comiple time error portion end -- +-- Next portion is for runtime error -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end + +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end + +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +set xact_abort OFF; +GO + +-- Error classification is done -- + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +begin try +select 1 +SELECT CONVERT(DATE, '10-10-10', 140) + +end try +begin catch + select xact_state(); +end catch + +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/TestCompileTimeErrorWithDefaultBehave.sql b/contrib/test/JDBC/input/ErrorMapping/TestCompileTimeErrorWithDefaultBehave.sql new file mode 100644 index 00000000000..85fba4bf404 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/TestCompileTimeErrorWithDefaultBehave.sql @@ -0,0 +1,190 @@ +USE master; +GO + +-- This file contains test cases to test un-mapped compile time error against Babelfish server. +GO + +CREATE SCHEMA error_mapping; +GO + +-- Example 1 +GO + +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('()[1]', 'VARCHAR') + +GO +GO + + +begin transaction +GO +GO + +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('()[1]', 'VARCHAR') + +GO + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('()[1]', 'VARCHAR') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('()[1]', 'VARCHAR') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + +-- Example 2 +GO + +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('(*:)[1]', 'VARCHAR') + +GO +GO + + +begin transaction +GO +GO + +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('(*:)[1]', 'VARCHAR') + +GO + + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('(*:)[1]', 'VARCHAR') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @xml XML +SET @xml = CONVERT(XML, '') +SELECT @xml.value('(*:)[1]', 'VARCHAR') + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end + +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO +GO + +set xact_abort OFF; +GO + + +DROP SCHEMA error_mapping; +GO + diff --git a/contrib/test/JDBC/input/ErrorMapping/TestErrorHelperFunctions.sql b/contrib/test/JDBC/input/ErrorMapping/TestErrorHelperFunctions.sql new file mode 100644 index 00000000000..61ebdf1d9d3 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/TestErrorHelperFunctions.sql @@ -0,0 +1,23 @@ +create table testErrorHF(c1 int not null); +insert into testErrorHF values(1); +GO + +begin try + insert into testErrorHF values('abc'); +end try +begin catch + select @@error, @@pgerror; +end catch +GO + +select @@error, @@pgerror; +GO + +insert into testErrorHF values(null); +GO + +select @@error, @@pgerror; +GO + +select * from fn_mapped_system_error_list(); +GO diff --git a/contrib/test/JDBC/input/ErrorMapping/TestRunTimeErrorWithDefaultBehave.sql b/contrib/test/JDBC/input/ErrorMapping/TestRunTimeErrorWithDefaultBehave.sql new file mode 100644 index 00000000000..1462d54cec8 --- /dev/null +++ b/contrib/test/JDBC/input/ErrorMapping/TestRunTimeErrorWithDefaultBehave.sql @@ -0,0 +1,207 @@ +-- This file contains test cases to test un-mapped runtime error against Babelfish server. +GO + +CREATE SCHEMA error_mapping; +GO + +-- Example 1 +GO + +CREATE TABLE t257(c1 binary(10)); +GO +DECLARE @a binary(10); +SET @a = CAST('21' AS char(10)); +SELECT @a +GO +DROP TABLE t257; +GO + +begin transaction +GO +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +DECLARE @a binary(10); +SET @a = CAST('21' AS char(10)); +SELECT @a +GO + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t257; +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO +--Pre +CREATE TABLE t257(c1 binary(10)); +GO + +DECLARE @a binary(10); +SET @a = CAST('21' AS char(10)); +SELECT @a +GO + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t257; +GO + +SET XACT_ABORT OFF; +GO + + +CREATE TABLE t257(c1 binary(10)); +GO + +create procedure error_mapping.ErrorHandling1 as +begin + begin try + select 1 + DECLARE @a binary(10); + SET @a = CAST('21' AS char(10)); + SELECT @a + end try + begin catch + select xact_state(); + end catch + select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +GO + +begin transaction; +GO + +exec error_mapping.ErrorHandling1; +GO + +if @@trancount > 0 select cast('Txn is not rolledback') +GO + +if @@trancount > 0 rollback transaction; +GO + + +set xact_abort ON; +GO + +exec error_mapping.ErrorHandling1; +GO + +if @@trancount > 0 select cast('Txn is not rolledback') +GO + +if @@trancount > 0 rollback transaction; +GO + +set xact_abort OFF; +GO + +drop procedure error_mapping.ErrorHandling1; +GO + +DROP TABLE t257; +GO + +-- Example 2 +GO + +SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); +GO + +begin transaction +GO + +SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); +GO + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO + +SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); +GO + +if (@@trancount > 0) select cast('txn is not rolledback' as text) else select cast('txn is rolledback' as text) +GO + +if (@@trancount > 0) rollback tran +GO + +SET XACT_ABORT OFF; +GO + +create procedure error_mapping.ErrorHandling1 as +begin + begin try + select 1 + SELECT DATEPART(xyz, CAST('2012-12-12' AS date)); + end try + begin catch + select xact_state(); + end catch + select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text) +GO + +begin transaction; +GO + +exec error_mapping.ErrorHandling1; +GO + +if @@trancount > 0 select cast('txn is not rolledback' as text) +GO + +if @@trancount > 0 rollback tran; +GO + +set xact_abort ON; +GO + +begin transaction; +GO + +exec error_mapping.ErrorHandling1; +GO + +if @@trancount > 0 select cast('txn is not rolledback' as text) +GO + +if @@trancount > 0 rollback tran; +GO + +set xact_abort OFF; +GO + +drop procedure error_mapping.ErrorHandling1; +GO + +DROP SCHEMA error_mapping; +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/authentication/TestAuth.txt b/contrib/test/JDBC/input/authentication/TestAuth.txt new file mode 100644 index 00000000000..91a6c1dcd13 --- /dev/null +++ b/contrib/test/JDBC/input/authentication/TestAuth.txt @@ -0,0 +1,13 @@ +#database name, username and password should not exceed 128 characters +java_auth#!#database|-|11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +java_auth#!#database|-|111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 +java_auth#!#user|-|11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +java_auth#!#user|-|111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 +#not sure why any password is accepted during authentication through cloud desktop +#This test should throw error but from cloud desktop a connection is successfully established +#java_auth#!#password|-|11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +java_auth#!#password|-|111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 +java_auth#!#others|-|packetSize=0 +java_auth#!#others|-|packetSize=-1 +java_auth#!#others|-|packetSize=4096 +java_auth#!#database|-|test1 SELECT 1 diff --git a/contrib/test/JDBC/input/babel_collation.sql b/contrib/test/JDBC/input/babel_collation.sql new file mode 100644 index 00000000000..1e45b68dc6a --- /dev/null +++ b/contrib/test/JDBC/input/babel_collation.sql @@ -0,0 +1,31 @@ +create table testing_collation (col varchar(20)); +go +insert into testing_collation values ('JONES'); +insert into testing_collation values ('jones'); +insert into testing_collation values ('Jones'); +insert into testing_collation values ('JoNes'); +insert into testing_collation values ('JoNés'); +go +select * from testing_collation where col collate SQL_Latin1_General_CP1_CS_AS = 'JoNes'; +go + +select * from testing_collation where col collate SQL_Latin1_General_CP1_CI_AS = 'JoNes'; +go + +select * from testing_collation where col collate SQL_Latin1_General_CP1_CI_AI = 'JoNes'; +go + +-- all the currently supported TSQL collations +SELECT * from fn_helpcollations(); +go + +-- BABEL-1697 Collation and Codepage information for DMS +SELECT CAST( COLLATIONPROPERTY(Name, 'CodePage') AS INT) FROM fn_helpcollations() where Name = DATABASEPROPERTYEX('template1', 'Collation'); +go + +SELECT CAST( COLLATIONPROPERTY(Name, 'lcid') AS INT) FROM fn_helpcollations() where Name = DATABASEPROPERTYEX('template1', 'Collation'); +go + +-- clean up +drop table testing_collation; +go diff --git a/contrib/test/JDBC/input/babel_iif.sql b/contrib/test/JDBC/input/babel_iif.sql new file mode 100644 index 00000000000..b1b8b395676 --- /dev/null +++ b/contrib/test/JDBC/input/babel_iif.sql @@ -0,0 +1,31 @@ +select iif (2 < 1, cast('2020-10-20 09:00:00' as datetime), cast('2020-10-21' as date)); +go +select iif (2 > 1, cast('abc' as varchar(3)), cast('cba' as char(3))); +go +select iif (2 > 1, cast(3.14 as float), cast(31.4 as numeric(3, 1))); +go +select iif (2 > 1, cast(3.14 as float), cast(1 as int)); +go +select iif (2 > 1, cast('$123.123' as money), cast(3.14 as float)); +go +select iif (2 > 1, cast('2020-10-20 09:00:00' as datetime), cast('09:00:00' as time)); +go +select iif (2 > 1, cast(3.14 as float), cast('$123.123' as money)); +go + +-- Error, unknown literal cannot fit target type typinput func +select iif (2 > 1, 1, 'abc'); +go +-- Error, different categories +select iif (2 > 1, cast(1 as int), cast('abc' as varchar(3))); +go +select iif (2 > 1, cast(0 as bit), cast(1 as int)); +go + +-- Null handling +select iif (2 > 1, null, 0); +go +select iif (null, 1, 0); +go +select iif (null, null, null); +go diff --git a/contrib/test/JDBC/input/babel_isnull.mix b/contrib/test/JDBC/input/babel_isnull.mix new file mode 100644 index 00000000000..de22de53032 --- /dev/null +++ b/contrib/test/JDBC/input/babel_isnull.mix @@ -0,0 +1,124 @@ +-- tsql +CREATE TABLE test_numeric ( + num1 int, + num2 numeric +); +INSERT INTO test_numeric(num1, num2) +VALUES + (10, NULL), + (20, 2000), + (30, 3000), + (NULL, 4000), + (50, NULL); +GO + +-- psql currentSchema=master_dbo,public +SELECT * FROM test_numeric; +GO + +SELECT ISNULL(SUM(num1 - ISNULL(num2, 0)), 0) +FROM test_numeric +GO + +-- tsql +-- Prove that a user may replace null values with a specified value using ISNULL +SELECT ISNULL(SUM(num1 - ISNULL(num2, 0)), 0) +FROM test_numeric +GO + +DROP TABLE test_numeric +GO + +-- Test cases with timestamps and sub-queries inside ISNULL +CREATE TABLE test_timestamp ( + serial_no numeric, + ts datetime2, +) +GO + +INSERT INTO test_timestamp(serial_no, ts) +VALUES + (1, NULL), + (2, '2020-06-22 00:10:00'), + (3, '2020-07-23 00:10:00'), + (4, '2020-08-24 00:10:00'), + (5, NULL), + (6, '2020-09-25 00:10:00') +GO + +SELECT * FROM test_timestamp +GO + +SELECT ISNULL(ts, '2021-01-01 00:00:00') +FROM test_timestamp +GO + +CREATE TABLE default_time ( + ts datetime2 +) +GO + +INSERT INTO default_time(ts) VALUES('1970-01-01 00:00:00') +GO + +SELECT ISNULL(ts, (SELECT ts FROM default_time)) +FROM test_timestamp +GO + +DROP TABLE test_timestamp +GO +DROP TABLE default_time +GO + +-- Test cases with characters and casts +CREATE TABLE test_char ( + name varchar(20), + employee_type numeric, + age numeric +) +GO + +INSERT INTO test_char(name, employee_type, age) +VALUES + ('John', 1, 45), + (NULL, 0, 21), + ('Jake', 3, 33), + ('Jack', 1, 64), + ('Jane', NULL, 51) +GO + +SELECT * FROM test_char +GO + +SELECT ISNULL(name, 'Unknown'), ISNULL(employee_type, 'N/A'), age +FROM test_char +GO + +DROP TABLE test_char +GO + +-- Test cases for incorrect number of arguments +CREATE TABLE test_args ( + word char(10) +) +GO +INSERT INTO test_args(word) +VALUES + ('hello'), + (NULL), + ('goodbye') +GO + +SELECT ISNULL() from test_args +GO +SELECT ISNULL(word) FROM test_args +GO +SELECT ISNULL(word, 'no', 'yes') FROM test_args +GO + +-- Test case for varbinary +SELECT ISNULL(null, CAST(0xfe AS VARBINARY)) +GO + +DROP TABLE test_args +GO diff --git a/contrib/test/JDBC/input/babel_money.sql b/contrib/test/JDBC/input/babel_money.sql new file mode 100644 index 00000000000..661c16587c4 --- /dev/null +++ b/contrib/test/JDBC/input/babel_money.sql @@ -0,0 +1,199 @@ +SELECT set_config('extra_float_digits', '0', 'false') +go + +-- test money operators return type money +create table t1(a money, b smallmoney); +insert into t1 values (1.1234, 2.1234); +insert into t1 values (2.5678, 3.5678); +insert into t1 values (4.9012, 5.9012); +go + +select * from t1 order by a; +go + +-- test implicit casting for money +create table t2(a money, b smallmoney); +insert into t2 values (CAST( '1.1234' AS CHAR(10)), CAST( '2.1234' AS CHAR(10))); +insert into t2 values (CAST( '$2.56789' AS VARCHAR), CAST( '$3.56789' AS VARCHAR)); +insert into t2 values (CAST( '¥4.91' AS TEXT), CAST( '¥5.91' AS TEXT)); +insert into t2 values (CAST( '0006.' AS TEXT), CAST( '0000' AS TEXT)); +go + +select * from t2 order by a; +go + +select sum(a), sum(b) from t1; +go + +select cast(pg_typeof(sum(a)) AS VARCHAR(10)), cast(pg_typeof(sum(b)) AS VARCHAR(10)) from t1; +go + +select avg(a), avg(b) from t1; +go + +select cast(pg_typeof(avg(a)) AS VARCHAR(10)), cast(pg_typeof(avg(b)) AS VARCHAR(10)) from t1; +go + +select a+b from t1 order by a; +go + +select cast(pg_typeof(a+b) AS VARCHAR(10)) from t1 order by a; +go + +select b-a from t1 order by a; +go + +select cast(pg_typeof(b-a) AS VARCHAR(10)) from t1 order by a; +go + +select a*b from t1 order by a; +go + +select cast(pg_typeof(a*b) AS VARCHAR(10)) from t1 order by a; +go + +select a/b from t1 order by a; +go + +select cast(pg_typeof(a/b) AS VARCHAR(10)) from t1 order by a; +go + +drop table t1, t2; + +-- BABEL-598 Money type as procedure parameter should work without explicit cast +create table employees(pers_id int, fname nvarchar(20), lname nvarchar(30), sal money); +go + +create procedure p_employee_select +as +begin + select * from employees +end; +go + +create procedure p_employee_insert +@pers_id int, @fname nvarchar(20), @lname nvarchar(30), @sal money +as +begin + insert into employees values (@pers_id, @fname, @lname, @sal) +end; +go + +-- test const 123.1234 and 200 are valid MONEY inputs for the procedure without explicit cast +execute p_employee_insert @pers_id=1, @fname='John', @lname='Johnson', @sal=123.1234; +execute p_employee_insert @pers_id=1, @fname='Adam', @lname='Smith', @sal=200; +go + +execute p_employee_select; +go + +drop procedure p_employee_select; +drop procedure p_employee_insert; +drop table employees; +go + +-- BABEL-920 +-- Test operations(e.g. +,-,*,/) between fixeddecimal(money/smallmoney) and int8(bigint) +select CAST(2.56 as bigint) + CAST(3.60 as money); +go +select CAST(3.60 as money) + CAST(2.56 as bigint); +go +select CAST(2.56 as bigint) - CAST(3.60 as money); +go +select CAST(3.60 as money) - CAST(2.56 as bigint); +go +select CAST(2.56 as bigint) * CAST(3.60 as money); +go +select CAST(3.60 as money) * CAST(2.56 as bigint); +go +select CAST(2.56 as bigint) / CAST(3.60 as money); +go +select CAST(3.60 as money) / CAST(2.56 as bigint); +go + +select CAST(2.56 as bigint) + CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) + CAST(2.56 as bigint); +go +select CAST(2.56 as bigint) - CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) - CAST(2.56 as bigint); +go +select CAST(2.56 as bigint) * CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) * CAST(2.56 as bigint); +go +-- select CAST(2.56 as bigint) / CAST(3.60 as smallmoney); -> see BABEL-977 +-- go +select CAST(3.60 as smallmoney) / CAST(2.56 as bigint); +go + +-- Test operations(e.g. +,-,*,/) between fixeddecimal(money/smallmoney) and int4(int) +select CAST(2.56 as int) + CAST(3.60 as money); +go +select CAST(3.60 as money) + CAST(2.56 as int); +go +select CAST(2.56 as int) - CAST(3.60 as money); +go +select CAST(3.60 as money) - CAST(2.56 as int); +go +select CAST(2.56 as int) * CAST(3.60 as money); +go +select CAST(3.60 as money) * CAST(2.56 as int); +go +select CAST(2.56 as int) / CAST(3.60 as money); +go +select CAST(3.60 as money) / CAST(2.56 as int); +go + +select CAST(2.56 as int) + CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) + CAST(2.56 as int); +go +select CAST(2.56 as int) - CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) - CAST(2.56 as int); +go +select CAST(2.56 as int) * CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) * CAST(2.56 as int); +go +-- select CAST(2.56 as int) / CAST(3.60 as smallmoney); -> see BABEL-977 +-- go +select CAST(3.60 as smallmoney) / CAST(2.56 as int); +go + +-- Test operations(e.g. +,-,*,/) between fixeddecimal(money/smallmoney) and int2(smallint) +select CAST(2.56 as smallint) + CAST(3.60 as money); +go +select CAST(3.60 as money) + CAST(2.56 as smallint); +go +select CAST(2.56 as smallint) - CAST(3.60 as money); +go +select CAST(3.60 as money) - CAST(2.56 as smallint); +go +select CAST(2.56 as smallint) * CAST(3.60 as money); +go +select CAST(3.60 as money) * CAST(2.56 as smallint); +go +select CAST(2.56 as smallint) / CAST(3.60 as money); +go +select CAST(3.60 as money) / CAST(2.56 as smallint); +go + +select CAST(2.56 as smallint) + CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) + CAST(2.56 as smallint); +go +select CAST(2.56 as smallint) - CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) - CAST(2.56 as smallint); +go +select CAST(2.56 as smallint) * CAST(3.60 as smallmoney); +go +select CAST(3.60 as smallmoney) * CAST(2.56 as smallint); +go +-- select CAST(2.56 as smallint) / CAST(3.60 as smallmoney); -> see BABEL-977 +-- go +select CAST(3.60 as smallmoney) / CAST(2.56 as smallint); +go diff --git a/contrib/test/JDBC/input/babel_numeric.sql b/contrib/test/JDBC/input/babel_numeric.sql new file mode 100644 index 00000000000..2dab904ac1c --- /dev/null +++ b/contrib/test/JDBC/input/babel_numeric.sql @@ -0,0 +1,63 @@ +-- Test numeric in cast function +select cast(1.123 as numeric(38, 10)); +go +select cast(1.123 as numeric(39, 10)); +go + +-- Test decimal in cast function +select cast(1.123 as decimal(38, 10)); +go +select cast(1.123 as decimal(39, 10)); +go + +-- Test dec in cast function +select cast(1.123 as dec(38, 10)); +go +select cast(1.123 as dec(39, 10)); +go + +-- Test numeric in create table +create table t1 (col numeric(38,37)); +drop table t1; +go + +create table t1 (col numeric(39, 37)); +go + +-- Test decimal in create table +create table t1 (col decimal(38,37)); +drop table t1; +go + +create table t1 (col decimal(39, 37)); +go + +-- Test dec in create table +create table t1 (col decimal(38,37)); +drop table t1; +go + +create table t1 (col decimal(39, 37)); +go + +-- Test default precision and scale is set to 18, 0 +create table t1 (col numeric); +insert into t1 values (1.2); +insert into t1 values (123456789012345678); +select * from t1; +go +insert into t1 values (1234567890123456789); +select * from t1; +go + +drop table t1; +go + +-- Test default scale is set to 0 if only precision is specified +create table t1 (col numeric(4)); +insert into t1 values (1.2); +select * from t1; +go + +drop table t1; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/babel_operators.sql b/contrib/test/JDBC/input/babel_operators.sql new file mode 100644 index 00000000000..62dda38355c --- /dev/null +++ b/contrib/test/JDBC/input/babel_operators.sql @@ -0,0 +1,58 @@ +-- test adding with varbinary +SELECT (123 + 0x42); +SELECT (0x42 + 123); +GO + +-- test subtracting with varbinary +SELECT (123 - 0x42); +SELECT (0x42 - 123); +GO + +-- test multiplication with varbinary +SELECT (123 * CAST(123 AS varbinary(4))); +SELECT (CAST(123 AS varbinary(4)) * 123); +GO + +-- test division with varbinary +SELECT (12345 / CAST(12 AS varbinary(4))); +SELECT (CAST(12345 AS varbinary(4)) / 12); +GO + +-- test & operator with varbinary +SELECT (CAST(123 AS varbinary(1)) & 21); +SELECT (CAST(123 AS varbinary(2)) & 321); +SELECT (CAST(12345 AS varbinary(4)) & 54321); +GO + +SELECT (CAST(9876543210 AS BIGINT) & CAST(1234567890 AS varbinary(8))); +SELECT (543210 & CAST(12345 AS varbinary(4))); +SELECT (CAST(321 AS smallint) & CAST(123 AS varbinary(2))); +SELECT (CAST(12 AS tinyint) & CAST(21 AS varbinary(1))); +GO + +-- test | operator with varbinary +SELECT (CAST(123 AS varbinary(1)) | 21); +SELECT (CAST(123 AS varbinary(2)) | 321); +SELECT (CAST(12345 AS varbinary(4)) | 54321); +GO + +SELECT (CAST(9876543210 AS BIGINT) | CAST(1234567890 AS varbinary(8))); +SELECT (543210 | CAST(12345 AS varbinary(4))); +SELECT (CAST(321 AS smallint) | CAST(123 AS varbinary(2))); +SELECT (CAST(12 AS tinyint) | CAST(21 AS varbinary(1))); +GO + +-- test ^ operator with varbinary +SELECT (17 ^ 5); +GO + +SELECT (CAST(123 AS varbinary(1)) ^ 21); +SELECT (CAST(123 AS varbinary(2)) ^ 321); +SELECT (CAST(12345 AS varbinary(4)) ^ 54321); +GO + +SELECT (CAST(9876543210 AS BIGINT) ^ CAST(1234567890 AS varbinary(8))); +SELECT (543210 ^ CAST(12345 AS varbinary(4))); +SELECT (CAST(321 AS smallint) ^ CAST(123 AS varbinary(2))); +SELECT (CAST(12 AS tinyint) ^ CAST(21 AS varbinary(1))); +GO diff --git a/contrib/test/JDBC/input/babel_print.sql b/contrib/test/JDBC/input/babel_print.sql new file mode 100644 index 00000000000..dce20955c06 --- /dev/null +++ b/contrib/test/JDBC/input/babel_print.sql @@ -0,0 +1,39 @@ +-- Test PRINT with T-sql procedures -- +-- Printing a pre-defined text +CREATE PROCEDURE tsql_print_text AS + PRINT 'Pre-defined text for tsql_print_text' +GO +EXEC tsql_print_text +GO + +-- Printing a user input text +CREATE PROCEDURE tsql_print_message(@message varchar(50)) AS +BEGIN + PRINT @message +END; +GO +EXEC tsql_print_message 'Testing message for tsql_print_message' +GO + +-- Printing a pre-defined and a user input text +CREATE PROCEDURE tsql_print_message_and_text(@message varchar(50)) AS +BEGIN + PRINT 'Pre-defined text for tsql_print_message_and_text. User input: '+ @message +END +GO +EXEC tsql_print_message_and_text 'Testing message for tsql_print_message_and_text' +GO + +-- Making a call to another function that prints +CREATE PROCEDURE tsql_print_function AS + EXECUTE tsql_print_text +GO +EXEC tsql_print_function +GO + +-- Cleanup -- +DROP PROCEDURE tsql_print_text; +DROP PROCEDURE tsql_print_message; +DROP PROCEDURE tsql_print_message_and_text; +DROP PROCEDURE tsql_print_function; +GO diff --git a/contrib/test/JDBC/input/babel_smalldatetime.sql b/contrib/test/JDBC/input/babel_smalldatetime.sql new file mode 100644 index 00000000000..390b4587710 --- /dev/null +++ b/contrib/test/JDBC/input/babel_smalldatetime.sql @@ -0,0 +1,102 @@ +-- Testing rounding behaviour when inserting into the table +create table smalldatetime_testing ( sm smalldatetime ); +INSERT INTO smalldatetime_testing VALUES('23:40:29.998'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:29.998'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:29.998'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:29.999'); +INSERT INTO smalldatetime_testing VALUES('1992-05-23 23:40:30.000'); +INSERT INTO smalldatetime_testing VALUES('2002-05-23 23:41:29.998'); +INSERT INTO smalldatetime_testing VALUES('2002-05-23 23:41:29.999'); +INSERT INTO smalldatetime_testing VALUES('2002-05-23 23:41:30.000'); +INSERT INTO smalldatetime_testing VALUES('2000-01-01 00:00:29.998'); +INSERT INTO smalldatetime_testing VALUES('2000-01-01 00:00:29.999'); +INSERT INTO smalldatetime_testing VALUES('1999-12-31 23:59:30.000'); +INSERT INTO smalldatetime_testing VALUES('1999-12-31 23:59:29.999'); +INSERT INTO smalldatetime_testing VALUES('1999-12-31 23:59:29.998'); +select * from smalldatetime_testing; +go + +-- Test comparision with datetime/smalldatetime/date +select * from smalldatetime_testing where sm >= cast('2000-01-01 00:00:59' as smalldatetime); +select * from smalldatetime_testing where sm >= cast('1992-05-23 23:40:00' as datetime) + and sm < cast('1992-05-23 23:41:00' as datetime); +select * from smalldatetime_testing where sm < cast(cast('1992-05-24' as date) as smalldatetime); +go + +-- Test rounding for 23:59:59 +SELECT CAST('1992-05-09 23:59:59' AS SMALLDATETIME); +SELECT CAST('2002-05-09 23:59:59' AS SMALLDATETIME); +SELECT CAST('1999-12-31 23:59:59' AS SMALLDATETIME); +go + +-- Test type cast to/from other time formats +-- Cast to smalldatetime +select CAST(CAST('00:00:00.234' AS time) AS smalldatetime); +select CAST(CAST('01:02:03.456' AS time) AS smalldatetime); +select CAST(CAST('2020-03-15' AS date) AS smalldatetime); +select CAST(CAST('2020-03-15' AS datetime) AS smalldatetime); +select CAST(CAST('2010-07-08 23:59:29.998' AS datetime) AS smalldatetime); +select CAST(CAST('1980-07-08 23:59:29.123456 +8:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('2010-07-08 23:59:29.123456 +8:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('1980-07-08 23:59:29.123456 -8:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('2010-07-08 23:59:29.123456 -8:00' AS datetimeoffset) AS smalldatetime); +go +-- Cast from smalldatetime +select CAST(CAST('2010-07-08' AS smalldatetime) AS time); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS time); +select CAST(CAST('2010-07-08 23:59:31.998' AS smalldatetime) AS time); +select CAST(CAST('1980-07-08 23:59:29.998' AS smalldatetime) AS time); +select CAST(CAST('1980-07-08 23:59:31.998' AS smalldatetime) AS time); +select CAST(CAST('2020-03-15' AS smalldatetime) AS date); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS date); +select CAST(CAST('2010-07-08 23:59:30.000' AS smalldatetime) AS date); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS datetime); +select CAST(CAST('1992-07-08 23:59:29.998' AS smalldatetime) AS datetime); +select CAST(CAST('2010-07-08 23:59:29.998' AS smalldatetime) AS datetimeoffset); +select CAST(CAST('1990-07-08 23:59:29.998' AS smalldatetime) AS datetimeoffset); +go + +-- Test smalldatetime value ranges +select cast('1900-01-01' as smalldatetime); +select cast('2079-06-06' as smalldatetime); +select cast('1899-12-31 23:59:29.999' as smalldatetime); +select cast('2079-06-06 23:59:29.998' as smalldatetime); +select CAST(CAST('1899-12-31 23:59:30.000' AS datetime) AS smalldatetime); +select CAST(CAST('1899-12-31 23:59:30.000 +0:00' AS datetimeoffset) AS smalldatetime); +select CAST(CAST('2079-06-06 23:59:30.000 +1:00' AS datetimeoffset) AS smalldatetime); +select cast('1899-12-31' as smalldatetime); -- out of range +select cast('2079-06-07' as smalldatetime); -- out of range +select cast('2079-06-06 23:59:29.999' as smalldatetime); -- out of range +select CAST(CAST('2099-03-15' AS date) AS smalldatetime); -- out of range +select CAST(CAST('1800-03-15 23:59:29.998' AS datetime) AS smalldatetime);-- out of range +select CAST(CAST('2099-03-15 23:59:29.998' AS datetime) AS smalldatetime);-- out of range +select CAST(CAST('1899-12-31 23:59:30.000 +1:00' AS datetimeoffset) AS smalldatetime);-- out of range +select CAST(CAST('2099-03-15 23:59:29.998 +6:00' AS datetimeoffset) AS smalldatetime);-- out of range +go + +-- Test smalldatetime default value +create table t1 (a smalldatetime, b int); +insert into t1 (b) values (1); +select a from t1 where b = 1; +go + +-- Test smalldatetime as parameter for time related functions +select day(cast('2002-05-23 23:41:29.998' as smalldatetime)); +select month(cast('2002-05-23 23:41:29.998' as smalldatetime)); +select year(cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(quarter, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(hour, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(dayofyear, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datepart(second, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datename(year, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datename(dw, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select datename(month, cast('2002-05-23 23:41:29.998' as smalldatetime)); +select dateadd(second, 56, cast('2016-12-26 23:29:29' as smalldatetime)); +select dateadd(minute, 56, cast('2016-12-26 23:29:29' as smalldatetime)); +select dateadd(year, 150, cast('2016-12-26 23:29:29' as smalldatetime)); -- Expect error +go + +-- Clean up +drop table smalldatetime_testing; +drop table t1; +go diff --git a/contrib/test/JDBC/input/babel_top.sql b/contrib/test/JDBC/input/babel_top.sql new file mode 100644 index 00000000000..9382c9d8c6b --- /dev/null +++ b/contrib/test/JDBC/input/babel_top.sql @@ -0,0 +1,40 @@ +-- +-- Tests for TOP clause +-- + +create table students (fname varchar(10), lname varchar(10), score double precision) +go + +insert into students (fname, lname, score) +values + ('John', 'Doe', 72.5), + ('Jane', 'Smith', 88), + ('Jill', 'Johnson', 98), + ('Jack', 'Green', 67), + ('Jennifer', 'Ross', 75.7), + ('Jacob', 'Brown', 95.2) +go + +select top 3 * from students +go + +select top 4 score from students +go + +select top 3 from students +go + +-- test top bigint +select top 2147483648 * from students +go + +-- test top 100 percent +select top 100 percent * from students +go + +select top 100.00 percent lname from students +go + +-- cleanup +drop table students +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/babel_trigger.sql b/contrib/test/JDBC/input/babel_trigger.sql new file mode 100644 index 00000000000..fcfc72c2d9d --- /dev/null +++ b/contrib/test/JDBC/input/babel_trigger.sql @@ -0,0 +1,329 @@ +-- a simple test +create table testing1(col nvarchar(60)) +GO + +create trigger notify on testing1 after insert +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Muffler' +GO + +drop trigger notify +GO + +-- test drop trigger if exists +create trigger notify on testing1 after insert +as +begin + SELECT 'trigger invoked' +end +GO + +drop trigger if exists notify +GO + +drop trigger if exists notify +GO + +-- test comma separator +create trigger notify on testing1 after insert, delete +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Apple' +GO + +delete from testing1 where col = N'Apple' +GO + +drop trigger notify +GO + +-- test inserted and deleted transition tables +CREATE TABLE products( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL +) +GO + +CREATE TABLE product_audits( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL, + operation CHAR(3) NOT NULL, + CHECK(operation = 'INS' or operation='DEL') +) +GO + +CREATE TRIGGER trg_product_audit +ON products +AFTER INSERT +AS +BEGIN + INSERT INTO product_audits( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price, + operation + ) + SELECT + i.product_id, + product_name, + brand_id, + category_id, + model_year, + i.list_price, + 'INS' + FROM + inserted i +END +GO + +INSERT INTO products( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price +) +VALUES ( + 1, + 'Test product', + 1, + 1, + 2018, + 599 +) +GO + +SELECT * FROM PRODUCT_AUDITS +GO + +drop trigger trg_product_audit +GO + +-- clean up +drop table testing1 +GO +drop table product_audits +GO +drop table products +GO + +-- CARRY OUT THE SAME TESTS WITH THE FOR KEYWORD -- + +-- a simple test +create table testing1(col nvarchar(60)) +GO + +create trigger notify on testing1 for insert +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Muffler' +GO + +drop trigger notify +GO + +-- test drop trigger if exists +create trigger notify on testing1 for insert +as +begin + SELECT 'trigger invoked' +end +GO + +drop trigger if exists notify +GO + +drop trigger if exists notify +GO + +-- test comma separator +create trigger notify on testing1 for insert, delete +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Apple' +GO + +delete from testing1 where col = N'Apple' +GO + +drop trigger notify +GO + +-- test inserted and deleted transition tables +CREATE TABLE products( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL +) +GO + +CREATE TABLE product_audits( + product_id INT NOT NULL, + product_name VARCHAR(255) NOT NULL, + brand_id INT NOT NULL, + category_id INT NOT NULL, + model_year SMALLINT NOT NULL, + list_price DEC(10,2) NOT NULL, + operation CHAR(3) NOT NULL, + CHECK(operation = 'INS' or operation='DEL') +) +GO + +CREATE TRIGGER trg_product_audit +ON products +FOR INSERT +AS +BEGIN + INSERT INTO product_audits( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price, + operation + ) + SELECT + i.product_id, + product_name, + brand_id, + category_id, + model_year, + i.list_price, + 'INS' + FROM + inserted i +END +GO + +INSERT INTO products( + product_id, + product_name, + brand_id, + category_id, + model_year, + list_price +) +VALUES ( + 1, + 'Test product', + 1, + 1, + 2018, + 599 +) +GO + +SELECT * FROM PRODUCT_AUDITS +GO + +drop trigger trg_product_audit +GO + +-- Test drop trigger without table name -- +-- First, test that triggers must have unique names +create trigger notify on testing1 after insert +as +begin + SELECT 'trigger invoked' +end +GO + +create table testing2(col nvarchar(60)) +GO + +create trigger notify on testing2 after insert +as +begin + SELECT 'trigger invoked' +end +GO + +drop table testing2 +GO + +-- Now, test that drop trigger works without tablename +drop trigger notify +GO + +-- Test that drop trigger statement on non-existent trigger throws error +drop trigger notify +GO +drop trigger if exists notify +GO + +-- Test that dropping a table with triggers defined on it succeeds +create table testTbl(colA int not null primary key, colB varchar(20)) +GO + +create trigger trig1 on testTbl after insert +as +begin + SELECT 'trigger invoked' +end +GO + +create trigger trig2 on testTbl after insert +as +begin + SELECT 'trigger2 invoked' +end +GO + +drop table testTbl +GO + +-- Test 'NOT FOR REPLICATION' syntax +create trigger notify on testing1 after insert +NOT FOR REPLICATION +as +begin + SELECT 'trigger invoked' +end +GO + +insert into testing1 (col) select N'Muffler' +GO + +drop trigger notify +GO + +-- clean up +drop table testing1 +GO +drop table product_audits +GO +drop table products +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/babel_varbinary.sql b/contrib/test/JDBC/input/babel_varbinary.sql new file mode 100644 index 00000000000..fc4c541b021 --- /dev/null +++ b/contrib/test/JDBC/input/babel_varbinary.sql @@ -0,0 +1,86 @@ +-- [BABEL-448] Test support of unquoted hexadecimal string input +select 0x1a2b3c4f; +go +select 0X1A2B3C4F; +go +select pg_typeof(0X1A2B3C4F); +go + +-- BABEL-631 Test odd number of hex digigts is allowed +select 0xF; +go +select 0x1; +go +select 0x0; +go +select 0x1F1; +go + +-- invalid hex input +select 0x1G2A; +go + +-- test insert of hex string +create table t1(a varbinary(8), b binary(8)); +insert into t1 values(0x1a2b3c4f, cast('1a2b3c4f' as binary(8))); +insert into t1 values(cast('1a2b3c4f' as varbinary(8)), cast('1a2b3c4f' as binary(8))); +go + +-- 0x1a2b3c4f and '1a2b3c4f' are different as varbinary +select * from t1; +go + +-- test bitwise operators on hex string and int +select 0x1F & 10; +go +select 10 & 0x1F; +go +select 0x1F | 10; +go +select 10 | 0x1F; +go +select 0x1F ^ 10; +go +select 10 ^ 0x1F; +go +select 0x1F * 10; +go +select 10 * 0x1F; +go +select 0x1F / 10; +go +select 100 / 0x1F; +go + +-- division by 0 +select 0x1F / 0; +go +select 10 / 0x00; +go + +-- test hex string in procedure +create procedure test_hex_bitop as +begin + select 0x1A2B3C4F ^ 101; +end; +go + +execute test_hex_bitop; +go + +create procedure test_hex_insert as +begin + insert into t1 values(0x1f, cast('1f' as binary(2))); +end; +go + +execute test_hex_insert; +go +select * from t1; +go + +-- clean up +drop table t1; +drop procedure test_hex_bitop; +drop procedure test_hex_insert; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/cursors/BABEL-1390.txt b/contrib/test/JDBC/input/cursors/BABEL-1390.txt new file mode 100644 index 00000000000..b99d1986b2c --- /dev/null +++ b/contrib/test/JDBC/input/cursors/BABEL-1390.txt @@ -0,0 +1,19 @@ +# Setup a table with some data +CREATE TABLE updatable_cursor_table(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO updatable_cursor_table values(0, 0, 0, 0, 0) +INSERT INTO updatable_cursor_table values(NULL, NULL, NULL, NULL, NULL) +INSERT INTO updatable_cursor_table values(1, 2, 3, 4, 1) +INSERT INTO updatable_cursor_table values(211234, 9780, 891372401, 56, 1) + +# Try opening an updatable cursor using JDBC API +# SET escape_hatch_session_settings to ignore CURSOR_CLOSE_ON_COMMIT +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +cursor#!#open#!#SELECT * FROM updatable_cursor_table#!#CONCUR_UPDATABLE#!#CLOSE_CURSORS_AT_COMMIT + +# Try opening an updatable (i.e. dynamic) cursor using SQL Batch +DECLARE updatable_cursor CURSOR DYNAMIC FOR SELECT a FROM updatable_cursor_table; OPEN updatable_cursor; CLOSE updatable_cursor; DEALLOCATE updatable_cursor; + +# Drop table +DROP TABLE updatable_cursor_table +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; + diff --git a/contrib/test/JDBC/input/cursors/TestCursorFetchNext.txt b/contrib/test/JDBC/input/cursors/TestCursorFetchNext.txt new file mode 100644 index 00000000000..22ffd004d35 --- /dev/null +++ b/contrib/test/JDBC/input/cursors/TestCursorFetchNext.txt @@ -0,0 +1,89 @@ +CREATE TABLE test_cursors_fetch_next(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO test_cursors_fetch_next values(0, 0, 0, 0, 0) +INSERT INTO test_cursors_fetch_next values(NULL, NULL, NULL, NULL, NULL) +INSERT INTO test_cursors_fetch_next values(1, 2, 3, 4, 1) +INSERT INTO test_cursors_fetch_next values(211234, 9780, 891372401, 56, 1) +# SET escape_hatch_session_settings to ignore CURSOR_CLOSE_ON_COMMIT +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +cursor#!#open#!#SELECT * FROM test_cursors_fetch_next#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#next +cursor#!#fetch#!#abs#!#2 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursors_fetch_next + +CREATE TABLE test_cursors_fetch_next(a CHAR(30), b VARCHAR(30), c NCHAR(30), d NVARCHAR(30)); +INSERT INTO test_cursors_fetch_next values(' ', ' ', ' ', ' ') +INSERT INTO test_cursors_fetch_next values(NULL, NULL, NULL, NULL) +INSERT INTO test_cursors_fetch_next values('hello', 'from the', N'server', N'side 😆') +INSERT INTO test_cursors_fetch_next values('Its', 'always', N'day', N'1') +cursor#!#open#!#SELECT * FROM test_cursors_fetch_next#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#abs#!#3 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursors_fetch_next + +CREATE TABLE test_cursors_fetch_next(a DATE, b DATETIME, c SMALLDATETIME); +INSERT INTO test_cursors_fetch_next values('2000-12-13', '1900-02-28 23:59:59.989', '2000-12-13 12:58:23') +INSERT INTO test_cursors_fetch_next values(NULL, NULL, NULL) +INSERT INTO test_cursors_fetch_next values('1997-05-07', '1900-02-28 11:23:17.895', '2000-12-13 10:23:44') +INSERT INTO test_cursors_fetch_next values('1876-08-07', '1980-02-05 16:11:45.215', '1987-10-01 07:55:24') +cursor#!#open#!#SELECT * FROM test_cursors_fetch_next#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#first +cursor#!#fetch#!#abs#!#4 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursors_fetch_next + +CREATE TABLE test_cursors_fetch_next(a FLOAT, b REAL, c MONEY, d SMALLMONEY); +INSERT INTO test_cursors_fetch_next values(0, 0, '$0', '$0') +INSERT INTO test_cursors_fetch_next values(NULL, NULL, NULL, NULL) +INSERT INTO test_cursors_fetch_next values(241.7832, 1214.691236, '62,514.00', '690.817') +INSERT INTO test_cursors_fetch_next values('32546', '980.709', '1,988,232.08', '$86,798') +cursor#!#open#!#SELECT * FROM test_cursors_fetch_next#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#next +cursor#!#fetch#!#abs#!#2 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursors_fetch_next +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'strict'; diff --git a/contrib/test/JDBC/input/cursors/TestCursorPrepExecFetchNext.txt b/contrib/test/JDBC/input/cursors/TestCursorPrepExecFetchNext.txt new file mode 100644 index 00000000000..e2627d990c2 --- /dev/null +++ b/contrib/test/JDBC/input/cursors/TestCursorPrepExecFetchNext.txt @@ -0,0 +1,97 @@ +CREATE TABLE test_cursor_prep_exec_fetch_next(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO test_cursor_prep_exec_fetch_next values(0, 0, 0, 0, 0) +INSERT INTO test_cursor_prep_exec_fetch_next values(NULL, NULL, NULL, NULL, NULL) +INSERT INTO test_cursor_prep_exec_fetch_next values(1, 2, 3, 4, 1) +INSERT INTO test_cursor_prep_exec_fetch_next values(211234, 9780, 891372401, 56, 1) + +# SET escape_hatch_session_settings to ignore CURSOR_CLOSE_ON_COMMIT +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_session_settings', 'ignore'; +cursor#!#open#!#prepst#!#SELECT * FROM test_cursor_prep_exec_fetch_next WHERE a > @a #!#INT|-|a|-|-2#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#next +cursor#!#fetch#!#abs#!#2 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#close +cursor#!#open#!#prepst#!#exec#!#INT|-|a|-|0#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#HOLD_CURSORS_OVER_COMMIT +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursor_prep_exec_fetch_next + +CREATE TABLE test_cursor_prep_exec_fetch_next(a CHAR(30), b VARCHAR(30), c NCHAR(30), d NVARCHAR(30)); +INSERT INTO test_cursor_prep_exec_fetch_next values(' ', ' ', ' ', ' ') +INSERT INTO test_cursor_prep_exec_fetch_next values(NULL, NULL, NULL, NULL) +INSERT INTO test_cursor_prep_exec_fetch_next values('hello', 'from the', N'server', N'side 😆') +INSERT INTO test_cursor_prep_exec_fetch_next values('Its', 'always', N'day', N'1') +cursor#!#open#!#prepst#!#SELECT * FROM test_cursor_prep_exec_fetch_next WHERE a <> @a #!#CHAR|-|a|-|hello#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#abs#!#3 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#close +cursor#!#open#!#prepst#!#exec#!#CHAR|-|a|-|Its#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursor_prep_exec_fetch_next + +CREATE TABLE test_cursor_prep_exec_fetch_next(a DATE, b DATETIME, c SMALLDATETIME); +INSERT INTO test_cursor_prep_exec_fetch_next values('2000-12-13', '1900-02-28 23:59:59.989', '2000-12-13 12:58:23') +INSERT INTO test_cursor_prep_exec_fetch_next values(NULL, NULL, NULL) +INSERT INTO test_cursor_prep_exec_fetch_next values('1997-05-07', '1900-02-28 11:23:17.895', '2000-12-13 10:23:44') +INSERT INTO test_cursor_prep_exec_fetch_next values('1876-08-07', '1980-02-05 16:11:45.215', '1987-10-01 07:55:24') +cursor#!#open#!#prepst#!#SELECT * FROM test_cursor_prep_exec_fetch_next WHERE b > @b #!#DATETIME|-|b|-|1753-01-01 00:00:00.000#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#first +cursor#!#fetch#!#abs#!#2 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#close +cursor#!#open#!#prepst#!#exec#!#DATETIME|-|b|-|1947-01-01 11:23:17.374#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursor_prep_exec_fetch_next + +CREATE TABLE test_cursor_prep_exec_fetch_next(a FLOAT, b REAL, c MONEY, d SMALLMONEY); +INSERT INTO test_cursor_prep_exec_fetch_next values(0, 0, '$0', '$0') +INSERT INTO test_cursor_prep_exec_fetch_next values(NULL, NULL, NULL, NULL) +INSERT INTO test_cursor_prep_exec_fetch_next values(241.7832, 1214.691236, '62,514.00', '690.817') +INSERT INTO test_cursor_prep_exec_fetch_next values('32546', '980.709', '1,988,232.08', '$86,798') +cursor#!#open#!#prepst#!#SELECT * FROM test_cursor_prep_exec_fetch_next WHERE b > @b #!#REAL|-|b|-|12.0834#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#next +cursor#!#fetch#!#abs#!#3 +cursor#!#fetch#!#next +cursor#!#fetch#!#first +cursor#!#fetch#!#next +cursor#!#close +cursor#!#open#!#prepst#!#exec#!#REAL|-|b|-|1000.241#!#TYPE_SCROLL_INSENSITIVE#!#CONCUR_READ_ONLY#!#CLOSE_CURSORS_AT_COMMIT +cursor#!#fetch#!#last +cursor#!#fetch#!#next +#cursor#!#fetch#!#prev +#cursor#!#fetch#!#next +#cursor#!#fetch#!#beforefirst +#cursor#!#fetch#!#next +#cursor#!#fetch#!#afterlast +#cursor#!#fetch#!#next +cursor#!#close +DROP TABLE test_cursor_prep_exec_fetch_next diff --git a/contrib/test/JDBC/input/datatypes/TestBIT.txt b/contrib/test/JDBC/input/datatypes/TestBIT.txt new file mode 100644 index 00000000000..8424eadd597 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestBIT.txt @@ -0,0 +1,16 @@ +CREATE TABLE BIT_dt (a BIT) +prepst#!# INSERT INTO BIT_dt(a) values(@a) #!#BIT|-|a|-|false +prepst#!#exec#!#BIT|-|a|-|true +#next two lines are not allowed +#prepst#!#exec#!#BIT|-|a|-|0 +#prepst#!#exec#!#BIT|-|a|-|1 +prepst#!#exec#!#BIT|-|a|-| +SELECT * FROM BIT_dt; +INSERT INTO BIT_dt(a) values(1) +INSERT INTO BIT_dt(a) values(0) +#next two lines are not allowed +#INSERT INTO BIT_dt(a) values(false) +#INSERT INTO BIT_dt(a) values(true) +INSERT INTO BIT_dt(a) values(NULL) +SELECT * FROM BIT_dt; +DROP TABLE BIT_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestBigInt.txt b/contrib/test/JDBC/input/datatypes/TestBigInt.txt new file mode 100644 index 00000000000..ef1451f78e8 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestBigInt.txt @@ -0,0 +1,24 @@ +CREATE TABLE BIGINT_dt (a BIGINT) +prepst#!# INSERT INTO BIGINT_dt(a) values(@a) #!#BIGINT|-|a|-|0 +prepst#!#exec#!#BIGINT|-|a|-|-10 +prepst#!#exec#!#BIGINT|-|a|-|122100 +prepst#!#exec#!#BIGINT|-|a|-|0001202 +prepst#!#exec#!#BIGINT|-|a|-|-024112329 +prepst#!#exec#!#BIGINT|-|a|-|-0000000000000000002 +prepst#!#exec#!#BIGINT|-|a|-|0000000000000000086 +prepst#!#exec#!#BIGINT|-|a|-|-9223372036854775808 +prepst#!#exec#!#BIGINT|-|a|-|9223372036854775807 +prepst#!#exec#!#BIGINT|-|a|-| +SELECT * FROM BIGINT_dt; +INSERT INTO BIGINT_dt(a) values(0) +INSERT INTO BIGINT_dt(a) values(-120) +INSERT INTO BIGINT_dt(a) values(00100) +INSERT INTO BIGINT_dt(a) values(-004) +INSERT INTO BIGINT_dt(a) values(-012245532534) +INSERT INTO BIGINT_dt(a) values(-0000000000000000002) +INSERT INTO BIGINT_dt(a) values(0000000000000000086) +INSERT INTO BIGINT_dt(a) values(-9223372036854775808) +INSERT INTO BIGINT_dt(a) values(9223372036854775807) +INSERT INTO BIGINT_dt(a) values(NULL) +SELECT * FROM BIGINT_dt; +DROP TABLE BIGINT_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestBinary.txt b/contrib/test/JDBC/input/datatypes/TestBinary.txt new file mode 100644 index 00000000000..0d2ffb12071 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestBinary.txt @@ -0,0 +1,29 @@ +CREATE TABLE BINARY_dt(a BINARY(8), b VARBINARY(10)); +#inserting random values +INSERT INTO BINARY_dt(a, b) values (1234, 12345); +INSERT INTO BINARY_dt(a, b) values (NULL, NULL); +#INSERT INTO BINARY_dt(a, b) values (0x31323334, 0x3132333435); +SELECT * FROM BINARY_dt +#prepst#!# INSERT INTO BINARY_dt(a, b) values(@a, @b) #!#binary|-|a|-|1234#!#varbinary|-|b|-|12345 +DROP TABLE BINARY_dt + + +CREATE TABLE BINARY_dt(a VARBINARY(max)); +INSERT INTO BINARY_dt(a) values (NULL); +SELECT * FROM BINARY_dt; +DROP TABLE BINARY_dt; + +create table BINARY_dt (a VARBINARY(max), b int, c int, d int, e int ,f int, g int, h int, i int); +insert into BINARY_dt (a,b,c,d,e,f,g,h,i) values (NULL,1,2,3,4,5,6,7,8); +select * from BINARY_dt; +drop table BINARY_dt; + +CREATE TABLE BINARY_dt(a BINARY(8), b VARBINARY(10)); +INSERT INTO BINARY_dt(a, b) values (1234, 12345); +prepst#!# INSERT INTO BINARY_dt(a, b) values(@a, @b) #!#binary|-|a|-|1234#!#varbinary|-|b|-|12345 +prepst#!#exec#!#binary|-|a|-|123456789#!#varbinary|-|b|-|12345 +prepst#!#exec#!#binary|-|a|-|1234#!#varbinary|-|b|-|123456789 +SELECT * FROM BINARY_dt; +DROP TABLE BINARY_dt; + + diff --git a/contrib/test/JDBC/input/datatypes/TestChar.txt b/contrib/test/JDBC/input/datatypes/TestChar.txt new file mode 100644 index 00000000000..c982a1e7265 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestChar.txt @@ -0,0 +1,16 @@ +CREATE TABLE CHAR_dt (a CHAR(24), b NCHAR(24)) +prepst#!# INSERT INTO CHAR_dt(a, b) values(@a, @b) #!#CHAR|-|a|-|Dipesh#!#NCHAR|-|b|-|Dhameliya +prepst#!#exec#!#CHAR|-|a|-| Dipesh #!#NCHAR|-|b|-| Dhameliya +prepst#!#exec#!#CHAR|-|a|-| D#!#NCHAR|-|b|-| 🤣😃 +prepst#!#exec#!#CHAR|-|a|-| #!#NCHAR|-|b|-| +prepst#!#exec#!#CHAR|-|a|-| #!#NCHAR|-|b|-|😊😋😎😍😅😆 +prepst#!#exec#!#CHAR|-|a|-|#!#NCHAR|-|b|-| +SELECT * FROM CHAR_dt; +INSERT INTO CHAR_dt(a,b) values('Dipesh','Dhameliya') +INSERT INTO CHAR_dt(a,b) values(' Dipesh',' Dhameliya') +#INSERT INTO CHAR_dt(a,b) values(' D',N' 🤣😃') +INSERT INTO CHAR_dt(a,b) values(' ',' ') +#INSERT INTO CHAR_dt(a,b) values(' ',N'😊😋😎😍😅😆') +INSERT INTO CHAR_dt(a,b) values(NULL,NULL) +SELECT * FROM CHAR_dt; +DROP TABLE CHAR_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestDate.txt b/contrib/test/JDBC/input/datatypes/TestDate.txt new file mode 100644 index 00000000000..5af2f499b2f --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestDate.txt @@ -0,0 +1,14 @@ +CREATE TABLE DATE_dt (a DATE) +prepst#!# INSERT INTO DATE_dt(a) values(@a) #!#DATE|-|a|-|2000-12-13 +prepst#!#exec#!#DATE|-|a|-|2000-02-28 +prepst#!#exec#!#DATE|-|a|-|0001-01-01 +prepst#!#exec#!#DATE|-|a|-|9999-12-31 +prepst#!#exec#!#DATE|-|a|-| +SELECT * FROM DATE_dt; +INSERT INTO DATE_dt(a) values('2000-12-13') +INSERT INTO DATE_dt(a) values('1900-02-28') +INSERT INTO DATE_dt(a) values('0001-01-01') +INSERT INTO DATE_dt(a) values('9999-12-31') +INSERT INTO DATE_dt(a) values(NULL) +SELECT * FROM DATE_dt; +DROP TABLE DATE_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestDatetime.txt b/contrib/test/JDBC/input/datatypes/TestDatetime.txt new file mode 100644 index 00000000000..a5df9ff4d9e --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestDatetime.txt @@ -0,0 +1,36 @@ +CREATE TABLE DATETIME_dt (a DATETIME) +prepst#!# INSERT INTO DATETIME_dt(a) values(@a) #!#DATETIME|-|a|-|2000-12-13 12:58:23.123 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.989 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.990 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.991 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.992 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.993 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.994 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.995 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.996 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.997 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.998 +prepst#!#exec#!#DATETIME|-|a|-|2000-02-28 23:59:59.999 +prepst#!#exec#!#DATETIME|-|a|-|1900-02-28 23:59:59.989 +prepst#!#exec#!#DATETIME|-|a|-|1753-01-01 00:00:00.000 +prepst#!#exec#!#DATETIME|-|a|-|9999-12-31 23:59:59.997 +prepst#!#exec#!#DATETIME|-|a|-| +SELECT * FROM DATETIME_dt; +INSERT INTO DATETIME_dt(a) values('2000-12-13 12:58:23.123') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.989') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.990') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.991') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.992') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.993') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.994') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.995') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.996') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.997') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.998') +INSERT INTO DATETIME_dt(a) values('1900-02-28 23:59:59.999') +INSERT INTO DATETIME_dt(a) values('2000-02-28 23:59:59.989') +INSERT INTO DATETIME_dt(a) values('1753-01-01 00:00:00.000') +INSERT INTO DATETIME_dt(a) values('9999-12-31 23:59:59.997') +INSERT INTO DATETIME_dt(a) values(NULL) +SELECT * FROM DATETIME_dt; +DROP TABLE DATETIME_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestDatetime2.txt b/contrib/test/JDBC/input/datatypes/TestDatetime2.txt new file mode 100644 index 00000000000..2a6062abb25 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestDatetime2.txt @@ -0,0 +1,15 @@ +Create table TestDatetime2(a Datetime2(6)) + +prepst#!# Insert into TestDatetime2 Values(@a) #!#Datetime2|-|a|-|2016-10-23 12:45:37.123|-|0 +prepst#!#exec#!#Datetime2|-|a|-|2016-10-23 12:45:37.123|-|3 +prepst#!#exec#!#Datetime2|-|a|-|2016-10-23 12:45:37.12|-|2 +prepst#!#exec#!#Datetime2|-|a|-|2016-10-23 12:45:37.1|-|1 +prepst#!#exec#!#Datetime2|-|a|-|2016-10-23 12:45:37.1234|-|4 +prepst#!#exec#!#Datetime2|-|a|-|2016-10-23 12:45:37.123456|-|6 +prepst#!#exec#!#Datetime2|-|a|-|2016-10-23 12:45:37.123456|-|5 +#prepst#!#exec#!#Datetime2|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Datetime2|-|a|-| + +select * from TestDatetime2 + +Drop table TestDatetime2 \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestDecimal.txt b/contrib/test/JDBC/input/datatypes/TestDecimal.txt new file mode 100644 index 00000000000..531fb023efe --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestDecimal.txt @@ -0,0 +1,180 @@ +CREATE TABLE decimal_table(num decimal(5, 2)); + +prepst#!#INSERT INTO decimal_table(num) VALUES(@a) #!#decimal|-|a|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a|-|-123|-|9|-|2 + +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + +CREATE TABLE decimal_table(num decimal(38, 3)); + +prepst#!#INSERT INTO decimal_table(num) VALUES(@a1) #!#decimal|-|a1|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a1|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a1|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a1|-|-123|-|9|-|2 + +prepst#!#exec#!#decimal|-|a1|-|2147483647|-|10|-|0 +prepst#!#exec#!#decimal|-|a1|-|-2147483647|-|10|-|0 +prepst#!#exec#!#decimal|-|a1|-|2147483646|-|10|-|0 +prepst#!#exec#!#decimal|-|a1|-|-2147483646|-|10|-|0 +prepst#!#exec#!#decimal|-|a1|-|2147483648|-|10|-|0 +prepst#!#exec#!#decimal|-|a1|-|-2147483648|-|10|-|0 + +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + +# JIRA 507, WORKING FOR BABEL +#CREATE TABLE decimal_table(num decimal(39, 20)); + +CREATE TABLE decimal_table(num decimal(38, 20)); + +prepst#!#INSERT INTO decimal_table(num) VALUES(@a2) #!#decimal|-|a2|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a2|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a2|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a2|-|-123|-|9|-|2 +prepst#!#exec#!#decimal|-|a2|-|2147483647|-|10|-|0 +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + +CREATE TABLE decimal_table(num decimal(38, 20)); +prepst#!#INSERT INTO decimal_table(num) VALUES(@a3) #!#decimal|-|a3|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a3|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a3|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a3|-|-123|-|9|-|2 +prepst#!#exec#!#decimal|-|a3|-|2147483647|-|10|-|0 +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + +CREATE TABLE decimal_table(num decimal(38, 21)); +prepst#!#INSERT INTO decimal_table(num) VALUES(@a4) #!#decimal|-|a4|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a4|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a4|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a4|-|-123|-|9|-|2 +prepst#!#exec#!#decimal|-|a4|-|2147483647|-|10|-|0 +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + +CREATE TABLE decimal_table(num decimal(38, 22)); +prepst#!#INSERT INTO decimal_table(num) VALUES(@a5) #!#decimal|-|a5|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a5|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a5|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a5|-|-123|-|9|-|2 +prepst#!#exec#!#decimal|-|a5|-|2147483647|-|10|-|0 +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + +CREATE TABLE decimal_table(num decimal(38, 23)); +prepst#!#INSERT INTO decimal_table(num) VALUES(@a6) #!#decimal|-|a6|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a6|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a6|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a6|-|-123|-|9|-|2 +prepst#!#exec#!#decimal|-|a6|-|2147483647|-|10|-|0 +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + +CREATE TABLE decimal_table(num decimal(38, 25)); +prepst#!#INSERT INTO decimal_table(num) VALUES(@a7) #!#decimal|-|a7|-|3|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|123|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|123.45|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-||-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|-123.456|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|-123.4|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|-123|-|5|-|2 +#prepst#!#exec#!#decimal|-|a7|-|0|-|5|-|2 +prepst#!#exec#!#decimal|-|a7|-|-1|-|3|-|2 +prepst#!#exec#!#decimal|-|a7|-|-123|-|9|-|2 +prepst#!#exec#!#decimal|-|a7|-|247483647|-|10|-|0 +prepst#!#exec#!#decimal|-|a7|-|-247483647|-|10|-|0 +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; + + +CREATE TABLE decimal_table(num decimal(38, 25)); + +insert into decimal_table values (2147483647) +insert into decimal_table values (-2147483647) + +insert into decimal_table values (2147483646) +insert into decimal_table values (-2147483646) + +insert into decimal_table values (2147483648) +insert into decimal_table values (-2147483648) + +#insert into decimal_table values(123456789123456789.1234567891234567891234567); +#insert into decimal_table values(1234567891234567891.1234567891234567891234567); +#insert into decimal_table values(123456789123456789.12345678912345678912345678); +insert into decimal_table values(0.0); +insert into decimal_table values(0.0000000000000000000000000); +insert into decimal_table values(0.00000000000000000000000000); + +SELECT * FROM decimal_table; + +DROP TABLE decimal_table; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestFloat.txt b/contrib/test/JDBC/input/datatypes/TestFloat.txt new file mode 100644 index 00000000000..37609dbf959 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestFloat.txt @@ -0,0 +1,24 @@ +CREATE TABLE FLOAT_dt (a FLOAT) +prepst#!# INSERT INTO FLOAT_dt(a) values(@a) #!#FLOAT|-|a|-|0 +prepst#!#exec#!#FLOAT|-|a|-|1.050 +prepst#!#exec#!#FLOAT|-|a|-|01.05 +prepst#!#exec#!#FLOAT|-|a|-|0001202 +prepst#!#exec#!#FLOAT|-|a|-|-024112329 +prepst#!#exec#!#FLOAT|-|a|-|-02.5 +prepst#!#exec#!#FLOAT|-|a|-|0000000000000000086 +prepst#!#exec#!#FLOAT|-|a|-|-1.79E+308 +prepst#!#exec#!#FLOAT|-|a|-|1.79E+308 +prepst#!#exec#!#FLOAT|-|a|-| +SELECT * FROM FLOAT_dt; +INSERT INTO FLOAT_dt(a) values(0) +INSERT INTO FLOAT_dt(a) values(1.050) +INSERT INTO FLOAT_dt(a) values(01.05) +INSERT INTO FLOAT_dt(a) values(-004) +INSERT INTO FLOAT_dt(a) values(-0122455324.5) +INSERT INTO FLOAT_dt(a) values(-000002) +INSERT INTO FLOAT_dt(a) values(0000000000000000086) +INSERT INTO FLOAT_dt(a) values(-1.79E+308) +INSERT INTO FLOAT_dt(a) values(1.79E+308) +INSERT INTO FLOAT_dt(a) values(NULL) +SELECT * FROM FLOAT_dt; +DROP TABLE FLOAT_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestInt.txt b/contrib/test/JDBC/input/datatypes/TestInt.txt new file mode 100644 index 00000000000..cd37ee7174d --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestInt.txt @@ -0,0 +1,29 @@ +CREATE TABLE INT_dt(a INT); +prepst#!#INSERT INTO INT_dt(a) values(@a) #!#INT|-|a|-|0 +prepst#!#exec#!#INT|-|a|-|-10 +prepst#!#exec#!#INT|-|a|-|10 +prepst#!#exec#!#INT|-|a|-|-12234 +prepst#!#exec#!#INT|-|a|-|22 +prepst#!#exec#!#INT|-|a|-|003 +prepst#!#exec#!#INT|-|a|-|9864 +prepst#!#exec#!#INT|-|a|-|-01625 +prepst#!#exec#!#INT|-|a|-|-2147483648 +prepst#!#exec#!#INT|-|a|-|2147483647 +prepst#!#exec#!#INT|-|a|-| + +SELECT * FROM INT_dt; + +INSERT INTO INT_dt(a) values(0) +INSERT INTO INT_dt(a) values(-10) +INSERT INTO INT_dt(a) values(10) +INSERT INTO INT_dt(a) values(-12345) +INSERT INTO INT_dt(a) values(22) +INSERT INTO INT_dt(a) values(004) +INSERT INTO INT_dt(a) values(-01645) +INSERT INTO INT_dt(a) values(-2147483648) +INSERT INTO INT_dt(a) values(2147483647) +INSERT INTO INT_dt(a) values(NULL) + +SELECT * FROM INT_dt; + +DROP TABLE INT_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestMoney.txt b/contrib/test/JDBC/input/datatypes/TestMoney.txt new file mode 100644 index 00000000000..39dddea4a40 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestMoney.txt @@ -0,0 +1,30 @@ +CREATE TABLE money_dt(a smallmoney, b money); + +prepst#!#INSERT INTO money_dt(a, b) VALUES (@a, @b) #!#smallmoney|-|a|-|100.5#!#money|-|b|-|10.05 +prepst#!#exec#!#smallmoney|-|a|-|10#!#money|-|b|-|10 +prepst#!#exec#!#smallmoney|-|a|-|-10.05 #!#money|-|b|-|-10.0 +prepst#!#exec#!#smallmoney|-|a|-|-214748.3648#!#money|-|b|-|-922337203685477.5808 +prepst#!#exec#!#smallmoney|-|a|-|214748.3647#!#money|-|b|-|22337203685477.5807 +prepst#!#exec#!#smallmoney|-|a|-|214748.3647#!#money|-|b|-|22337203685477.5807 +prepst#!#exec#!#smallmoney|-|a|-|-214,748.3648#!#money|-|b|-|-922,337,203,685,477.5808 +prepst#!#exec#!#smallmoney|-|a|-|214,748.3647#!#money|-|b|-|922,337,203,685,477.5807 +prepst#!#exec#!#smallmoney|-|a|-|214,748.3647#!#money|-|b|-|922,337,203,685,477.5807 +prepst#!#exec#!#smallmoney|-|a|-|#!#money|-|b|-| + +SELECT * FROM money_dt; + +INSERT INTO money_dt(a,b) values(100.5,10.05); +INSERT INTO money_dt(a,b) values('$10','$10'); +INSERT INTO money_dt(a,b) values('-10.05','-10.0'); +INSERT INTO money_dt(a,b) values('10.05','10.0'); +INSERT INTO money_dt(a,b) values(-214748.3648,'-10.0'); +INSERT INTO money_dt(a,b) values(14748.3647,-922337203685477.5808); +INSERT INTO money_dt(a,b) values('$214748.3647','$22337203685477.5807'); +INSERT INTO money_dt(a,b) values('-214,748.3648','-922,337,203,685,477.5808'); +INSERT INTO money_dt(a,b) values('214,748.3647','922,337,203,685,477.5807'); +INSERT INTO money_dt(a,b) values('$214,748.3647','$922,337,203,685,477.5807'); +INSERT INTO money_dt(a,b) values(NULL,NULL); + + +SELECT * FROM money_dt; +DROP TABLE money_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestNumeric.txt b/contrib/test/JDBC/input/datatypes/TestNumeric.txt new file mode 100644 index 00000000000..966ac435876 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestNumeric.txt @@ -0,0 +1,228 @@ +CREATE TABLE numeric_table(num numeric(5, 2)); + +prepst#!#INSERT INTO numeric_table(num) VALUES(@a) #!#numeric|-|a|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a|-|-123|-|9|-|2 + +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 3)); + +prepst#!#INSERT INTO numeric_table(num) VALUES(@a1) #!#numeric|-|a1|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a1|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a1|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a1|-|-123|-|9|-|2 + +prepst#!#exec#!#numeric|-|a1|-|2147483647|-|10|-|0 +prepst#!#exec#!#numeric|-|a1|-|-2147483647|-|10|-|0 +prepst#!#exec#!#numeric|-|a1|-|2147483646|-|10|-|0 +prepst#!#exec#!#numeric|-|a1|-|-2147483646|-|10|-|0 +prepst#!#exec#!#numeric|-|a1|-|2147483648|-|10|-|0 +prepst#!#exec#!#numeric|-|a1|-|-2147483648|-|10|-|0 + +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +# JIRA 507, WORKING FOR BABEL +#CREATE TABLE numeric_table(num numeric(39, 20)); + +CREATE TABLE numeric_table(num numeric(38, 20)); + +prepst#!#INSERT INTO numeric_table(num) VALUES(@a2) #!#numeric|-|a2|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a2|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a2|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a2|-|-123|-|9|-|2 +prepst#!#exec#!#numeric|-|a2|-|2147483647|-|10|-|0 +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 20)); +prepst#!#INSERT INTO numeric_table(num) VALUES(@a3) #!#numeric|-|a3|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a3|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a3|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a3|-|-123|-|9|-|2 +prepst#!#exec#!#numeric|-|a3|-|2147483647|-|10|-|0 +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 21)); +prepst#!#INSERT INTO numeric_table(num) VALUES(@a4) #!#numeric|-|a4|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a4|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a4|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a4|-|-123|-|9|-|2 +prepst#!#exec#!#numeric|-|a4|-|2147483647|-|10|-|0 +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 22)); +prepst#!#INSERT INTO numeric_table(num) VALUES(@a5) #!#numeric|-|a5|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a5|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a5|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a5|-|-123|-|9|-|2 +prepst#!#exec#!#numeric|-|a5|-|2147483647|-|10|-|0 +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 23)); +prepst#!#INSERT INTO numeric_table(num) VALUES(@a6) #!#numeric|-|a6|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a6|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a6|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a6|-|-123|-|9|-|2 +prepst#!#exec#!#numeric|-|a6|-|2147483647|-|10|-|0 +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +CREATE TABLE numeric_table(num numeric(38, 25)); +prepst#!#INSERT INTO numeric_table(num) VALUES(@a7) #!#numeric|-|a7|-|3|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|123|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|123.45|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-||-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|-123.456|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|-123.4|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|-123|-|5|-|2 +#prepst#!#exec#!#numeric|-|a7|-|0|-|5|-|2 +prepst#!#exec#!#numeric|-|a7|-|-1|-|3|-|2 +prepst#!#exec#!#numeric|-|a7|-|-123|-|9|-|2 +prepst#!#exec#!#numeric|-|a7|-|247483647|-|10|-|0 +prepst#!#exec#!#numeric|-|a7|-|-247483647|-|10|-|0 +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + + +CREATE TABLE numeric_table(num numeric(38, 25)); + +insert into numeric_table values (2147483647) +insert into numeric_table values (-2147483647) + +insert into numeric_table values (2147483646) +insert into numeric_table values (-2147483646) + +insert into numeric_table values (2147483648) +insert into numeric_table values (-2147483648) + +#insert into numeric_table values(123456789123456789.1234567891234567891234567); +#insert into numeric_table values(1234567891234567891.1234567891234567891234567); +#insert into numeric_table values(123456789123456789.12345678912345678912345678); +insert into numeric_table values(0.0); +insert into numeric_table values(0.0000000000000000000000000); +insert into numeric_table values(0.00000000000000000000000000); + +SELECT * FROM numeric_table; + +DROP TABLE numeric_table; + +# BABEL-1459 +declare @numvar numeric(5,4); set @numvar=-0.010; SELECT @numvar as N'@var'; +declare @numvar numeric(4,4); set @numvar=-0.010; SELECT @numvar as N'@var'; +declare @numvar numeric(5,4); set @numvar=1.01; SELECT @numvar as N'@var'; +declare @numvar numeric(4,4); set @numvar=0.01; SELECT @numvar as N'@var'; +declare @numvar numeric(4,4); set @numvar=0; SELECT @numvar as N'@var'; + +# BABEL-2048 +CREATE TABLE babel_2048_t1(a int, b NUMERIC(19,4), c NUMERIC(20, 9), d FLOAT); +INSERT INTO babel_2048_t1 VALUES (1, 2.3, 3.123456789, 4.123456789); +select b - 1 from babel_2048_t1; +select b - a from babel_2048_t1; +select a - b from babel_2048_t1; +select a + b from babel_2048_t1; +select a * b from babel_2048_t1; +select a / b from babel_2048_t1; +select b / a from babel_2048_t1; +select b * NULL from babel_2048_t1; +select b / NULL from babel_2048_t1; +select b - NULL from babel_2048_t1; +select b + NULL from babel_2048_t1; +select SQRT(a / b) from babel_2048_t1; +select ROUND(a / b, 3) from babel_2048_t1; +select SQRT(7); +select ROUND(10.1234567, 5); + +# test operation between int and numeric(20, 9) +select a+c, a-c, a*c, a/c, a%c, a%NULL from babel_2048_t1; +# test operation between numeric and numeric +select b+c, b-c, b*c, b/c, b%c, b%NULL from babel_2048_t1; +# test operation between numeric and float +select c+d, c-d, c*d, c/d, c%d, c%NULL from babel_2048_t1; + +# test NULLIF with numeric args +select nullif(c, b) from babel_2048_t1; + +# test Coalesce with numeric args +select coalesce(NULL, NULL, b) from babel_2048_t1; + +# test Case expression with numeric args +INSERT INTO babel_2048_t1 VALUES (2, 3.7, 4.123456789, 5.123456789); +select case when a <= 1 then b - a when a > 1 then c - a end from babel_2048_t1; + +# test Max() and Min() with numeric args +select Max(c-b), Min(c-b) from babel_2048_t1; + +drop table babel_2048_t1; diff --git a/contrib/test/JDBC/input/datatypes/TestReal.txt b/contrib/test/JDBC/input/datatypes/TestReal.txt new file mode 100644 index 00000000000..5c05836a056 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestReal.txt @@ -0,0 +1,24 @@ +CREATE TABLE REAL_dt (a REAL) +prepst#!# INSERT INTO REAL_dt(a) values(@a) #!#REAL|-|a|-|0 +prepst#!#exec#!#REAL|-|a|-|1.050 +prepst#!#exec#!#REAL|-|a|-|01.05 +prepst#!#exec#!#REAL|-|a|-|0001202 +prepst#!#exec#!#REAL|-|a|-|-024112329 +prepst#!#exec#!#REAL|-|a|-|-02.5 +prepst#!#exec#!#REAL|-|a|-|0000000000000000086 +prepst#!#exec#!#REAL|-|a|-|-3.40E+38 +prepst#!#exec#!#REAL|-|a|-|3.40E+38 +prepst#!#exec#!#REAL|-|a|-| +SELECT * FROM REAL_dt; +INSERT INTO REAL_dt(a) values(0) +INSERT INTO REAL_dt(a) values(1.050) +INSERT INTO REAL_dt(a) values(01.05) +INSERT INTO REAL_dt(a) values(-004) +INSERT INTO REAL_dt(a) values(-0122455324.5) +INSERT INTO REAL_dt(a) values(-000002) +INSERT INTO REAL_dt(a) values(0000000000000000086) +INSERT INTO REAL_dt(a) values(-3.40E+38) +INSERT INTO REAL_dt(a) values(3.40E+38) +INSERT INTO REAL_dt(a) values(NULL) +SELECT * FROM REAL_dt; +DROP TABLE REAL_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestSmallDatetime.txt b/contrib/test/JDBC/input/datatypes/TestSmallDatetime.txt new file mode 100644 index 00000000000..748651c6a54 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestSmallDatetime.txt @@ -0,0 +1,35 @@ +CREATE TABLE SMALLDATETIME_dt (a SMALLDATETIME) +prepst#!# INSERT INTO SMALLDATETIME_dt(a) values(@a) #!#SMALLDATETIME|-|a|-|2000-12-13 12:58:23 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2007-05-08 12:35:29 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2007-05-08 12:35:30 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2007-05-08 12:59:59.998 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:59:59.999 +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-02-28 23:59:59.999 +#prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:29.999 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:30 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:29 +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-02-28 23:45:30 +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-02-28 23:45:29 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-12-13 12:58:30 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2000-02-28 23:45:29.998 +prepst#!#exec#!#SMALLDATETIME|-|a|-|1900-01-01 00:00:00 +prepst#!#exec#!#SMALLDATETIME|-|a|-|2079-06-06 23:59:29 +prepst#!#exec#!#SMALLDATETIME|-|a|-| +SELECT * FROM SMALLDATETIME_dt; +INSERT INTO SMALLDATETIME_dt(a) values('2000-12-13 12:58:23'); +INSERT INTO SMALLDATETIME_dt(a) values('2007-05-08 12:35:29'); +INSERT INTO SMALLDATETIME_dt(a) values('2007-05-08 12:35:30'); +INSERT INTO SMALLDATETIME_dt(a) values('2007-05-08 12:59:59.998'); +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:59:59.999'); +INSERT INTO SMALLDATETIME_dt(a) values('1900-02-28 23:59:59.999'); +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:29.999'); +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:30'); +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:29'); +INSERT INTO SMALLDATETIME_dt(a) values('1900-02-28 23:45:29'); +INSERT INTO SMALLDATETIME_dt(a) values('1900-12-13 12:58:30'); +INSERT INTO SMALLDATETIME_dt(a) values('2000-02-28 23:45:29.998'); +INSERT INTO SMALLDATETIME_dt(a) values('1900-01-01 00:00:00'); +INSERT INTO SMALLDATETIME_dt(a) values('2079-06-06 23:59:29'); +INSERT INTO SMALLDATETIME_dt(a) values(NULL); +SELECT * FROM SMALLDATETIME_dt; +DROP TABLE SMALLDATETIME_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestSmallInt.txt b/contrib/test/JDBC/input/datatypes/TestSmallInt.txt new file mode 100644 index 00000000000..79e6427345e --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestSmallInt.txt @@ -0,0 +1,24 @@ +CREATE TABLE SMALLINT_dt (a SMALLINT) +prepst#!# INSERT INTO SMALLINT_dt(a) values(@a) #!#SMALLINT|-|a|-|0 +prepst#!#exec#!#SMALLINT|-|a|-|-10 +prepst#!#exec#!#SMALLINT|-|a|-|100 +prepst#!#exec#!#SMALLINT|-|a|-|002 +prepst#!#exec#!#SMALLINT|-|a|-|-029 +prepst#!#exec#!#SMALLINT|-|a|-|-1234 +prepst#!#exec#!#SMALLINT|-|a|-|876 +prepst#!#exec#!#SMALLINT|-|a|-|-32768 +prepst#!#exec#!#SMALLINT|-|a|-|32767 +prepst#!#exec#!#SMALLINT|-|a|-| +SELECT * FROM SMALLINT_dt; +INSERT INTO SMALLINT_dt(a) values(0) +INSERT INTO SMALLINT_dt(a) values(-10) +INSERT INTO SMALLINT_dt(a) values(100) +INSERT INTO SMALLINT_dt(a) values(002) +INSERT INTO SMALLINT_dt(a) values(-029) +INSERT INTO SMALLINT_dt(a) values(-1234) +INSERT INTO SMALLINT_dt(a) values(876) +INSERT INTO SMALLINT_dt(a) values(-32768) +INSERT INTO SMALLINT_dt(a) values(32767) +INSERT INTO SMALLINT_dt(a) values(NULL) +SELECT * FROM SMALLINT_dt; +DROP TABLE SMALLINT_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestText.txt b/contrib/test/JDBC/input/datatypes/TestText.txt new file mode 100644 index 00000000000..30e90391ffa --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestText.txt @@ -0,0 +1,10 @@ +CREATE TABLE TEXT_dt (a text, b ntext) +#path to file should be with respect to root of test suite +prepst#!# INSERT INTO TEXT_dt(a, b) values(@a, @b) #!#TEXT|-|a|-|utils/sample.txt#!#NTEXT|-|b|-|utils/sample.txt +prepst#!#exec#!#TEXT|-|a|-|utils/blank.txt#!#NTEXT|-|b|-|utils/blank.txt +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-| +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-|utils/utf16.txt +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-|utils/emojisText.txt +prepst#!#exec#!#TEXT|-|a|-|#!#NTEXT|-|b|-|utils/devanagari.txt +SELECT * FROM TEXT_dt; +DROP TABLE TEXT_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestTime.txt b/contrib/test/JDBC/input/datatypes/TestTime.txt new file mode 100644 index 00000000000..fcb6805a487 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestTime.txt @@ -0,0 +1,104 @@ +Create table TestTime(a time(6)) + +prepst#!# Insert into TestTime Values(@a) #!#Time|-|a|-|12:45:37.123|-|0 +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Time|-|a|-| + +select * from TestTime + +Drop table TestTime + +Create table TestTime(a time(5)) + +prepst#!# Insert into TestTime Values(@a) #!#Time|-|a|-|12:45:37.123|-|0 +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Time|-|a|-| + +select * from TestTime + +Drop table TestTime + +Create table TestTime(a time(4)) + +prepst#!# Insert into TestTime Values(@a) #!#Time|-|a|-|12:45:37.123|-|0 +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Time|-|a|-| + +select * from TestTime + +Drop table TestTime + +Create table TestTime(a time(3)) + +prepst#!# Insert into TestTime Values(@a) #!#Time|-|a|-|12:45:37.123|-|0 +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Time|-|a|-| + +select * from TestTime + +Drop table TestTime + +Create table TestTime(a time(2)) + +prepst#!# Insert into TestTime Values(@a) #!#Time|-|a|-|12:45:37.123|-|0 +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Time|-|a|-| + +select * from TestTime + +Drop table TestTime + +Create table TestTime(a time(1)) + +prepst#!# Insert into TestTime Values(@a) #!#Time|-|a|-|12:45:37.123|-|0 +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Time|-|a|-| + +select * from TestTime + +Drop table TestTime + +Create table TestTime(a time(0)) + +prepst#!# Insert into TestTime Values(@a) #!#Time|-|a|-|12:45:37.123|-|0 +prepst#!#exec#!#Time|-|a|-|12:45:37.123|-|3 +prepst#!#exec#!#Time|-|a|-|12:45:37.12|-|2 +prepst#!#exec#!#Time|-|a|-|12:45:37.1|-|1 +prepst#!#exec#!#Time|-|a|-|12:45:37.1234|-|4 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|6 +prepst#!#exec#!#Time|-|a|-|12:45:37.123456|-|5 +prepst#!#exec#!#Time|-|a|-| + +select * from TestTime + +Drop table TestTime \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestTinyInt.txt b/contrib/test/JDBC/input/datatypes/TestTinyInt.txt new file mode 100644 index 00000000000..077492f45f4 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestTinyInt.txt @@ -0,0 +1,24 @@ +CREATE TABLE TINYINT_dt (a TINYINT) +prepst#!# INSERT INTO TINYINT_dt(a) values(@a) #!#TINYINT|-|a|-|0 +prepst#!#exec#!#TINYINT|-|a|-|-10 +prepst#!#exec#!#TINYINT|-|a|-|100 +prepst#!#exec#!#TINYINT|-|a|-|002 +prepst#!#exec#!#TINYINT|-|a|-|029 +prepst#!#exec#!#TINYINT|-|a|-|004 +prepst#!#exec#!#TINYINT|-|a|-|87 +prepst#!#exec#!#TINYINT|-|a|-|0 +prepst#!#exec#!#TINYINT|-|a|-|255 +prepst#!#exec#!#TINYINT|-|a|-| +SELECT * FROM TINYINT_dt; +INSERT INTO TINYINT_dt(a) values(0) +INSERT INTO TINYINT_dt(a) values(120) +INSERT INTO TINYINT_dt(a) values(100) +INSERT INTO TINYINT_dt(a) values(004) +INSERT INTO TINYINT_dt(a) values(0) +INSERT INTO TINYINT_dt(a) values(002) +INSERT INTO TINYINT_dt(a) values(86) +INSERT INTO TINYINT_dt(a) values(1000) +INSERT INTO TINYINT_dt(a) values(255) +INSERT INTO TINYINT_dt(a) values(NULL) +SELECT * FROM TINYINT_dt; +DROP TABLE TINYINT_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestUDD.txt b/contrib/test/JDBC/input/datatypes/TestUDD.txt new file mode 100644 index 00000000000..3469c3da351 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestUDD.txt @@ -0,0 +1,79 @@ +CREATE TYPE udd_varchar from varchar(15); +CREATE TYPE udd_nvarchar from nvarchar(15); +CREATE TYPE udd_int from int; +CREATE TYPE udd_char from char(25); +CREATE TYPE udd_nchar from nchar(20) NOT NULL; +CREATE TYPE udd_datetime from datetime; +CREATE TYPE udd_numeric from numeric(4,1); + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +CREATE TABLE udd_dt (a udd_varchar UNIQUE, b udd_nvarchar, c udd_int PRIMARY KEY, d udd_char DEFAULT 'Whoops!', e udd_nchar, f udd_datetime, g udd_numeric CHECK (g >= 103.5)) + +INSERT INTO udd_dt VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + +#passing a non unique value in column a +#throws error: duplicate key value violates unique constraint +INSERT INTO udd_dt VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + +#passing a non unique value in column c +#throws error: duplicate key value violates unique constraint +INSERT INTO udd_dt VALUES ('Banana', N'green', 1, 'Bangalore', N'Crying😭', '2007-01-14 23:34:23.749', 908.7); + +#passing a NULL value in column c +#throws error: null value in column "c" violates not-null constraint +INSERT INTO udd_dt VALUES ('Guava', N'yellow', NULL, 'Mumbai', N'Smirk😏', '1907-05-09 11:14:13.749', 245.6); + +#passing no value for column d +INSERT INTO udd_dt(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + +#passing a NULL value in column e +#throws error: domain udd_nchar does not allow null values +INSERT INTO udd_dt VALUES ('Kiwi', N'purple', 4, 'Kolkata', NULL, '1907-05-09 11:14:13.749', 874.0); + +#passing a value less than 103.5 in column g +#throws error: new row for relation "udd_dt" violates check constraint "udd_dt_g_check" +INSERT INTO udd_dt VALUES ('Grape', N'white', 5, 'Pune', N'Angry😠', '2000-02-28 23:59:59.989', 100.1); + +SELECT * FROM udd_dt; + +DROP TABLE udd_dt; + +CREATE TABLE udd_dt (a udd_varchar UNIQUE, b udd_nvarchar, c udd_int PRIMARY KEY, d udd_char DEFAULT 'Whoops!', e udd_nchar, f udd_datetime, g udd_numeric CHECK (g >= 103.5)) + +prepst#!#INSERT INTO udd_dt VALUES (@a, @b, @c, @d, @e, @f, @g)#!#varchar|-|a|-|Apple#!#nvarchar|-|b|-|red#!#int|-|c|-|1#!#char|-|d|-|Delhi#!#nchar|-|e|-|Sad😞#!#datetime|-|f|-|2000-12-13 12:58:23.123#!#numeric|-|g|-|123.1|-|4|-|1 + +#passing a non unique value in column a +#throws error: duplicate key value violates unique constraint +prepst#!#exec#!#varchar|-|a|-|Apple#!#nvarchar|-|b|-|blue#!#int|-|c|-|2#!#char|-|d|-|Chennai#!#nchar|-|e|-|Neutral😐#!#datetime|-|f|-|2006-11-11 22:47:23.128#!#numeric|-|g|-|512.4|-|4|-|1 + +#passing a non unique value in column c +#throws error: duplicate key value violates unique constraint +prepst#!#exec#!#varchar|-|a|-|Banana#!#nvarchar|-|b|-|green#!#int|-|c|-|1#!#char|-|d|-|Bangalore#!#nchar|-|e|-|Crying😭#!#datetime|-|f|-|2007-01-14 23:34:23.749#!#numeric|-|g|-|908.7|-|4|-|1 + +#passing a NULL value in column c +#throws error: null value in column "c" violates not-null constraint +prepst#!#exec#!#varchar|-|a|-|Guava#!#nvarchar|-|b|-|yellow#!#int|-|c|-|#!#char|-|d|-|Mumbai#!#nchar|-|e|-|Smirk😏'#!#datetime|-|f|-|1907-05-09 11:14:13.749#!#numeric|-|g|-|245.6|-|4|-|1 + +#passing a NULL value in column e +#throws error: domain udd_nchar does not allow null values +prepst#!#exec#!#varchar|-|a|-|Kiwi#!#nvarchar|-|b|-|purple#!#int|-|c|-|4#!#char|-|d|-|Kolkata#!#nchar|-|e|-|#!#datetime|-|f|-|1907-05-09 11:14:13.749#!#numeric|-|g|-|874.0|-|4|-|1 + +#passing a value less than 103.5 in column g +#throws error: new row for relation "udd_dt" violates check constraint "udd_dt_g_check" +prepst#!#exec#!#varchar|-|a|-|Grape#!#nvarchar|-|b|-|white#!#int|-|c|-|5#!#char|-|d|-|Pune#!#nchar|-|e|-|Angry😠#!#datetime|-|f|-|2000-02-28 23:59:59.989#!#numeric|-|g|-|100.1|-|4|-|1 + +#passing no value for column d +prepst#!#INSERT INTO udd_dt(a, b, c, e, f, g) VALUES (@a1, @b1, @c1, @e1, @f1, @g1)#!#varchar|-|a1|-|Orange#!#nvarchar|-|b1|-|#!#int|-|c1|-|5#!#nchar|-|e1|-|Happy😀#!#datetime|-|f1|-|1900-02-28 23:59:59.989#!#numeric|-|g1|-|342.5|-|4|-|1 + +SELECT * FROM udd_dt; + +DROP TABLE udd_dt; + +DROP TYPE udd_varchar +DROP TYPE udd_nvarchar +DROP TYPE udd_int +DROP TYPE udd_char +DROP TYPE udd_nchar +DROP TYPE udd_datetime +DROP TYPE udd_numeric +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; diff --git a/contrib/test/JDBC/input/datatypes/TestUniqueIdentifier.txt b/contrib/test/JDBC/input/datatypes/TestUniqueIdentifier.txt new file mode 100644 index 00000000000..b8a33fba24d --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestUniqueIdentifier.txt @@ -0,0 +1,34 @@ +CREATE TABLE uniqueidentifier_dt (a uniqueidentifier); + +INSERT INTO uniqueidentifier_dt VALUES ('51f178a6-53c7-472c-9be1-1c08942342d7') +INSERT INTO uniqueidentifier_dt VALUES ('dd8cb046-461d-411e-be40-d219252ce849') +INSERT INTO uniqueidentifier_dt VALUES ('b84ebcc9-c927-4cfe-b08e-dc7f25b5087c') +INSERT INTO uniqueidentifier_dt VALUES ('bab96bc8-60b9-40dd-b0de-c90a80f5739e') +INSERT INTO uniqueidentifier_dt VALUES ('d424fdef-1404-4bac-8289-c725b540f93d') +INSERT INTO uniqueidentifier_dt VALUES ('60aeaa5c-e272-4b17-bad0-c25710fd7a60') +INSERT INTO uniqueidentifier_dt VALUES ('253fb146-7e45-45ef-9d92-bbe14a8ad1b2') +INSERT INTO uniqueidentifier_dt VALUES ('dba2726c-2131-409f-aefa-5c8079571623') +INSERT INTO uniqueidentifier_dt VALUES ('b3400fa7-3a60-40ec-b40e-fc85a3eb262d') +INSERT INTO uniqueidentifier_dt VALUES ('851763b5-b068-42ae-88ec-764bfb0e5605') +INSERT INTO uniqueidentifier_dt VALUES (NULL) +SELECT * FROM uniqueidentifier_dt + +prepst#!#INSERT INTO uniqueidentifier_dt VALUES (@a)#!#uniqueidentifier|-|a|-|51f178a6-53c7-472c-9be1-1c08942342d7 +prepst#!#exec#!#uniqueidentifier|-|a|-|767392df-87d0-450d-9b24-85a86c02811d +prepst#!#exec#!#uniqueidentifier|-|a|-|9bcb5632-53c3-4695-b617-d9a7055813ce +prepst#!#exec#!#uniqueidentifier|-|a|-|ac81a140-f686-4259-9b90-dd46f10b355f +prepst#!#exec#!#uniqueidentifier|-|a|-|3d08372d-770c-48b9-9740-a667d036680e +prepst#!#exec#!#uniqueidentifier|-|a|-|518d52c7-4d79-4143-ab33-b3765689fdf4 +prepst#!#exec#!#uniqueidentifier|-|a|-|bc3fa456-7391-4060-b5d8-430048075cf4 +prepst#!#exec#!#uniqueidentifier|-|a|-|3b75b2dd-01b7-4958-9de7-f92410693547 +prepst#!#exec#!#uniqueidentifier|-|a|-|ce8af10a-2709-43b0-9e4e-a02753929d17 +prepst#!#exec#!#uniqueidentifier|-|a|-|5b7c2e8d-6d90-411d-8e19-9a81067e6f6c +prepst#!#exec#!#uniqueidentifier|-|a|-| +SELECT * FROM uniqueidentifier_dt; + +# to demonstrate the truncation of data when the value is too long +INSERT INTO uniqueidentifier_dt VALUES ('51f178a6-53c7-472c-9be1-1c08942342d7thisIsTooLong') +prepst#!#exec#!#uniqueidentifier|-|a|-|767392df-87d0-450d-9b24-85a86c02811dthisIsTooLong +SELECT * FROM uniqueidentifier_dt; + +DROP TABLE uniqueidentifier_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/datatypes/TestVarChar.txt b/contrib/test/JDBC/input/datatypes/TestVarChar.txt new file mode 100644 index 00000000000..c5337b3aba6 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestVarChar.txt @@ -0,0 +1,48 @@ +CREATE TABLE VARCHAR_dt (a VARCHAR(20), b NVARCHAR(24)) +prepst#!# INSERT INTO VARCHAR_dt(a, b) values(@a, @b) #!#VARCHAR|-|a|-|Dipesh#!#NVARCHAR|-|b|-|Dhameliya +prepst#!#exec#!#VARCHAR|-|a|-| Dipesh #!#NVARCHAR|-|b|-| Dhameliya +prepst#!#exec#!#VARCHAR|-|a|-| D#!#NVARCHAR|-|b|-| 🤣😃 +prepst#!#exec#!#VARCHAR|-|a|-| #!#NVARCHAR|-|b|-| +prepst#!#exec#!#VARCHAR|-|a|-|#!#NVARCHAR|-|b|-| +prepst#!#exec#!#VARCHAR|-|a|-|d#!#NVARCHAR|-|b|-|D +prepst#!#exec#!#VARCHAR|-|a|-|abcdefghijklmnopqrst#!#NVARCHAR|-|b|-|abcdefghijklmnopqrstuvwx +prepst#!#exec#!#VARCHAR|-|a|-|abcdefghijklmnopqrstu#!#NVARCHAR|-|b|-|abcdefghijklmnopqrstuvwxy +prepst#!#exec#!#VARCHAR|-|a|-| #!#NVARCHAR|-|b|-|😊😋😎😍😅😆 +prepst#!#exec#!#VARCHAR|-|a|-|#!#NVARCHAR|-|b|-| +SELECT * FROM VARCHAR_dt; +INSERT INTO VARCHAR_dt(a,b) values('Dipesh','Dhameliya') +INSERT INTO VARCHAR_dt(a,b) values(' Dipesh',' Dhameliya') +INSERT INTO VARCHAR_dt(a,b) values(' D',N' 🤣😃') +INSERT INTO VARCHAR_dt(a,b) values(' ',' ') +INSERT INTO VARCHAR_dt(a,b) values(' ',N'😊😋😎😍😅😆') +INSERT INTO VARCHAR_dt(a,b) values('','') +INSERT INTO VARCHAR_dt(a,b) values('d','D') +INSERT INTO VARCHAR_dt(a,b) values('abcdefghijklmnopqrst','abcdefghijklmnopqrstuvwx') +#INSERT INTO VARCHAR_dt(a,b) values('abcdefghijklmnopqrstu','abcdefghijklmnopqrstuvwxy') -- for this case, BABEL and SQL both will throw an error +INSERT INTO VARCHAR_dt(a,b) values(NULL,NULL) +SELECT * FROM VARCHAR_dt; +DROP TABLE VARCHAR_dt; + +CREATE TABLE VARCHAR_dt (a varchar(max), b nvarchar(max)); +INSERT INTO VARCHAR_dt values ('hello', N'hello😃'); + +#checking with string of length > 65535 +INSERT INTO VARCHAR_dt values ('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', N'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😃'); +INSERT INTO VARCHAR_dt values (NULL, NULL); +prepst#!#INSERT INTO VARCHAR_dt values(@a, @b)#!#varchar|-|a|-|hello#!#nvarchar|-|b|-|hello😃 + +#checking with string of length > 65535 via prep-exec +prepst#!#exec#!#varchar|-|a|-|5E3yMLSKs9f2gi7D5YnzWssgaxFjWPxx8aCS3F2rRGrtOX4AYNVGag0LuBXt5j8nEnxPVijkhpFY5OuPRSexMgZC83b8eVx1v0637CSetIMBFab7xz3bBKx41ELPBGSstz7kba5DajWRItW7Isk9QM7X0H4MNAIa49eR25U7qLUY7gm0j8sh5iKIvxW58bTVzMGM7Nz912oOQhgM5yMVEFbdnDKVqPNlnnBZJJVmzZ6u7RkHsoOwUDNprObG8ggQHsn0KLSg2YQZ8sDApFFvDJtDsNsisAYQWk07apBelFSRSUUsH1Dcj2jda06x6RWgaQO4137Ot9ysMn8zwnRjKY4JCB5l6Xq4olVZgzkoIat31B8d90HZJNr0ff0UMMRjF0bMvl32bsnGNwMG9bCKn3FhHCD6daOsZyjDUtk40Mui9DTsJeLmRoLkLLOnwL1L8EBeeR9G0TYPKawja0o8FMZCSmk5gJVbVTFzhfl72JDJikey4wJbrZUpMQZSkiw4O4QLVbR01AsdtxQmJVnl9BbMcj4KbhwPj00uALRY817aQTIfG3GyYfyJFuDNtVrynt4cMv1mu1p9qoM2u4fBo4bce1k5qATYbPKe0QPNfJm51ePbNTwZUOzRhJWBxHQz5Yc7u1YejKV3X0OON4gnVfamM5zVJWBgDVyUGm4Qlrux7KwjgkvK1gv9PiofjVX3BCFj8JrlFUYBms3OXQhhpbjUSftHrukQAeqXPbm9D68RR0DutD62WJuT7BOGaljWodv5LYfnSCwxZFNT65XQtuCzqgAL0n13wh0bl2yXNHe8JPMb8LxYn8Yua3KVs4pCEy7YrHwXmQjASOw9PVZwBtIGkfNYCW9ronmZS5v7blETt2XHbLDLSUqUSP7nhf32tMP8qnVM8zTy5XVz2zfyI9Jy58ctMWNlTW1uI9w9CCfCv8n22L7xZJq6WGfUebTIxsNi7PGiHGmCOLmuiYlLsEdBV4zFztmqtfRQwzNj8xLMx6uHpitRx2O3Pjctx5GaXdXJKtK2FNDxcxhnHoQruMBcM6dERui4dpYK0pLTXzSaash9DfaaOQDtyYxDuZoGwGpvVAwuEPHTn4b58Dg9Dh18DKhLOA0U6XDbgc14gT0V1kUlax1UMrtPdsesVUTM8TuHUVnUd5vBXO00mW9y6ILNDt6LafnZHVGLWt3QBKRMrPsyWJ3QiUy6PErrI3BRX8GC1UluMU1wpAKhcbYlG4ReTYhusIAjVATpWFMiQMI7MDg8MR24wGwUVmzlaJfl2E3puiWNRDAoLB5pAE7DJ7HISSfTZctDqXiJLt19BC5XVfDMkociSe28ypSQbYVsJNXRKQkCiYycptDKEUcKsI0q5YIThi7ED1ZZshRXe3gdkHixsRjsCroanaIfVh3BKdfIaVTCyECFrnlRc9Thg0svgajZ2UhDE5l90spJY3kRiP7vK5pJWDKo5CLEvRn232fnxqeGF6Ouh4X1sR4tBET9W5BUx3bT4NALnxzoZXHyNm41uQSwCxAWawLDE2nta8TxvbnFJFwccTV0T0SM6swBE2NigSWyZpALUnAh9wrFKl5s5npGv0IGOmeKyqgoMAf0b7OBaMqBATwB3675iFlMt3FkvetTbUuCLzHt9Vc3QJ5H7102kJVKy7vtPlU3kuNPItxUA9I85n3K62FDe0dGxleTWmztsw0jJPOKLluHOv9z4S916Ab0Qk6nR62KdOqtUlmj0L962n2Tg6yDo0bchTjjTSLnike0yDOLfcsFAuPafKpBludIs9rcloWaj9Yz2KeOefrOxbtEfC7rkkMXtyGJFZXO8KUg1m09xNUU2uVXQVyyGoRZMzxKEZHudfaeXxx9z9WvS20lwRwysuy0YnG0CMuQQuOydGtenSmdKhilD3m1VOIpkx8uPYswR2CC0Q4z9gA3RGiu2GPpU8nUAQw3QNzjepxXL1EDqiHk6505ya1obb9hzZ8RdAE1TkYSkOr96tU5AF44vURcYzbr14mhk9ccj8T17dNF8nlYMRvPXXU4uqfKXatQ2CTGM1s7drYhuEauUj4CE4c1zRscjQACspwvJG8BXugLrz8OFxlW7ghvWWs0GWC6r6N8E6gbQrnqp86YzIWLA1uEcOUHnAeqAv3vX3YS2BjXFgfWOHFhRjUHaAA9Lt8kC1l7Zo9NZILe4MNPyJ3qdpUxWvz1WqZ0JxxTLqpWoxQyyWWhPXSsZ4JNRxQ7SDbSThk9dheuri5APDtUdnTAUxuvixhAb8Hjy48CuFL504UWU40UWHLtCYqZlJADx3p7IPcoX4O9cDI9jdtO1dOQsHe3UB9JvyqTkAoiohH2K7KdpRdefJfSr5vt14QVdThnllrZWp7OQviutTouO9gXZWv1BmNVgprxbjJyBVcyKQYilIk1ejHqwWd41zq0fOkqkd6AwRMSb6htusEqzeGWBfhoJQRJ0UY2xa6GDmOgl8sOBhSFkeLY1cYfvFadVaOVoRNQtT1ekxpFQmXLYqcjKcKdOQB5FfRzzXJPBA8aFmiJh7PeZOhUFOGQH60tFBXYwLW2H6kyVh7ZZiLUdgAdN8uhU1OaH9mgPPEBSEhXCqRhj7TUJiZ90dJohv1FqWPTC9rc2yxPYrEsvmOAfA4f0wVVc1uny7jQ7K0At15uqkf0LjdX5TLDujz2MRPR8pZZh2G3eItksoixsyr4fgHTXH8RghRvDqRkTKzWWbjtl44c3PPYjRoKxZr48FgGXiMbsgMGuJqUCg7i4WS6zQlA3SDMUwAstISaZwsTO761RsvDAHHOfLY4z69xfajTTNEYprrkqh000Ce3CuJvL4890nGj9XtZBKWfqTMUe6D5FYUykJRFvKdLPbeWsGI4eQvZJuL22b6qJHhs6kkk7nItAnmiZtE1P5Q8020JupoxMdJHxFryvUKrxhaEduBS1qAIVhLgBgadwM6G2tSYwZZHRbPmKmPCWNIU3KEg3BO6TvoXFAWuieTr7C9GmZ2aW5FMOmNBjR0qDM135InITOsnPfPQodmUWlyklAmesWy2AA5S9sqmEXoIQbnsDNHPNhWC6rRaqxYzYfsW9TVWMFLKkYA5Rn7DaJkk7gA7LtPDyTHD5DbtJ2VyCPC1aTLUdpD1jIrksUw7XNgVkdZAlu31BHQVH8lQgHGrJYgRwhmxQMYEbaqhAwICRADGcSqBoFR6L83ZgpbRI3NVYzNndH0WKZ01txEwcNlurSJSICMkU363i15J1bMT7LCpRhgNqfaJCHygIXnCmHlKg81hgmVEFfeYQA2nslRFqz9o1fB09gjQgFug68taSbZxYAWZNz0pCiGEEdW1nyHkcy5dMzf8rAOQrZme49KBgdyDvVnbO9Dcj0ncfipvnUYaNMMtFBWHePJ1QK28lYTXEaHhFrzboEbfkUIgdv3dnv8MLo93I1PENotOcERvM9DGxkia36prCxwrwCJCCVHjzVuFQgjeWvBQskSkPv42t8bofztrOg6vmiYL6zpxXGlztwKnJmc0Cyl6bb8C6CvCYQuITcrBT0prPckU6EBTI4CDPur1MmgcJXeuH15CNbByWmGRZnA11OaY38mUqyuDbelDMUjrslu0GLT1ECC0QuPo91jkvsSpaCLglC3D8IqqN5rJu2IDv3DZyfpT0Q4hoNCPnhvtJC9wbgxq4irSqd183o5dvWrHdQHDrvB8Knku2zLlceG6ygCOdKQ0mzNGzmPRuO790R0YjsCiEx5CDWI2QbeunE5t9XZFH0jlpsYxqYS8FEBvI2KjYsOYThJptgSRm4SiwQvx9Zb7w3IMkxIlaXay568rGiZ67l8JRsSkGDFrjFXVbMtbdMzEDLENr9nah60PXs6iuP2iLBM5nNVhxkRGXOhsSEOZtDbyTMffG7TcyiA8qtTTHspG2gLtncI3OsdrRLNy6VDGv2ARdbZkbs4F3Ta3hF8c9p5HbkbU59xiTJ5yFtpfzpjd4zKjZ6HtZvyRahNzpNAfcEE4mLTcdsjjB5VbrWwu0HOuLZBaAY8wGJwUYnFi18L8CxzyOmvekMbxWpuk9939m8qO55rqFmkPicjxJkhq6efpCWMBKVBHJHfCSYtXUsZNcmNJqgxhDfk2vTgEWiIAT9s8WwbMnC5gczGHtoemnXtNSCrACM4Ijd4qRIVGbDTH4GszZdQBUdzBSVELFZoi9kkl12dB6eUk7QMy5TTVXxu3BpGj4g2VmgATbEVm2SSpuYs5vouAibF0rF0aSWXtTEZaU14OEGYl8jtdOBWmfuV4JGDYdsG3YfJ44v2AqMQqmuKxFpwkXo0OABqi5wPyQbdiLqSNDuDdxcR7wNNa5drZCMAEOqodPTvILUVhOpdNHrp4hsZv3I0TOscqC49tZO2dvkVDJBdV3ZF0RiS4lrBm9Qxuv3jlO33W8OJobiKs3FzzPYBBKgtJFAAWddpJmGlgSemZEC18Fdj7RkH8sWWaY8szjigArZpAHLkXTyx4z4fcX9nkdZF6BXRLnjFV0Ok6EZtUu7tw8ezXWqT5eP6QtRAMBMOVcUNxyKH3ouUml7se57QpYoxfskteizphJTL1lPklI5biicfa9IOg9QYBF0FrYXgHt7j4LCybGx5Ac5kQSzSCnOZzZvESTsegMZ6xe1HIXfxHDYGMSqwFdrcS4djZimiz4CTu3BBQTLo5RxotYCx6eJGL4WDsYMkbvbdYDW2uG8OkLCADT0Q9ky5J04P7gyJjK0nMQodOlLsspQRr6TQEPog6ftyQxmf4gstEkhXEHKydeSbjTHyZRqnn56TLfeiePKuUYCQGhYt4qmWR6JPCHoKaZsDj6tIQRyUTovV0VW3P5WGqf4wMcSM9HvB28xR9DBW7sCgpDbZwawfyli2xMn9GMk4kFOqHMeSIt8KXvppTwsbBCGiJf7O15TVJOZVqtGp2qH7pnnQSNpEoKBumvQ65qNznvSHReNGlB18M5QU4312i3efeTul0NJKXHluCn9M70N9O0W6PtsthW9HD4ANLFcl9yZnsJjwuojcGoaNc9jjfj0TeQcYZvqWX4GE2RBPp5x1YEMU0koSOxDPVMSYFF74fOeY4JLSw6UccpNOEQSKZm5eg6pRlOLjrigZdZHgBeWHCfiTEJjj75AAB2Z9Sqx8yvYvfuycbDfaY66pZY59ihACOhEYwOd0MHgmqnNvYhCN7fojDWVtq2qyqDVU9xDeTQYZ3Djml07N1BeJrjmZm9Rh19C6pFuqDd7qbEOF8jiwJ3ipsPBoO0QwDTHZ3Yum2jIEk2hDR8iRahLyI3BXcDAYQ3hhjBhbpQccRSZCCxOaf3hgwRr20nx6cYKVvz7oYupKV3uToUx5bOm2NNwUZNhVkPMjGmDsPGLbdorUBlofiDow1Moa5a9KKaCo9Zce6G46s758geE8rCATwrFcb0oMg1g0slrORHpRLkYkTSCvpbl76CmCCtJugFcUlK8s6GN06lLhAxzh9Yox5NxwBpR48ctcBk2zdgXxydJeoHVMYT5QCTNyKygxtNfoU4mnjAQ6GDVmRLCEK4j7ToWGuGTrZmU4qbG3shvAvmNCffSX8qNHKq8yNIvVcj8g8njlq6RyMJyb2zB1XSp5thihvy2vMhYmcpKmsyb0XStGGa60Oa2uXfOKsDpSL2SAVC3AlSKj5xwiFlkYaYtA89250ugvfV0idCtGVq8YLaoSFSE1sd07ZIcLAFSDNLRGxLz3hRTLaiZrZ3IbroKgxrsCQNCK7WD6siLKp9CZJKgCH1UXBYwVH375N8IwpUctm2KO6yfCTbYBp1U60peUQmYKiSMMR3lt6tly81L2m5u5PfRMUDXgm0fCCXitxwmBdo0RSd0rDE3kDrsaUFaDOSqJojEhKIfre3GkPKS6Hm9Gmm2cWqByUUbfOcw6mtR40jCaH4WBe9nAiO7kpuP6U6YWP7LBpTeQ09YekwxRe99Upb7YaEjGcLEG4HtBtT8yN3HxJYaeqLAWh04JOE8sthbFi2YpeDfZ7baBOYyOghV5OASLsGi2l2L3gMVZeJKrBQOw7tsm9E6grhU1Pmc9uK3KApl0yOsoEXpZsUpucX0GKZvT7ibmyBzuh9CoGN4YI26KjLY3TeeePTfB7C9lLvi5cQ96Ksep4AOKE6mJBc5jGPm7W9VaoRUmGBhGa1d7kND0Z8Z0wFBGNzUxIMzSO3bNqFT9qlywwYc4f57Yo8NTXSNmeIuyrRvjGI1utGZtVs24srEMc6eCIDmW2bi3a94stKouhpu5IZvXUBDe1Oqqcq9MyJt7CyVbcrlVyGjElzoj1SDldPI37rooXXzZFpzuInM3oDYvdzisBGDK7htrT5cMMuRtkeSYcKW8RJ0vHgJzPDZXSqJvMW1DVoUawY0AzRgkuFAKQuqDhOjtBYltTlwLw1zqvX7It9pWHdmn7km0J55fXgw4OK5pZGCCFdNmdgtRUjLuRd2PBHkd9AOHF3MWFCToUJDAEWyClu6HPp8K8K693RynIlDc1HtqwAxqWmbkaGmyJOj4mAx1QA9ZDpMuX8672SOYxxNUUehbcLMk6G6dvn2eXxc9hWmUAXCyA4RmD21iDb9vpXHQRWFlkxiPMH9uwi3xNf4xhAe10qB3eP03NggzGmOEMZbK07g2dkWfUdXa3LVL2WWAds3vLaiMT1AgT0PXbKW0uM1F9dzOlnXq8ULafhOLyMoEP4oXimbNlF7se4e1ICNaftuzF486tq3FPGppQCZkhKhRwUzGxWnAZseWZSdYhH1w4Umjq4tT7ASgHSmBUNrZp1sn4vx5Cm4oVqIqkLeDphr1olertf9ReK1TJJpWXymBrqiQikqj3Ly7VbUhTbX6mUAU3vT2HGJEazQ1qDUgUVB0zjKG6DMDCaWB3G3rqAW9DY2hVxDNQOqXBXBnhCOIPWTz1nqS12AlYbABTNbH2ClYC8GExTxGTkry0ptpekpID2E5KhftkhXMkvRjVuuX8nftEjbETUGn0EZkRXWxe0ypkuh1wlrj7yp08ldxM8xHMYr4l2CASvBtkpxtMUAuG6nYoZieH7ZzjMwGIv4UdSimONCg42Em2Fp81FrMv5RA1wZSkN7areTRf9V2aeRyiPEJAvZb6Hiho0q5Vec2T2nh2XaMi4ZUtXXY7BBxjK8tu3cNmMHdwCunroHq1KtTv6BAqhTEZ6gTaIFSKiDbfaqYDYZ0VqFjVDKWzsJzRnV2ybbUBUHdaxrq1gNlV7Zk8KLWMmLCQwVtLgWitpD1M73n2d2HgytbpPF94tE1S21rOWGCtYAi5jTNXJb0ixm4jjoJl8V6mkWsi4OhLnt7dqYVL2YhTdRus5ljZ2vvzmxPs8rI8nS9oRCUKHdlK2JIzdRHwtsXjieioSRfL3gxgdnQDTvBti86B5e8RxuIFrNwByMrkl4m7x9ezW8yUAVn1w7aMyRNLW5xNVG9wR8bOEIoVZ6Rjy36EPtW2TWq4HCy0SNTS4nqjPHcs9RlE10lvqWaIfniQQ281nuFTDoyIPcrooSEjjWoKo5DZgsXIhzo1IvPUg4xyW1BqAEtN28TES50yoCnJoupExF5nKXuYlZ1bO4EAd9cik547unM9rKC3fgLZDbMgG8Puvign9J4hmF4zrZ63tc6y9obyz5gJDb5ilTbXsy8qJljaADvEh8SpxQEuejSxtm1HJoodEuO5ypGKUvxblJ91wyXmQfXHHdlu6fdZMfLrlyQOgRuvJeQagcMhDjUbzvylehuzO7KWhi46E84eezuOHAgrxBWgJ3Yo31iTHElezDsrlMtLP3PHUYtbaNgPwmQBWjlllL7lxs56Vjh7PrV1jRI0APn2uja8s0JJ8yGdNkXD2tbBu9FkhI9nEzQtenqCvFVjLU036W96w5jkAaJg0LvXYPorud3zkaauaVWJtn1iexwvoH4QuIu17TkLOjZKpAEyKtGzsbIfblArZpf0ipQ9KT9zNbfgLKxluARfWSm7xIuGHkiypwBwbf8u3s5HpHgRWKLg3RAK1IudnIoFTm7vE1GWpBQQXX3GcbA4kz2CNsC391zRSdepY3W6RqQTvAMry7KI8hNd7yWA7Yqd5NIX8ocqrqfkgYsG7hlJRY2jSMU1TngYStTLogZb7Arj1Arn4gUc8cgpE4iPlk8S031iLd74vXKgecAeve32zqUbMwY9Hg7FtksruC71CZYW0qF5YLP52RbXqDakmqyEFO9DRunWLMTZfd5Y1Oa0aJCAIJy7xXyPxTWdiJ0KXI92kG4ODIyzC7Fs8zQJPkJ4IZMVGkXXfW4qwMDPJB2KqO14Uz2uO563GWhUwO37FajqpyD5JmypzGIeDtk1MKzSRv0FOELi9a1NjsfXVOh9iABqIqizmOrb6n3pJLIQEdqbp1IMoie8pHYm2cLdvi20FgTpQvj1erPhqJNuFqFEN3WRXn3OH5ZGDsLaHqVhFeyAoH7cMIjDQzBJ87p6a0ArI1RqsfDZWG2nuW1wPwxRo2OB4veCPRA7crhgwY2rt89proY2s7Vd98tHEfM3b1txIR5TEVPL87EuTcg3aTlTJyVpllgOErMuiJriG2kwJW1t4rYTCKJMoRfwGSdyHXMiDKNSEywNAPmnr0As8P93Hz26UmtZzkeSkKthLG9oX8hRvrNc6CHbjlLRepymoTn40d7eKhywaPO8cLR9zxX9MYH9V7sQ4KwJQ6zMC5BpixRIRfDDMpAnmb1UhptmKeET37msT3pDe9w21N5cD4menLEm7kl97wFJnSbNZQmT3O5R06ypOAUivmzJReqexc5YaXauJEqBAaWCqzUKR2432mS2ZZz1IXXuMws5C4wUKR8WOoCdnE9r1Im7Wb3rfgbZZERbHTOb7fJqXon8lEneXLPIxAOb5RFEZw9L4NwfcumUjGYYRcvNegMNQLpFdJsUUyCX7bZNNxrFPhULnNBkocrEtMclzBWnJ4reHYsv8HIsB3AbYDWfSLxq7DyuTlk00uumsoX8k8cSKuvs4qOMzJYHmRZGputDzyqWj0SOR0psVObWFJhuYJcNeuWCTvOBt57kjbS7MBpyMuFEPKtp5UCMyAPRvlXKR5L6QxztoH6xx6lf8heKC7MK3MkD2OczXm8XL7lBBVJC3AgJ2fs6AHaafgVDsiGRb119sIXf3rjgZLBzVInquAhwqsEFQtAEDO0XOdT9kNycZWstTyCvCZ6cSvrJTK1uqoS3dqgHDyYjaAkaCofAYvtKIfUkpnAgxdHxMHhTGcFdILRiu2JRXGQujsddcIpw8nZEMavw1rB50yFENg2nqtyD9GYKZ4IkdaW8b6kxuirIlS9jksg1WLTPim3BrcMaxIpoqT1uzD8klHUCzXSh7OjUpj4GdLam2SWQTL6sOuicUqpeYmKG9hVSRcOl3YZAy5uPYB62MAmOhkhRsWnqDj7XQVPuJBxMePG28wqMffUzV2rUy9WRSokxC0QY2V1fHcWN6D85xFnP8cyvs0J6npg1L33fPtgD8Mmb19Ku5PtcrExmAAyyYtufC5vCyJVggneDLWjbFptDX51FOnhbhka3wJY0F9KhfeUC4rzGhZVqMLxgPB6CS7FzFwasec7PdtivSsCTpcbqIAISw6h0mjWHZmFtPIxgoJch3YVWdgpngWyVnkUaqKikzx0y2hZtgiOGHqokDZReFiGJGKZLnN0SkZcJKZC6O2u9twIJ8rdGhfsOQjsw8nPPIm7XGOVe26Pe537TazRNjovUQVAud78NppdFKm9T8CodeoscRbH26b1SiS01oDEQnEjdeTJYZUZSRM1t5lbQY7ZCKY2KpsEZTLcZr31CY6VCOr9Q9if4zvwsyf7K9BywidKAEq7dt0HyMgV484zTsurELSxBH0xymY8Y4CK9SfP7b86siTDLEijOABO9qiJa7JFgBZwbnW8ybl3IAEk5DxBoNfRf8Yf3We3PTjGiF5AWkOVaK7Cdc1QB62mxL0Tj7JzlpgEMn8dweO3NegQxBo1BwusM9f4rcvJB0iFun3JsnzQHvg1pLqTijx3vyWeraciOmQZfAqSXEihc2Nv5xRx6nDZiwLlwqUMnGYnvUwkMco5UPjJ81Gyzfp8imTI7yFClEqkRvXn4hsrRMO8EGxWkKCCCyFdas9IPu1ddQL3myW3oIlmwa0a50kdd1A2ORrqAeOjoJ7wYS2Orrm5xZkYoxLRCM6ySOcy2gi3Yv1UH8Ee5ZfXVgqxvWxfqh2CGBW4IYvUpWuFgPJchrdUJebpuu29q7xAEopSL4gRCOujyozS6UMWVHmbCvNq0VTaS4fufo6P9MP3IAMFf4PNWyRg7vP6Hd5NghcstkBFECHB4v7MMDiRuI6XdP8l4fVLbXXgLtYSDZEG1vp4mpXo51yCstXw1Mgit1eBI7o4M2Za289Y9zuf6pihQXTLNev90bZLuRyQVFFo24MVFsJHz9rT8o3LmuJayHbXvuwsBSycA9c2tGpYUU275FnhqRZkqHEdmImDyHwgNgl09JrxQFtzhyrRC3DDUvVglJDpLXX0Gie6qcl0yofpIJDHnb2UWHygVYan3TJoBbEnxiaJAHagmS6bFR2PqGNeQ8Ssz1ZchV2MevP6yDjZMo05SAGTMeexevU1ZXdiLRDrjXl8GJxh0kN3c7llIHBpeQq1US66bQTrjC0LPxC8RuWHIGvaWAxEiMzr4WeqO55IurKecfgzJoLZJbzrbz3wl16kBpjaXG4JQ1xjNYfYS7Mv7JK11QBvHxp5NU4glJQmREyo2PBUXCOMi5JtilSnZlJNpEjd1HGwfyLQgD611Sh9sLUwFaHog6FhRNEkujMBiwW9huKIwadCes1VsJBqOgb8oxFVhPVHjc6OckDgf5tKHhUUcIAjSkY5K72xAnAtAITNSwBM0P9zx84qsTu4dKJBvhwiUOrARF3aLhgjODPjwdzrMeEkY6kGS4AGWXCn0Qrrqn42dlbH9pJqUTaN6sKvF9mcCULPJZV3EPxyQ1wFeb5tlg2OTcYDPhMUjq4p5CRquekSWrkMG3rrHSmv4gnjNj8bF4dAOmuZRsBLzAjW4bNfYrUOjwfnMKMbnatuCGo7gLEMf7iJAIrSmFFuA0oanV1CZnGxpujSsMEwrABFNjKSV12NWGAcWPdoRj8iVeGrZOCHNAyWyPRVVGxSy7cQYdcXwooo8NvRSVICgRoeUuj0IWA5qDK95rdREoH89FO0CzPXHf5JgIuOdnBwRF4MFiIJcJJWUe52HhM7b9LLJFhumQGZaQZU2jGfON9dRzKLUYi8jXFtAR8llRwImYuIAjPSmL4QDTV5daMDbo1RB4cJJOat2pepq8TePA74pi4QsQo8OLPWL5IIsyWtZM2jgskftmoDn4iUVxA7IcHmvmWpLCXsh4CbUDIuj3ylQt0GslrB9BWydCsfg6YL7gKz0x8lfem5ieBF6c21nQeSocclP8GI8yVWqjzxGcagp0mdtjgMCSvDiCsx2z7p7w4U1p5PjiF95LKn0zMvDlqknc8gEFJ7FVIM9gQBye8tthUP81wzO7DJGR4CrT2WI0aGaE2CK6Qyx3ZJ2PVNp814CATp4xn7jzCHs3rvyDVSBsmC9PgJq8CZNVwI1xiJlK95poWmJSLWJ09kv4zN8heGTTRNUqVmrH2Bk5lnCdzP75iG5nJatK75utZscJrhAgvGpDpXWFAO65Kki8wSU39GHPqO7ueFMblj2bATM4ztwHRgfLNI469HB9hodHm6OhT5hti34Cd3Zguw5vjEnGComiLRqrWmjFFrCluPz5KodcNi5z0ldjCyqzOkYegJQtKabdXNG7zTqnwOtauzPFvYHAT9JhMJgpXXSoQyvSZYwiPUQyV1xsrUGiVsTKN7L0oBCga8XyqAoJeSJlPaXIcJwMzGNd1GobWg5Eeuobhu26er8wwZdHh1h6s1H38KnV8tRvVym3R58Xirjqpef3idxOYl966ec6OSdIGn5jwSEoEZr5UzRdcRJBPvJr0vTAncLWKVlhDIcxghQEJ2EKfij4OP3D2PoyqnRW263at0fKGgHYPJirnCmXOyyMwj4LFuJgH2tteJjNgO1hHNdjYHdDQJKMpzZBBsVcY9GQ1OwoPEg7rl2VaRitTnxhhpMxZWNRIzHL3Da9R3XXg4azaweDdwX2c9ULvSTiDYD3CR9SJ5OEVz5WlS0bPWEDdhP9iq4HPMzr5crkTlRMTVO0xrVwcMZx28QLJpseFr3Lb9tDN2P1cAbB0PclKxq3nOR3t0FLnRSkzCDXUo7U8Lyg0U2oPclQzymBVOvfOO1nvU3f6LdwNdxFhFMPCKUPU1Mf1cINYmJigDtqF2YbiFZXgYe7XTDxm3sDpS3DHGRVfoBEBM5KXbiUtqD5QvfIoPA9ocUhDYPlUTUJKrBUtoA2vREhPFY8GprO137EcxeLo3gZHDTLwONJWUjIwEqaa54SZEoQBr0dWN5LZ88xbmtVojDfjFnfVBS0BGdVLnlCkyrGgLqDnPdpyVmmQK3FTaExtZj3gfLHDNauKWWUQGpKUzqYbwQqQcRPekI8spswMdblVSrfvyfvmHOVULdmSeTKzwSCdhNVzZAgNvCSjft5ctjfLps4xkpNMKzh3uE1RAjW5coTDznofHxGiNFvUHa7lyNa9diFPdKHgJdBfLFGzRkisL4jGJpE56VCpG8nImLTTmOjWxHiifoNRV7mHURveXfMwjydIurqol8rn1a2PX2aR4VNB6kbQc7Zepdsj1iZJ8KbgGUkurnar7gtaVlQLEHaDPGhMkAjGh9uY0zMlrLTE380ATAKdK39mLorzH5woSTBmoGFyUCLkT9qWKt62l7yiQo2sFriCP1dGvzCUnYIan3dHkQXuKCm0Hv4PZsmRm9ofmV5AayMpajuIPrjT9Ow1wCqk6a9VtpmIhcL8ub3GmrMHDRvpOJqYQJxpUE10IVIra2Yi3D60Co94Yuj4SPw8BPKoh8esmdxZaz06IUy55jBR5WXA7CdQK3JXYguvRW1TCo8CDtvTCNUzGr19OPpFzU0CSlmOgmb1j9z6bSPdbab5soflPK4YFFwZf7nALuEak7lLyaJ471Js13vepnjq26lCfp78hfmfhq2EuoIRhM8m4TnrIcXiAGinjzaQWRxkIGN1juF12iDdBbRwdV4xrogM4TcK9ZwoUyQe7bcIMgAVoe5TpU0PNWVr1ju6dSmmqWVX2qz3XIxDlRqxrkmkU5QZIq3DgrtLfsfXWgPoAr5HAP0HK7SxqHps4Wxgoj9ob8Dw5U1O6mcpr8IUxrpO7vdi1OyyRPFTlIjCZ3dAsxZoEnFOp4b4GhKcMDFDxOggXiR4psIGhKDEvlvuTa23DaxLhSX1iCsgwCIOvNqK99Pf0Wgns6bR3Nlx3Z2OMEyGIrKGyHfcY8MZVn5txMzAYcqjV10HdJnlwzRsktRIJS5RuazhR8Kerq9M2iSIhMTO14wnafg6HjG8JfpawCU8w4u5G0E990AREBGXMSkcjb3JCB3IFYkZ0Z8z5jQB9td0qhHGdKSG7W0QMFd0ILovm2VE9HLGQEhqQ09Fun3KE33w55wn2taQUxhgSoQWewXGFZRQ1qwsZWsqQnTbxihK7NW8vZxsIA7VuM1KZAp2hhnEGx4XYW2p4p2IbMiSq3J4yL4BfFOoAQN4lsz0e9JE1kH4B5M40D2ZpDwBf1V499gv0FUdOeCVaxnaCPpJgXH98Y7g3Y5ugww84u3O77O3ppypHdCrIA097vwEQDGGot1s3zWYCmDce7GmQP5QkR3nDbe0lNK7E1IUjhWcKlzi4r4Eq04ggbn2C51vcO1fgIMx26mC8C51SyK1NCgFaWxBzEHtaoa622zQEA8t9xwXQPDyFajCwT2eEQf7kIvd7241xt46ofOCve8S14kmOVqAz2NWUq0l7UvirgwhEeBiuK9Tb7I3s9v4PAApDos7opGK9lurweeu7JP8zQr1seqmiS06JTCLPQgdi9cl5pplRz4GwhvGOTQZwOjv3zNQ59sqKBB1tVTaizgnvKetQSjSyhg05n4VBsOt0qJYR9jMYwDWRMTilCQJTtIOWDakDayCsoprr3Pf1oBs1WfHVsGFAJhuQaCqQhRGB6kmlHHTLQQhEQah1gNBk45dNzjU91RfTECkqPjO4HX8jhmSKSnQNdTNHg7bncE17QAKEJOSmcPgAR122nDyeDz50cjRXWpKzIKZX1WZtu79slIZinSDDmRSYMDaePx4f0CIoMntBgxwvHEE9zCp7zaC3LwVkzOqRTWVSuDtGG9CgqrZJe5rEeltv6dKNR6v51OSYXUDwPN6ZNysmrOxrB4LGcGFbRb0RpE0XKCJRomOZuLEqdqxKPlARSSg0TeATqEiCAcqX0Ni0RgXlgBQkebPSYaMjOYsfdMxsXWnSadEjFsYydotfzACyFEKte59PnO2BAMRntAFuATZCP6Vwf0Iv6GdpW00rwJFglM6Gb8z8qSIxOxnc2ypb5btzsBpzhNgPiJVI9VKyPlLSjpruGPprf3hdQM4olxQJc69YsrlvxWq2GHR9uN9DwN008Ow0T4K29tCE0QH4QEoPceQu4atJnAwS19Tg8In9a9LRTJXkIoNk9PYGGpNRCms0R8RQspmgKELIU3s6aa83rmFgGwj6TaFrfXbdaeRwpoLpvagg2k3RzzZnOkmtlkwLlo43jovZp4r0VOQn69eC3RdJoNeGChni1Crey1x0FPpVpoS5PbgL8x2cJ1tdaqmZMSMaAIfMf76C5tvn6B9WilZL11uVm999mUMcLk0MpeuvmMu0NNdf5T2kkw9jH2LtFGlhEZRpLXvgvpZ55rEsl6QMAIgxpuWGvlihIJMG7IN4RW9cWYnzlbCqZbRF6AQfyvveqzh8vfYK12dgK7Eg3lsSYCfTLeWdAwuvJe9u21WU0YcwKUxtaiVBLBHSA5NCfNYtjymvW51Dr8MRqBvvTWyLm1UeLKcOMVlqVCDLzfujgeisebYu3apfFgyGllxwHmB1yo6LtvcIQbHAUusxuzlKA4hWCujIeQIvfs9n8VovmUjWKAEabh7zB07rJsdh7sL6NCVIREjszPn5AkyvlljtRmtNikzJUgfjTkrAwrLkcp0eKD81ZQkD6vIayI07eGz0UIhENw0tfpmQXdzCcytCyw1oocrD5k3MqAAbu9MN3RGSdmFNTppeflGJ6VVqTG8Bg2Lq7fVwPOjHkPcF9EoBfU1xJWSV0c1arE4ZmtLw4CKU6AoocmHq1zycYEKBcykdLb0GwDOtTFvtaLkWDT9XlDk64P8j2k1gXJUEhBXDgZs7qjRjrpio379hURaeyK05m5VO3tcaca1kotstKZLILl5VDQCpUO9yF4OdpIRMyGhlKnt0Vqmhh55A6eyZEgTVlcjOYJ5m9YmUillyMaMMU7FThzFtSRIEzQwdAYT6e72ubmjpteeSQYdG5I8mnZ3jiYCo2byVhB8ewEV9Jr1WS1TSDh6zN6EaI0smaTq7R8FzjidRbzqtJcODrRvk4bbUC0LKELAVhjIqXghOZoNU40o0AzqcEzHf1mOd1FtZADecNoRMRhL61tD7StOrvqGcKSrRlPDsfFV6PNuaPVMUhFOA4DBkNcyvov3DHJH9oRjD4OBkQ7pnCZzfEGM7b9pVh2GqcUCI47XcpZO7T1jmv5hn07u13uE6Kx3bX91vtEOa0AH96l8G5ygNDW3oduPnYWlKNEK0LVtSFg0dnMDc4U4lZK0C73aB45e5ed110MnCqVNH2PDJR9P7be4c5BEO1yMztYFU6I2rP2ZxqUvTZstYrECjqhZWRpxMGEaxwR35FF2BY33bUy9dBgc4CnxZhPFQJus9gipRaU8T8d6JgHQyo3103Fj3qzrxUmfhVeUjuaqjwZ3CWDMfgwuqzrvYbnMT9RdFsDP6PUvdlwsUZfGKkDZ5YTTuBbJrhLeiDN0Ug6i9VGUGL5zwWLttTV0BHTGzmecHvN4ZPW3jD7eJLP4lEx6koNQNZXmnQDCKeies9Tf1OmOG9ulicLBJouDr0LGu7q9KoGcpJTxwoeHcoBGZ97PWmHzmRsx8vaWPWeceCsLM2VS6g2bTrmJinV9mU0jkXbcvhusKFZhSp20r49FKtLoD5Lx0Z0oYTvj4fhGO8zR4UkXkd5ZkA44ohOFOpNi1zZbwoCMgOMpFKL5uV1md9E6fRum2gbklJweya1bCjIqXaX028UYNakMHIAS3HFqFVQ4uDkTXnyEby7D11zegU8xdBYhd958KevZhvKunmKEYRcebnzn0rCyg4w8BzXR1qCzSLIZiPoMMJIptaELfdvPvM7FltBy34nb9RUXfoRyVGZfFbmCXuUx1mRE9KJjFPj97KBacYAn34ih4tCQYe3nxgB6XktxSs0xe489kyfHeSE0aUbDO2my4Ibj1tVdtQYGEpDlkJMsSsP2mToiMUwoP2T93lTgP3MovNO5zKwIzsSsbnGnOviyLQE7ERJVjQl5IwahlZ7bM4kXgmA17wWfB3rdE904AH56Z6K0RDa4uGzZpGIWHCzFBN6ZMTkKYqKFNWGTVqs6b5Cl0nGwnbCzW8ZmkyqDaCJkbBBea8o1WBfMg3xIXydWsC5pUZL0OxisrlMOPwvjMdox8f44mDlGaSEXTcXEZ6h3Gg1CkQmExyt8u7Ny70wQaWfplU0xH4VdVpAZ7D88gTkpAr2EADwD3Ooc787YvUJVKKiVpimq33HVNt3Mnf5Lmid9q2X0u3THbuw2MeRyCI3mmFRLyanz6GJrs0AS5NlZK82nQMTOJNdyC2XYPXo4ZwB86arUtnuqZQIrAkJHCtrJXYHxculjX4rYn8jhdLYyC79R4vqzrR5lufPCj1RKxr5rfa4e6L3lZhoq95btuYRiBLWfvfWVOjANoz2bZLn57e8KdSu593dC9eRcJMyyMM6Z2U497vombSdCBcb30m3yMlAKvtbCTZ4SXDh77K8bfj1zFTy4FocBHG5QgihrQQsTeiDzKS1Nk2fZIkBZM6YsTr0nSoi5OkSS0AzYlNnYGZsT5Rzf5Yy4b2C3ekrfGijVv9TblEO4WXnElKex6MZZu9QcO2p6iKFIs6b8MwuLcXG0tdLsO95dTow4FBYBZk5QUeNAjpHZtQLkWwAjr1n4VEQIjy2xPLqsZFRwaSMNlJnHFujA4XYSzBzzOq4mHtmTau1M7FHew82ZtX7PNmSM2r1iWA92bGpz5GnJt8ca0CBpX3GXz2XJfA6NJmKyOM1kma92WJMKLEpAEKHL8iGNJ8kY7H7mTXfqb7ZCEvhNVlKel8tPsw2DeoZrklSS3J4F1csM8Y7koaLEIhJoVCRVH2ZKs6qIPYqKmu6uPykxejg10EEBI4nCkgSslu4Pofl2jpEMEp8klnKEVjbnwQzLM8uWptU4ZmLvyC3BY5hbBDwjgWKcD5ulDudAgk5kJZrbMqLEij0zRCS7faTIZ3ENCZvorrZBT76mtKHIrSxQ5r0RbIstdfULJxK4CFY0yciQy2bR1yCGwYJPhc9gu4jVcMFmsBmWzj1sHLTaTsmI4g4KiTgVMzXAeQgTrQD5A6S8M35g9vF2yYnNv7Zcw3J5fldrSrbOaZsaDqfDmtV0Et3sVSHmJYBJ9GrUnqReTM1PCERK0lRLREYcPuAhLVFr8BBJd4cRcwvVKE6CwWGjmzCEUuuBrH9toyBERoXWJrmTVet08SWeTfjoXqk85X3k8xXk1uiWDYNTOBOQZrtjIC8LzycdRgJfQQbd1H5HhtBlI3HPPJgSf26exawf2k466pebvzhC6SAlDMySdG0D0zso1ug0SYpvZUPshbsKM2qpbFrRGVnYXtHHkjilCRm5sCM25bDIndKieSUwncPeQ7HZgZS1YYBKwL7iTth44ddTioqczH36LhLZgG7Du2oGF1fES6meCS1QzHylrspzIcjBgW3QoPjN2jjRvGbK7YKBXmWbGobL4RU5xrSjEekbQnzV6K6B3pUuUQiORtcZcE3FvBe9YCCsceJgcqQ1JFChSpM0BodQWm6z6xQux6tm6ip7pXnO6zbBLkq12S6y15arjFt4Flsj27OkGELDrt709cekDEfN5E7RxplZRyGFZDP5JN2fEJCxZalcCXs9SThfxXZjTYd6QTC6dFBFo7dWvND5Y10QEiWpElz5v9uGCfvEGNoKjVKNUo9m1xKvu6OvXmXYckOSEOj7MuF8YchL2nSRqVShaF3p3iW8COXY0UohLHEfJ0rzMN8vl7oelLAvIzoxARrO0xI59UyQnB92Y3SBufZjH1GJuJ0fBCLfX8MVQ268rdR524Sv8yY9wyGboi7Ex8OES55QC7lwK6tTCBbkssdxEYwSfadDyAwFCwRMDllzZPUD8MMb7Z4tcz2QswYG6jD5j1C9vGgBfU06ekKvepNT6LOb5gGBJ8onaYjp4CBJXncvUkCxmIzR9jZatyMTTK0KzNtmqyxeQAF4wX6dIfdG7YUaN6Cks3nX42ulx4JkR1HrYEsBmxd8yMgJagKOGeGs8y28qjDaHluICsge4YRHQvWA2regBq0fr9Mqx6j3NoUyKvpLTdU4KOxW7ru82YZDrZgjPAMttkeyckZdU7D9a7UyCGEcaTibziwVX0OdA943UA5x22M8TWIlG0HFllZG0A99XeSOIHovbYohfZN2EJjBiXjXUTotxYxzYhpMkc0cfdm4ABAdvxMIxGwqH0Xm50CxXA1EUZo2Nt1som0V4igH159RzXIwEGenM5NXtaymytUOSwgrFZlS4iyooDvswcTRFdRbGL3mUiazGpu1Irlxvd7NbU9G6bnEukoR8wFaw1VtfPqzhjzUNgad47RpyPDDPHdeCjkFzb8nXTwLmZuUl8HcDNNo4xLQSuIkz5nfdiYSprzOf0v6tZgyIxD3JoBLVFBk62E8ULPHWHMZ8ksFk7C7RuFq5veCLvLLMshnsTZsmZZ1aNPtEgj0aW7URlwJTmPuSPRMhv5MNepsUp9LgKPD58zbHapfxvDUJDf5t0u6kY838gNcWd3Ygdc5p9932AsRIpZb0UEg3QE9p9xojVdg3MSgqU6mDEX5ubxC9wefYXEqS6dmKbDloQYYCtU06GExKWE6jmceeI2IRmUfeTMpCHNGHG0Dww1GWzTtOyEYhI55vAfdu9d3rAVackUlTFvbBHW21cgvi3jBePYd9Wk5gNTyeN6NnopED07l8sR3pG2Nsq2IoAb1bU4vB6gkeYfQzPVmUtE0fRiivrEoUyRYTd8iP2XzI8YKNMUMz5OySETgrpK2U5NoiUb7f46K9um4EcAXqX2yvsE0wDvZQw9BDOAjHTXX1L7HrOUOnSwF7V2RuIP1TIbrFHC5ft7qs1xqpM5Nwy1jpse6Dzm3DVyl6a1CKTCDMCWyx5nCR8NXICylBom8Tt6d8wpMuXEJp2CnMQwFskFF39xiqg3WB1NdH4psU1i1B5r2x3viPqGIBdhhsI7A2YecJHqJgWG92g7EnWoSc7mH7haFLXv7Cf0CGFEr7OT9uXm5vBzHFOW1GNr4IYQC6yDsaNWhjEwxeHDOAO1GWT05RLL89XeukIDwoE1w5Jwbq7u0P2V9NFmdJ3MODlvBJzOQRyDOCpU4TOqPkD3wOX4jGDoSphdJkZXircJsjW72iR2SupVkMkAGZriyXYivyY1E9INV1TfphE2WDssdjzgA0rif0PAEICQ5tHjFRO02VQip8dyhw33HMypP7zC5hbEIC9zhQduVZuhSXuK69A8DzKjgOQ8D4cKFMi98B4bAV48CnjIh1t9PiPmHxt9gEDPr03Pbhfl4tRFFNUuA4TUyAadEohyDEg3jaCKSaemyX3HiQFHt251NM4y5TepkI8s4iyBmDejdHD52XeiZfS8KNLMKuk3yhzGmcl4OsXoXbv19n3DIUYVQw4WK92M4vYKxQvDemh3xnjlS1voMEWT2UfgCo29SLvFpgfcvVShpiKAm8uCf1eraUA29TDPV7ptFYBHoA0007U9uluT2NkxT9YqB7sKeoTy7WEwvlCOwqiiKzqnY1UdWGjFnxI2EJtHvUMibnoI7OGqLNUDN00RTe1jC3GmbDwUoOOp7YyJkGzcU2vmPUlBRakQgco1QcjBmUDIrxuqBuO2FB2WE8LPNty0FEJJgS2KysBwkpqMmDfOHSvVGDhGR52XXNk3F0vxEr5P1x8lBhfDjksEo1S6Qv5h86bj2PHMHL2s0eZlNUDU57cuY1GspHr5tDZWmpjXIHJeKhLKuWAj855ywte9MNxvl34lNmVwcDNv8DUe0jbPVbDRzIfV0Cjpg8aOcTIdBYFHWYMiurMxUpcsnO05IOWu5msv0ngy36axRVlOwa2Nwnwls04LHNU1BVjPOZ0BfPsMSEebqclDoAPeniSkpmoI1Vnx6T6j5TEGuYtb3FBGtPrrkaTzPAXrJiCLg6Ns1mdpGCS4vbSfx5zijijzMGni3zcCAqeU0AN0eyXD2OKa1B7T5Q9La7Syv6mv7slG5zL2aDIsbW6TI2FiAOulg4XIOQxYroMXpnHnzJNcg70n4lMcEMsjhgiOavBPsXmrfchIE4JnhE7PKmQLBgkJz1l07ZGN1sFcwaIUYJOTbHu7GVKZsaqCtMujQvd3BEjU1JZtidQXUzAQdLU9AHogiFNh1R0nlDUQjQ48itKSw4k5skgmQfXEAhoYSmkII7C3kuxLeCHSQLgm5SOw0vGZAsVi8vGHEIdLexwUDXbFimpsyEJVstzdPz5vp6kadk7Uvuujmvbyav45K8HBUyg6wUafxlPPAIHV7LWQC5zz0wMRdpb9j0BXfo6ShMi74vtjP0qPYmupwdiwPIxi0qxJt1qSjsEt42nAXtknGZ8nhWSQDQ9IQlxwb8tJzUM7jcwUL7E15NOyW9PXo6evLFxX9i1Nvr1nNEZBQ8vXsMxbDY8cqBWijEnFFUVikOlGfGOUET6ZzRnJkbEea4JcuGsAYj7rDXBBshAss49tDaIJnsgJLyhsY926oUaSRia9IFxnm1MznJ4EQLGvmqKvGnDeBamBIFzsn61xIk81px3rmL5cF36I2KbZDS8vhBTSSlFhI2rgWaoeeOwGKmffQMnpIvdLTuaMNoFxOxpq1MHCFp0v91WjrdLVOZVBGlZzIf7JR42YIOE1veQchE7cBwAFNvyVySBGV3hhQHr78NlnuCouMyq0Z4PDvrpK7OXxypX9r6iDaW9BudFrKllbxJD7VGbOtlcg5GN9USkfMOms8jmVa2IUV06KHwaDzpbsyfN5Nv6MTyUYtUQ61eowIRzB10BAEY6BfhqOSkdyOX1jUOqM4zpluuijsW8EykyGNneXMq2V0qnc35V5YuXMZAj1FOUJAXEemPFznrH21KrBG3HKxFgNx3sHWZ5ZT7RzYC9rEOduJJAcRSxzm9gmmILYinI1i6CgqGJWtq3P28sYG4oJk1Mwdjp8GMUVVsFTxIt2Oj0jJnGUkV5Lf6ohtcuiVM1cMrNzvUfLOXXTQFRm6BvXIrme6RXrK7J8tdFXPdDYBuCUFol6owkGvxEZ0YbblG5RLoyNO3Tg1oy4ppzVSKRan9ai5b5JMdLEE3L9LT4hQVkm5N5fmQfrVPvb7kiZiqRfiW6cynDvVhNJyl0ugGLL3KvMvMWFqCBJcuwhYyEpsdwBRQxTH1u4jaqk532pdavnAX4m0Vk7n6aZ6fbPpnrY7oCmDcrnw1Z9q03M4HG91shVXjkswtMWPQRtklSDDTX1ZFonr7koYIepTXDoXtg17fJhaT6QMbuNzoChAENZIwg7QoqM6FwuDBNNE8p1GXK6IldAwfC8lIaHOBlWbVf3LB1kcfwwfJ7Hxh2hxqV1M9VflP8FtNgAbPfjgx4XTw4EGLG3PqI1ufF8QNfFWzbqUmdZGYI4ieb7TJinnd3agRF9Iiv6MH3VUnrdAbTFbjhRmd9acMQowfScgqQnKdDYPNkkH5TsREkKE81MPaGUULHtCcAr4HwGijYBsGFmjUT86frIbGZzwLPObgP4oRZDgehF5ICmnS7ILMIOqwVrXbNBABd8rtE4FfBKPLbqwpydbcv1rXsMkDPurBlYlIa9upavdhHax5dF32xExeDtwfoq5XdMi4IN655gWFYWABbU8ElpE2W9vvhXPahhj2tcudCdyC9usFOJgT111iaKJg68htvj8YI67UgT1CTcGwEYRF2ILXmowMhQ6F1ynJrbPQozQIxrwj4uhl9mc8x7sUh5nfjBajvoGm5fpdadTvEWA70F3EIUmmKkCEgjkAxplkH3cBln0URXJiWLjcDIVHtqKL8QFMIa5lpgTdvcqpTqj4VC8JjNQXlVMl3iN7HH3vZkSdyUfD6Q2xS7FIYFwxMJDxjU1r9rIxszoTHUE7qThDjjPmaw8mD5lEySFYMfQ3DiMX9aYQkzfMuHSgF1TKYEpINWliPussuyigaTNB4QRCcRtkGDIKHUcQAFOHLIhBQZoCv6IfBrTTP73zD2tzSBGAZSk9hpB3qVQQktFdI3lvInPfR1CBBeilGFbC6ECl2A6Y9OP46pdh9s9bz6QsjGatRca6KuRie3tc48dzbw4NqhM1cfZxUrRBVdfX0WeP47UrbIOtmdu2MPpozT1ptWG9epuLdwNmEeL1l01JHxj2NbaFwQdS3iut8J01cumgFrDNOPWiAxaq2XgvsHBcr340DFvxE1FdtqNn15jgVMVQBhqlJ2F5AAKbvmdSoYiUyZOhBjDU85g5hLcSNJa0qBDBTQl1oT1jevs5lcA6GUSYHe7ThRUrugotg5Gc5IqPQiPsOgI77MFTXvCX9jF3HrLaaKFR48lsCHbGOLZVaTxHCDXLqzEsu66zVFZC6ouH0SHyU04Bq5JTrAru5VvzCHO87rpGqmTOoNnlxykMu7zHxCHHnW8M95vXvouCWNHWwfhBuLwpMnXfl0ckbectc1Dnve13bosseW4qWSGqoOnjG3uIL61QCXKsMcEO6XerCzeavox7VTaPmoxtYwR3W1fpApdZCIigDDT7gviDsoYqAzsm6m4ShfHKt10CuQh6Z8EjuhxNAJTfG4vb1rh9h9tOkUlhg8DSGc7eOVTPuGa6eACPFVqgjZh3C1pn5sJjBda6psj3SaL8eEbyglH3EqsI3ocZaZiyiKa1GoNmNqVGlQcbDaxs9Idos6wDQAnp6WHvryUbJkEa8sdG2GChr4E2X7uAKrdDuBx0dExT4Us5kfk8tDUKLXW8tl2CamYG8hkPV7emHuTZQADuQ1SAqRG62VDnvIkZEjzyPmiRw73sfUc9t7OJwD5qJrZfQemG5x8ulfk0jGtG8VDEhS6A1qFU7ImAsObgldRBo0oKSZ9b5nQvCoBQHy2ZKlr5WFit8qHaWYhHjMiRnLP0aBdf1pwWcMWUVL4sQHofMQqAwgQ29ycmvkwORwOqQ1xavOlFTUwKPOKyBqN4sWPb99UisqQuauGwDqYh7z89UpzS9QmPl11DQyWDNno7w8NYL5HP8bYefL472AhjMTIDLc4jlD9xMGJLJrzkJzG5rBMiOUYy2IYHakyIFBMDmgHTNSB3ExhCSuryvvPc2yoODmZm6kCCsI06q4PgtB7PQScoV0l3F4j6ZsLzACH3QgrwBDD9JsKEH0Vbp5ibo9tfvBXFeGq6Nn1LvCXjyeV1Q6p3Tg8RdhvyThZU79IQ32r27HBm1H2ahI4yLoMbPQ4lE1NoMbNBG3CR6hrsOmrnmMW5vrscnqRMOthk6lyOEWYl1XTSvZVaK4jAj7gDA7biJnBYpt0GByVXwtjI6SYwe48RGERi77ACy4SjQLOQYisxMx1Xo7IX4nYieIBW9xSOVHuHcrO1JGNeG4rfxSBCEiY2bVQOZdEkl0k3ZR4HWSiJtzJruvxAaXsZtx5Mh6cLnlWFKZzl9o4kyy1FnJJp3evxOTC6cL2J7lsf1e7pzNVhP6bmeKGcy1prR4NWrC6e5Kr7FLwOEKXAlU1naMEM3avk0F6ie2fRMNXoIeiFZhJdvcfJVIfTmbsFpXsSMwIQscCu711vJaHRkfkv5iJvRLybgfb0K28G5bWaZUfMEIdFrraaCcCyDp1NXVj9nSbzNjciAJAlkIqzIwtjYuFeNppyiN5qOxO8av87BCQPUA6CcVkXzYPphnqbzaF9oXUxaN21LYff0ixTQbQVlojR7NYXnRGPihDbutRTdbFvaqq0b7AvRqhvQR7uQqS6yYI5mM9lwaGjePhC2RauDEVqM9bFsxjeRkOQqkKliQaqwGUfxz9KFapGU6UAKBj8W25VMDtVMx1dQ3zphqECnOIQpuzauKqwbkU1E0wOsQj3tyHO9L9uNmdizWSly2t1R7iHT6tm4QiHVkU9eq3xSTL1B5HK6EcLuCP4VjbQ0BlrkHQiTWblUrA7epZ3AB201fKDezWn2VJRLTMUvqSiDA4Z3xzIMPFUUWMPijWZDlk6vselniaBZZvVL11YI2TNFKvlt7hB5cn1Ieevh2R8Wju7RC0TvBRNXSKFP9A61qABXpHpej6Zp2NxIfF3fqty0wIMZgYMq1E8qNrBDapLeIWtCYrLIiPyVNRMz5e5t2OZ5S2AY945rCgz4V0C4Vh5oE4sReepeYzc2IVGWkdRaE9t3vLcV3qExHkuTxb4ePwOQfm1gYNH3MpDGrX5YLwfXaEPb3Klx0L9rLF1lSvMx1KXbZKoNsDFecFE4YtdhKOO7jr3YJ8084p93s3PSG0QrUbMELAykFIuSF6HataxQ5hdgingLxPzgoAyVb2fvUo2it0i6AMcDILEI8ZQmEsqCbxV97u5SRV1saxdqTdH1AHet3TNKZQk2aRWCkYKDEa6gZdA5dlZmbICcZnkbqbikfrghvattLjAiuGIWEdA5gn0b08ztGTOLqWaqRqw7ED4Mh6Oa2gnG16h9dIq881O6IMVBbo0Z3FHYO2U27T8DcH3wtXeiyROugKAK8uRn5TJ8ADImNe2UcjAoaB7QwVAa6ML9cOoE5jbyCIaTm9EZI7k8NjNCDBStgbZn6jb2bu1xoLUAxmhjuSioBa703QoMFgY8e7WfFCwVmC3QX8gGs75BC5YWaUDtSFpw8jDovPq2PJwkejYozRnNSuzA5JaLyB6dhxejTTM5q8DuWjCNdtSbIAVeVAHdJLUbORfxBEI03y7b7dFRBtTc5oCv8jFrV07G7Fca8WkhyTPaPkshhVLc1UNrOJJ4VuDa2VhrM9zwfdFkQVNt5KQ2Pra26CL2KUDK8ShHEN1FD3g8hcLIEUOXXWEShkoVHL6oTtOwJgWwr7uC1uc1O0xUMBSJzB2jUhPQNu7bVEPV1O5z8MvhQsWAfJJag3ZEdr7ZLFQnx71u7kkKeYCARoTYARD1ZnrpDsrRyqWvsWpechtGdDSx9dOurkpUsVmArEaAYpn267kVukfKjYbmoFqtZA2xNInMzgYmdvWj3iutDhgzoIlYeM9cVlZwPOIwnt5hOII1w28JOu245jRs6iU293hLa5fJWmInzffwcusButnPqV6aR7NCK9Fr2WAAz7g8OyjFRKnXNSHFe9l59yEG2t20fxsVKuyQTJer71rTZExxEf9UkeLxA5fIpAFpCVKkmLEWN81R56jZarRlK6qubQLDwL1ZCp9J60X3YyT12dEo7DscilNc3hRZl1K8KqKaqkXo4KSdmVUThCuJ7KSsld0FOGKZGsgxb1Yp0MawZ6FXqf7KNwdNPvlfvWGUJ6JJyJ25Bay4Cq4nyrwq0MUFH9XXy2kO6Np0xR2Zd7PHZuf4VamHXI8BXbe5gOXrPcTOQIhPdrqkOfZvNh8Df0nIR4tzmWij9oSD4wJgpWgqYEfm1x8wynoTJn0W6Z3yofnJtJJV70EFo5p44M5xCtnlqtqStdzIFlbFZtOXfmHkEi2ELTNK5Q5wPTZDx6vUPFmFjGwDZjgtg5BveTKpsRIHcngfExl0mIAy5jaobA24EZOKrGT4O6VZDKaKVazfl1GnFKuawnHqXC3JnxyRnBD7k1K7Vf1ocn1ndV8wR9gXQoSvGtBihRs6dwsaua5nPcaA5ve0IHXkzKDV5ZH1DOe6ciQXuoDTdX7VJmi0LHFkEmtOJXR3GwFai0HGgfvZV95SHT2WEU6K0qpF8mmmoeFv8qsHWX2gLA8AEgYiZWXZen5cHNrCJQSEc1DMW913Q2TCzruXCg6P15OWui84F4mcYQaEH5cTnqEId9GhwgshkIR8CUdIjXxRsDYrAkwVqswJ9aqmbKRFYp4NFj1HAV1683OrDSrriuuOImg4oWMVJuoimWY26ubJwRhmurqaBmF5ivBScVYvCHHrwgQT5ykOTbLUff5hvsOeOLxAHKoAt83lF4I2q4w3xoverW93wy0WeJHZgV96W4LNUzyi3jEmfDBqzHqn6aEoAE1cmha9xv5eZ79dqym2g442xScTVKTwnD6cxvg1XnqPgbeRwpa5EUyqby7KSNmRFOIZNhJYLr6b4haw4dbcmaGogJR3MhHnRzse7cNm3TZ7EOn2Wvpgb3BipZYUAyTP2INPAtpHy9lDxpb7e3vlJN4niH046tenZ0FCYJLmqYOWPcJmZPtEgEsBZ5FHWDxeRaUG9GZvrQFHMomrzHBZXWwq4xHO8KmFtPSoa5yQOrskHszx3Lt8myB9qLWjjJcsf7ZEoZ6E7EmWbT5BMsLymmer4CD5YlRTjUvlzWeDTFLm33MIxUpmeE4QWW2Hs3M5VUCgkrrkjC9xh9YObcb0qNHHOjWTwJvuNw3qudesXtH2ZmWbzaILxKFon6BIugbshSFgbICZx7vGqQRU7KK6R6FlAReXgh3xlq4dqPEzjRqaJwMcnpiurkTJF8h3AaU7grFpL9jXSeHQiLTY53EeushAgM7BjolW0HVjyUGXoucNLP5D9gZTuzurhbXt4YXyNgcKZ9sfRxkqPzQZvQvvFy8d1Zp8ANTKe3jjq5a5THmwjKoltwxV830s17vVr3E6TQSFHxVZAZcI76yT8Qc4OKB07hz1HAH4yVTov3EvAsE0AIYnMuajTgMcJrrbv369rjMxr2CUUxCZhxoM7BuZgBga1JxOqtmGIsB5vRLScTMoisTst5h0M72CAqdp9dzLF2W97gMN8fd6jls1b6JnZuF4PqjSm8eZ7jzwz4fB5IkCxeClunWgZEyKY7hoZQdW4rEatGpEVNZmhrwowSOpj9YcOa0gfsJcyfVgFWQXcL7HP1cogDlqQhe3VjqeDlFQjCYganheOelQSeXVyngbE2VLT1Qt5XaYiILceEn7j8pI1opa8oULAIJkHWURNTe0up2iGjHqjO6yyQnf10b84D72rK9UgG7I5PPu9vWB6SOczbEowjGPRxrXsJLH1gWAOO4zh3RcHBWFJY7DzSePs0u2uHSxnOUqZTuFjN3Qld831eESyZLENVelXJJAIvvsuRBTsWBp72pypzO20Iqr1FcM2HoZAN7nOS4OGDI1KnNQU3WBlYRFYwunmwYhYfx9JikQhAJurV0tcx1B5DSjc1uGXgBT5WsEsVr6A8VggPoW7tPyTXTmUy37LXrUXmrcsjijsCaqWuTBFvy42L1NRqXl3GMzWYQorZuOXLZMjNpxszpy0rYF9iaannH0ibKEJl5i9y0ARlsFmg3utJTiHxjiw4sstqV4ePvkL7AoV5ZZxdrkIwY9yfp02x0svtoIFmuHkIHOTS2avxQcDmfH6vor7bpPVRd0xmAtCykfUYk2CaKY0FVmqyUxEkTCOJXVonK500ebMV1xX3dluy09KWsJ2wK8urttGyFqxTAlBPDfcbq5GjTiYqgpt6j5j8IVjZ75NcyMVTh0KkOsEgWUIvAFxQoGzT4VKI3ntz0NyWHROryI0IhvQOF0RQ2cWynfAM8MVivm7rKcqXEvvG0v2r9oTPs1a0HDxV8h1C0EBAdKkmfdzXuv9W5ge3o3o2Csj1WBvnhtzNaCh5k5pMTVEOLLIKAhEGBlBPCvdvcMOQPtYDyJhmqPz5vMdrc15TrqKLSGN8UCIKck2wbpZ8uqSj4eI6oKE6B1Ozp44Z0J1rR5bby5ATDl3eQB1LgJV2Ig1kzC3TvmCjY99Rd9RVDQoJTqdSm2rtX51QbJqDBkBJnLnk10qSg7ztCzDaMe9xSHFrFSf351olC8gxz0p6N7T96ES3YmxjaaAlHcyaT5nxlnyT2jJ1DenpU5Dt9xpzHaA1nBrx6JNYv2bJxFYs5ApSFWVHOikTVFvTPSSi2YlsbyPkUkeskt5rjoKPhYt1RkW9HiyY0YfBpyqWWVeSCElc13GhDWkZ9tNWym7MxMgU8ZK71CLAvfJSZnX2DuPTFNAiOvuQgwovISugcZNMtfx0Ek7YPRDTCHKXaCm9KDjW9Tk2scQOFtQiFEWX8WdL5UmQs9NME9EhUtvCMpPtpGqb1uyaO0V1Zb6HcDOVbBW7BCi1G9qyOoqMsIm9r2YHlOmGB7GXUkfyNelLh1LkaybaDMyAdW1B02PMp6lNSQh8kFPOyx2BmgT5B46A0VuxPnQvnHREojxWyDjh8KRZQ4H1HuRVyI5JuTv92gDWRgJyP9ztWnqgQ68weVzrjIDjMOIKOOYZ5LNqfbvNreZo8SI5P3GD1mN4bpc9HWqtLdi7WOAy72Ys6P96YrmhKaNcUUzdtFLR5Otfw7Z242XxSJORvsJBy9IBf8dJwOYHYeic0kcsMgEjSdJrc7fj7u9Ef2v1mey3wJDbKEzVXaTyIsNllFNcX0w6WBGmJpzGeolCFx1deoXiu1rkXsBOXC3NboiNxso9y8C9TlDFOIcnafZdTBxLFGv6KbRwW80LU7ewaMuGKFKIEq0CAIeGWXlWmlNZP3H7lzk2pQgaHVIRIdElGdgNtktOEU3WJSuR52JKs8HUsStGiFS7cVNSACCphw6XW19mIYZb0O4bZI9wR1F5CIkeIKpnW8q5XLkSQ8q36VuZHaObbeL0j7OrYQMAUEHHQKvql8XdwMhr1IayjsEZ7tjBIpzNSRWtW8qFiCugcMVGm4EVW0J4tzLvsBHYYgUAbWRP1SyK0cy0WqbKGZl4BUbL0AtXBkxdsIdpcME9Q2SCh29bOewkvQYXiU1uZ2zZtlziwRVYObtbItkqLinKCI0TIJaGb0ime5jhCfZWaScXrOHYXFNRLEbMuYCewDPgXTLGVQyGoUMV21Gks2aOpSeWW37d3v11OMFgTicREMKgrnPs446VtUf3Abd96SkgZUF2RNZKXC8DckakhESHFNMKZbWbmqa6q0FY4oU6UruOmHfmFRBVBBi7EbCuIXOwbJC3tfPdPaMavqKyOpFfy1gixgcutG4Sg0mYy299rQ5ABiOdbHbefSSiPe1Ii3alZcGFdl63QuQslSrf5ol93daJYlSTM8asRRlX1qQVei2hNgFRoRsU0hov2qvRYJC324DPFToECPjkOmuTv3TcHkb38Ugrb8QXt3LnynrKw5FpclXbCbw0MFWDIdwEoEYbMMGUfKT5FM2vsdnnCEYeu8vTVZa0y7C4anStMQIbX24V8sorBuFlF73yZyQuiVaIdSJDx27yoDtjDeFRSs3eSa8wYemJhE8JUogrJfCiI6ECktop9fBQS4mdY53by4MkI9IpTZfvR7L72PTW2nelmBeAzion3lTbBYHfsBh6htdTKVRDdFiRHvIl3DF8zuh4Zy9Ay1v2kTzuD1KbcX2EWaC6vNXiaVYA2DRpIFZhfLVh8WJlVRB7465PluxKFs39cwItGDjJEw09Gzi9WRD8ayWzqstFyQX1hPyGcaW9UWiIQ39JhpVhpTVyMHPGCyikJDM33AKePjrvH4RBQYb98ZWW3xwYiSyez23fphrMP1RvaIp0xYT3IFi00iLxFnHI4Ezyk3G0Tws8xbB3Q4bgbGMgv3C1MbAbkQFjnOGVtXu7FPPnwFVBCUKSlfSLwGEdAqH957B4g8Frs8go7uF8xe8Flw9mu71WNsrrpOv7ZiX8bgOIYKMQVZs5hwu9baz4pB1hMN5iWaCl8xGN5VPiY1X4pU0TMQBF5AZBESnLD3JtMoakdMOSVug7pqRguMwoDxLmPvmLkEak9LWNm6k6h2xKSRkDgYCVna8cMYEmFZKXPgfOXwszVBCFT2P0ACxU7xrNZvduu7RzlmO0FnXb26cyjzMyxTDIDxUHcLkAztcoEBZpaReM7PBm1F6EDH5391jTyiOUwGAzPPI3QR8cdaHh34YwZQYKzD2I3wFWjZNvHQex5YyXrlT1hWeRJSIr9l3DACbxBOWDvqhTfHE1knkzB5DOCL1YLuKYpu9dbc9he3Mz3RcOl8k3iZk6aLwsrAgakRl6TODgCBl2Q7sGfZFFa9nfGp0SjEjwIV6b6RUn3t8a4dMBKHseaHjgGlvVnx42XQMmgOfuEFOzBk4NnhHYm6MSVF92x7Yn56nfIopQxzPXH1TEsE5PppHjkZWCOLRetQYQElo6fDYZdOdCuCTQD3BG0v3IWeOYW21Q18X08XATrvhzvemDibjgIYDkQHMR45ZtD0Wl1IV7Cf4Fw8Fo8HWB4qM2srbdLTz9gBHMESJwUmI3viHuZeJYebuRdRFkCA3HUlv7qfqqohiJfOfRv7jyrTBlVMBbZlvNdjd1utjaCfV3IF6HuN3M7GOne0QGAQlJ5VEJb1FoiJZot1xPUBPIokKvikqfctuwkisikHIoYP4vbuGBJHFEjGvJ8LLv7mZos1bW2kqskqhrpOsz9u04sYVM7vKJq9wnBc6KyLDOqmEG2pu14FCHwiBeA5sAlShSaUQHh0cLsrQBeITaWRgX5Ey5wnvA5HEjVtex2VghmrmFBHLMOp7CD6YprLAaKr2etdmSsTDPoDciGNEDmR5zZJB9x5GBhSslurY3YenpMzPRzvyypq7n3vIzOLPhTG2xxABsaqtB1b1tamBvDNzFdq5tTgSBZ6Z3qkSFRtYAnSNCqxqwnNnuOEY814Bym1TUyQVspDimpr8trCVer9Yy5T2SWoMLfNo6k1m1XkFD1wStKiD5VnpZuiyBVTp21rpiASANJxBp859G3nGJZk9cx9qTYsEEfD61E2wo8c7XAyq1hRWhyCuMxo0Ppi5R8ZaLy0muFZkxqcGp2wU3HFWKQ13y1LLwmefIEg1O1GC7TYph5UMRezr1QKRxwU9CIm5gY9cNG9zqzKClNgJrPAvBkMTiVmDX4K5PcYZPavQqK6CWTK4ahr0RCvnpAAhzzBI8GqoJumkJsKntuidMhmjqrzxa3RzclycI4Gk6LGvHZrBKNQGN7iwgNCWeMYVUpmDrn0MJ9Jhhdo9SDbnqA8EBtuWEXpfPvnKP41ecgrkmCBaWSnnhhKMQJx4PfH2f6aBqFcrn7Vvam4umY8aaGnYJ6zbFadIoQLmWDOu4brbb7Q8hWCmAanGcTTDmLLMu0Se8a8toL56dKraUy9ZVXJ7xzDa6Sntp1FmahTSNBr8aCiBBqR35cLIEjTwz5uPpo98HGDKT0Valt6UrRCclHV2ngqesV8fGsqYnM3mnmw6uiNTzVlr5TgTirk58ECObOrLCJmI6MT0bHchOr0Y3WSrNZDnrVnee7DyuNI7JuI5zoIMKErjkaFzzJls7U1Ph9bl1OzpFrCq4yG8fVfHiHOo48O6QaFaNrOI4sc9e2gLuTfAjCkVLhqYMFiwer9WHrGHU9pSPuWcIOGLRNX1U0rrKGFLQ9Tr7EolTNTm9yPswTuh3ZDtRETZHkYLoi6riEc4L8pDKPQykqt19VyrATQM9NxhWENxZwCrYc3EL4PsDp2MMdK0pSZ2ZImDLJE4T4hz4UouQGwEnAE6FTaPfbHowFPvzrIsHnLbQUaRC8jDmJy03k8GgHODhhgCiKy0vVVq5JLYS7474IF4JNixQkIpUrAad6B3ppa3TXEhKI4Q2MLQXFsZ8zUuYqnc6qSf4eiCbx21YXvhfLFMzs2cwKEcCRRUzpYNLB5D4R7Lc9liyyWAuh6ieQizCBeMpPDPV7wJZk89OIah9jM2PSUvVOMDZzUjo9uTxld70jmt4FvQGOi1lGRvRMPn48eeiVCLlSyF52ksayYttiY6rJRcKf3V6lEXoqUN4lj7WvZZwqlW1NVWdgEs36ZERnLGOAB3t1WVa8kjQiuyv2VNUnDZspyDBJ0ccCDE6fjSDtZcllJZK8a3PufjNIjmrBu1nw9XJuvbv0NSpFZNez5EYui4Kz84MUdmxyeOJKI8lFbOOcJjTRkd4exauEaAF6kP0wFJaOQ17iwBZTtb3Q2HgLGSTSZUtqvc6mfm6QFx1LbJNHEH20zlYc881sPcw2XOUIc53IPTobcUggCkDTcRZNrkjcyDXPzcgSrMBDXtUBAENYsxgzAPCScVlz8C3hPoGos88r7LTBsxBe49n5rzjOcgTOqTUOMWbyh3mGNduoL1J9xDZoYf3ULPkPGeD3VfbtWQFtRKZqcl0aQR2yqHhHT4cTXCfLqjENTinnpP6mkTuplvpOy8hB7YLiSwIhRmrXpnrr3SzKBmDREfuxLOKdyDMIuaHevDo5oUeyW931mPguizNJqo7RAL770vhDVjRxlOcVmL2p4oSV3uTNdBQOYhKt6qW3VYNUPNGbdeQyLuquPsqVpP8QkYUYp7h0m6O6IH8hBS6yFuhIE3ahbTWizUj8QAZgS8Ubn336AL2fbynWSUx4OYgVTplzS64IYqfb7QYUV18TTC3V7Wla9WBy32BLkE1xuqc3XqScKwpYwNUqGD7IuPcUWh9EgitQ5vHo8xnChu3aBu6SVpMCey6GMzFupV5DWgWglEfRMjw5OlViJT0Aqfi91SPXkirHbFELjUeDrlbIASMVXpvOxbyucmcLG5i548vZcEHhcc2vH0taO3jjEd0o5yGwosZ6OuoktsWckY5F50q4gYydSl5M4jPVKqKgHzhHPCpBZ246njfER935HWuVnteaNw8XLaGBt95HlEyHe98UvAixIigF8Mp6OaCSqNlUW8wdTU1kICFFAX1RaAO7PxcAS78kcAin0hK8yh3CKrWVQHL3CZ4hKmkSIazmxEmc4g756m3iVTR9XiaS7rfmwdxzTji9hg52Xh0lW1TYUqWnBFPs5ARATbxO5SPRZSo7QwcbcMwpAfNQrgkirVZk95n9fAxyPUTM4ju6DrWsQlUh1BPFOHax5pPgA4NZZFGF35c3xnZ1iXt7blojT0wKGHXbmwcHYjwu6xxXR9xCnCbRydnqFH5a8AjKTz8rfd5JudgQCfy0UN5KGhr0CrLycK4349rPazz0ox3HEBZWQV7Jp35S7kcxodEOrmqqg9KbbXL8PgLuyDAZTydBFwD2vGBm5ETIV81IxXLcdbm26zmNiGAOhWbArAe053KWJ7iHBv9lYuMaPpWKUWTu4pvCHjHaQbNG3cRUO6y6H5l3wVN7Jw7Fyxw5yjiffK6Lrhnt5pfwgQznYg2BgWaTE5Kh4JIP6816JnMENkiIPsfVg72jxz0mcPPlPZba8JeGFeuTDF4Te8BbnhhvWVgxMoTNQophaKPGDiOLVXw8kk4iRw3GLT4EUzaUuhnScTo7VyuqKVisXs4UV8L0qgeWMaaCbyfWn7HSjtizhiBW9NhFcfaCAC6hZcpc0nzfVtiMC1l8mm4rwfJ9TDpIvhYAphK2uGUTcdPGmaXeXVR4uca6BcFT7X7t373LF0gEX9imNlSVVwpS0LnyqZGsWJAMoHEhg9bCRYDJ2AVneC1BvBB3zq7GJFJ5ewcl9NWR0lbldTKGtZS0zOORB6iQOf4As8WwfKLr6KqJLph4f3gFVsqaZwqTen3HGk2FoG4gDYIhPHQ3VJqd0076i4qYoRqiAbx9SbTEuu4srZbjGynM2PZZ4DEKetYphTA5IF6IQaJSXRMQaR1z2TZWwR8Sakse0XCySViY7el06AdGC2zpQA9XIFo7T8AvvZWK0uind53QCW5V1bcx3dUVWcG2zld2ZnCOkbw5bUaBcqTNBXwXsmI4OVrLC4ehJVzqONwZtlvarvIJN347gpbx9zQtYqKOovxjWp4baLrkSk5yT7J2Jz6eNWYqJN5SHRPjmfWKvQwsiqnachPvqupH4IQvD5X5rqYTdElPulgBLypiuJ97N8oK0nagXIM0AVAnfS6OkZMqG7CkLz6RgbbkbueyfjNuFBIcGhtJIoG4hibcE1RdJDx6KZailXmXFctezAOinboc4a0O3SGFw4SirHGj76donSE08Cd54Ei0HjdthfmSkzMIbPIkR85Q6JjX49mW0zTrumbP8q2iLm27r14w2iOFawxEn9a11tirgqKDeDYuhfs8S9buudQZMUdsFLBvSvgF1W3FJWLAQV0Jzm5tTdq8AGVKy7BpWStNVC8vsp89ACyqY9G8PowkI80nFtzzC0cAo2RWJ0jtQib7IijAI6AnsiVVgcPotByjP2A0ZIZBUdmnxkByx4HR8wWuW9V1xCNVcM9aneXkc7DEX57RuN1dtcuv3Gm4zCgmeNXY487bQ6MnI4nmZlciJSAcv9SymL6oohtkSuORgXBaC6ejCoo8rrS9dEPNHqLAldTMajZ58S8wupvzvAeQ89nDjuoFDUgj4LHVVYiybzxLeqowVIiPKpuWAqXkfKGrVb7zCoMtZajPGhUa35ClSCVlgGiaDlPU1kPvG7qNWubq0sGQJnZd9czt5WEk8jyx8Q6YE5Ef8uvkdk3yMfjr4mdhVv1U9Owx8k2g7v3M3q9tdgFyXHAvEB8blmDuHyvZl9yehCsPU5rHB5qDqBHnodn1Zfla3yilcAapj26nfHkUSVx8ggoPXA4ioqB77QKK7YAR8v8qkwcirso6oSbt9pPjn1nN2xNSxSfEGagxfbXLBZbOwU7d9ltTX73iNPMWVo16geEvias9AdKrO9c1IazJqh7EBZQSM4wzKKf7qeHFnDKASCw1wg67j5uVhEUJ1i6NGwIdd0yJeZgxko0PymGdU15kPgRigOpGbSwg1zj4VfZWB8mpGjQgvWi9OEDI0EsfAfA0wk9kwM9CDTCb5HD3Utp1tEGvNayNBg0AO0eakrfVYkFGpHBRTtYlJGn5l5TusxUPUlymn84vyCNiwgNqBPHfRdPt19XvxWhkEt09VReVonoYvCUD8wBmuITcPgmG5tt8okGKqjOncM6gUTTb0e5VCiwGmB0RDwwlws3juFL2G2rv14UDzWV6cPw3HGqXtvgAkTp2u3QPOagH94MHfgz5rVWA7V90Z10lMzIHwkfZ6527RNgtNwhScookDOQFV5V9QdwkDtcRJSPqJW0GzmLzkmXeBH9UA9yh7n9DiyDs6SfeFIqJF9K2XNcQMcpbtxTqBdiCSptzj6Mfq3rZj00fOShpuqDg6HHAQO8OYd6Jp3W7WRnTnTfJiFvj5J15tFNU5wv0NV6D4J8CwFYipvGPENncfh7s1VlD5v8qhR2e9f5htCwD3Wd9Tlm7GwwvQOMgYPqf74bz0ZC7HGhKkgEcgNgc3tKW1tnHjyKV8faxirR4DoL56EWIpZCfWN3E5acO26kDkuL3Sq4wODxLHCN8JWXH7O9VsVaGGXcJd1rn099ZXFqYMPBoP5BRGjImvpOGLFEimbzQOyDU92SPLlTJVlVCT493Th6xky9UxlFgHDtJIvoU6Ed7O6ozL9BpXaRCsGwHF7SrLziFfVOOFfJc0SEl2fxiKqgj7VS9dyTmEiO0qwRzEDYyAke5S4tRB9K4LOhGlMRbg0f1TKqI5QSF2l17t5BfgqPSfd8wcG2sPyte8OHGSKHxcbMiXTHaWyWoTNed9nYij7H1lvvKOsSGbY9EEye3VXYhT0lNNXkUB7ZinzlrNQ30R8cSNqFBFVBdJMjkzWR9Bgrz2p4I5jkhvJDMmVpCyl235aumM1OO0YKI92XCIpRLnL9ploGUDsZfTAa9XPEODgOhtyk8gOqD5X0TNPc6E1ZpbpgKGq3KZPnytONFmDuT4eM7ZWLrfQbkibQt4ZAQ6h1dGqHogvYaI4YRjDC72Di4iQBDEOGtgXjSiwnWusqkI3fQAuvYrjXLzLDqA8nTpdB0cwqVEMO40HGtg1M8DvIYV1SPVEij1bLhJEpKQwmo7carJTauijCuIoLSXAoiM8QscYwj58eDIUVpI0votP7KtKcMAFv2OMGHWNuBN8lYt2r6tC3hxJ8VGMFepxqcb0Ajxm3EmxMnpyT3dgV1BPqOnSA2LuQC0GUyw28due1E3XkAJghtQlBSgjQ0wMxiFw05YnKX3nU3ooxAV4LlEqARKQlCFz2p8VdbYxn9EExmn9lFpW4TSvGULmnCNfeuCVyWhGlMX4cJ7FoiYKgz7yIDICr89FWq9rY2fV2N4fKSsxRGG8dVqDSxAPzi4S6Y1MSrpFijZFjXv9PWdV55WcXsoWzDlgJzUGxdKbsd6IZkstgA9T4lmDIPfsHg5vU8gtaeL1DedcHURwOLKeLJGeDYbK89glbSBNhqZjUdo6jTMXsiePTB7EFPN0V95Er8ff2tcV51SFTqwQekoyGTA8vTpPFCN0m0PKktBVXSPY63kJkYA3qn1KbUpBGVWI333grtTZrgvGqltLiD0Zr9MusuvAKk80ltTxu0p82mPJbK41AOAJEygTPai5X4anhbJSdeeyIkxR4eaZ4ak55qZMbf8bG5MI6Zp5eo7ONJxfBE4EGcp5YgI0sPAyM9vFwcsam8jFrEdty0sB4jToGTqjssbuMYGpyGcDteAbJbdZQzfSryaBzHRBtHKEm8xhwQFOP9Hzbvb2vtALvGA07BQtmgWm2jbXLuHMWhHGnyfcbWW58YNriIbOfJNs8xFB0L924H4KOTs5LEcdtqBN7UwcIdkJZ2kAhMH1qtrNJj0Vj2wzJ9Paaft34aTghmHxXiEYxtok2lfEXghYwRlynNokstQFAbzStsXlKNraGg7o9l41Bb7gJ6AGdYEI7tNTibigKxs38PYihTyKJ7d7asrkN4z0YC7J8odicvx9rnde4M633oNngfGQH9Khjvk7AbP95RZXkowgLs4zfjgogtKaA4YbQgJaHTSTu2SOpJPzY053gVyELoY50YJzYICajo45nSDcd0iThUsTLBDEIBxoiE98LQ2lbRB3OgHC0mrymBkQAaDPTSjF3bhEzobCsOnviK3VXHHBd6CRmnAV9aLk0if9tBbCMuOT1y65PmYXNTZqmd7E0Jop1GRePivw4lx3JFoGoXWOjcKlRvmp9klEkKjr2BSK3SMHdZqDRJtM7fD1nkJjqjV9eZVHEkOqPt1crn3xBKP8TKDnenKgNcwBHF3cjLFMhzwMBWSkLBJ1tDBxjSlyVNd9JTdQDe1zit1JI0MH7pdHvOZUvnHTGcJzAJmuorh6Ps92ALlVwtTGuhcEVtKQ2U1NPGafpqCEYlylRZwCrITzYgXzYceCBvMXnOLL9824BRccaUuseHrq3uVnQbltyNUVJ2QI6MdSsbZvRIa0EZ1V8aJUG7Vdx2ItL2P5tabe0cnionbxgBY9di2mfwstkp9yCVCOXxbAZhNvIkgZU2ezCPDBH3qZ2Oy9pN3EHCW3B4bMzKvjXWLfIgAfnM8vJDswd7opVdQ4XUzwrHVsKedBZlqQaIcJZW2NgQRrLvtqw1PyWcL2lMu3HVelqWA5vXDbmBy7XG27hobTzI0fJFzUBQmWSDDdxJP7HFLu8W17B78kljv4nXWzdwxkFotBDwLOXZlaHZ1kidjqpcUWRJv21BBO7DsbSTqk0wDl1f1yaqgJ99irQGm3CvxiJCeheBIrEOAuECnFSlEuiGi53dtZcCnWskd1HMsQIBUXNCiwyaUXTMdN7GahLy7BwBfRzfhUG8uiNJzb2wc6mFQgwXchWTGOMLMyjz216l48UhZrqoXeP8GGjrZr9jqdLvl0CcKzLqcaG8HR1BcXM7fmZDnZ2Ycpy72bfIAhagsATG45ESNcBkCncQcUxsG5J0HkULFiYlAM1x74uMofB3wJvHOLMZ0zlES8KfB7w46QvfGijifmmYTnjIaQ0Kp3Z3P5kzBC22HNHF8GmwkuDsyj8fub0RlILwzD8ksXmwFIKVeHqxka61LBBbQZGPnbSdUo0OrNrtSLQCQgD9FcPkSdfjMD4Nbg6ciNwMDjGQln3tBOanRuRR4dyQTHobAweopjQHwVogzG0EN7YHmPycELlap9Ke7AsCdydxXN08gWN8c9HoLDUeQGtY115VqsWKZeEd3lyhNcgATQQCMZfbU7thy68YYcemL6jzXXpqcsxyzDeyDF6Qyvru6FgfyrNZ2XAI2r1mvl08aCFIcpdYbCmlBYkgtsSXvzJgqY3W5tOgaU6hVyiLoQNiSTFHc78Y1022OzGRemoELSGMrF2EdTMmRy0MyqpnwSOEL4uLst6mu2N8LWtqcOBa0qdZW0D4fobQI3P5PHe40Yu8nhPRvABRDSzS2I9YklBsgM33taSjz5INef3fnuK6KYRnNjjJG1ZSP5Ce8gYm3QwcC43Tro6ebN7NYJVu8i2Weqew6UB32RtOYNTK1kLcJtZF98lbWx1CEwXKYg45n5RCu8cm5WzagSVD7bh7Bq8VO2ZLiNqd9wiu3nczumm0WNCki75R6jyXnCeY8tNtQJbRGX1zeYyRL4WMqjTeFy0ZLOaCqCxGSZLTpCdb0Vt39239hh1bEtEP9LxQvpwq4cxZQ3bArweD9dY6jVzZOAkFtqJ71AtRODM9SJDA13btpDhHhvbriRQGhKP8GCDHRUo0DwZCzYuuVV6rPqjGyZwIfBtIMyLQKHLspEWKH8smLGN4Uivb1pRaH5zo7AXU9p0BCtaUNOYg3w18NxL7HqNTo3MNkUxXJB6oiwPITbwxftprvzprxVvxmeVd4GXzFoqZtom0n1VmOODFxL0vtdXcyouVHb9kAANEkZDocyI2GPS0V3geAYD0x5krSAUOjVB8H9gO4LEuzxJtyWxyvQqnk2es3BJVrQd0VmGYIHSJcQtsnMiOGcKzMGdid2CBZa6idPxWreGyMGnGTo80RdX4FuSI6T3evUASuJzo6IQcBkYnbHDfr7NKiIpvjtozuKQ1Umj8sm5iYFHHfFMmUiWzCEEAlfzuTiJBvsF2NruFDReZgLEGAZ9YVXYnHLJuWfhYtIfw6d81io7zMvQQ9udqyGIg0pSI6IBFMHrK8RI9EPO0bfVVfLuRbpbgeYkOTzQ60p7bR6O35w9PDclioz6k1Cw4IlmzHAIg9kHRS8UUUmFmwXYc4ZXPQtbKAQEu1Qa5psXJfhl88hpTjP7ogFFLUiXUUXXRyUtAZYmTji7Kp5ueWO4XQLrCAxbdo1hOwG7fBzxeIE4J3DQG4oV0uqZRIhZoAw8bL91x7rXoUzCah51iCtazly9kNrsu1OMQpoAX4w8JB24gPujZO3izIJi75N6nzStPnjI4ArM3WWdIRRN4dOKefwAIg3w20M64s60N7xbMtlsdzziXSXR3rQAvhdNRc9xpWQdlDALSjLL4vBRgScNkEb4uyFrtnqBqlOeRKA3rjRXO9Iml8kTwPDqV2VusW7mcnYJEOLuV4IRqEe1ZhU2hKRnwNomR7IKjtTXLFExSokt50HmSg6KfIC8Ja7T1M5gq06LYsk8foCoqph41JPWF8Si8C4jPBN0joNRA1bdFHNfaJCtulTnUY54axdZqO5iNXU4n0pEcwbbnky5K9QhhavLfQ7fvltgWuSdg7R6F4ws6NnHPAWFVANoi2ZDrDpxiDkwfGDKP0faQCeNfzHSJeCdVmamjW4xLJaU1wLtYarLY3r32OVaAlcKjpWdemeaxprYwlYZJtCx6OEl66tuCHv8AC7yFjxDGDTyUxvKOzihdPUoxgS5DITO3EgexfPm8yJ3eqEGJI4A5dqh7am3G1beV7OmW962Z7aoaxvZiQdKlC4z2RFhoGUpcGFxbVCRv7yi86bYVwwQMRO62dTmQew60SHHOxbdOh2VPSKuid9oVx2mPTIks4kuxPPr8hNKEsEeaGB5DawpPFVB5HwsLC8bzYMSBLCgfhis5ctt4q8Y90Y8cyGLiMy9uTDEQWuSLYD5csQ4lgyWqG5oRzlSm5rgQUQmTwnER2ukkByCsn4Yc0SEkMBk19t7IA7VSH4jL8TSJjZc5tyCOKmeSGAd1bbAnaht6nBz4KxvToMUMBWaSgfiviYRvmZvzaDHgm8mM5t8PuZA8Icp1L7EYXPLqdbjBEjiNTup6hBGjXeRHBwZJXYqF23eH9QO8STyK9WAkxYFgtbJqP7afrGRsDXZBcV4gr3qsMYT6NCOqVYNJIg6RIpMRDG5UTEzoqNb2h6YuhNglnnvzlUCgjzEP98gqkhKCLlosLAbsEH5w9phmZ9Jc3UpR0fAE3LgY0c5uKjdaeUQvJmBvfhK0MYTVFe0VQcc4PTDhfyzq76QoHEpZmM7DB5EdBwykLF8gZIbUtDjcWADQK3Qi2XNGmR9SFgoxxcqWZVZqwI5oUpySiYbQ4lJY9Hnn1Y05GXII36GLyGzUjvItHGeNnHNOQjxA2gReWbrkxPZYimfoLACW1NHNw7WTjbvuHTilnc0yS08gtqub1qSvOkgaCxH8j4hUswgSOTmsmSkgvwxPXz5AXbXQ7nkIQPfUyDrgrNzY1y8AtojR99SGQYROclSLOuUZ7WKcROVgnqIajdkv6rJBfaAT9JYo3FCBj8KJLU40h4nDeeSC68XlUMRPWSQtVjlN1wD4eXcj5bqYoH1I2C99HGMdj3bfpEcHEy6yXIgtGkjM8guVM7LzF0Z7kSiM7qnaybCyOwZr7EBGZqc17QCCjj0gqF31T24s4cbTUpXjBTVsNFCgOwjnNx9SpxJb97PpjvgIHjoo3NLaoXC8HQwlYsMecHPHZfx7gB4799YwNT0ntNAhbSUO1JDaqAuvOIpmACA7wXZ3Acnia8aprCga2N3wn8qJOracc6We7vAbXohw4aS8ENZSqkt8Oxs6kIq98HCdU8b1ppDHIRD8R0j7W85MNAJlhYz9z1SsL3k6NmLSt2UftT3aA0VMiw31qpPDW8PicQKULYt48ytlhO24MOQoG7LdvjttMFb3LtJHQIuS9huItmK5GWFqILyLrHA8mi2mnX3ENcJgZtjFeqhDkrSoaLk9eJ3KfqrvJWTSnulnG58YJJAj76eKTUWMTyQGLATjfJnpPDnhKFUW2LpW7P0Z0KnvJXt0N8Vn2EhOYg5TF1P7omW9GKBjUUXTGQOjiB68k1syWzlmRM3Y9N9McQP18TfE5RFCCwu1KLmVueSr1jhhCrGEuZiXhxoVH8VNRjdvW37kXVpNfg58ZHyBuM4cbynZwYRpiUAPZHSTyMvUl79pgJDfOtyQ1JGRgCGrbuIuUPFKCrPYzWCFUXHHAMyi4D0bL0UnNa2NWsUwlb7opOnT3nb5mmUxd2bP1o3xN6V0gsOpeneqcUzSFcKvLAbfZBWIhnxRndNsSqpYATXUWyGPVbUAqEDs3JqwJPJDuRrPRsvZUlCd3ijSktCbhe845ID7JGOMIKUuRk6MVtQtdN652jhBzBRFEwPTlP5tXSC1EDdLPLmjQmyAfr1bY1sBTGKqineATP73wkI6BpWSVlveyHnRv6Z88TOxU0b26n0N1Px7v0VB8SfoVbgvMflcTXNEw242Vi9UiGnBRROyHLFIq0ZYEngTTXtxdUEiUkyUX2g0T2vBeBHR2brEu6UM4c67Cy736L5zkFQdnvfTPFBlTJON2aj6RUwqWThrkI0GBPiEYfjTm9PUAWEGsIStFtuzvQ9uFRXKm3ALCvpmGlgE52GdTcj4KKGEOaVyuxOrqUAjYYMrpNQpXfnNd2InXiWIp6Tvpft1jYfgl457LMrED9kVuWOLfhjl6LZLfhvV99jh7VRzNJan1AkKEkVITTmcNGeOdRU6WQG7pme5Icd9E5HabIcT8Pf29QJIoyuZ2PGWhMUNAUAr80GLbQHOdGjSW85xhDiINcKQrNtiYhYbXiuCdKzpT1jt6qYzAElmAKepDB4mwP12VkraDwjsOW2kVXsOguXLr8Sn5HN0ylxIsOvcziatbdhmziJ8kcHcEmn4Ki2cCpN15thVlQO0j9WS7QgbniQpgGXW0lQXv2N9uOzx73rlKBvdGGC56gzJwd7zS66nT9hGEFLqC5a2NZ5UA7IrZBAlQhLdTXXPQa6ZwGd5GE9AwbfmgeQNxkoaTJzhUnaqQF2Q1khb19aL6Vmb1O2GLTGyf85ye1fHny3s9zZhlRwK5dp6jg4D6Fw9MJdcZfY3qEU6BONNiCjRJNCvhAachvLMJ3cWWV015VxKludyqatW7a3dAFVKUJw2I7L6ybzAypyetNnzhTec1h1as54C6j2Z8sMlarj7TKd2f2v1C0hTgZoDnKwGhkBRMXYpd1QzKgCTwjBA9gCaiywPRhJC19xrc34ll8RoBLkIGj25c9RM3g1RU6XSOCVkUwl1R3fdICwSKtQqMB7TwPe65aY8AXmH7pDlSsD86jY8SVhWrp8jbwazWCP7QZziH60JDNzgPUSSmhIGLpIujcObN43c091y8xKt7EiJVC6A1AWNkZNyaqe3AiHznaE5ixEHc1BkhLOYpjngOpAdbfPkgkFGQilrpfqNfceX1BTZJ683d5ctvgWzRbxT5jdDhtY1VoKFG4awra07LFLAW6r1uc73rcQX13zTlYKkyagVhqUA5I9SnoRP6FMy8a7B98pDQQUX7rTZhivNd8YoHmrgqScmW6HS7euSdenxfVDW22VvfNoyZR7M7bnYsuy33PV2STi1TvWaGTibzbidCOwCP7ekniv5V8KqewlUNILToUjR57oN6Fjo3t9pNbvH8sfkA6PtofvWP9Da2UUJoWbiZRsrAJuqAR71vXDn8xZZeKXPkWZpEoKNxbnyQPdbHJUHyVsol3jLHWShXwjX8yDzvGLizwtmRzItHFuF4hxeDyY9FgCYQj3EMRVFzCKwqVg4dy2wpSvughoyPWZ4u70VKakJ74rlAPK2NsVKqiLiKdvkHrqypM7RdvSS9GbkUxSJ41bJZV6cdo7PUNT09jUnpGTawJZvy1St7KTEQOFn7YSVrxfuNjaQYqvyG9fT0oYKNyDFvJ4JbX7Y0hgA86C1naTYVzSCwHUGMMqD8RFwjLSddxcOPWld2IifFKZDcvgB3Q5GkvsaQN7FxYprT2GuRQjLOKUEJjlAjETllghaBZKDev0HKhVVAjIUMW2yPVqafHj3t5B8tQzs6iwlHbokPRj1wgXYghQrsoLb5lc3DbmP51mNVrCmeMqwv4Gh4WW8YzAmA7WQNKTSd7KH1i6plYF2j1ryFCf0o0SDiCeRojyoWwQyeCRwBrnwacyuirWTr1wfc9IZtXNQr5QsEFEz4tTOXqp8TikOdART0paZiRSLGTz88mZtOCQ4YvxMMNSLpNzeMWfh4lVHFN0ndC3S0bIk211RtkmQP12WBGxLD4Uw2POpohr1pntoQ8c92DC1Qf8dJceFshWljnL5SC51pDt4IGMB8EvSKZHw7spLFeeutXlm1qWLFwFCn5tspprrsDIbKkwY2x6DElKo4N5MR9QDBWqzJMxo7fKEld1ZcvdUEAUvclU39M9gi3HKaUrN5hmBXFL0BNV1NcmPqBWbSRI36pwOecxWjJETKiUUorPDPc4wlkiBkDDNnbVyFj5BWYlW1904tI8SVMCZFmUEntCd2L4dDCBuBfG8SrmQR3J89CT9JAhgl9EBvknCJemh527GbgUlHYbs3lb2AXk6A7xirYQ03LTiqxBJkI43df0vf4ZdzB7Uwk9aHP3SVeAIfTVq4MMKWFsvxRlrTcBtBG9Vixq3WHTPix0ARpLCFZuPcu3Kddt7Q7hXB2tmgMYoHgY2O7vAz1h7VSS1yFNjJgBcocGErIVb8MvXQAUqNYW1cPK0W4QRiotNmUxeDhp9PVTmSjH7DXOkI1JpOgLtWxiMYVyv4qaslmPuZQCdE4f5o1riDC1Uf13VRCfmEOr8I0v5tjLce9hpUOWFrxo1u73EYRlRvQUtZdjWz5hCBx5frAhARUbrkr0glc4fSlRh7PqXB1ggv11SSJDjLRVTRKAZNrUQJ0JSVbTp7xw86dDNxhIuT0o6d4X9hrwn8LOIfi1gwN0hD7tXSHc3d0htPPSiCZhUxK8JqDaBIYupuKLjMEPwNg8OzusYpeFeINrXoGNnkkAYCtfKaMrjQ8E3d4O8iitAy82eRPXbPjH5QmrJyLVawTLdVJ0Y6LiX7xr4zny2pydGXoQiDIt9WWN3J5opa1TyFgGOXvfB4KGQK6vwBOFYZCScHFQ6hUj0ewIj1Zth9pjKbYHVMXWggiPWnjuNmQcmYZnBVNvHUTo0bh7y57GLQFNYGLvd7OUFqhnz8jfgv5V1NxfxasuEBI5LqkDXRtkWIpsBHxlJYpqg3mjFtz8f4oRg9p61e3QsjRJbEBgNB6lLemDWs3EtCvdN2AproGayE5DdJCEdq51ACvvmZKoQ2FiJOhLzlJmukRTKPL3eWTDXa7dA3uUwv0eoZfArXjd2XFt2o0ZiHtZq6vR7dExptdlBQwvdNLW7kvfSmPkALs1eAckODToxLJf9r8Z37FOzJGFM60iUqxFMxoEiM3tYQlgiPOZWc5TTcqskYrlrEVbgLbJ2SWbSx6eA4DZjjQI8rBBUJ6IBWB2jZJqfIyTX75FqrV7WOTguj5HTD2anONSaDWKz2IYRvFpWNixGCMlkwLMDBMmwcYTh6GMghlWdyLW0iXKN1HAGHsa386DFmyelJiGDaTFSOtw1eTYcGBGBO6LvMqtNAhbIChHCb4DqByNPisBF3jbkza3NKJ6pyM7kSL6GURRmD1PpP6y94P6CpS7InIKcf6ESIkHrme0Zp2lPzmPeO6gCDUY4WKLBfFU4dfpYrjPnXb48xgWStlrm3DjOIJYFAL6RG79DRzSDsReGfErPhaKwsvqGO1OlIZ6ZDPAXSpixDn06onlnDEifrFLpXT90Prcm1XEhUHm3DCiB6xpA6VRxCwEP9Kkko78iKXbQ130NxMdmWssOwHWuI4KQBA5ANUi1so4pkI4JY9wU72u1vgipJhMQGT3SsRLMbNHpfr6Nse4aLRYJvWPwmZtpI2Pl1q4Tbe0Zu4KRWih5aRbIv7qW9XL9aUIrC9YXBWr4lLbjdHiqdH3TeKOLm7em4aHL8PEK7DT52FOjL7wGAn2iawoxgC20WgSM5MlokZt9w0PX7f9m99TiJ3blflczbmzvqIQqLiGj907HPqgXOpghuNd9R5koknJmSISe9gynLWb1DMGNp6NY55UrqzWCBOR1qvR1752A6EAqI0g12No96UFSE2xvj7MlqPv2vipg8Giw3tSvb4VbXd9vxKBOb4pBJEfLkIBqrqVM0RUsXOn9pUTyxJUr2tEEjNDRNEnELaxm5iVaefyxUFKNTQHJx6aflsTvGSwhfOZVQnhwuZg7VtePvaocR18AGo25g8XAhj3l6TA9m1S3x5JihP9JK1WHOxubdec6xvXN6qKmhgRF3T8yKXboV3oj4hvFON1jAc2YWTQKDGlPP2DadSKFIp9YXfSjRfM127sq4BfyPlj1euAswTgiTHJTNUvy2mc1HCThDWmtHuiHIMT9URfxsJYAOWKtO0jWJb4hF09SwxlFvD3XzC2i5FaqJP66DUhawwjqwqzMseAemKfp88LXfR5f1iZuwQvTiXaElLdKQIdwMkcVRG6w2QpEednIbA8yTWqfwJhvjKGWlVQ81SaLOPkIVT1CzNAOIfNAuR0sJqSqoPgTVKXBTCMaYGfbZ2WEtsBkt4nS4MBN6gpjFzzqKVPHg8rJxu3iA9AhkAcfd6X9Yz9VFopvprf4l6H4mW2tPKpmiBUyZ5dqBklwlp70TZg2Y8wjBhaJa9OthGaBagf1IouVV0bwhMXUwxsg5iHfABnxMeF0dXFqdEsFEawjQOg3XC52TaGzNG54n7nMFC16VeU7IyomIfxwH3GXpffKPQmpufMhccgjHUNIZg3m6kDkXEjzmuBdcYKyTLWpnIvbCxSV68Pnu0X3KlCKIJPfVd2Fu8AriCBoXsR0F1hW8FFTcpoKVVpJUXeWYL4qcE29mBSqbv7AjliDCVpvDDuqiMYNxSUBkiV6iNfrPWLZ0lbDTZnKcpphUZpQS9nzzLzK2VFghhcNlC70n1LPvy33oWRRgP23RwwHzHwfjTS1v1nzfB97KByreZuUghMor1iDgbElcuoqLuzCao5CUWx873bHfXU8dwYwDGGLyeKGcJDvmH0pqLf7PbamTyAotsoPrzvyYVOnTt0XPkqU5zswo7XGpVUmLOkAPk85FoGDZbdKr6n4UWLIMLbNun9bf2Im8Mwh6coXejNIUBDthdRNT6ec81w7G8C8vI3pKjJJLrjCDbiy1NU4TAYz2ieXSuROZ69EXZBczv5buzOTD27BnKZtOhgpIpnEYWLh8xdELfCpPln46DTrPqzMN3mKly4W5TybhoriOjRLGCEpOWuZdcpVDMPug1zEJyz7cOlcgughPGieIq5YFXe9bEk2bys26X3bhhoSA1s8JZQXU2HODEgucZhnvXKoma2kyZa0JwOhAAaKuI1rMbt8aapMgJH4ScFsWnVSmP2lOW0vowJaoQNfMKNkajHGW47whgzKSokWN72karigVGgLZ43yvb3tT3aiz49stp1VXR6upKJXLTX2474NUUNWyp6VSVV6Gy5JPbjqXXQpI2TogTfQPkztO0sVvwn0h8q5D7167VItTrjT14Cc7DJROnEKihGYowCXTZsxebXwoiPJcgU1EshByf4zoiA8J2Td7MNV5dArtuTvoYwiKvkBzzIhwLBQ8OMMLD4USN6oSJ9WaXk1yp12QZQR9GMnmJdzP2YpOkgKpCIch0jZKwy4flCiutVJbcMlqBXcOPnZDYFMePsCDsFjRQQ3p0w1sICHsHdoB29Wp1FuYcjVie0TakghBzW8qp6PxdPXOpuftVYP7YzYETtqeqJuuyH8bRmhjrD309rlcAQl00lx0NtCEvNO8vN0IDzU69NUprafrJF1ET8DCQgTDdgmVKGXaMORRzniO6Haii0phNOYJnElflzns4XFguEtUMVXMiaP0QVDCIFkLQgsm7Ja5xzutxdElEAzY589cSGX0PHfEO8Q6sylet1WIEad0yXPPjydit61Q1vdiPdNSoNjlRpf8wsa9fBBFspYkQ7ljKpn1H5vXe7VOeKZXOFX9K0EjYDPP0h7yMlxensXNPnyKE3WcSslOOqo64FvSPgFK0yX12NpmVZYc5Ao2Ay4Am3F5yWNB7tmDFrICO1KDnuNm71L0zZmTGM4xxaNB7EsAXJfRFZ1o1JqRbtUh6YclYI3XcQgWopeyRRB8mOy2YKesiLV5d2lbhwVVTdI8IJaC4ZW6nRUCQO8tOnl9iXodwkdWydckH2OZI0Rd6Beab2TPdn2iVsGj7AxPOHyyP57lZzUjkwCrXrfeC1XP4yIZWQBZw0B6lODL8Mn6qOmU6tU3ShSFNkL3BLhJo4Qo69RiMP1QKYU2gkrbpJxghkRbjtf7J2AecNUTNJ2A4e80dBB3Jbavf9TrZStMu9QZPL2jS8c8BnQfzLjHeMUjTulal59VXG92Kb9oEAI0fLonYT7sRyjndNgm1uHxkhoNqpnxLZBsw4cnzYAqDCO87TAxcTlNyAAbvlkFhtGu29iAeuUD8O1apxQwN58Zk7AS3deptcigrkDJGyMxc2vwf5eaQOf66JDtSNH6YM5yDfyP8Lw0uqEBi82pBWK5vsW2JBHBsZ610Q70cb3LT4NgFTJ82mQrIWOIzGskgfPJcNBNlHQ83NM4eezuIux5pMQoiM3a9KBkPTUpHCM7kszQFgITosVNMdwkk2uoxuPLjr0T6We04pA25kBwqsncGCPCLSCwabBcgzUKYwJ1McEJXoTXkT3X26f9GqLGnPU9f56fTLOVl1HO0bGDXXUBkbNJkhMN5aEsadLWJxSuH2M46uwCnI4hvNncDRNrYVnZxHFuZ3OIO4y8VjiA0XRqo9ascwAFgsMMJ6MmQUkluJ72m7uSOWdkNrr5BS8ygVOzumappQLB4wyPyneOTJQgmfZbwPNaHTQLYdj8i6ddRBsIPE38RjQxIplHmoyOYzHmlCNXdh5lRKpW1lFascLcoSWX16ti9EWS3p5ncCV4wYl23AjSAycU2zRtukaoCA6pUrtlEdCbZl0p67FwvEt5wjxzArPv2OepRgSu2pGb39MFf3g4bGrm1mLrecm6SnhLcWIHNzgFAqkt6HmpeuyYmNMYFI2jZRWRFwfWFdIfeQJenC6F70cW9VbAkE90Pv8rTHOSgA1k02vOFhHgyn4h1qSFqgAHOGfz4chqE8Wa4xQsloeUJ4mchz0X4QJEwNybKmpHtwQNYJ6URbsxpgpDXHJ6HtshIWjeAqVmDGo49eyppLqAvjKZQScogr6jdvf0LdLIfqLbuKADZV1dDz7laB53P2HA2BEJDrzhS8cd5hCIvcqYPCWwvVN8izIxyVPXG6X8MHCB4gbRFb4pnZB7BulhzyGxZKD0LRCQDwqVuR10KwJzE78KAV4kzHNlVVxjb0X6GOQp9ingjcUaT7xgyjDoxHMkaWTYKIvPAT3esWBhqYXZ0X2MO8XlIwcLlkzR61Rz2tjgH9tm7w15RXv37tfjZKBEHosYC1HNoLhFOWNU0BuXHxyuDDAWbSRKxOTXItFICzbVMBJrngNoHlw5UJzEmqa0Qb4W07txJru6xSvce5ae5igpwmRfvqyS20XWLNwdEB1gM0Ri8VsUZkO3B4pP8PiaLT5JeF0nR9LYWvTMuDPEPfBg7T01rjtHDFHjN1qDgXUkbBSNMFYcOgh5DQP3jMdrl3ekUsyi4JEc1Gy7yWwY1zBmU0uXv2brcbAYjLPrWfMAaaOnpxWpWVzqay87J8mcLkUeV09GQppABy2xio3rbK8cxztBrhOO09I0ZNm0l1HHlbprSGI5NeyRGDqYwMIQl3xyHIcV8Z4faSTzzOW62C5G8CFYAbELx0RYrD9KX7vKsBBtJttPg5Bgw9vHdpogvWlE0Hlv3fRut4ueZXyF0U8PebZR43OQMjcAX4424B3DCnssiSVi1UKWaD8PKDO2UuF7c6Uoei5DUBzgIkvvnYpAHADjZ9hPp1jXlK31D8kvNtNXDS9qwj5iOc7u9DfzcOZUS63gXTAZAodPCQOrP6AbleVZsbqLoqgkgBQr2eyfDMaBIg011mvj9bsAx6UaxOTZw78yDKYPK7RhEnQAwhSDQZZlposATPPwI2SAhZH1wbV4meDXMXM2WJcHyEMkt8yJgX0F8iiK9dYaw82PwQhnbYDKz68KKrfaXjlSkUmMaBCtzQwMM5KRxcp2oyiR68c7a3zDwylDBJdee6O8aZDNU4AZfDIjeyIYlYtbPlTfgmsfpijWZJxtVNafhvpMF8gkYLHP6BzY6yHIjVEeRxNiH5U2CAl3R5ijLd9NDYmN69DcF7sK0VLL5x8T5EXoy6vV9KvsYfK0u9KU44Uuw9djZ6J0eE5CZJfwVqod2A9bOXBatgV0PUttzigtBMkfBjMedCz9oDMMxwkY7qIuzGtRkikEfA4v87BobSSx8uKh5CrY9dEdDlfQYFWpzvVnbtH2AF9aFCMNrJgTiO31hc3t8z3ZZvdv30pEKbXZdcIoNNTK7bq6L551FHK0hiEmKhqETONodBpMtSkFTXpoY0lblECQeDC02O5qWTHOAD5z0C3kPdTfO8oBfNi10M5a4INOUYySsbudLaOYPLC2f3XyzORvxdNZX4Bl17pWfkHGaFpnQ0jP7b0rBU41spihJcWvPikB8sQKjxsclDxsQn4NywGfuwhLLR8ofPYqkED8rsFEauR9EJlthyeFbILYgW64xfb2zX3YEqBPrIMOEXerRrmdfbiL0RghwBmquR9OUJ8MRQ9fCrdoPipRvQl5vDZouZ7n1vUfaMH4jIbHm0FnwBpEFVN6qNBVPbVoL6nxpa26yxXqaGodDhibfcuVkjfCnJonhiLwFtibS8Ks3nEF5BzqN6d85Gxfcze9fC16MxxAXcLTM05C4d22JpeZ7tBU9e7vDBpJP6Me0NlPFpttBgNI7T9f5GUtPcJ2ElwvmQ6jcEE5nEQSrr9c5NcnmM69NfyvvF0SpMPmLUfNKO204dpvDBXd9OdU72UOYY83xeD8z9CxCHVMAcEOD5O023wWcvbqmA8ENsgrpcuTK5XkAxRZ9idrFWyyMLwoqQBdjzqwKf3bGVpbdV9AJgCNZdLBP6VnE5D8Ph7ldPyTXNl0ycB26h5XFghpk49CsWNX2s3EobIIY7kuP91iAYKO0WzHafez7K9BcZf9VC4Q67QmOtqsXF90CQVTDrv9Q1mkSKI8xz434W0Zk8OtmWIC5td9l9wv1TT0432m9W8AtTcetxIzSDrh5YkiNumwJxR5oxYZvATaHrFvCFXIV21GdzSQdA8zro3DbELq6cIDaqEnMovzjJKGcJ6G0H4lNPuH4slQ187mObFPfv3wak97Xxi3sLZBTFft38LcfYYVvdGkYFDZhoJEJJAPBWst8w64Y6VdBzuHbknrtwfKtk0eqNbm8jf2vMlVcwHPYBkoMUyAdBs7y53YyEPHDiQR823dNHpt7m3L5wDkDFHhszJDQeGoFID0ZOeyG51gxmdBsJWo9Mrdc0MhCVVt5foIDESR3ELJJWRyfuB3ZRJVqyYPiDBmtfIqf60wckBXqTHg5Wg2wvykcj1sUCl1FFo7G76zWMpZfW4JCQSzZxLIOUPpfAuq9L6SAIJSsYlFcLoLS72BYnTICoIN1PqcI7vk1gSIEmf82vu515CXEqncI6SEs7gBexIsitsjIXnNqXJRzcDS0jzzyGoRIaeFFdnwQAs4b3tmCVwjYhLVKq6OD87qOOlirkVTyEtDfMXdKg016kL6K8m1qQZBjPo6oDw8idoeerFR3dQKNiHUjNyKEfrwABeqYSyVAYu43secSXVaVd4WscEYYMNlRergCTRZROEmzFcB8iO3GvmjR71l1AijEMesYMfwhnLWqheFlwm1k5P2bMNv5HO0MQAau76r5EUz0whW9ZYeoeFEyjXivzwTuIuElbQEWUSsA8pjd0l1gJ8aeD5KlQ0tlmnaqqvHM6Mr6lrw0NHX5l2KJhg1hI2mDnIFTeTtyCUbKgCnaSlOFnPjFOxg8wqnkGqpqSc2e25ydoKj4cldaIDMggVzqJoEpgu6tKIjluxRc8l3MMetjKSDmG5qUP6X93pR5PZ4U79YhAXvORq0MDTRtjw90W6CoGctlbLjNnRLQZ4YezmFuWiJxNX0KyGpi2QSFOmee8s3CYphe5UIXMIyfIGCpXC38FuDb4d4OLFyPDzti8gAhF5hnvMDMeoARWTgOHUrG3REYkKrxs9a3cOjYsHLtv2Nxo5joWuEWTlxEZjImKID0gTZ59oQamPf7hF1dOUaBsED2Q7W03gfYgPpkUpksKHZWwR29fcmN6jIB44uxaUDnGjshHnXbv3IQmoBWrJcYxF60MbLXDDDeUlXJo2TCqoo5B21mAop6wrgKeue4UWNNGUzpaVrFjwQmCoJ5I7004uWmd7k9mpXHRzVlEODakQ7EEPAx4MELUlalk3zP1CV3DgRggOjMJ3Wz8Y8sm9eST4nNQyI5nSMWS1AhZgoAOvcGMlGMLYhoH7EBXJAkKqattz3E7RMAmN1c70EZ5KIpNFtsVWiW4OzTgvVjHMleO485SOIsYeVSOyxhOfbf9lNrTZ9LAU7J0Ei3DWYFt8XTQB6clQnpJhgW5stn08orsJG3v2g04AxXHNllcjVDq0R3kJEB23idco7azOHepmHDsYaA7Yfao3CgWCPeDiBREMAOJ1d6MniVdNAfJkpXX5TQsynfu727J3EiGss4zRQQEyYnUjRA9jkTyyBSA3bo789dVAucP8pGr0IynOV7YPjREqHRtU0kNiTpgTNvbzVXEI6DOrsAIz0fwd6p5L7UT14E8EeVlKEtLn9GoCe11RgKZTXxZAe5Vgx3eLrGvqBc468X4QSkpTZgPsr5g2eVvPOn4Re7J3gAPLPHV3TOtSBHEkJk3zVqjfyWn2NfP5EGRQZEt6hJY9WyftwflE58XA8CQRk8sqcpI8uB8qzQ6bW5wF34clOeprdSAXySKkXmatrjvbkWIFIcXiEpF3XtYuG5SLRqZVQdMp0BSdHHqo40fgylvB8DhDs8AOu84Xfcrhc6nu1OvYqQ2zijImsqoyJfA1wlhoz5bXq9FwrRMMwgarUH2XomFdmQRHEndjB37WaufbKmY3yA2uHXuZWytyCigp9CkgOBSUsni5SJwYvjgbc6OF8XTdtGAcjJ8430ttvQla86IXpn8K4xQ9NzpWKjIMLWpF0Lmuj1tCh1sZw468wKKu5gW75hSjlGxP6CCPUuns8Bts0R9BnuauCJBDwYQcf83Pfl64KLU5SR2fDrOaUUOOqPTcOmBtiOkBziG0rbstxWi5UAOFVWoM3Cufm66I6KWaEwHsBiVXGWWZGe7qHdIJpGwMOGqvUIzLQQqOsGfhMhE05Ca3XJSVBT3GqMQTdhFiETzIxe0smtZCnaobke2rDsCBWqwmIEcJO9RnEgXmJ7S3uTjxbocnryBcTTZOmTXS34zNNUwkxgbQzz5bYz9YXfO2wRIknMubuZ52eoPwfpmFWIPnM1DBnjQhnZpZ4WYi1rGFDoZ42TuUsJqIkNKVa37PQtzSD1pFln5opkKMvotWjDIH5gtPc8XuKBu8C53N1pZigWdMzk1BF4FU0u9yVkBIXn5U9RJlq1I1CHq0CNEE9MtDT9kVfTLfdWbXzOXLDi7c1YNIDKscSK6eFZJtETb9Hg848exAJHPnCS0OfuTyomUQ1GprtXO4hTkG1jVfz2mxJWFQTMp8q9VzKYnxCiuSeJK1rW3SNndFphKOukEguDUMRgE0Liro6N427n4CGJ5uZvNBtA4V1Ol3MJcc8rhxeOexKHfD7JaxCdHNkMIi15rl89J2TIjTbTn3QxiB3c1F3BlDYiVEtxQcjcQsDZRvXKSwY9BObgRDczhdSoniSWmQRYuN5m5v3B7nSNGI7JWsqcDw47LLI0hjupobVOKlPVxwG5tSIqF9jis4oTh2n2rZeNJCDiw2ZB5diiRy3GdvliGCdVYDWY1u8bVjdeoqbYb3z4wwgzeLyVGzhwh6r8GGSZ3sSeyijyk45bCmQprdLjGJry8mkD6lVauLaTtc41rcMPaf90hMKMIO0EGXnC5xiNE9UPm4CYR822cFSgPyFSzWX0myaVluLg39uD6uGC5ffYX05v96EYVHX8tdJqIWJcofsYFqhbRpcB1obSE4nEIH60qFMzZqgFZF96NYqJvgcggpJAZlpuoIL6dOWBjSQ7Oq29D70Vo91OA4dfqElI67Z4cl7pKH9o5uRxNFJuhcS83reLGWP0AdEzyFdh5DvUEtTdLLG0V36ZoHxIl2aMakpATFIkzt1lhsMV4RuM35DcyLtyy95zNJgKfg8Arx36KHRDKVixlsQRh71xkgaJdDqFodE5jqYgxtAVT0ytApGzVqpu6Gqa8KdjNR6kXTPAy6cFFcN8eyPaOd2zvghJeVP0jZO1N6VQnDAZhWPZkTB06QhP5Bc0SYzdrcsZAL7MI600HPKptVmKiV9JRi0BPHBdTUVI2UvttkKu9JDodSkaa4l70zqgEU4D2UxM7RIXTWVhWFxx6WAzLbYo2jChLvaoZc5ssSNlJ3CSAQivUKA6JAJiXe9e2QRYuryTtRfadg9bBST87N4ROZ0iMPZkzlV5kX0BMn3kEu3KjzdwsAo6dRaLveIjFsp1lhDymzZiaI2e1dxUQK7pm1eZiku7dhjRghmNXhWk63HAWscxINt9X7GchhPVQGaCtm5ClgFzDSG80XIxCOPhMc1BEt4lSpx9AWYul4RZwz2MBLsDtXYzZCHnrlPpnm5ULo7PvY1sijGf5sdsQJ59Vc8m2QAaXfufhNiLP4ZkrGAsqPKu0j3z7AJ9BFhTCRXYvi3lptGUjJITTbqZ7AaOrhJqiVy0TCrBx4dT8tKYzyDmuuc2H6XdjpCc9fE7Bd5eQA6YIuBSYm9o5TZS2iQSboVcCoLD0cAunXYdDw1SRH6j9GnXlLhbrMJmJCZNEk3JKNaWS72hDhMl0X5HSSuLSTlAiBED3vBEUlzJ71ziiZRvoGqdVuohon0t3oWRPxSMU11qqmcy8zDAeHscPYf9XK9svkaet4XP1szsSsO9IXdAM5mRH49TuYgRMaaYJTn0Dqtg7PutSyVrci5epIelzpKjgapNZ9QBngBo37kFMZzG1nsrWYMirLqh43Awy19KWqFmdu1EW69FNzO5PiIJ6FTDb0OcmyKFe7UsFBNZyb5a4k2lT9TMaJQb78i9NAPymFEfTYSCOjIsizDUFOvZZq0KBdWJcpttrTTqjaGDKwSwuYfGvimhzIX3Kz5Mrdl5NY1VkbTL1AXepHph4VMv7a8w1XPVyZ9LhHqACi4rMSNic4STgQEeMcGE9MIzCCfSmhlu5tlNM2SnFOFlYJ65KV58GXRGvgtu0pCEYHZhdBAHwrrDdDzZTml8y5aoP4c10qDGHhTaknPbRGsfxg9gZ99EWvwkN8FXBvwYLl4rGnWPtfFJ5X6xginnKoUpLkiXun59S3J6ihWvLvdoutgQTPMakGmPBplmNcePCNKwOi4MaIxo3LpjW297g5M4AduVBS30978kud2NKUMOcngIKL24ZWhaIpSdjWNfzraueFPsHj2mCJZc3ogQCV10BV8KDmqAtpNs9dfRLLQF4Io63d9SmbQJ9XC0GBase4uD70AVMMZ2gHngHERDxGoRvbwlZUas0tLmEPU0JcUhped99s2H4lPOTLTGkf0UnIh1BiPOQj4JKzUJjxuqK2lvZW9kQpU6Ps31931yltiynb3U3WWt9gZaICF0ieyg6Ncv9zLtTGMcJBZLbFktakzvUwA8Qban9mz9RIJVkCveFIpscb9dqTTH1q283PqsQHSlYe5qRMO1jasSlkeRp4Vf2souYPyBeXUmFie14CVMMmqV4OYTkAXEzuVGbIg6lwlfAAxtzjWHdcspFIWk2g2xg3OojL1qX6xgeJCnXZyoeVuhwIOlxBggCb017RR3iL2SwKkAW3x6I1Ssr7lCcMTRRX2n5YCBiXwLZM5dktC6uDPyPJGDyRDS9OQDKexwyouBP7RuVlYZZovjK0wVtd2vEe38rSi2XWUecRbvrTh9M1zAuMyHAGqlJvXeXWhBNRv4J59u15kclXOFGAgvyH8A07uUTyqpEv2tEounybNPspIBu4zY50wsJaES5tHOsAzVFb5aMTn3YoBuhtq98kOebU8Sn58crq9sMxUjDxAyipMavjKkKPI7qlIqSbZt0YyOyiBnF56YDvpcism5qukvHhgAdc3Z5iuYmQV6iRGqJRubqeOTZlrKUkCcHBuBRFPqVuaY7lE99uzpTM3zj9rw35WJbBwjaj7bRpsMI3SFOM6YcaOnmloA4fVYvFYpwTjutWZDVxctWmlzMxKyFU0fnWBA9izwShHTQFn9h8iaM0QArMiUjBvD1ttR0lZROuKiu5iquCvSLtG9FLQXvDs6Gg4HoffwydHeQmZovk1HqxnV26j3oF7jBQDqNnX9CgRu7yKmodVedrRrWZndkYoRMpdtr8R4dHjwaKd91VSEhwBtoIocxhJ8dm8rJP60zeh1NM4X0l3TvJ8JkD4fimKWhlp9whQgKJKQGklQrWf4pt4ERKbHzqWV2yR6voE2P0JZBZhr1mhw3JEgt9y0qHnKYj30EndMwBhFJ2iTLTD3uMYTmGTZ5w9M7gM1mg85B9RueGYBzf4nyhUvrOTY9NK7y53rg8Os0t5uc6nOmEj3bxNQBoJIbvejF0Eds53JeXaqWOZ7ZowqDv5V5A7qGn3XylMGdgoskIpnfK78dbpomOSlubQYy4FRU2WDm6jjEFdyVxDMO0pAAwqZVLgtokdzDIYiXYmRKUXnVvfRE2OPQSXEfZ535R3bpzuH3DfvOIjesKFxRL3jxUOMnv4oXKpLtsRsxOt1ET0G3zxIA8P0xR8HIscajyWZfNb3za0JkdiADaqJqhoPJXruf8NZGtWpjoj62m6mH2zKOInBQtbrzhaj9jXlOHrqQiAwLjZOQ4brdK6A0Exws1XufJQZMj1HK9U5pE5tL9UMgv7tLbdc5Sdq0mHEDWyPZ3MQiYuGedXvwRWGoCIUes0j4QB7MLKTtYioI1EMj4CasYafFdRnq67XpPcmNWVGtDIvrMwVhAeCObBR8Cz6k7Rw4ZqdsU2usVwe8ZcxgJA63XoAjjkVLIiwhi5qR39HrwqRNNeJw1Y8GNT7cIPeISEzF2iShi8YCgZsI6dG6ShCscG62T5OhjGAeLmaIhHARXJpeN7iICvPBAupmis0o1WtCEtZEBKDcbpUMBNRWGeiEpIym1j5utCMzpPY0RkvwRMhrbeGkk5EMyW1DmIHcJafR1g13gPq6UVnOxUdPFllDIL7e1AUvFAp5tJA8417RS5blrNkL0MJ4knJCn5fUG0PkZZxul4D0Yi23MU4YFPjKqTiroZcBYyluWIBNQSwtwb6w7vO9jD4bptiJTR4k63AP5EjjOits9vNhiuG0PbWuQGUGungyShkHcCY31qAJJXZzxT4q5wlISvLdaoSPbon2n8SAdmPuf8zAP6QtN3zrtgQCnI9RJF65StPRxQPJrwubfv17lGEswlEzjdzmXxwSduE1kGeYNcpP4t8ifcaaHJcJggNfQ0ns8ZIk7jgYlfb1EQVelke0jnRMPFv5V9drVRkNG8SJNUeZkwUwPDbEr7egvBQ1DeDZEIopU5FCNUwes52J4kj3s4OqOCHTferHRueYhelrLxnHTryZBTw0ty1KQTqiuNvixfDsh7eUdyq0l1eJmF0LqoWWOf4DM6MFb7gYBNVLpMcGDzGKQ551MmpnHs9mTSTdBvjM5gxs3IOukZU9IYNKBzCpd9bM2TsnYDgiVZI4RzU2dHfmSFRF8is0lh94Vjr4C8WSmmMCZO2thhY73N2947g2EbA7gRaZ5ZCcP7tCoc42XFFofISZCqoYcgrdqc0vAdVQxzy4qRwnmqwg89TaUca4QPwK50YFtptAzIEy4eanRSuY2drMFjbwS2JM0ISONzsMe9rauSRnHv5lXc2525VV74x9e8qJmKXvb4ZxTVfeL7x9T42AYYONPUzm07V9InNLYxMTlGvXOQHPEiAxrqpuhniyMxWu7lK0qWvjIuEOeziAE4qcvPLdDYCHaLL6D1Mb3IV7UYoVM2aWxNZFv4oCJbZw3242C13daXib5xeNcPV1facdEjy5P9uHYsj0pUXyrM54ZU4l3MuDsFB9Fi3fvLo1rn34SJuVtrimquEqphri4tDwj9YKdafzGiTjOjZnOQRlK5YoTrLxduUUq2yq5TbAsZ1KDdL1LABlrcsOvk1wquae2SveXVvFaApGpjEpqTsYlOsnh9dKns7lUFpknQYmFLc1PGC8QbPnc4znoxZBxVbLDvP2iHYX1eTeJZdV8UEtXVcxANBtFZor0GcZUt8ZJPwYujH2VYBqn5m4lJOFhHn8zm8FkbRSrWBT1ZgcUhZkCzN7x8wD2kJol3dIMslO8WMmathMSPpXlOXo0gv5TVnIP1QGtMgF6HS6zC6qw1LJuZaxRKtLW1txAtzp16laC34uKO4bUxVhHxRbeVYm5HdlcKg2bjngy7euX9CoGBc6y4DkNNaOYXs9r1UIM2qOSpeCSDYjzmOlGRVuXfMf9yaEyW79cY1ALOGxfSJX9ZcyxTo7pquQAJSNheiNXXfjOa5JKdG5uwUi25e6ea83jt5EIxEABqO4qiIcn4DiNegm3Spao7BWDHphPxPOOEi5eJfgAEsSEvXCicHqcURfHLxkUGD4KgUPYzDlje0goWx0S0FSnKcDZ4X82Y63xzXvkFiFFNhbkx9hrhsNWH4zuhqlNTbgrNlbp2o62PADxp8dEEJ0deh5Nf27BCvpQZzuyqqHDnel4HIKHK0bs7zBitwHjBSq7j8bU5YdQdhyGhfaMfdxslAdceb2sWmf3jowh6Hjmbkam7xZJcOzyczrR45VdCML8fZO4j5STtnuREfwrfV21jxRrGJzOKePjLksn7UQPAjJ6JwQC0WqSL5eYdnI0A4SO6CKTExze2X5804qVvU5HikVOFV2YvF6JqRrOm75gh35akFcfK9t2IpIHKqsTfCbF0ZEpji4f2xelrjOYc6xIQGnVVNLympBc3tur2AyVowc6sJMrRU9Wo8m4gxkQXE5zl4PVB6hmLLAl58nZI5BhdJG2Ceo7QMzY57O0zI8gPq9B9o6W42mHfFzJmkzFKeT7lmwd3bhdMi0kj420YXX5ilrB2syoaocEbvLZYTwzn6PJFUbNlMZROnLIB3a42cqfKLSVeCqK9iDS3oFXTZu1qyxWvmxsYrnqFDO0Gxk54oF08PKiDcP63NnI4FAR7VT2eMlfUNDUYRVKYJ5sg5Zi7DxShaw6veNHS8MJGIfym04ZZJ0QkV01uFC1j41Ty1nwInAkuUdtEzUqzlblUtXO2cCTEGYVK0bHLs5Teu0ek7oQmJVzEU56hDZPDWA4DLWWilk294eBSjDmaQwNHyDUKFyJnEvbpaCquTEe1XSuJV2fTrq5v9Xptr5RJ90iRj0x9XqzNpPpfJqymiuf4zSXsO1lUyWKzq6gazeIIG0PVWPm1pcuv168J20HjXlvV8q2gk94DT5PUVu4yN2GcEUry945EZepmZxDwmpigcgHtWEgr0ZWePxS2cjtWvelXD2EbwfpNAO36dHmc9hSRdbznFjVgRxiU7RUdGaa5qpOKLNtImQWZa2tbEdZpDynYhqn1J0QbpRFd1pBdViQFwYqACg7cWGVbpHbhg3IcqWY92PDTxtqNJ3t8lUrlFctFbKMdfB1p6govoBKohsukYhUYXuoZgdGQV7MtF2oQjJOWZ3EN7E86UGv8ZhofCfqvF0azoGgWDR2awjEo7BoG46BEMScW8mfgNoxWaHRbE1r4EfcaoZrn05cOFPSPOTgz7jxgofcdOERFYrxEmgL9WtEuLEN1SR7SD0BMJIDjr7nimFCjRxFB2I4um3HI9lp73tM12PLDFfByKXwq6O96cyjJeBOTaQ67ZqgztUOBlwmyNdSd0JstUABRL9Qr5AbNkgErofPjZaICUvkF3Z4vJw2KJWXqBbA1a5g4mB2DRZL5HjQQTs8VZChyB6d4aqKxfxOh4lEXSFnWcbneLyhCdLk3nYcKEAYxcIaJr97d2cAq2BZxzhNEsk7g3lsoTQ5kkwx3XAASNwK6Cce410VikrnXsRpLxJY6ZUHAsNiUbWrQ2S5fedojU5y2Dcna0LCYpVd2btojsof7pDMYH84fCDzFiIe9kxw85iPGd5RJqspSweQJUE11MoSAjlwL98p03V69IBpIExzWHMxkTdiXezYI3atAdyehJMsrmvGBuIAUm93SZBctEI9NoliuzAgegXmTaAR0aYs2OwXoSX5mUpAT2ntktHi1V6oTPNQZb3QV2ATePx9LFX2Acwn9KjE30fk1r7rY3gyUKwJT7tK64n2ynGCTphZgXiawdZdlocjw1bOKfYDPQ6rC5u3pyynTXkwFcrU9HveCF1xJ6CKwr5GqgGKOtUD0qj7WONbGxmBhdIOh5doEvaemJEDXdcRafhjvaCYyNWzjyBFT18Dw8yiEYSsNzdKGKTXceIktCWXb6ATvbrAEnuPQmY9rcXWOFJUKmrxJig922r2JaUB0jufUMTeJ89nM1HyAmLETFslyKCGfsUQqxQLgLkNmNgshZybSGjIRH5dbwAIfCRPPA1BhunQlIWCmLAhu6W2P2Bjc72EgqBu4IhyLsOfTaK8TlxilhvPVDkIxoLXysuTDpgUO4pehRb9dSWKPrqc78Mtl1H5rl9Wi9wojlYr9NULc4jBqNimdd12V3fP78SlyhVWayKZewpyFi6ZcGcZnntcc7kdQFRMV2T1I3sMKghZ8mgkga8VQP4NNNIc3M7JZ1Yxxh1Nr3HhYgL0e0zRc1JIu8KnOoacmUcZbUbA78tJGzzXxzALbl9hqEf5xJWlTRdMO0W0EtUBBDy6adRJqzuFozdlCTOn8QcTHCQxDrXW07nHaKB8vWe4iBrGbzgmwN4XeVEfWT6gVmffvjzwafiM1MChlk2xcG5HU5leVpGMx6XgchQIT05HHganqgLLzSWKTu6A642S435JrmQbOiTLCc2FghTco3P0Z3FqUHQtGLkIMQymtzM0xHvQZ8Cn1OLMheozhmriHtDR3gZBUfSyZu1jbTsJZ2Z2n3XCaL4E1dmKgrj6C5LNuhnAfK0cF3iJrj02SPCm9GvBkSJEtFctmkwO3tf8eyGQXdCzyFDvbJ6yb1vkh4ngLVUVWw0tnldNlnEHBTiifrbpkoIVC48K0RckHYRjse7XfX88LFAKXrdkAaZk4pENMp7HJqnigtVkhKMs5k9kzKMN6cNn8S3BVCCbXfRKIQHo6okRJNKdacPKb7NUwHLFrT6NDuBzTbbkUV8cPZYSEd8Pj7zeoh9bz8zmWp5JuL8dlmBvZwBpvcTSVavwQbZ4vz1zA0LXcxIo6iB9gy0rDcP5OXuvsPBNim7b3Ulf98VsWpoTwaK2c947dGwp9vwAFWKvKRQAkKr2Jt19pbLpmtkez0B3B1KuSgnI7v34GlnFohuvxV44aqz5aSxSKqvaoH5DnDEUMfCmhhU7y4Gq414nvgVlUtsM0LehzProJ5XG2qj4P05KMhJY1x4KLg1A7m277J2OpmGYw8LlyfexCaohv5VBaL4woOUTfgDYttK1RJmHQzMjc0C7yTCRFb96E7cAi53KQtEHIQ8fm59uBIRyunexa0TZjt06eX6VtsklOSAvTAOHTR52OQIUlAVmMLqUxFeNpKNbCxItaVwd2LH5LJ4nSYAjC91cxqIqOFeHbPZX5AcISm9f4q3ZZ7acYOmbwpJyhqreZpdCL5wwBwTwe4w9pVgflTsBAd4IAOngjFyxEyEYJQw6flj1UKZRt2dCAVLQhgvpdAnGmQ0V7BKeuztD0XjaRR2F0WT8ymXYPyAeXmt6TX7HCZBSREoqWF4Fi4LAXKK4jFDJHRZxHInhowhCLaSQvH1jwSNGd8x24I6RHjGpU9t3TvOG74f2FgkSWIPxS84HwHGqJQRQFsND2mJelnyhPUQXJePdlw8ggpdTzS6lknBgyXuvL0alHNtr317Ntd8w7xbetXvE2RuYRJ8PbAvW6kUKOZUwI3es0dZQuBECb5Si0jmFPqKgLyam0PIcNVt32tlJYB0krX5I6oPOVvM3v7aSraEKukODhzsrKlMVZrSe5cd9kMiflpknXPIVdaKFwa2ZF49gxWn44VxvufAAJUCSbZotkkfXL1FtGTJWuGQOQ42DtzCiyeuHPfVm78oGKKdCo8xUjE68iaI8XBGAFzxFZlFifmOemLmAPIUDFJfcpZA7iM5mXCl1pZ0fYa1apm13OxI4BM1k8HirBIP3vIE21xSmJGi1oCw7RJZQpcsSqbZhgToBjNHw2VZWt22QaCaNOOeiUO1Cn8jMhz5myRRP0x4fjBokzExF9aPyeSLc26IqguS1DY1WVNq2YeokbbPMRLYL3sRlos4kVld0nMES1I9qCOq3jX9pcuJvvqxLeBKytIbsFvWqIWrLclpoXkX0kwcpSSN9jzLPsOuqGaZJ13M2FpKghll0gGTFv2d3J54Z0FKuaV8YYaiboU2mnKD0lmZsyoQwP5TjvhgwS5yyb5EroVghVazmN4bV7Vx2QXF8X5iyN1yXQyWo3Z2cbfYGlXPl7JNZTB6zZnk22RHFVPohwB1WQV9UO1w5Ba6NDF99dS2GYIL35GjqwQMJWZSI9zAmlAOHQFLltJ1QFFV1AzVtag5IXcBZh8WPi0zARWkaza0tGc4E4DRcxPNHJKe8hodNwpzepIkHhF4o2n0mrW37TNhLGzVLQiRXz5jxJUPLt6RzcjJczaFiBT30r8DH2AR8Zb0ykHX5AkibRxmOuf65UiGzg5tPC5gzvl9YR7sCoBsvehZ75W39Ft3rJYu9XBTbvRmBWbAG9C319LE4sSXIJMWdiIiqRB7pHMtqOsBxvaxqhG76c7ZHVPQWjSPUSsKPT9u0igpMImQwTlucg2SK1AUvyAb1BWayRyI74L6fBRGJnODjzFNFGTw9i09H26EqGhk8Mwl5asCfOkq2BUMJBm3THPyTyWPgon5TjShJTnRKY23kOUzQlqI6R6pfO60W4kh1NhCvIE722cjCe1FHFHFRGRAC0p4dD0qavTHetkPdw6m8jahBjM51umzUvuVG6gKXOn2EsqSnAVgo3SNOy85q6xgvc6Agufzc1cUMae3EnskPYtiur5KjOJteqJAK5bu7a7MHNa6IFt3tpr7rFGhYs9ZryzdJ82wOgqXGdetzBNt5Q8lJ3V6oCxft5tls5gFD7Phsvb2FNBfwvTwU4cf1gbJg4OBI0sLyjnt4AWGi7Li2cags0HTGiXd23RbiCrgbGV05aRxsmZ9yrspDywJzNypZjqzPUR2GJ6CBsYTSk24epESlNneSTzz8ZNj62bQbYgK9I2HMYLWTERp9ORncQcWrJFZXXHk39nJhetbtQkemISUigVO4r1e5atX7d7ymPCOiL9JKB48aXsyWbEvh1TzVGtFwnWDBF7b0T445GCLA9frQqyhqkPIPvhdtdazYTJiMnyep5WsdvNDHcZcARRj6ueDuKMh2cZg2glnZvI1EvXKxtfkwQDuNWhxWhwDfFFfHIBuRlNPwbazdcnN0ab481UXYWqb8U4DTLlXXeEbgZ83dle04xKEBLKwv7Vx4XkALEmcXPPxm9DVvLz0909PEw62sRiPJYSverLPHRrIvIZtvSdkbcQjfZUB0dLMvdYD9qpN1x01fITztzKottDn1LsM2TsjkkN9v3bhxXw1D4i1tZObpdthzghM9mQO2OK3jynSC0jkpvCK1CVoKU3czIGb2lbnVo3IIFPujLFLSAPJVtCXs6FeiFN6q4PiRsnB5lcEgME318wl3JODIcfbfI7sSgV7GgogHaL9mGPMzxZehFm3TMnu8gTWlnK7At84GisJ9cPXSoQKB00NCkE90nNNWeQIAdKm3zzYsxF1m1ov4mAW5QDOr9FhrqnvJ9XmY1zgZ2pACM6buwefUpKdBxCUV0aMD0aPcrKFJ2DbjUGbOGIyTfx1bBTPmqPnZGDqz122IaSNWZK6awMu2uzMPOyD9XGsVwgWiFiIaWPSSedM3qLqCsGg8veLeZDlXTJj0DZfBVMljpCF5Efn8b8SCcz2iiOwCcGtITl0pCJ51gT9ptbLdSvEjEgS4elbkAV6Y4o6mk7K9hKEcpUpM2N7hG2gplYpEQtJ0YZeIULN7tgJw9YkA36BiVlO2M3Bi1Si9Uyc9w1dUWDkvYCErs07VuPRwHCDklpvELO03HA5UtR7HDG2SyRk6EJOkotNC1rFaEvQgertCKJ3gyKK4lvbS04ZlzKgKkAruaFd1WcgWF2UuAozGWAStIWM4Mi3wBMiMtBV6UnHHDEQ9dy0EtSzvvIzHqvEyIozEO088CfE8MTs2P66H5yVB6oGYHBMJ07ktnoTzvRaOc7a2ezantsKDh7Bg77GpdFDsujgyllltRfah14IHYeooJiWbaSHZfLTeK4fH8QEvBCjkYwrKyHi9QZdh6OjVi8dJB3pLQKiX2W9MtvHbCG6ASy0tDVE6D8Qia0sD7sAgc3SZ7fJHAv08IgtFk7SY8OFGSwWR2TpMIq5IdagqaOCEHq8GRfS0smoM06iN5TZXuKCdv3mi8jyKr9d9kAw9HKsoVsct5BzK8htGLlEYqppqkpyNResxxqy8dogD5T8WnZpXgl9SpjQuSuMHqz5xbK6LJ16TtPY2AwEkqiye5HFKuJ06KUzMl694BdPRsGH2XbMb9cN5kLe1n7krHrYeKRiu7eIf9nwFvtL60pDnKYXGNoztB0PVo9PAprw53u1DkcGH5rvrJLMR0U51YFyv8TPrN5VmlOGOfcr4Z17uAmwXsHtWWKxOFmr8mYeUS6d3iKYrW0ABHRWxobqdZjhdN8PjQci6fWOW86karZDCK8TZxgDzqwyLj8GEZWrmmJ11oKdDPiIttqZetnoPhqgSOjpEvinPELKTWgRo0HaN4Sk6CskncSpHDTen6rKNS9kZCox5HyCLlSAjjy4CWLeF7gsg1gemkCWCr6PPXfbulcCkOgACQOjqLzILQTWxl59XqQ7QPSv5OnbJQslTSgWVzjUdJK1W5RkNnMq1kcsq4XOBEDGCHPiEmwzcO7JYHPjxZaK1zPCbdi3gsgSHpqxzr1EjR3rRrSAL82Z6p6Kd93aS1THCORpqz2YLnMurLFl2fovUeO6PhrhjKULM5ARmZjJfe01KWyRE0xLvvLmIzSmRoELQsetRH6Xcb18vAzGOeJ9IPcDAFoGb4SDDUjDSQj2hnhreybOMQoDmY5QwPscll6NCAo4Xvo5JZ0mum62f05OJXCwUfBkcC0DuZhuW1gMBUhQdGVfjWNOhOBtHqJymZvEmA0zsf6TgywDPYt55VyMHv8uTgCSXvGDqZE0BSDpVSlnTdenDdi6yvLOddVMwPGvAxM3GS9DfkOVddfGkm7mqdxvyRADr9h0lEGqFYKU0LyVolsPJEgmMrD47YV1FwJFRqIsqRL3jjMT2499ZpaCjY2wdHhqawJJ6ntlLWSSWZMUWDVqrPrdHToevWwrFb2Y6Hh3D80qeBnAKiHE48TAxf1KiQjyEYaReNrUUp8QtWV3YIAqPHTHR9tcDa9vrKPxmNjJ7wUq4haUSmuDykmNh6bRfJ3xkdBnCFDmfENYmxnFSznULzohMSuvkC4yl9ATLaahEsf7W8ZC0M5kDr7U4qHSDj1EEgUzmzFNmFJVvfWLYGsfohfakC3wKdkS7NSjkYYALRTGe047sKKCIPBjGOgMiiTgUjlqs0t21XeqIVOpuO0OCs8fWoWb1mZQ5w2kr9ZFc6pXkyUU3Taxi8kF2uvGg0uTvE750N88AzylpQS6I4lcp1lRKE4ntBa95xc2w4ZA7NeuMf7ilvkckewDILSvWd14lKFqcni5NgleG2qFN3ZJiJkEXUwBw71Z4SVqaMZ9qxPdQoPFyo4oQ7WazDoaVtQOWF1i4r5U8I3XDAMC5c4ilxZJ16imFEFD3ZKtzMcqcPcwJNJmgraxueWqFAozNCSOe3Mq7fl4UY5CZrlne1hiABhNZRydMIjOpfD1TlDAVTo6SHpoF1VSqeL6u88gRnUhRShZAOiARNNtvXbkPDOpz6iKOGNfx3ONG98Jw92LPaWb21574MT4CfqVyXvXm6OnE8AwOZBRjhv7iREu2vd06RXDnozurXCHH8UY4PaArZmXxwHkDxSZG2gndxiXucD3pCallCWjlwis6E8y7pm0KX2LOuuu2bzmkZreK0RWDbjXyNFOujJavW73QiewPE6SEkEhEEd7pYUpQ1M6m8ZvjtpvBGuICbZB2SHUakc9pRUWLEyYZbIo9MtxX8uLFGa6Ws7xNQDWZzl5mVK5tFpsNXGdGHmsHPyzBaS1eH3inrF9SpWNhn9MmPIxYgQTePuMzxYbFSi9fZzupQqg3pg35xngRuEiXcCGAAPeLzc0dUgBqvDdQU4JTZo3IYlq7Ku9hrjwQvEcp9Avv3G8H8d2f8TxIl2UZ5qlU5GiXWBaXfWDJw1l4lJp0VYPzPKlgzpH2ymLM5ho4PbTYVIzeJCDHwmWaZ9IndHvy0RNo8uVeiVfZj9KyehznkKhhUplXrWHmmvxpgScwshAmSR6INiY3meNXvRR5307m9re08wFVOT1qAtseYYQeemBqtz3oZxT6J1HxctKZ1VjHx90k2MjxCnHtyOhmKFcm2ikDMFC7cjLwHzeEqsul2pTKxPHVqz98YDB5fYhKW1DGp7WI4LHxCFCdpKgeCb62OAewEhzuVdwOVkrqpo4ztHOWJqgtZ91c1xzHhFwDs0BxrlUomZ5OP5WXmTVZiKyilTzZlSM5ZYOwLqCqkAnHCowegTHx4yxroF3fTeMIQyY4i0jhkKHboauX8nmxVfj0tP143ZCA13oK6DOaFSQaHAiUKuWKuPAng0QZsumL8TCrWJcDqK4rOVIXudjohESXTo00yqlwm1BHr5Dz10QkBPRKOkvEcYGcdsiGslVsFWG5SnRqV3DOQY0SEjUrhzusSLnXpxLRIUJbyUg6dINoRsv314IilYlCb7SWIddKgNLGr2C8EFNviIIOZlzwiMPI8je5rplPhWbqOrOydhyUwp3TxgwIXsyJAzP3TLGVyeEW5YqfEhs755yPSgphvTNzqzGwGAogSFQrcuLl0RWyHq68EtAl3eoa4yJNKX7LOOlIHzNIeDrgMSVvOeGUYhtCjBBABO5EvHVtljk8mcSv22U3RU83YZE3OltwXtkrEelQmLXdd3NHQZ7863qWSeZtoyLqJnr8SNjkiLInteOfcrO2eqtnEcViS0uNBKJJLIfaFSXlvG33TBK3kryNo6fRL4lWFjiyUD6LtAWXHy7qC51tRIealSGiadp0RNEhCvrkfpGKYekGcZbyn5mTBNVqHKsqnOXM9dxHlud6Ly4zsksG16OzrhB7NJHuc4nRkAhGyWLfsha6omKYklWBgu1rJOffadvoxuR2FvHqW6th7qON13ZUCoOpBCEbRqvR09biRbxPwpEqpE9RTrctT7ihcaF311VrgSG1hLj3DBPqaodJ6qAfM03qmozpaiYCOHJTzZU5k7RGbfEsReiRZUinS0qmU90Jgfh5etU3pFjz0UcG5V62SEtQEPpgKuqEmFbAy0fHBjlCwt74bKNnjmAnasd8WV2uM5lNZdqDDQYJKMUM83cVY1MGBFYooRuzXEAWuolj2NZb8Kky2hDtnitt91Hlm0Q9sgf7sJkHNs1OIt4QMR9casnkIXXA4IiUnQYbzLHtBGIRDSQKLNay2sBTftNqdKpJ2akuEWD7BVLm3HyAgiRqPH3srBnYdQKvCre65ukj3l3EIecuy2pm5cznUWPfUERwhwARCTUVcQ69GIHehTixL40zoDf4OP3mdHJrm#!#nvarchar|-|b|-|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😃 +prepst#!#exec#!#varchar|-|a|-|#!#nvarchar|-|b|-| +select * from VARCHAR_dt +drop table VARCHAR_dt; + +create table VARCHAR_dt (a varchar(max), b int, c int, d int, e int ,f int, g int, h int, i int); +insert into VARCHAR_dt (a,b,c,d,e,f,g,h,i) values (NULL,1,2,3,4,5,6,7,8); +select * from VARCHAR_dt; +drop table VARCHAR_dt; + +create table VARCHAR_dt (a varchar(10), b int, c int, d int, e int ,f int, g int, h int, i int); +insert into VARCHAR_dt (a,b,c,d,e,f,g,h,i) values (NULL,1,2,3,4,5,6,7,8); +select * from VARCHAR_dt; +drop table VARCHAR_dt; diff --git a/contrib/test/JDBC/input/datatypes/TestXML.txt b/contrib/test/JDBC/input/datatypes/TestXML.txt new file mode 100644 index 00000000000..11b9ac8e138 --- /dev/null +++ b/contrib/test/JDBC/input/datatypes/TestXML.txt @@ -0,0 +1,11 @@ +CREATE TABLE XML_dt (a XML) +#prepst#!# INSERT INTO XML_dt values(@a) #!#XML|-|a|-| +#prepst#!# INSERT INTO XML_dt values(@a) #!#XML|-|a|-|Contact Name 2YYY-YYY-YYYY +#prepst#!#exec#!#XML|-|a|-| +SELECT * FROM XML_dt; +INSERT INTO XML_dt values('Contact Name 2YYY-YYY-YYYY') +INSERT INTO XML_dt values(NULL) +#INSERT INTO XML_dt values('') +INSERT INTO XML_dt values(Contact Name 2YYY-YYY-YYYY) +SELECT * FROM XML_dt; +DROP TABLE XML_dt; \ No newline at end of file diff --git a/contrib/test/JDBC/input/ddl/temp-tables.sql b/contrib/test/JDBC/input/ddl/temp-tables.sql new file mode 100644 index 00000000000..c09433ecc55 --- /dev/null +++ b/contrib/test/JDBC/input/ddl/temp-tables.sql @@ -0,0 +1,282 @@ +USE master +GO + +-- +-- Tests for T-SQL style temp tables +-- + +-- Basic temp table create/insert/select using tsql dialect + +CREATE TABLE #local_tempt(col int); +GO + +INSERT INTO #local_tempt VALUES (1); +GO + +SELECT * FROM #local_tempt; +GO + +CREATE TABLE ##global_tempt(col int); +GO + +CREATE SCHEMA temp_tables_test; +GO + +CREATE TABLE temp_tables_test.#local_tempt_withschema(col int); +GO + +INSERT INTO temp_tables_test.#local_tempt_withschema VALUES (1); +GO + +SELECT * FROM temp_tables_test.#local_tempt_withschema; +GO + +DROP SCHEMA temp_tables_test; +GO + +-- Implicitly creating temp tables + +CREATE TABLE tt_test_t1 (col int); +GO + +INSERT INTO tt_test_t1 values (1); +GO + +INSERT INTO tt_test_t1 values (NULL); +GO + +SELECT * INTO #local_tempt2 FROM tt_test_t1; +GO + +SELECT * FROM #local_tempt2; +GO + +-- Implicitly creating temp tables in procedure + +CREATE PROCEDURE temp_table_sp AS +BEGIN + SELECT * INTO #tt_sp_local FROM tt_test_t1; + INSERT INTO #tt_sp_local VALUES(2); +END; +GO + +EXEC temp_table_sp; +GO + +-- BABEL-903: create temp table named #[digit][string] +create procedure babel903 AS +BEGIN + create table #903 (a int); + select col into #903tt from tt_test_t1; + insert into #903 values(1); + insert into #903tt values(1); +END +GO + +exec babel903; +GO + +-- BABEL-904: drop temp table +CREATE PROCEDURE babel904 AS +BEGIN + create table #t (a int); + create table #tt (a int); + drop table #t; + drop table #tt; +END +go + +exec babel904; +GO + +-- Visibility tests + +create table #tt (a int); +go +insert into #tt values(0); +go + +CREATE procedure temp_table_nested_sp_1st AS +BEGIN + CREATE TABLE #tt_1st (a int); + insert into #tt values(1); + insert into #tt_1st values(1); + insert into #tt_2nd values(1); + insert into #tt_3rd values(1); +END; +GO + +CREATE procedure temp_table_nested_sp_2nd AS +BEGIN + CREATE TABLE #tt_2nd (a int); + EXEC temp_table_nested_sp_1st; + insert into #tt values(2); + insert into #tt_2nd values(2); + insert into #tt_3rd values(2); +END; +GO + +CREATE procedure temp_table_nested_sp_3rd AS +BEGIN + CREATE TABLE #tt_3rd (a int); + EXEC temp_table_nested_sp_2nd; + insert into #tt values(3); + insert into #tt_3rd values(3); +END; +GO + +EXEC temp_table_nested_sp_3rd; +GO + +-- should fail to find these tables +select * from #tt_1st; +go +select * from #tt_2nd; +go +select * from #tt_3rd; +go +-- This should print 0, 1, 2 and 3 +select * from #tt; +go + +DROP PROCEDURE temp_table_nested_sp_1st; +go +DROP PROCEDURE temp_table_nested_sp_2nd; +go +DROP PROCEDURE temp_table_nested_sp_3rd; +go +DROP TABLE #tt; +go + +-- creating temp tables with duplicated names. +create table #tt (a int); +go +insert into #tt values(1); +go + +CREATE procedure temp_table_nested_sp_inner AS +BEGIN + CREATE TABLE #tt (a int); -- same name as the top-level, allowed + CREATE TABLE #tt_sp_outer (a int); -- same name as the outer procedure, allowed + insert into #tt values(3); +END; +GO + +CREATE procedure temp_table_nested_sp_outer AS +BEGIN + CREATE TABLE #tt (a int); -- same name as the top-level, allowed + CREATE TABLE #tt_sp_outer (a int); + insert into #tt values(2); + EXEC temp_table_nested_sp_inner; +END; +GO + +EXEC temp_table_nested_sp_outer; +go +select * from #tt; -- should only print value '1' +go +drop table #tt; +go + +-- procedure with exception +CREATE procedure temp_table_sp_exception AS +BEGIN + CREATE TABLE #tt (a int); + CREATE TABLE #tt (a int); -- throws error +END; +GO +EXEC temp_table_sp_exception; +GO +select * from #tt; -- can't find the table +go + +-- drop/alter tables +CREATE procedure temp_table_sp_alter AS +BEGIN + CREATE TABLE #tt (a int); + CREATE TABLE #tt2 (a int); + DROP TABLE #tt2; + ALTER TABLE #tt ADD b char; + insert into #tt values(1, 'x'); +END; +GO + +EXEC temp_table_sp_alter; +GO + +-- constraints + +create table #tt_con(a int CHECK (a > 10)); +go +insert into #tt_con values(1); -- errorneous +go +CREATE PROCEDURE temp_table_sp_constraint AS +BEGIN + create table #tt (a int CHECK (a > 10)); + insert into #tt values(11); + insert into #tt_con(a) select a from #tt; +END +go +exec temp_table_sp_constraint; +go +select * from #tt_con; -- should print 11 +go + +-- statistic + +create table #tt_stat(a int, b int); +go +insert into #tt_stat values(1, 2); +go +-- valid T-SQL create-statitstics is not supported yet +--CREATE STATISTICS s1 on #tt_stat(a, b); +--go +drop table #tt_stat; +go + +-- BABEL-322: '#' in column name is allowed in tsql + +CREATE TABLE #babel322(#col int, ##col int); +GO +DROP TABLE #babel322; +GO + +-- BABEL-1637: rollback within procedure makes top-level temp table gone +create proc sp_babel1637 as + create table #tt_1637 (a int) + begin tran + insert into #tt_1637 values (123) + rollback + select * from #tt_1637 +go +create table #tt_1637 (a int) +insert into #tt_1637 values (456) +select * from #tt_1637 +go +exec sp_babel1637 +go +select * from #tt_1637 +go +drop table #tt_1637 +go + +-- cleanup + +DROP PROCEDURE temp_table_sp; +GO +DROP PROCEDURE babel903; +GO +DROP PROCEDURE babel904; +GO +DROP PROCEDURE temp_table_nested_sp_inner; +GO +DROP PROCEDURE temp_table_nested_sp_outer; +GO +DROP PROCEDURE temp_table_sp_exception; +GO +DROP PROCEDURE temp_table_sp_alter; +GO +DROP PROCEDURE temp_table_sp_constraint; +GO +DROP TABLE tt_test_t1; +GO diff --git a/contrib/test/JDBC/input/errorHandling/TestErrorFunctions.sql b/contrib/test/JDBC/input/errorHandling/TestErrorFunctions.sql new file mode 100644 index 00000000000..30612073958 --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestErrorFunctions.sql @@ -0,0 +1,190 @@ +CREATE PROC errorFuncProc1 AS +BEGIN TRY + RAISERROR ('raiserror 16', 16, 1); +END TRY +BEGIN CATCH + SELECT 'CATCH in Procedure 1'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + + THROW; +END CATCH +go + +CREATE PROC errorFuncProc2 AS +BEGIN TRY + EXEC errorFuncProc1; +END TRY +BEGIN CATCH + DECLARE @msg NVARCHAR(4000); + DECLARE @sev INT; + DECLARE @state INT; + + SELECT 'CATCH in Procedure 2'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + + SELECT + @msg = ERROR_MESSAGE(), + @sev = ERROR_SEVERITY(), + @state = ERROR_STATE(); + + SET @state = @state+1; + + RAISERROR (@msg, @sev, @state); +END CATCH +go + +/* Outside of CATCH -- test 1 */ +SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; +go + +/* Outside of CATCH -- test 2 */ +BEGIN TRY + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; +END TRY +BEGIN CATCH + SELECT 'Not arriving here'; +END CATCH +go + +/* Multiple errors in single batch -- test 1 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + SELECT 'First CATCH'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; +END CATCH +BEGIN TRY + THROW 51000, 'throw error', 1; +END TRY +BEGIN CATCH + SELECT 'Second CATCH'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; +END CATCH +go + +/* Multiple errors in single batch -- test 2 */ +/* Nested TRY...CATCH */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + THROW 51000, 'throw error', 1; + END TRY + BEGIN CATCH + SELECT 'Inner CATCH'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + END CATCH + SELECT 'Outer CATCH'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; +END CATCH + +/* Multiple errors in nested batch */ +EXEC errorFuncProc2; +go + +/* + * BABEL-1602 + * Output of ERROR functions should be the same as error message + */ +CREATE TABLE errorFuncTable +( + a INT, + b INT, + c VARCHAR(10) NOT NULL, + CONSTRAINT CK_a_gt_b CHECK (b > a) +) +go + +INSERT INTO errorFuncTable VALUES (5, 2, 'one') +go + +BEGIN TRY + INSERT INTO errorFuncTable VALUES (5, 2, 'one') +END TRY +BEGIN CATCH + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; +END CATCH +go + +INSERT INTO errorFuncTable VALUES (1, 2, NULL) +go + +BEGIN TRY + INSERT INTO errorFuncTable VALUES (1, 2, NULL) +END TRY +BEGIN CATCH + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; +END CATCH +go + +/* Clean up */ +DROP PROC errorFuncProc1 +go + +DROP PROC errorFuncProc2 +go + +DROP TABLE errorFuncTable +go diff --git a/contrib/test/JDBC/input/errorHandling/TestErrorsWithTryCatch.sql b/contrib/test/JDBC/input/errorHandling/TestErrorsWithTryCatch.sql new file mode 100644 index 00000000000..3580bdefc4c --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestErrorsWithTryCatch.sql @@ -0,0 +1,979 @@ +CREATE TABLE ErrorWithTryCatchTable (a varchar(15) UNIQUE NOT NULL, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) +GO + +-- setup for "invalid characters found: cannot cast value "%s" to money" error +CREATE TABLE t293_1(a money, b int); +GO + +INSERT INTO t293_1(a, b) values ($100, 1), ($101, 2); +GO + +-- setup for error "column \"%s\" of relation \"%s\" is a generated column" error +CREATE TABLE t1752_2(c1 INT, c2 INT, c3 as c1*c2) +GO + +-- setup for "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" error +CREATE TABLE t141_2(c1 int, c2 int); +GO + +-- Error: duplicate key value violates unique constraint +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + + + + +-- Error: check constraint violation +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + + + + + + + +-- Error: not null constraint violation +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + + + + + + + +-- Error: creating an existing table +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + + + + + + + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + + +-- Error: "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_2; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_2; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: "value for domain tinyint violates check constraint "tinyint_check"" +-- Simple error inside try-catch +BEGIN TRY + SELECT xact_state(); + DECLARE @a tinyint = 1000; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;';; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- valid INSERT inside catch after an error +-- Simple batch with try catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Orange', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END CATCH; +GO +select a from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + +-- invalid INSERT inside catch after an error +-- Simple batch with try catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END CATCH; +GO +select a from ErrorWithTryCatchTable ORDER BY c +GO +truncate table ErrorWithTryCatchTable +GO + + +DROP TABLE ErrorWithTryCatchTable +GO + +-- cleanup for "invalid characters found: cannot cast value "%s" to money" error +DROP TABLE t293_1; +GO + +-- cleanup for error "column \"%s\" of relation \"%s\" is a generated column" error +DROP TABLE t1752_2 +GO + +-- cleanup for "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" error +DROP TABLE t141_2; +GO + +while (@@trancount > 0) commit tran; +GO + diff --git a/contrib/test/JDBC/input/errorHandling/TestRaiserror.sql b/contrib/test/JDBC/input/errorHandling/TestRaiserror.sql new file mode 100644 index 00000000000..4b10bdc575e --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestRaiserror.sql @@ -0,0 +1,410 @@ +CREATE TABLE raiserrorTable (a INT); +go + +CREATE PROC raiserrorProc1 AS +BEGIN + INSERT INTO raiserrorTable VALUES (1); + RAISERROR ('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (2); +END +go + +CREATE PROC raiserrorProc2 AS +BEGIN + INSERT INTO raiserrorTable VALUES (111); + EXEC raiserrorProc1; + INSERT INTO raiserrorTable VALUES (222); +END +go + +CREATE PROC raiserrorProc3 AS +BEGIN + BEGIN TRY + RAISERROR ('raiserror 16', 16, 1); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (11); + RAISERROR ('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (12); + END CATCH +END +go + +/* XACT_ABORT OFF */ + +/* 1. Not in TRY...CATCH block, terminates nothing */ +/* Not in TRY...CATCH block */ +RAISERROR('raiserror 0', 0, 1); + +DECLARE @m1 NVARCHAR(10); +DECLARE @m2 VARCHAR(10); +DECLARE @m3 NCHAR(10); +DECLARE @m4 CHAR(10); + +SET @m1 = 'raiserror 0'; +SET @m2 = 'raiserror 0'; +SET @m3 = 'raiserror 0'; +SET @m4 = 'raiserror 0'; + +RAISERROR(@m1, 0, 1); +RAISERROR(@m2, 0, 2); +RAISERROR(@m3, 0, 3); +RAISERROR(@m4, 0, 4); +go + +DECLARE @msg_id INT; +DECLARE @severity INT; +DECLARE @state INT; +SET @msg_id = 51000; +SET @severity = 0; +SET @state = 1; +RAISERROR(@msg_id, @severity, @state) WITH LOG, NOWAIT, SETERROR; +go + +BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +TRUNCATE TABLE raiserrorTable +go + +/* Nested procedure call */ +BEGIN TRANSACTION + EXEC raiserrorProc2; +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +TRUNCATE TABLE raiserrorTable +go + +/* 2. In TRY...CATCH block, catchable */ +/* RAISERROR in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + RAISERROR('raiserror 10', 10, 1); + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + COMMIT TRANSACTION +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +TRUNCATE TABLE raiserrorTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + SELECT xact_state(); + EXEC raiserrorProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +TRUNCATE TABLE raiserrorTable +go + +/* Error in both TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES(1); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES(3); +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (7); + SELECT * FROM raiserrorTable + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +TRUNCATE TABLE raiserrorTable +go + +/* Error in nested CATCH */ +BEGIN TRY + BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES(1); + EXEC raiserrorProc3; + INSERT INTO raiserrorTable VALUES(3); +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (7); + SELECT * FROM raiserrorTable + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +TRUNCATE TABLE raiserrorTable +go + +/* Error in CATCH chain */ +BEGIN TRY + BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES(1); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES(3); +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + EXEC raiserrorProc3; + INSERT INTO raiserrorTable VALUES (7); + SELECT * FROM raiserrorTable + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +TRUNCATE TABLE raiserrorTable +go + + +/* Nested TRY..CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (6); +END CATCH +go +SELECT * FROM raiserrorTable +go +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); +END CATCH +go +SELECT * FROM raiserrorTable +go +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END CATCH +go +SELECT * FROM raiserrorTable +go +TRUNCATE TABLE raiserrorTable +go + +/* XACT_ABORT ON, RAISERROR does not honor XACT_ABORT */ +SET XACT_ABORT ON; +go + +/* 1. Not in TRY...CATCH block, terminates nothing */ +/* Not in TRY...CATCH block */ +BEGIN TRANSACTION + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +TRUNCATE TABLE raiserrorTable +go + +/* Nested procedure call */ +BEGIN TRANSACTION + EXEC raiserrorProc2; +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM raiserrorTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +TRUNCATE TABLE raiserrorTable +go + +/* 2. In TRY...CATCH block, catchable */ +/* RAISERROR in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + RAISERROR('raiserror 10', 10, 1); + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + COMMIT TRANSACTION +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +TRUNCATE TABLE raiserrorTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRANSACTION + SELECT xact_state(); + EXEC raiserrorProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM raiserrorTable; + IF @@trancount > 0 ROLLBACK TRANSACTION; +END CATCH +go +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY..CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (6); +END CATCH +go +SELECT * FROM raiserrorTable +go +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); +END CATCH +go +SELECT * FROM raiserrorTable +TRUNCATE TABLE raiserrorTable +go + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO raiserrorTable VALUES (3); + RAISERROR('raiserror 16', 16, 1); + INSERT INTO raiserrorTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO raiserrorTable VALUES (5); + END CATCH +END CATCH +go +SELECT * FROM raiserrorTable +go +TRUNCATE TABLE raiserrorTable +go + +/* + * SETERROR option + * 1. Outside TRY...CATCH, SETERROR would set @@ERROR to specified msg_id + * or 50000, regardless of the severity level + * 2. Inside TRY...CATCH, @@ERROR is always set to the captured error number + * with or without SETERROR + * TODO: After full support, @@ERROR should return user defined error number + */ +DECLARE @err INT; +RAISERROR(51000, 10, 1); +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +RAISERROR(51000, 10, 2) WITH SETERROR; +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +RAISERROR('raiserror 16', 16, 1); +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +RAISERROR('raiserror 16', 16, 2) WITH SETERROR; +SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +go + +DECLARE @err INT; +BEGIN TRY + BEGIN TRY + RAISERROR(51000, 16, 1); + END TRY + BEGIN CATCH + SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; + RAISERROR(51000, 16, 2) WITH SETERROR; + END CATCH +END TRY +BEGIN CATCH + SET @err = @@ERROR; IF @err = 0 SELECT 0 ELSE SELECT 1; +END CATCH +go + +/* Clean up */ +SET XACT_ABORT OFF; +go +DROP PROC raiserrorProc1; +go +DROP PROC raiserrorProc2; +go +DROP PROC raiserrorProc3; +go +DROP TABLE raiserrorTable; +go diff --git a/contrib/test/JDBC/input/errorHandling/TestRaiserrorFormat.sql b/contrib/test/JDBC/input/errorHandling/TestRaiserrorFormat.sql new file mode 100644 index 00000000000..d7df924b3b2 --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestRaiserrorFormat.sql @@ -0,0 +1,53 @@ +RAISERROR('%s', 16, 1, 'Hi'); +go + +RAISERROR('Hello %s', 16, 1, 'World'); +go + +DECLARE @str VARCHAR(20) = 'Multiple variable inputs'; +DECLARE @p1 TINYINT = 1; +DECLARE @p2 SMALLINT = 2; +DECLARE @p3 INT = 3; +DECLARE @p4 CHAR(5) = 'four'; +DECLARE @p5 VARCHAR(5) = 'five'; +DECLARE @p6 NCHAR(5) = 'six'; +DECLARE @p7 NVARCHAR(5) = 'seven'; +RAISERROR('%s: %d%d%d%s%s%s%s', 16, 1, @str, @p1, @p2, @p3, @p4, @p5, @p6, @p7); +go + +RAISERROR('More than 20 args', 16, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21); +go + +RAISERROR('Signed integer i: %i, %i', 16, 1, 5, -5); +go + +RAISERROR('Unsigned integer u: %u, %u', 16, 1, 5, -5); +go + +RAISERROR('Unsigned octal o: %o, %o', 16, 1, 5, -5); +go + +RAISERROR('Unsigned hexadecimal x: %x, %X, %X, %X, %x', 16, 1, 11, 11, -11, 50, -50); +go + +RAISERROR('Not enough args: %d, %d', 16, 1, 1, 2, 3, 4); +go + +RAISERROR('No arg for placeholder: %s', 16, 1); +go + +RAISERROR('Invalid placeholder: %m', 16, 1, 0); +go + +RAISERROR('Null arg for placeholder: %s', 16, 1, NULL); +go + +-- Datatype mismatch +RAISERROR('Mismatch datatype: %d', 16, 1, 'string'); +go + +RAISERROR('Mismatch datatype: %o', 16, 1, N'string'); +go + +RAISERROR('Mismatch datatype: %s', 16, 1, 123); +go diff --git a/contrib/test/JDBC/input/errorHandling/TestSimpleErrors.sql b/contrib/test/JDBC/input/errorHandling/TestSimpleErrors.sql new file mode 100644 index 00000000000..95832cfe32f --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestSimpleErrors.sql @@ -0,0 +1,2532 @@ +CREATE TABLE simpleErrorTable (a varchar(15) UNIQUE NOT NULL, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) +GO + +-- setup for "data out of range for datetime" error +CREATE TABLE t517_1(a datetime); +GO + +-- setup for "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" error +CREATE TABLE t141_1(c1 int, c2 int); +GO + +-- setup for "column \"%s\" of relation \"%s\" is a generated column" +CREATE TABLE t1752_1(c1 INT, c2 INT, c3 as c1*c2) +GO + +if @@trancount > 0 commit tran; +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1 +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + declare @err int = @@error; if @err = 0 select 0 else select 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE simpleErrorTable SET c = NULL WHERE c = 1; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; + rollback tran sp1; +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: creating an existing table +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +CREATE TABLE simpleErrorTable (a int); +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +rollback tran sp1; +GO +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +commit tran; +GO +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +commit tran +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran sp1; +GO +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +DECLARE @a tinyint = 1000; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +DECLARE @a tinyint = 1000; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +select @@trancount; +GO + +if @@trancount > 0 commit tran; +GO + +-- Error: value for domain tinyint violates check constraint "tinyint_check" +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DECLARE @a tinyint = 1000; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran sp1; +GO +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +commit tran; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- cleanup for "data out of range for datetime" error +DROP TABLE t517_1; +GO + +-- clean up for "A SELECT statement that assigns a value to a variable must not +-- be combined with data-retrieval operations" error +DROP TABLE t141_1; +GO + +-- setup for "column \"%s\" of relation \"%s\" is a generated column" +DROP TABLE t1752_1; +GO + +drop table simpleErrorTable +GO + +while (@@trancount > 0) commit tran; +GO + diff --git a/contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithImplicitTran.txt b/contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithImplicitTran.txt new file mode 100644 index 00000000000..81899318253 --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithImplicitTran.txt @@ -0,0 +1,9 @@ +#Setup +SET IMPLICIT_TRANSACTIONS ON + +#Run error handling tests +include#!#input/errorHandling/TestSimpleErrors.sql + +#Cleanup +IF @@trancount > 0 ROLLBACK +SET IMPLICIT_TRANSACTIONS OFF diff --git a/contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithXactAbort.sql b/contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithXactAbort.sql new file mode 100644 index 00000000000..0137b7cc36a --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithXactAbort.sql @@ -0,0 +1,2379 @@ +SET XACT_ABORT ON +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +GO + +CREATE TABLE simpleErrorTable (a varchar(15) UNIQUE, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1 +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: check constraint violation +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: not null constraint violation +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE simpleErrorTable SET c = NULL WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: creating an existing table +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +CREATE TABLE simpleErrorTable (a int); +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: deleting values from a table that does not exist +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DELETE FROM simpleErrorTable1 WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + DELETE FROM simpleErrorTable1 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + DELETE FROM simpleErrorTable1 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: syntax error +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +commit tran +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +select * from simpleErrorTable ORDER BY c +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +select * from simpleErrorTable ORDER BY c +GO +select @@trancount +GO +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- cleanup +SET XACT_ABORT OFF; +GO + +drop table simpleErrorTable +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; +GO diff --git a/contrib/test/JDBC/input/errorHandling/TestThrow.sql b/contrib/test/JDBC/input/errorHandling/TestThrow.sql new file mode 100644 index 00000000000..c789a07c631 --- /dev/null +++ b/contrib/test/JDBC/input/errorHandling/TestThrow.sql @@ -0,0 +1,342 @@ +CREATE TABLE throwTable (a INT); +go + +CREATE PROC throwProc1 AS +BEGIN + INSERT INTO throwTable VALUES (1); + THROW 51000, 'Throw error', 1; + INSERT INTO throwTable VALUES (2); +END +go + +CREATE PROC throwProc2 AS +BEGIN + INSERT INTO throwTable VALUES (111); + EXEC throwProc1; + INSERT INTO throwTable VALUES (222); +END +go + +/* Error -- THORW; can only be called in CATCH block */ +THROW; +go + +/* Error -- THROW; can only be called in CATCH block */ +BEGIN TRY + THROW; +END TRY +BEGIN CATCH + THROW; +END CATCH +go + +/* Re-throw current caught error */ +BEGIN TRY + THROW 50000, 'Throw error', 1; +END TRY +BEGIN CATCH + THROW; +END CATCH +go + +/* Nested TRY...CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + PRINT 100/0; + END TRY + BEGIN CATCH + THROW 50000, 'Throw error', 1; + END CATCH +END TRY +BEGIN CATCH + THROW; +END CATCH +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + PRINT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + THROW; + END TRY + BEGIN CATCH + THROW; + END CATCH +END CATCH +go + + +/* XACT_ABORT OFF */ + +/* 1. Not in TRY...CATCH block, throw exception */ +/* Not in TRY...CATCH block */ +DECLARE @err_no INT; +DECLARE @msg VARCHAR(50); +DECLARE @state INT; +SET @err_no = 51000; +SET @msg = N'Throw error'; +SET @state = 1; +THROW @err_no, @msg, @state; +go + +BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRAN; +go +TRUNCATE TABLE throwTable +go + +/* Nested procedure call */ +BEGIN TRAN + EXEC throwProc2; +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +TRUNCATE TABLE throwTable +go + +/* 2. In TRY...CATCH block, catchable, abort batch without rollback */ +/* THROW in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + COMMIT TRAN +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +TRUNCATE TABLE throwTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC throwProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +SELECT * FROM throwTable +go +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +SELECT * FROM throwTable +go +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END CATCH +go +SELECT * FROM throwTable +go +TRUNCATE TABLE throwTable +go + +/* XACT_ABORT ON */ +SET XACT_ABORT ON; +go + +/* 1. Not in TRY...CATCH block, rollback transaction */ +/* Not in TRY...CATCH block */ +BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRAN; +go +TRUNCATE TABLE throwTable +go + +/* Nested procedure call */ +BEGIN TRAN + EXEC throwProc2; +go +SELECT xact_state(); +SELECT @@trancount; +SELECT * FROM throwTable; +IF @@trancount > 0 ROLLBACK TRANSACTION; +go +TRUNCATE TABLE throwTable +go + +/* 2. In TRY...CATCH block, catchable, abort batch without rollback */ +/* THROW in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + COMMIT TRAN +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +TRUNCATE TABLE throwTable +go + +/* Procedure call in TRY...CATCH */ +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC throwProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; + SELECT * FROM throwTable; + IF @@trancount > 0 ROLLBACK TRAN; +END CATCH +go +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 1 */ +BEGIN TRY + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +SELECT * FROM throwTable +go +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 2 */ +BEGIN TRY + BEGIN TRY + SELECT 100/0; + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END CATCH +END TRY +BEGIN CATCH + INSERT INTO throwTable VALUES (6); +END CATCH +go +SELECT * FROM throwTable +go +TRUNCATE TABLE throwTable +go + +/* Nested TRY...CATCH, test 3 */ +BEGIN TRY + SELECT 100/0; +END TRY +BEGIN CATCH + BEGIN TRY + INSERT INTO throwTable VALUES (3); + THROW 50000, 'Throw error', 1; + INSERT INTO throwTable VALUES (4); + END TRY + BEGIN CATCH + INSERT INTO throwTable VALUES (5); + END CATCH +END CATCH +go +SELECT * FROM throwTable +go +TRUNCATE TABLE throwTable +go + +-- BABEL-2479 +THROW 50000, 'Throw error', 1; +go + +/* Clean up */ +SET XACT_ABORT OFF; +go +DROP PROC throwProc1; +go +DROP PROC throwProc2; +go +DROP TABLE throwTable; +go diff --git a/contrib/test/JDBC/input/forxml/TestForXML.sql b/contrib/test/JDBC/input/forxml/TestForXML.sql new file mode 100644 index 00000000000..f4ca2da9dfa --- /dev/null +++ b/contrib/test/JDBC/input/forxml/TestForXML.sql @@ -0,0 +1,250 @@ +create table t1 (id int, a varchar(10)); +create table t2 (id int, a varchar(10)); +insert into t1 values (1, 't1_a1'); +insert into t1 values (2, 't1_a2'); +insert into t1 values (3, NULL); +insert into t2 values (1, 't2_a1'); +insert into t2 values (2, 't2_a2'); +insert into t2 values (3, NULL); +go + +-- Test Select For XML syntax +select * from t1 for xml auto; +go + +select * from t1 for xml raw; +go + +select * from t1 for xml raw('myrow'); +go + +select * from t1 for xml path; +go + +select * from t1 for xml path('myrow'); +go + +select * from t1 for xml explicit; +go + +select * from t1, t2 where t1.id = t2.id for xml path('myrow'); +go + +select * from t1, t2 where t1.id = t2.id for xml path('myrow'), type; +go + +select * from t1, t2 where t1.id = t2.id for xml path('myrow'), type, root('myroot'); +go + +select * from t1, t2 where t1.id = t2.id for xml path('myrow'), type, root('myroot'), BINARY BASE64; +go + +-- Test result format when all values are NULL +select a from t1 where a is NULL for xml raw; +go + +select a from t1 where a is NULL for xml path; +go + +-- Test result format when one value is NULL +select id, a from t1 where a is NULL for xml raw; +go + +select id, a from t1 where a is NULL for xml path; +go + +-- Test for xml with order by clause +select * from t1 order by id for xml raw ('t1'); +go + +-- Test for xml with group by +select count(*) as cnt, a from t1 group by a,id order by id; +go +select count(*) as cnt, a from t1 group by a,id order by id for xml path; +go + +-- Test BASE64 encoding on binary data +CREATE TABLE MyTable (Col1 int PRIMARY KEY, Col2 binary); +INSERT INTO MyTable VALUES (1, 0x7); +GO +SELECT Col1, CAST(Col2 as image) as Col2 FROM MyTable FOR XML PATH; +GO +SELECT Col1, CAST(Col2 as image) as Col2 FROM MyTable FOR XML PATH, BINARY BASE64; +GO + +-- Test for xml in subquery, The subquery is supposed to return only one XML value +select id, (select a from t2 for xml path) as col from t1; +go + +-- Test 2 levels of for xml nesting, with TYPE option +select id, (select a from t2 for xml path, type) as col from t1 for xml path, type; +go + +-- Test 2 levels of for xml nesting, without TYPE option, expect special character escaping +select id, (select a from t2 for xml path) as col from t1 for xml path; +go + +-- Test 3 levels of for xml nesting with TYPE option +select id, (select id, (select a from t2 for xml path, type) as col1 from t1 for xml path, type) as col2 from t1 for xml path, type; +go + +-- Test simple for xml path in procedure +create table employees( +pers_id int, +fname nvarchar(20), +lname nvarchar(20), +sal money); +insert into employees values (1, 'John', 'Johnson', 123.1234); +insert into employees values (2, 'Max', 'Welch', 200.1234); +go + +create procedure p_employee_select as +begin + select * from employees for xml path; +end; +go + +execute p_employee_select; +go +drop procedure p_employee_select; +go + +-- Test for xml in procedure with parameters +create procedure p_employee_select @minsal MONEY, @maxsal MONEY as +begin + select * from employees where sal > @minsal and sal < @maxsal + for xml path('Employee'); +end; +go + +execute p_employee_select 150, 300; +go + +-- Test for xml in create view +create view v1 (col1) as select * from t1 for xml raw, type; +go + +select * from v1; +go + +-- Test for xml on view with xml column +select * from v1 for xml path; +go + +-- Test for xml on pure relational view +create view v2 (col1, col2) as select * from t1; +go + +select * from v2 for xml path; +go + +drop view v1, v2; +go + +-- Test for xml and union all +select a from t1 UNION ALL select a from t2 for xml raw ('myrow'); +go + +-- Test invalid syntax when FOR XML is on both sides of UNION ALL, this is SQL Server behavior +select a from t1 for xml raw ('t1') UNION ALL select a from t2 for xml raw ('t2'); +go + +-- For xml can access CTE from same query block +with cte as (select a from t1) +select * from cte for xml raw; +go + +-- BABEL-1202: For xml subquery can't access CTE from outer query block +with cte as (select a from t1) +select * from t2, (select * from cte for xml raw) as t3(colxml); +go + +with cte as (select a from t1) +select (select * from cte for xml raw) as colxml, * from t2; +go + +with +cte1 as (select * from t1), +cte2 as (select a from cte1 for xml raw) +select * from cte2; +go + +-- Test for xml and recursive CTE +CREATE TABLE employees2 ( + id serial, + name varchar(255), + manager_id int +); + +INSERT INTO employees2 VALUES (1, 'Mark', null); +INSERT INTO employees2 VALUES (2, 'John', 1); +INSERT INTO employees2 VALUES (3, 'Dan', 2); +INSERT INTO employees2 VALUES (4, 'Clark', 1); +INSERT INTO employees2 VALUES (5, 'Linda', 2); +INSERT INTO employees2 VALUES (6, 'Willy', 2); +INSERT INTO employees2 VALUES (7, 'Barack', 2); +INSERT INTO employees2 VALUES (8, 'Elen', 2); +INSERT INTO employees2 VALUES (9, 'Kate', 3); +INSERT INTO employees2 VALUES (10, 'Terry', 4); +GO + +WITH managertree AS ( + SELECT id, name, manager_id + FROM employees2 + WHERE id = 2 + UNION ALL + SELECT e.id, e.name, e.manager_id + FROM employees2 e + INNER JOIN managertree mtree ON mtree.id = e.manager_id +) +SELECT * +FROM managertree mt; +GO + +WITH managertree AS ( + SELECT id, name, manager_id + FROM employees2 + WHERE id = 2 + UNION ALL + SELECT e.id, e.name, e.manager_id + FROM employees2 e + INNER JOIN managertree mtree ON mtree.id = e.manager_id +) +SELECT * +FROM managertree mt WHERE mt.name = 'Linda' FOR XML RAW ('managertree'); +GO + +-- BABEL-1178, data type of variable is lost during variable binding in FORMAT +-- function +create procedure test_forxml @pid int as +declare @a int, @b smallint; +set @a = 1; +set @b = 1; +select a, datalength(@a), datalength(@b) from t1 where id = @pid for xml raw; +go + +-- datalength(@b) should be 2. But the data type of @b is lost during the +-- query transformation for FOR XML, so we end up getting a wrong length. +exec test_forxml 1; +go + +-- test string variable can be binded with for xml query +create procedure test_forxml_strvar @pid int, @str varchar(10) as +select * from t1 where id = @pid and a = @str for xml raw; +go +exec test_forxml_strvar 1, 't1_a1'; +go +-- test NULL parameter +exec test_forxml_strvar 1, NULL; +go + +-- clean up +drop table t1; +drop table t2; +drop table MyTable; +drop table employees; +drop procedure p_employee_select; +drop table employees2; +drop procedure test_forxml; +drop procedure test_forxml_strvar; +go diff --git a/contrib/test/JDBC/input/interoperability/TestBasicInterop.mix b/contrib/test/JDBC/input/interoperability/TestBasicInterop.mix new file mode 100644 index 00000000000..589497d7cd4 --- /dev/null +++ b/contrib/test/JDBC/input/interoperability/TestBasicInterop.mix @@ -0,0 +1,129 @@ +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +SELECT CAST('$1.0909' AS money); +EXEC [public].pg_interop_proc2; +BEGIN TRANSACTION; +COMMIT; +GO +-- psql +CREATE PROCEDURE pg_interop_proc1() +AS +$$ +BEGIN + CALL master_dbo.tsql_interop_proc1(); +END +$$ LANGUAGE PLPGSQL; +GO +CREATE PROCEDURE pg_interop_proc2() +AS +$$ +BEGIN + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC [public].pg_interop_proc1 +GO +-- psql +CALL pg_interop_proc1() +GO +DROP PROCEDURE pg_interop_proc1 +GO +DROP PROCEDURE pg_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +EXEC [public].pg_interop_proc1; +GO +-- psql +CREATE PROCEDURE pg_interop_proc1() +AS +$$ +BEGIN + CALL master_dbo.tsql_interop_proc2(); +END +$$ LANGUAGE PLPGSQL; +GO +-- tsql +CREATE PROCEDURE tsql_interop_proc2 +AS +SELECT 1/0; +GO + +-- tsql +EXEC tsql_interop_proc1 +GO +-- psql +CALL master_dbo.tsql_interop_proc1() +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc_sp_exec +AS +DECLARE @SQLString NVARCHAR(100); +SET @SQLString = N'commit;'; +EXEC sp_executesql @SQLString; +GO + +-- psql +CREATE PROCEDURE pg_interop_proc_sp_exec() +AS +$$ +BEGIN + CALL master_dbo.tsql_interop_proc_sp_exec(); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRAN; +EXEC [public].pg_interop_proc_sp_exec +GO + +-- tsql +SELECT CONVERT(datetime, '2021-12-30') +GO + +-- tsql +SELECT DATETIME2('9999-99-99'); +DROP PROCEDURE wrongProcedure; +GO + +--tsql +SELECT CONVERT(varchar(50), GETDATE(), 200); +GO + +--tsql +select set_config('babelfishpg_tsql.auto_commit_batch', 'off', false); +CREATE TABLE batch_table(c1 int); +GO + +--psql +SELECT * FROM batch_table; +DROP TABLE batch_table; +GO + +-- psql +DROP PROCEDURE pg_interop_proc1 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO +DROP PROCEDURE tsql_interop_proc2 +GO +DROP PROCEDURE tsql_interop_proc_sp_exec +GO + +-- psql +DROP PROCEDURE pg_interop_proc_sp_exec +GO diff --git a/contrib/test/JDBC/input/interoperability/TestInteropMultiUser.mix b/contrib/test/JDBC/input/interoperability/TestInteropMultiUser.mix new file mode 100644 index 00000000000..b9721c4eac0 --- /dev/null +++ b/contrib/test/JDBC/input/interoperability/TestInteropMultiUser.mix @@ -0,0 +1,22 @@ +-- psql +CREATE USER normal_user WITH PASSWORD 'normal_password'; +GO +CREATE USER createdb_user WITH CREATEDB PASSWORD 'createdb_password'; +GO +GRANT sysadmin TO createdb_user; +GO +-- tsql user=normal_user password=normal_password +CREATE DATABASE normal_db +GO +DROP DATABASE normal_db +GO +-- tsql user=createdb_user password=createdb_password +CREATE DATABASE createdb_db +GO +DROP DATABASE createdb_db +GO +-- psql +DROP OWNED BY createdb_user; +GO +DROP USER normal_user, createdb_user; +GO \ No newline at end of file diff --git a/contrib/test/JDBC/input/interoperability/TestInteropProcedures.mix b/contrib/test/JDBC/input/interoperability/TestInteropProcedures.mix new file mode 100644 index 00000000000..bc71fa6edee --- /dev/null +++ b/contrib/test/JDBC/input/interoperability/TestInteropProcedures.mix @@ -0,0 +1,161 @@ +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +UPDATE procTab1 SET c1=10 where c2='b'; +INSERT INTO procTab1 values(1,'a'); +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + CREATE TABLE procTab1(c1 int); + INSERT INTO procTab1 values (5); + ALTER TABLE procTab1 ADD c2 char; + CALL tsql_interop_proc1(); +END +$$ LANGUAGE PLPGSQL; +GO + +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + INSERT INTO procTab1(c1,c2) values (3,'c'); + UPDATE procTab1 SET c1=10 where c2='b'; + INSERT INTO procTab1(c1,c2) values (4,'d'); + DELETE FROM procTab1 where c2='a'; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 order by c1 +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc1() +GO +SELECT DISTINCT c1 FROM procTab1 +GO + +-- tsql +DROP TABLE procTab1 +GO +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +DECLARE @SQLString NVARCHAR(100); +SET @SQLString = N'ALTER TABLE procTab1 ALTER COLUMN c2 varchar(10);'; +EXEC sp_executesql @SQLString; +INSERT INTO procTab1(c1,c2) values(11,'abc'); +UPDATE procTab1 SET c1=c1+1 WHERE c2 IS NULL OR c2='a'; +EXEC psql_interop_proc3; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc3() +AS +$$ +DECLARE myvar varchar(10) = 'def'; +BEGIN +UPDATE procTab1 SET c2=myvar WHERE c1 BETWEEN 2 AND 10; +INSERT INTO procTab1(c1,c2) values(12,'xyz'); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 WHERE c2 LIKE 'a%' +GO +DROP TABLE procTab1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc() +AS +$$ +BEGIN +CALL tsql_interop_proc(); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc; +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE cursorTab(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO cursorTab values(0, 0, 0, 0, 0); +INSERT INTO cursorTab values(1, 2, 3, 4, 1); +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select a,b,c,d from cursorTab', 2, 1; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 2; +EXEC sp_cursoroption @cursor_handle, 1, 2; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +EXEC sp_cursorclose @cursor_handle; +GO + +EXEC psql_interop_proc; +GO +DROP TABLE cursorTab +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + SELECT 1/0; +END TRY +BEGIN CATCH + SELECT 1; +END CATCH +GO + +-- tsql +EXEC psql_interop_proc +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc1 +GO +DROP PROCEDURE psql_interop_proc2 +GO +DROP PROCEDURE psql_interop_proc3 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc +GO diff --git a/contrib/test/JDBC/input/interoperability/TestProcedureWithErrorHandling.mix b/contrib/test/JDBC/input/interoperability/TestProcedureWithErrorHandling.mix new file mode 100644 index 00000000000..5fae9b3a298 --- /dev/null +++ b/contrib/test/JDBC/input/interoperability/TestProcedureWithErrorHandling.mix @@ -0,0 +1,292 @@ +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE procTab1(c1 money, c2 char); +BEGIN TRY + SELECT xact_state(); + INSERT INTO procTab1 values($1,'a'); + INSERT INTO procTab1 values('i','a'); +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@trancount; +END CATCH +GO + +-- psql +CREATE PROCEDURE psql_interop_proc() +AS +$$ +BEGIN + CALL master_dbo.tsql_interop_proc(); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC [public].psql_interop_proc +GO +SELECT * FROM procTab1 +GO +EXEC tsql_interop_proc +GO +SELECT * FROM procTab1 +GO +DROP TABLE procTab1 +GO +BEGIN TRANSACTION +GO +EXEC [public].psql_interop_proc +GO +SELECT * FROM procTab1 +GO +COMMIT +GO +DROP TABLE procTab1 +GO + +/* TODO: BABEL-2377 */ +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +/* SELECT * FROM procTab1; */ +/* GO */ + +-- tsql +SET IMPLICIT_TRANSACTIONS ON +GO +EXEC [public].psql_interop_proc +GO +SELECT * FROM procTab1 +GO +IF @@TRANCOUNT > 0 ROLLBACK; +GO +SET IMPLICIT_TRANSACTIONS OFF +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE procTab1(c1 money, c2 char); +BEGIN TRY + SELECT xact_state(); + INSERT INTO procTab1 values($1,'a'); + EXEC psql_interop_proc1; +END TRY +BEGIN CATCH + SELECT xact_state(); + SELECT @@TRANCOUNT; +END CATCH +GO + +-- psql +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + INSERT INTO procTab1(c1,c2) values('i','a'); +END +$$ LANGUAGE PLPGSQL; +GO + +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ +/* SELECT * FROM procTab1; */ +/* GO */ + +-- tsql +EXEC [public].psql_interop_proc; +GO +SELECT * FROM procTab1 +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- psql +DROP PROCEDURE psql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + RAISERROR ('raiserror 16', 16, 1); +END TRY +BEGIN CATCH + SELECT 'CATCH in Procedure 1'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + THROW; +END CATCH +GO +EXEC [public].psql_interop_proc; +GO + +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ + +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc2 +AS +BEGIN TRY + RAISERROR ('raiserror 16', 16, 1); +END TRY +BEGIN CATCH + SELECT 'CATCH in Procedure 1'; + SELECT + ERROR_LINE() AS line, + ERROR_MESSAGE() AS msg, + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + + THROW; +END CATCH +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + EXEC tsql_interop_proc2; +END TRY +BEGIN CATCH + SELECT 'CATCH in Procedure 2'; + SELECT + ERROR_NUMBER() AS num, + ERROR_PROCEDURE() AS proc_, + ERROR_SEVERITY() AS sev, + ERROR_STATE() AS state; + THROW; +END CATCH +GO + +EXEC [public].psql_interop_proc; +GO + +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ + +DROP PROCEDURE tsql_interop_proc2 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc2 +AS + EXEC [public].psql_interop_proc2; +GO + +-- psql +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + SELECT 100/0; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC [public].psql_interop_proc; +GO + +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ + +-- psql +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc +GO +DROP PROCEDURE tsql_interop_proc2 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRY + SELECT 100/0; + RAISERROR('Hello %s', 16, 1, 'World'); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO + +-- tsql +EXEC [public].psql_interop_proc; +GO + +/* -- psql */ +/* CALL psql_interop_proc(); */ +/* GO */ + +-- tsql +SET XACT_ABORT OFF; +GO +EXEC [public].psql_interop_proc +GO +SET XACT_ABORT ON; +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS + RAISERROR('Hello %s', 16, 1, 'World'); + SELECT xact_state(); + SELECT @@trancount; +GO + +-- tsql +EXEC [public].psql_interop_proc +GO + +-- psql +CALL psql_interop_proc(); +GO + +-- tsql +SET XACT_ABORT OFF; +GO +SET IMPLICIT_TRANSACTIONS ON +GO +EXEC [public].psql_interop_proc +GO +EXEC tsql_interop_proc +GO + +-- psql +CALL psql_interop_proc(); +GO + +-- tsql +SET IMPLICIT_TRANSACTIONS OFF +GO +SET XACT_ABORT ON; +GO + +-- psql +DROP PROCEDURE psql_interop_proc +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc +GO diff --git a/contrib/test/JDBC/input/interoperability/TestProcedureWithTransactions.mix b/contrib/test/JDBC/input/interoperability/TestProcedureWithTransactions.mix new file mode 100644 index 00000000000..5dcaed860e7 --- /dev/null +++ b/contrib/test/JDBC/input/interoperability/TestProcedureWithTransactions.mix @@ -0,0 +1,457 @@ +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +UPDATE procTab1 SET c1=10 where c2='b'; +INSERT INTO procTab1 values(1,'a'); +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + CREATE TABLE procTab1(c1 int); + ALTER TABLE procTab1 OWNER TO master_dbo; + INSERT INTO procTab1 values (5); + ALTER TABLE procTab1 ADD c2 char; + CALL tsql_interop_proc1(); +END +$$ LANGUAGE PLPGSQL; +GO + +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + INSERT INTO procTab1(c1,c2) values (3,'c'); + UPDATE procTab1 SET c1=10 where c2='b'; + INSERT INTO procTab1(c1,c2) values (4,'d'); + DELETE FROM procTab1 where c2='a'; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +COMMIT +GO +SELECT * FROM procTab1 order by c1 +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL tsql_interop_proc1() +GO +COMMIT +GO +SELECT * FROM procTab1 +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL tsql_interop_proc1() +GO +ROLLBACK +GO +SELECT * FROM procTab1 +GO + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1 +GO +EXEC tsql_interop_proc1 +GO +INSERT INTO procTab1(c1,c2) values(11,'e') +GO +ROLLBACK TRANSACTION sp1 +GO +SELECT * FROM procTab1 +GO +ROLLBACK +GO +SELECT * FROM procTab1 +GO + +-- tsql +DROP TABLE procTab1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +DECLARE myvar varchar(1) = 'f'; +BEGIN + UPDATE procTab1 SET c2=myvar WHERE c1 BETWEEN 2 AND 10; + INSERT INTO procTab1(c1,c2) values(12,'x'); + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + SAVEPOINT sp1; + INSERT INTO procTab1(c1,c2) values(13,'w'); + EXECUTE "ROLLBACK TO $1" USING sp1; + ROLLBACK; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + INSERT INTO procTab1(c1,c2) values(13,'w'); + ROLLBACK; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1; +GO +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +BEGIN TRANSACTION; +UPDATE procTab1 SET c1=10 where c2='b'; +INSERT INTO procTab1 values(1,'a'); +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + SAVEPOINT sp1; + INSERT INTO procTab1(c1,c2) values(13,'w'); + EXECUTE "ROLLBACK TO $1" USING sp1; + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +BEGIN TRANSACTION; +UPDATE procTab1 SET c1=10 where c2='b'; +SAVE TRANSACTION sp1; +INSERT INTO procTab1 values(1,'a'); +ROLLBACK TRANSACTION sp1; +INSERT INTO procTab1 values(2,'b'); +EXEC psql_interop_proc2; +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + SAVEPOINT sp2; + INSERT INTO procTab1(c1,c2) values(13,'w'); + EXECUTE "ROLLBACK TO $1" USING sp2; + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc2 +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + UPDATE procTab1 SET c1=c1+2 WHERE c1 BETWEEN 2 AND 10; + INSERT INTO procTab1(c1,c2) values(13,'w'); + COMMIT; +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO +SELECT * FROM procTab1 +GO +DROP PROCEDURE tsql_interop_proc1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc1 +AS +DECLARE @dt DATETIME = '2019-12-31 14:43:35.863'; +CREATE TABLE procTab1(c1 int, c2 char); +SELECT CONVERT(varchar(30), @dt, 1); +INSERT INTO procTab1 values(1,'a'); +ROLLBACK TRANSACTION sp1; +INSERT INTO procTab1 values(2,'b'); +SELECT CONVERT(varchar(30), CAST('2017-08-25' AS date), 102); +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +SAVEPOINT sp1 +GO +CALL tsql_interop_proc1() +GO +COMMIT +GO +SELECT * FROM procTab1 +GO + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1 +GO +EXEC tsql_interop_proc1 +GO +ROLLBACK +GO +SELECT * FROM procTab1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle int; +EXEC SP_PREPEXEC @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +EXEC ('select datetimefromparts(2016, 12, 26, 23, 30, 5, 32);') +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc() +AS +$$ +BEGIN +CALL tsql_interop_proc(); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc; +GO +COMMIT +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle INT; +DECLARE @batch VARCHAR(500); +SET @batch = 'begin transaction; insert into t1 values(1); commit;' +EXEC sp_prepare @handle OUT, NULL, @batch +EXEC sp_execute @handle +BEGIN TRANSACTION; +EXEC sp_execute @handle +ROLLBACK; +EXEC SP_UNPREPARE @handle; +GO + +-- tsql +CREATE TABLE t1 (a INT); +GO +EXEC psql_interop_proc; +GO +SELECT count(*) FROM t1; +GO +DROP PROCEDURE tsql_interop_proc +GO +DROP TABLE t1 +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +DECLARE @handle int; +DECLARE @inner_handle int; +DECLARE @batch VARCHAR(500); +SET @batch = 'EXEC SP_PREPARE @inner_handle OUT, NULL, ''SELECT 1'' ' +EXEC SP_PREPARE @handle out, '@inner_handle int OUT', @batch +EXEC SP_EXECUTE @handle, @inner_handle OUT +BEGIN TRANSACTION; +EXEC SP_EXECUTE @inner_handle +ROLLBACK; +EXEC SP_UNPREPARE @inner_handle -- unprepare inner first +EXEC SP_UNPREPARE @handle +GO + +-- tsql +EXEC psql_interop_proc; +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +CREATE TABLE cursorTab(a INT, b SMALLINT, c BIGINT, d TINYINT, e BIT); +INSERT INTO cursorTab values(0, 0, 0, 0, 0); +INSERT INTO cursorTab values(1, 2, 3, 4, 1); +INSERT INTO cursorTab values(4, 5, 6, 7, 4); +DECLARE @cursor_handle int; +EXEC sp_cursoropen @cursor_handle OUTPUT, 'select a,b,c,d from cursorTab', 2, 1; +EXEC sp_cursorfetch @cursor_handle, 2, 0, 2; +EXEC sp_cursoroption @cursor_handle, 1, 2; +BEGIN TRANSACTION; +EXEC sp_cursor @cursor_handle, 40, 1, ''; +COMMIT; +EXEC sp_cursorclose @cursor_handle; +GO + +EXEC psql_interop_proc; +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +SELECT DATEPART(weekday, CAST( '2021-12-31' as DATE)); +SELECT DATENAME(year, cast('2002-05-23 23:41:29.998' as smalldatetime)); +SELECT @@DATEFIRST; +SELECT dateadd(year, 2, CAST('20060830' AS datetime)); +SELECT + lower(cast('A ' as char(5))), + left(cast('a ' as char(5)), 2), + charindex(cast('a ' as char(5)), 'a '), + ascii(cast('a ' as char(5))), + datalength(cast('123 ' as char(5))), + length(cast('123 ' as char(5))), + len(cast('123 ' as char(5))); +BEGIN TRANSACTION; +SELECT cast(cast('2021-08-15 ' as char(11)) as sys.smalldatetime); +COMMIT; +GO + +EXEC tsql_interop_proc; +GO +EXEC psql_interop_proc; +GO +DROP PROCEDURE tsql_interop_proc +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc1 +GO +DROP PROCEDURE psql_interop_proc2 +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc1 +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc +GO diff --git a/contrib/test/JDBC/input/interoperability/TestProcedureWithTriggers.mix b/contrib/test/JDBC/input/interoperability/TestProcedureWithTriggers.mix new file mode 100644 index 00000000000..1c4641357bb --- /dev/null +++ b/contrib/test/JDBC/input/interoperability/TestProcedureWithTriggers.mix @@ -0,0 +1,597 @@ +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc1() +AS +$$ +BEGIN + CREATE TABLE triggerTab1(c1 int, c2 varchar(30)); + CREATE TABLE triggerTab2(c1 int); + INSERT INTO triggerTab1 VALUES(1, 'first'); + INSERT INTO triggerTab2 VALUES(1); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +EXEC psql_interop_proc1 +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +INSERT INTO triggerTab2 VALUES(4); +UPDATE triggerTab2 set c1 = c1+2; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +INSERT INTO triggerTab2 VALUES(2); +UPDATE triggerTab1 set c1 = c1+2; +DELETE FROM triggerTab2; +INSERT INTO triggerTab2 values(2); +GO + +-- psql currentSchema=master_dbo,public +CREATE PROCEDURE psql_interop_proc2() +AS +$$ +BEGIN + INSERT INTO triggerTab1 VALUES(2, 'second'); + INSERT INTO triggerTab1 VALUES(3, 'third'); +END +$$ LANGUAGE PLPGSQL; +GO + +-- tsql +CREATE PROCEDURE tsql_interop_proc +AS +INSERT INTO triggerTab1 VALUES(2, 'second'); +INSERT INTO triggerTab1 VALUES(3, 'third'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +EXEC psql_interop_proc2 +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +CALL psql_interop_proc2(); +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC tsql_interop_proc +GO +SELECT * from triggerTab1 ORDER BY c1; +GO +COMMIT +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO +ROLLBACK +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc2 +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +ROLLBACK +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +CALL psql_interop_proc2(); +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +COMMIT +GO + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1; +GO +EXEC psql_interop_proc2 +GO +ROLLBACK TRANSACTION sp1; +GO +SELECT * from triggerTab1 ORDER BY c1; +GO +COMMIT +GO + +-- tsql +BEGIN TRANSACTION +GO +SAVE TRANSACTION sp1; +GO +EXEC tsql_interop_proc +GO +ROLLBACK TRANSACTION sp1; +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +COMMIT +GO + +-- psql currentSchema=master_dbo,public +BEGIN TRANSACTION +GO +SAVEPOINT sp1; +GO +CALL tsql_interop_proc(); +GO +ROLLBACK TO sp1; +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +COMMIT +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc; +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(2, 'second'); +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc; +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(2, 'second'); +ROLLBACK; +INSERT INTO triggerTab1 VALUES(3, 'third'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- tsql +DROP PROCEDURE tsql_interop_proc; +GO + +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(2, 'second'); +SAVE TRANSACTION sp1; +INSERT INTO triggerTab1 VALUES(3, 'third'); +ROLLBACK TRANSACTION sp1; +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * FROM triggerTab1 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * FROM triggerTab2 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO +DROP TABLE triggerTab1; +GO +DROP TABLE triggerTab2; +GO +CREATE PROCEDURE tsql_create_table +AS + CREATE TABLE triggerTab1(c1 int, c2 varchar(30)); + CREATE TABLE triggerTab2(c1 int); + INSERT INTO triggerTab1 VALUES(1, 'first'); + INSERT INTO triggerTab2 VALUES(1); +GO +EXEC tsql_create_table; +GO + +-- psql currentSchema=master_dbo,public +CREATE FUNCTION trigger_txnTrig3() + RETURNS TRIGGER + LANGUAGE PLPGSQL +AS $$ +BEGIN + UPDATE triggerTab1 set c1 = c1/2; + DELETE FROM triggerTab2; + RETURN NEW; +END; +$$ +GO + +CREATE TRIGGER txnTrig3 +AFTER DELETE ON triggerTab1 +FOR EACH ROW +EXECUTE PROCEDURE trigger_txnTrig3(); +GO + +-- tsql +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +INSERT INTO triggerTab2 VALUES(4); +UPDATE triggerTab2 set c1 = c1+2; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +INSERT INTO triggerTab2 VALUES(2); +UPDATE triggerTab1 set c1 = c1+2; +DELETE FROM triggerTab2; +INSERT INTO triggerTab2 values(2); +GO + +-- psql currentSchema=master_dbo,public +CALL psql_interop_proc2(); +GO +SELECT * FROM triggerTab1 ORDER BY c1; +GO + +-- tsql +BEGIN TRANSACTION +GO +EXEC psql_interop_proc2; +GO +SELECT * FROM triggerTab2 ORDER BY c1; +GO +COMMIT +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO +DROP TRIGGER txnTrig3; +GO +DROP FUNCTION trigger_txnTrig3; +GO + +-- tsql +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +UPDATE triggerTab2 set c1 = c1+2; +COMMIT; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +INSERT INTO triggerTab2 VALUES(2); +SELECT * FROM deleted; +DELETE FROM triggerTab1; +GO + +DROP PROCEDURE tsql_interop_proc +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +BEGIN TRANSACTION; +UPDATE triggerTab2 set c1 = c1+2; +COMMIT; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +INSERT INTO triggerTab1 VALUES(2, 'second'); +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +BEGIN TRANSACTION; +SELECT * FROM inserted; +UPDATE triggerTab2 set c1 = c1+2; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +INSERT INTO triggerTab1 VALUES(3, 'third'); +SAVE TRANSACTION sp1; +INSERT INTO triggerTab2 VALUES(3); +ROLLBACK TRANSACTION sp1; +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SAVE TRANSACTION sp1; +SELECT * FROM inserted; +UPDATE triggerTab2 set c1 = c1+2; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +ROLLBACK TRANSACTION sp1; +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +ROLLBACK TRANSACTION sp1; +UPDATE triggerTab2 set c1 = c1+2; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +SAVE TRANSACTION sp1; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +SAVE TRANSACTION sp1; +UPDATE triggerTab2 set c1 = c1+2; +ROLLBACK TRANSACTION sp1; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +COMMIT; +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +SAVE TRANSACTION sp1; +UPDATE triggerTab2 set c1 = c1+2; +COMMIT; +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab2 ORDER BY c1; +GO + +-- psql currentSchema=master_dbo,public +CALL tsql_interop_proc(); +GO +SELECT * from triggerTab1 ORDER BY c1; +GO + +-- tsql +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO + +CREATE TRIGGER txnTrig1 ON triggerTab1 AFTER INSERT AS +SELECT * FROM inserted; +SAVE TRANSACTION sp1; +SELECT dateadd(year, 2, CAST('20060830' AS datetime)); +UPDATE triggerTab2 set c1 = c1+2; +GO + +CREATE TRIGGER txnTrig2 ON triggerTab2 FOR UPDATE AS +SELECT * FROM inserted; +INSERT INTO triggerTab2 VALUES(2); +SELECT * FROM deleted; +EXEC tsql_interop_proc1; +SELECT * FROM table_interop('tsql_interop','psql_interop'); +ROLLBACK TRANSACTION sp1; +DELETE FROM triggerTab1; +GO + +CREATE PROCEDURE tsql_interop_proc1 +AS +SELECT datepart(week, CAST('2007-04-21' AS date)), datepart(weekday, CAST('2007-04-21' AS date)); +GO + +DROP PROCEDURE tsql_interop_proc; +GO +CREATE PROCEDURE tsql_interop_proc +AS +BEGIN TRANSACTION; +INSERT INTO triggerTab1 VALUES(3, 'third'); +INSERT INTO triggerTab2 VALUES(3); +COMMIT; +GO + +CREATE FUNCTION table_interop (@arg1 varchar(5), @arg2 varchar(10)) +RETURNS TABLE AS RETURN +(SELECT @arg1 as a, @arg2 as b) +GO + +-- tsql +EXEC tsql_interop_proc +GO +SELECT * from triggerTab2 ORDER BY c1; +GO +DROP TRIGGER txnTrig1; +GO +DROP TRIGGER txnTrig2; +GO + +-- psql currentSchema=master_dbo,public +DROP PROCEDURE psql_interop_proc1; +GO +DROP PROCEDURE psql_interop_proc2; +GO + +-- tsql +DROP FUNCTION table_interop; +GO +DROP PROCEDURE tsql_create_table; +GO +DROP PROCEDURE tsql_interop_proc; +GO +DROP PROCEDURE tsql_interop_proc1; +GO +DROP TABLE triggerTab1; +GO +DROP TABLE triggerTab2; +GO diff --git a/contrib/test/JDBC/input/pg_stat_activity.txt b/contrib/test/JDBC/input/pg_stat_activity.txt new file mode 100644 index 00000000000..2a3fc913247 --- /dev/null +++ b/contrib/test/JDBC/input/pg_stat_activity.txt @@ -0,0 +1,17 @@ +SELECT sys.babelfish_set_role(session_user); + +# Babel-1294 Support application_name in pg_stat_activity +select application_name from pg_stat_activity where pid = pg_backend_pid(); + +# BABEL-1326: Support query in pg_stat_activity +select query from pg_stat_activity where pid = pg_backend_pid(); + +# BABEL-1326: Checking if the right query is returned for Prepexec with batch too +prepst#!#select query from pg_stat_activity where pid = pg_backend_pid();select @a as a#!#int|-|a|-|1 +prepst#!#exec#!#int|-|a|-|1 + +# BABEL-1326: Checking if the right query is returned from a procedure and a nested procedure as well +Create procedure proc_pg_stat_activity as begin select query from pg_stat_activity where pid = pg_backend_pid(); end; +exec proc_pg_stat_activity; +Create procedure proc1_pg_stat_activity as begin exec proc_pg_stat_activity end; +exec proc1_pg_stat_activity; diff --git a/contrib/test/JDBC/input/sqlBatch/TestSQLQueries.txt b/contrib/test/JDBC/input/sqlBatch/TestSQLQueries.txt new file mode 100644 index 00000000000..8b7e65c1fc8 --- /dev/null +++ b/contrib/test/JDBC/input/sqlBatch/TestSQLQueries.txt @@ -0,0 +1,234 @@ +#1 CREATE TABLE with primary and unique keyword +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +CREATE TABLE tmp(a int PRIMARY KEY, b int UNIQUE); +INSERT INTO tmp(a,b) values(1,1); +INSERT INTO tmp(a,b) values(2,2); +SELECT * FROM tmp; +DROP TABLE tmp; + +#2 2 table with foreign key relation +#CREATE TABLE tmp1(b int PRIMARY KEY); +#CREATE TABLE tmp2(a int PRIMARY KEY, b int FOREIGN KEY REFERENCES tmp1(b)); +#INSERT INTO tmp1(b) VALUES (1); +#INSERT INTO tmp2(a,b) values(1,1); +#SELECT * FROM tmp1; +#SELECT * FROM tmp2; +#DROP TABLE tmp2; +#DROP TABLE tmp1; + +#3 Repeated #2 with CONSTRAINT keyword +#CREATE TABLE tmp1(b int PRIMARY KEY); +#CREATE TABLE tmp2(a int, b int, PRIMARY KEY(a), CONSTRAINT FK_tmp FOREIGN KEY (b) REFERENCES tmp1(b)); +#INSERT INTO tmp1(b) VALUES (1); +#INSERT INTO tmp2(a,b) values(1,1); +#SELECT * FROM tmp1; +#SELECT * FROM tmp2; +#DROP TABLE tmp2; +#DROP TABLE tmp1; + +#4 CREATE TABLE with NOT NULL column +CREATE TABLE tmp(a int PRIMARY KEY,b int NOT NULL); +INSERT INTO tmp(a,b) values(1,1); +INSERT INTO tmp(a,b) values(2,1); +#INSERT INTO tmp(a,b) values(2,NULL); +SELECT * FROM tmp; +DROP TABLE tmp; + +#5 INSERTION and DELETION +CREATE TABLE tmp(a int PRIMARY KEY); +INSERT INTO tmp(a) VALUES(1); +INSERT INTO tmp(a) VALUES(2); +INSERT INTO tmp(a) VALUES(3); +INSERT INTO tmp(a) VALUES(4); +INSERT INTO tmp(a) VALUES(5); +SELECT * FROM tmp; +DELETE FROM tmp WHERE a>2; +SELECT * FROM tmp; +DROP TABLE tmp; + +#6 IS NOT NULL condition in WHERE clause +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) values(1,1); +INSERT INTO tmp(a,b) values(2,1); +INSERT INTO tmp(a,b) values(3,NULL); +INSERT INTO tmp(a,b) values(4,NULL); +SELECT * FROM tmp WHERE b IS NOT NULL; +DROP TABLE tmp; + +#7 Add new column using ALTER TABLE +CREATE TABLE tmp(a int PRIMARY KEY); +INSERT INTO tmp(a) VALUES(1); +INSERT INTO tmp(a) VALUES(2); +SELECT * FROM tmp; +ALTER TABLE tmp ADD b VARCHAR(20) NULL; +SELECT * FROM tmp; +INSERT INTO tmp(a,b) VALUES(4,'Dipesh'); +INSERT INTO tmp(a,b) VALUES(5,' Dipesh'); +SELECT * FROM tmp; +DROP TABLE tmp; + +#8 Repeated #8 with default value for newly added col +CREATE TABLE tmp(a int PRIMARY KEY); +INSERT INTO tmp(a) VALUES(1); +INSERT INTO tmp(a) VALUES(2); +SELECT * FROM tmp; +ALTER TABLE tmp ADD b VARCHAR(20) DEFAULT 'Dipesj'; +SELECT * FROM tmp; +INSERT INTO tmp(a,b) VALUES(4,'Dipesh'); +INSERT INTO tmp(a,b) VALUES(5,' Dipesh'); +SELECT * FROM tmp; +DROP TABLE tmp; + +#9 Change datatype of existing column using ALTER TABLE +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +INSERT INTO tmp(a,b) VALUES(2,2); +SELECT * FROM tmp; +ALTER TABLE tmp ALTER COLUMN b VARCHAR(10); +SELECT * FROM tmp; +INSERT INTO tmp(a,b) VALUES(4,'Dipesh'); +INSERT INTO tmp(a,b) VALUES(5,' Dipesh'); +SELECT * FROM tmp; +DROP TABLE tmp; + +#10 UPDATE TABLE with fancy condition +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +INSERT INTO tmp(a,b) VALUES(2,2); +INSERT INTO tmp(a,b) VALUES(3,3); +INSERT INTO tmp(a,b) VALUES(4,4); +INSERT INTO tmp(a,b) VALUES(5,5); +SELECT * FROM tmp; +UPDATE tmp SET b=b+1 WHERE b>2; +SELECT * FROM tmp; +DROP TABLE tmp; + +#11 DROP some column using ALTER TABLE +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +INSERT INTO tmp(a,b) VALUES(2,2); +INSERT INTO tmp(a,b) VALUES(3,3); +INSERT INTO tmp(a,b) VALUES(4,4); +INSERT INTO tmp(a,b) VALUES(5,5); +SELECT * FROM tmp; +#ALTER TABLE tmp DROP COLUMN b; +#INSERT INTO tmp(a) values (6); +#SELECT * FROM tmp; +DROP TABLE tmp; + +#12 CREATE TABLE with fancy constraint +CREATE TABLE tmp(a int PRIMARY KEY CHECK (a>10),b int); +INSERT INTO tmp(a,b) VALUES(11,1); +INSERT INTO tmp(a,b) VALUES(12,2); +INSERT INTO tmp(a,b) VALUES(13,3); +INSERT INTO tmp(a,b) VALUES(14,4); +INSERT INTO tmp(a,b) VALUES(15,5); +SELECT * FROM tmp; +DROP TABLE tmp; + +#CREATE PROCEDURE tmp AS +#BEGIN +# CREATE TABLE dip(a int PRIMARY KEY CHECK (a>10),b int); +# INSERT INTO dip(a,b) VALUES(11,1); +# INSERT INTO dip(a,b) VALUES(12,2); +# INSERT INTO dip(a,b) VALUES(13,3); +# INSERT INTO dip(a,b) VALUES(14,4); +# INSERT INTO dip(a,b) VALUES(15,5); +# SELECT * FROM dip; +# DROP TABLE dip; +#END; + +#13 invoke simple stored procedure using EXECUTE +EXECUTE tmp; + +#14 invoke simple stored procedure using EXEC +EXEC tmp; + +#DROP PROCEDURE tmp; + +#15 UPDATE rows in existing table +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1),(2,2),(3,3),(4,4),(5,5); +SELECT * FROM tmp; +UPDATE tmp SET b=b*2+1 WHERE (a>2); +SELECT * FROM tmp; +DROP TABLE tmp; + +#16 TRUNCATE table +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1),(2,2),(3,3),(4,4),(5,5); +SELECT * FROM tmp; +TRUNCATE TABLE tmp; +SELECT * FROM tmp; +DROP TABLE tmp; + +CREATE TABLE temp1 (i INT,j INT,t TEXT); + +CREATE TABLE temp2 ( i INT,k INT); + +INSERT INTO temp1 VALUES (1, 4, 'one'); +INSERT INTO temp1 VALUES (2, 3, 'two'); +INSERT INTO temp1 VALUES (3, 2, 'three'); +INSERT INTO temp1 VALUES (4, 1, 'four'); +INSERT INTO temp1 VALUES (5, 0, 'five'); +INSERT INTO temp1 VALUES (6, 6, 'six'); +INSERT INTO temp1 VALUES (7, 7, 'seven'); +INSERT INTO temp1 VALUES (8, 8, 'eight'); +INSERT INTO temp1 VALUES (0, NULL, 'zero'); +INSERT INTO temp1 VALUES (NULL, NULL, NULL); +INSERT INTO temp1 VALUES (NULL, 0, 'zero'); + +INSERT INTO temp2 VALUES (1, -1); +INSERT INTO temp2 VALUES (2, 2); +INSERT INTO temp2 VALUES (3, -3); +INSERT INTO temp2 VALUES (2, 4); +INSERT INTO temp2 VALUES (5, -5); +INSERT INTO temp2 VALUES (5, -5); +INSERT INTO temp2 VALUES (0, NULL); +INSERT INTO temp2 VALUES (NULL, NULL); +INSERT INTO temp2 VALUES (NULL, 0); + +#17 CROSS JOIN +SELECT * FROM temp1 CROSS JOIN temp2; + +#18 INNER JOIN +SELECT temp1.i, temp1.j, temp1.t, temp2.k FROM temp1 INNER JOIN temp2 ON temp1.i=temp2.i; +SELECT temp1.i, temp1.j, temp1.t, temp2.k FROM temp1 JOIN temp2 ON temp1.i=temp2.i; + +#19 LEFT JOIN +SELECT * FROM temp1 LEFT OUTER JOIN temp2 ON temp1.i=temp2.k; + +#20 RIGHT JOIN +SELECT * FROM temp1 RIGHT OUTER JOIN temp2 ON temp1.i=temp2.i; + +#21 FULL OUTER JOIN +SELECT * FROM temp1,temp2; + +DROP TABLE temp1; +DROP TABLE temp2; + +#22 dropping all columns +CREATE TABLE tmp(a int PRIMARY KEY,b int); +INSERT INTO tmp(a,b) VALUES(1,1); +INSERT INTO tmp(a,b) VALUES(2,2); +INSERT INTO tmp(a,b) VALUES(3,3); +INSERT INTO tmp(a,b) VALUES(4,4); +INSERT INTO tmp(a,b) VALUES(5,5); +SELECT * FROM tmp; +#ALTER TABLE tmp DROP COLUMN b; +#ALTER TABLE tmp DROP COLUMN a; +#SELECT * FROM tmp; +DROP TABLE tmp; + +#23 +CREATE TABLE DATE_dt (a DATE); +INSERT INTO DATE_dt(a) values('2000-12-13'); +INSERT INTO DATE_dt(a) values('1900-02-28'); +INSERT INTO DATE_dt(a) values('0001-01-01'); +INSERT INTO DATE_dt(a) values('9999-12-31'); +INSERT INTO DATE_dt(a) values(NULL); +SELECT * FROM DATE_dt; +ALTER TABLE DATE_dt ALTER COLUMN a DATETIME; +SELECT * FROM DATE_dt; +DROP TABLE DATE_dt; +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; diff --git a/contrib/test/JDBC/input/storedProcedures/BABEL-1365.sql b/contrib/test/JDBC/input/storedProcedures/BABEL-1365.sql new file mode 100644 index 00000000000..832608d3f4a --- /dev/null +++ b/contrib/test/JDBC/input/storedProcedures/BABEL-1365.sql @@ -0,0 +1,23 @@ +-- Test normal case: @v = SP_EXECUTESQL 'SQL' +DECLARE @SQLString NVARCHAR(100); +DECLARE @P0 int; +set @SQLString = N'SELECT 123;' +EXEC @P0 = sp_executesql @SQLString; +SELECT @P0; +go + +-- Test case: @v = SP_EXECUTESQL 'SQL' where @v is implicitly casted to varchar +DECLARE @SQLString NVARCHAR(100); +DECLARE @P0 varchar(3); +set @SQLString = N'SELECT 321;' +EXEC @P0 = sp_executesql @SQLString; +SELECT @P0; +go + +-- Test case: @v = SP_EXECUTESQL 'SQL' where 'SQL' statement does not return result set +DECLARE @SQLString NVARCHAR(100); +DECLARE @P0 int; +set @SQLString = N'CREATE TABLE t(a INT); DROP TABLE t;'; +EXEC @P0 = sp_executesql @SQLString; +SELECT @P0; +go \ No newline at end of file diff --git a/contrib/test/JDBC/input/storedProcedures/TestPrepareExec.txt b/contrib/test/JDBC/input/storedProcedures/TestPrepareExec.txt new file mode 100644 index 00000000000..ba229a4434a --- /dev/null +++ b/contrib/test/JDBC/input/storedProcedures/TestPrepareExec.txt @@ -0,0 +1,15 @@ +#single parameter bind in target list +prepst#!#SELECT @a#!#INT|-|a|-|1 +prepst#!#exec#!#INT|-|a|-|1 +prepst#!#exec#!#INT|-|a|-|1 +prepst#!#exec#!#INT|-|a|-|1 +#no parameter bind +prepst#!#SELECT 1#!# +prepst#!#exec#!# +prepst#!#exec#!# +prepst#!#exec#!# +#empty query +prepst#!# #!# +prepst#!#exec#!# +prepst#!#exec#!# +prepst#!#exec#!# diff --git a/contrib/test/JDBC/input/storedProcedures/TestSPExecutesql.sql b/contrib/test/JDBC/input/storedProcedures/TestSPExecutesql.sql new file mode 100644 index 00000000000..fb71ce869a4 --- /dev/null +++ b/contrib/test/JDBC/input/storedProcedures/TestSPExecutesql.sql @@ -0,0 +1,194 @@ +CREATE TABLE spExecutesqlTable1 (a INT, b VARCHAR(10)); +CREATE TABLE spExecutesqlTable2 (a INT, b INT, c INT, d INT); +go + +/* 1. Execute a string of statement */ +DECLARE @SQLString NVARCHAR(100); +SET @SQLString = N'SELECT N''hello world'';'; +EXEC sp_executesql @SQLString; +go + +/* 2. Execute a statement string with parameters */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable1 VALUES (@a, @b);'; +SET @ParamDef = N'@a INT, @b VARCHAR(10)'; + +DECLARE @p INT = 1; +EXEC sp_executesql @SQLString, @ParamDef, @p, @b = N'hello'; +SELECT * FROM spExecutesqlTable1; +go + +TRUNCATE TABLE spExecutesqlTable1; +go + +/* 3. Execute a statement string with both unnamed params and named params */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; + +EXEC sp_executesql @SQLString, @ParamDef, 1, 2, @d = 4, @c = 3; +SELECT * FROM spExecutesqlTable2; +go + +TRUNCATE TABLE spExecutesqlTable2; +go + +/* 4. Nested sp_executesql */ +DECLARE @SQLString1 NVARCHAR(100); +DECLARE @SQLString2 NVARCHAR(max); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString1 = N'INSERT INTO spExecutesqlTable1 VALUES (@a, @b);' +SET @SQLString2 = @SQLString1 + N'EXEC sp_executesql N''INSERT INTO spExecutesqlTable1 VALUES (@c + @c, @d + @d);'', N''@c INT, @d VARCHAR(10)'', @a, @b;' +SET @ParamDef = N'@a INT, @b VARCHAR(10)'; +EXEC sp_executesql @SQLString2, @ParamDef, @a = 10, @b = N'str'; +SELECT * FROM spExecutesqlTable1; +go + +TRUNCATE TABLE spExecutesqlTable1; +go + +-- Nested with param name collision +DECLARE @SQLString1 NVARCHAR(100); +DECLARE @SQLString2 NVARCHAR(max); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString1 = N'INSERT INTO spExecutesqlTable1 VALUES (@a, @b);' +SET @SQLString2 = @SQLString1 + N'EXEC sp_executesql N''INSERT INTO spExecutesqlTable1 VALUES (@a + @a, @b + @b);'', N''@a INT, @b VARCHAR(10)'', @a, @b;' +SET @ParamDef = N'@a INT, @b VARCHAR(10)'; +EXEC sp_executesql @SQLString2, @ParamDef, @a = 11, @b = N'rts'; +SELECT * FROM spExecutesqlTable1; +go + +TRUNCATE TABLE spExecutesqlTable1; +go + +/* 5. Execute a statement string with OUT/INOUT param */ +DECLARE @SQLString1 NVARCHAR(100); +DECLARE @SQLString2 NVARCHAR(max); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString1 = N'SET @a = @b + @b'; +SET @SQLString2 = N'EXEC sp_executesql N''SET @a = @b + @b'', N''@a INT OUT, @b INT'', @a OUT, @b;'; +SET @ParamDef = N'@a INT OUTPUT, @b INT'; + +DECLARE @p INT; +DECLARE @a INT; +-- OUT param +EXEC sp_executesql @SQLString1, @ParamDef, @a = @p OUT, @b = 10; +-- Nested with OUT param name collision +EXEC sp_executesql @SQLString2, @ParamDef, @a = @a OUT, @b = 11; +SELECT @p, @a; +go + +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = @a + 1;'; +SET @ParamDef = N'@a INT OUT'; + +DECLARE @p1 INT = 1; +DECLARE @p2 INT = 1; +-- Declared as INOUT and called as OUT +EXEC sp_executesql @SQLString, @ParamDef, @a = @p1 OUT; +-- Declared as INOUT and called as IN +EXEC sp_executesql @SQLString, @ParamDef, @a = @p2; +SELECT @p1, @p2; +go + +/* 6. Implicit cast for IN param */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @b = @a;'; +SET @ParamDef = N'@a FLOAT, @b FLOAT OUT'; + +DECLARE @p FLOAT; +DECLARE @a MONEY = 3.14; +EXEC sp_executesql @SQLString, @ParamDef, @a = @a, @b = @p OUTPUT; +SELECT @p; +go + +/* 7. Implicit cast for OUT param */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = CAST(''2020-01-01'' AS DATE); SET @b = @a;'; +SET @ParamDef = N'@a DATE OUT, @b DATETIME OUT'; + +DECLARE @p1 DATETIME; +DECLARE @p2 DATETIME; +EXEC sp_executesql @SQLString, @ParamDef, @a = @p1 OUT, @b = @p2 OUT; +SELECT @p1, @p2; +go + +/* Exceptions */ +/* 1. Wrong order of named/unnamed params */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, @b = 2, @c = 3, 1, @d = 4; +go + +/* 2. Number mismatch of param defs and given params */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, 1, 2, 3; +go + +/* 3. Invalid param name */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, @a = 1, @b = 2, @c = 3, @e = 4; +go + +/* 4. Invalid param def */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, 1, 2, 3, 4; +go + +/* 5. Unsupported implicit cast */ +-- IN param +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'INSERT INTO spExecutesqlTable2 VALUES (@a, @b, @c, @d);'; +SET @ParamDef = N'@a INT, @b INT, @c INT, @d INT'; +EXEC sp_executesql @SQLString, @ParamDef, 1, 3.14, 'hello', 4; +go + +-- OUT param +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = CAST(''abc'' AS VARCHAR(3));'; +SET @ParamDef = N'@a FLOAT OUT'; + +DECLARE @p FLOAT; +EXEC sp_executesql @SQLString, @ParamDef, @a = @p OUT; +go + +/* 6. Invalid OUT param */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = 20'; +SET @ParamDef = N'@a INT OUT'; +EXEC sp_executesql @SQLString, @ParamDef, @a = 10 OUT; +go + +/* 7. Param declared as IN but called as OUT */ +DECLARE @SQLString NVARCHAR(100); +DECLARE @ParamDef NVARCHAR(100); +SET @SQLString = N'SET @a = 20'; +SET @ParamDef = N'@a INT'; + +DECLARE @p INT; +EXEC sp_executesql @SQLString, @ParamDef, @a = @p OUT; +go + +/* Clean up */ +DROP TABLE spExecutesqlTable1; +DROP TABLE spExecutesqlTable2; +go diff --git a/contrib/test/JDBC/input/storedProcedures/TestSPPrepare.sql b/contrib/test/JDBC/input/storedProcedures/TestSPPrepare.sql new file mode 100644 index 00000000000..a0f20968e5f --- /dev/null +++ b/contrib/test/JDBC/input/storedProcedures/TestSPPrepare.sql @@ -0,0 +1,357 @@ +--- Simple SP_PREPARE +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO + +--- Simple SP_PREPEXEC +DECLARE @handle int; +EXEC SP_PREPEXEC @handle OUT, NULL, 'SELECT ''OK''' +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO + +--- Basic SP_PREPARE with args +DECLARE @handle int; +EXEC SP_PREPARE @handle out, N'@a int, @b int', N'select @a, @b', 10; +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_UNPREPARE @handle; +GO + +--- Basic SP_PREPARE with args +DECLARE @handle int; +EXEC SP_PREPEXEC @handle out, N'@a int, @b int', N'select @a, @b', 1, 2; +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_UNPREPARE @handle; +GO + +--- SP_PREPARE Batch Support +DECLARE @handle int; +DECLARE @batch nvarchar(500); +DECLARE @paramdef nvarchar(500); +DECLARE @var int; +SET @batch = 'IF (@cond > 0) SELECT @o = @a ELSE SELECT @o = @b' +SET @paramdef = '@cond int, @a int, @b int, @o int OUTPUT' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, -1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_EXECUTE @handle, 1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_UNPREPARE @handle; +GO + +--- SP_PREPEXEC Batch Support +DECLARE @handle int; +DECLARE @batch nvarchar(500); +DECLARE @paramdef nvarchar(500); +DECLARE @var int; +SET @batch = 'IF (@cond > 0) SELECT @o = @a ELSE SELECT @o = @b' +SET @paramdef = '@cond int, @a int, @b int, @o int OUTPUT' +EXEC SP_PREPEXEC @handle out, @paramdef, @batch, 1, 30, 40, @var OUTPUT +SELECT @var +EXEC SP_EXECUTE @handle, -1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_EXECUTE @handle, 1, 10, 20, @var OUTPUT +SELECT @var +EXEC SP_UNPREPARE @handle; +GO + +--- Parsing specific +DECLARE @handle int; +EXEC SP_PREPEXEC @handle + 1 OUTPUT, NULL, 'SELECT 1' +GO + +DECLARE @handle VARCHAR(20) +EXEC SP_PREPEXEC @handle OUTPUT, NULL, 'SELECT 1' +GO + +DECLARE @handle int; +EXEC SP_PREPEXEC @handle, NULL, 'SELECT 1' +GO + +--- Corner case 1: empty batch +DECLARE @handle int; +EXEC SP_PREPARE @handle out, NULL, NULL +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO + +DECLARE @handle int; +EXEC SP_PREPEXEC @handle out, NULL, NULL +EXEC SP_EXECUTE @handle +EXEC SP_UNPREPARE @handle +GO + +--- Corner case 2: nested prepare +DECLARE @handle int; +DECLARE @inner_handle int; +DECLARE @batch VARCHAR(500); +SET @batch = 'EXEC SP_PREPARE @inner_handle OUT, NULL, ''SELECT 1'' ' +EXEC SP_PREPARE @handle out, '@inner_handle int OUT', @batch +EXEC SP_EXECUTE @handle, @inner_handle OUT +EXEC SP_EXECUTE @inner_handle +EXEC SP_UNPREPARE @inner_handle -- unprepare inner first +EXEC SP_UNPREPARE @handle +GO + +DECLARE @handle int; +DECLARE @inner_handle int; +DECLARE @batch VARCHAR(500); +SET @batch = 'EXEC SP_PREPARE @inner_handle OUT, NULL, ''SELECT 1'' ' +EXEC SP_PREPARE @handle out, '@inner_handle int OUT', @batch +EXEC SP_EXECUTE @handle, @inner_handle OUT +EXEC SP_EXECUTE @inner_handle +EXEC SP_UNPREPARE @handle --unprepare outer first +EXEC SP_EXECUTE @inner_handle +EXEC SP_UNPREPARE @inner_handle +GO + +--- Corner case 3: mismatch paramdef and params +DECLARE @handle int; +DECLARE @var int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = 'SELECT @a'; +SET @paramdef = '@a int'; +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 100 +EXEC SP_EXECUTE @handle, @var OUT +EXEC SP_UNPREPARE @handle +GO + +--- Prepare DML statement +CREATE TABLE t1 (a int, b int); +GO + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +INSERT INTO t1 VALUES (@v1, @v2) +INSERT INTO t1 VALUES (@v3, @v4) +' +SET @paramdef = '@v1 int, @v2 int, @v3 int, @v4 int' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 1, 2, 2, 3 +EXEC SP_EXECUTE @handle, 3, 4, 4, 5 +SELECT * FROM t1 ORDER BY 1, 2; +GO + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +UPDATE t1 SET a = a * 10, b = b *10 where a = @var1; +UPDATE t1 SET a = a * 10, b = b *10 where a = @var2; +' +SET @paramdef = '@var1 int, @var2 int' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 1, 2 +EXEC SP_EXECUTE @handle, 3, 4 +SELECT * FROM t1 ORDER BY 1, 2; + +EXEC SP_UNPREPARE @handle +DROP TABLE t1; +GO + +--- Transaction with SP_EXECUTE +CREATE TABLE t1 (a int, b int); +GO + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +INSERT INTO t1 VALUES (@v1, @v2); +INSERT INTO t1 VALUES (@v3, @v4); +' +SET @paramdef = '@v1 int, @v2 int, @v3 int, @v4 int' + +EXEC SP_PREPARE @handle OUT, @paramdef, @batch + +BEGIN TRANSACTION; +EXEC SP_EXECUTE @handle, 1, 2, 2, 3 +SELECT * FROM t1 ORDER BY 1, 2; +COMMIT; +SELECT * FROM t1 ORDER BY 1, 2; + +BEGIN TRANSACTION; +EXEC SP_EXECUTE @handle, 3, 4, 4, 5 +SELECT * FROM t1 ORDER BY 1, 2; +ROLLBACK; +SELECT * FROM t1 ORDER BY 1, 2; + +EXEC SP_UNPREPARE @handle +GO + +DROP TABLE t1; +GO + +--- PREPARE Batch with Transaction +CREATE TABLE t1 (a int, b int); +GO + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +DECLARE @paramdef VARCHAR(500); +SET @batch = ' +BEGIN TRANSACTION +INSERT INTO t1 VALUES (@v1, @v2); +INSERT INTO t1 VALUES (@v3, @v4); +SELECT * FROM t1 ORDER BY 1, 2; +IF (@v1 = 10) + COMMIT; +ELSE + ROLLBACK; +' +SET @paramdef = '@v1 int, @v2 int, @v3 int, @v4 int' +EXEC SP_PREPARE @handle OUT, @paramdef, @batch +EXEC SP_EXECUTE @handle, 10, 20, 30, 40 +SELECT * FROM t1 ORDER BY 1, 2; +EXEC SP_EXECUTE @handle, 50, 60, 70, 80 +SELECT * FROM t1 ORDER BY 1, 2; + +EXEC SP_UNPREPARE @handle +GO + +DROP TABLE t1; +GO + +-- Test Save Point +CREATE TABLE t1 ( a int, b int); +GO + +DECLARE @handle int; +DECLARE @batch VARCHAR(500); +SET @batch = ' +DECLARE @handle int; +BEGIN TRANSACTION; +INSERT INTO t1 VALUES (1, 2); +SAVE TRANSACTION my_savept; +EXEC SP_PREPEXEC @handle OUT, NULL, +''INSERT INTO t1 VALUES (3, 4); + SELECT * FROM t1 ORDER BY 1, 2; + ROLLBACK TRANSACTION my_savept; + SELECT * FROM t1 ORDER BY 1, 2; +''; +SELECT * FROM t1 ORDER BY 1, 2; +ROLLBACK; +SELECT * FROM t1 ORDER BY 1, 2; +EXEC SP_UNPREPARE @handle; +' +EXEC SP_PREPARE @handle OUT, NULL, @batch; +PRINT @handle +EXEC SP_EXECUTE @handle; +EXEC SP_UNPREPARE @handle; +GO + +DROP TABLE t1; +GO + +--- Test string type +CREATE TABLE t1 ( a VARCHAR(10), b VARCHAR(10)); +GO + +DECLARE @handle int; +EXEC SP_PREPEXEC @handle OUT, '@v1 VARCHAR(10), @v2 VARCHAR(10)', 'INSERT INTO t1 VALUES (@v1,@v2)', 'abc', 'efg' +EXEC SP_EXECUTE @handle, 'lmn', 'opq' +EXEC SP_UNPREPARE @handle +SELECT * FROM t1 ORDER BY 1, 2; +GO + +DROP TABLE t1; +GO + +-- Test transaction begins outside the batch and commited/rollbacked inside the batch +CREATE TABLE t1 (a INT); +GO + +BEGIN TRAN; +GO +DECLARE @handle INT; +DECLARE @batch VARCHAR(500); +SET @batch = 'insert into t1 values(1); commit; begin tran;' +EXEC sp_prepare @handle OUT, NULL, @batch +EXEC sp_execute @handle +EXEC sp_execute @handle +EXEC SP_UNPREPARE @handle; +COMMIT; +SELECT COUNT(*) FROM t1; +GO + +BEGIN TRAN; +GO +DECLARE @handle INT; +DECLARE @batch VARCHAR(500); +SET @batch = 'insert into t1 values(1); rollback tran; begin tran;' +EXEC sp_prepare @handle OUT, NULL, @batch +EXEC sp_execute @handle +EXEC sp_execute @handle +EXEC SP_UNPREPARE @handle; +COMMIT; +SELECT COUNT(*) FROM t1; +GO + +DROP TABLE t1; +GO + +-- prepare time error 1 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT * FROM t1' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO + +-- prepare time error 1-2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'DECLARE @var int; SELECT * FROM t1 WHERE c = @var' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO + +-- prepare time error 2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'EXEC my_proc' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO + +-- prepare time error 2-2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'DECLARE @var int; EXEC my_proc @var' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +GO + +-- runtime error 1 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'SELECT * FROM t1; SELECT * FROM t1;' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO + +-- runtime error 2 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'EXEC my_proc; EXEC my_proc;' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO + +-- runtime error 3 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'IF (1=1) SELECT * FROM t1;' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO + +-- runtime error 4 +DECLARE @handle int; +EXEC SP_PREPARE @handle OUT, NULL, 'DECLARE @var int; SET @var = 1; select * from t1 where c = @var' +SELECT (case when @handle IS NOT NULL then 'true' else 'false' end) as 'Prepared' +EXEC SP_EXECUTE @handle +GO diff --git a/contrib/test/JDBC/input/storedProcedures/TestStoredProcedures.txt b/contrib/test/JDBC/input/storedProcedures/TestStoredProcedures.txt new file mode 100644 index 00000000000..e0eeac847af --- /dev/null +++ b/contrib/test/JDBC/input/storedProcedures/TestStoredProcedures.txt @@ -0,0 +1,140 @@ +# PROCEDURE WITH NO BODY +#create procedure sp_test AS BEGIN END; +# PROCEDURE WITH NO PARAMS +create table temp_sp2(a int); +CREATE PROCEDURE sp_test AS BEGIN insert into temp_sp2 values(1); END; +storedproc#!#prep#!#sp_test#!# +SELECT * FROM temp_sp2; +drop table temp_sp2; +drop Procedure sp_test; +# PROCEDURE WITH INPUT PARAMETER +#drop table temp_sp; +create table temp_sp(a int); +Create Procedure stored_proc1 (@a int) As Begin insert into temp_sp values(@a) End; +# NOT WORKING FOR BABEL,RAISED JIRA 444 +storedproc#!#prep#!#stored_proc1#!#int|-|a|-|-100|-|input +# MISMATCH IN RETURN VALUES +exec stored_proc1 -200 +exec stored_proc1 0 +exec stored_proc1 -1 +exec stored_proc1 2 +# filed Jira on this, doesnt work for babel +#exec stored_proc1 2.2 +SELECT * FROM temp_sp; +DROP table temp_sp; +DROP Procedure stored_proc1 +# input parameter +CREATE PROCEDURE sp_test1 (@a INT) AS BEGIN SET @a=100; Select @a as a; END; +exec sp_test1 2 +Declare @a int;Set @a=1; exec sp_test1 @a;select @a as a; +storedproc#!#prep#!#sp_test1#!#int|-|a|-|1|-|input +storedproc#!#prep#!#sp_test1#!#int|-|a|-|10|-|input +DROP PROCEDURE sp_test1 +# TESTING OUT PARAMETERS FOR ALL THE DATATYPES +# int +CREATE PROCEDURE sp_test1 (@a INT OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +#Declare @a int;Set @a=1; exec sp_test1 @a;select @a as a; +storedproc#!#prep#!#sp_test1#!#int|-|a|-|1|-|output +storedproc#!#prep#!#sp_test1#!#int|-|a|-|1|-|inputoutput +DROP PROCEDURE sp_test1 +# smallint +CREATE PROCEDURE sp_test2 (@a SMALLINT OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +#Declare @a smallint;Set @a=1; exec sp_test2 @a;select @a as a; +storedproc#!#prep#!#sp_test2#!#smallint|-|a|-|10|-|output +storedproc#!#prep#!#sp_test2#!#smallint|-|a|-|10|-|inputoutput +DROP PROCEDURE sp_test2 +# bigint +CREATE PROCEDURE sp_test3 (@a BIGINT OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +storedproc#!#prep#!#sp_test3#!#bigint|-|a|-|10|-|output +storedproc#!#prep#!#sp_test3#!#bigint|-|a|-|10|-|inputoutput +DROP PROCEDURE sp_test3 +# tinyint +#CREATE PROCEDURE sp_test4 (@a tinyint OUTPUT) AS BEGIN SET @a=100; Select @a as a; END; +#storedproc#!#prep#!#sp_test4#!#tinyint|-|a|-|10|-|output +#storedproc#!#prep#!#sp_test4#!#tinyint|-|a|-|10|-|inputoutput +#DROP PROCEDURE sp_test4 +# float +CREATE PROCEDURE sp_test5 (@a float OUTPUT) AS BEGIN SET @a=100.12; Select @a as a; END; +#Declare @a float;Set @a=1.1; exec sp_test5 @a;select @a as a; +storedproc#!#prep#!#sp_test5#!#float|-|a|-|10.1|-|output +storedproc#!#prep#!#sp_test5#!#float|-|a|-|10.1|-|inputoutput +DROP PROCEDURE sp_test5 +# varchar +#CREATE PROCEDURE sp_test6 (@a varchar OUTPUT) AS BEGIN SET @a='helloworld'; Select @a as a; END; +#storedproc#!#prep#!#sp_test6#!#varchar|-|a|-|hello|-|output +#storedproc#!#prep#!#sp_test6#!#varchar|-|a|-|hello|-|inputoutput +#DROP PROCEDURE sp_test6 +# char BABEL-705 +#CREATE PROCEDURE sp_test7 (@a char OUTPUT) AS BEGIN SET @a='b'; Select @a as a; END; +#Declare @a varchar;Set @a='h'; exec sp_test7 @a;select @a as a; +#storedproc#!#prep#!#sp_test7#!#char|-|a|-|t|-|output +#storedproc#!#prep#!#sp_test7#!#char|-|a|-|t|-|inputoutput +#DROP PROCEDURE sp_test7 +# nvarchar +#CREATE PROCEDURE sp_test9 (@a nvarchar OUTPUT) AS BEGIN SET @a='helloworld'; Select @a as a; END; +#Declare @a nvarchar;Set @a='hello'; exec sp_test9 @a;select @a as a; +#storedproc#!#prep#!#sp_test9#!#varchar|-|a|-|hello|-|output +#storedproc#!#prep#!#sp_test9#!#varchar|-|a|-|hello|-|inputoutput +#DROP PROCEDURE sp_test9 +# numeric +CREATE PROCEDURE sp_test10 (@a numeric(10,4) OUTPUT) AS BEGIN SET @a=100.41; Select @a as a; END; +#Declare @a numeric(10,4);Set @a=10.04; exec sp_test10 @a;select @a as a; +#storedproc#!#prep#!#sp_test10#!#numeric|-|a|-|123|-|10|-|3|-|output +#storedproc#!#prep#!#sp_test10#!#numeric|-|a|-|123|-|10|-|2|-|inputoutput +DROP PROCEDURE sp_test10 +# decimal +CREATE PROCEDURE sp_test11 (@a decimal(10,4) OUTPUT) AS BEGIN SET @a=100.41; Select @a as a; END; +#Declare @a decimal(10,4);Set @a=10.04; exec sp_test11 @a;select @a as a; +#storedproc#!#prep#!#sp_test11#!#decimal|-|a|-|123|-|10|-|3|-|output +#storedproc#!#prep#!#sp_test11#!#decimal|-|a|-|123|-|10|-|2|-|inputoutput +DROP PROCEDURE sp_test11 +# binary +#CREATE PROCEDURE sp_test12 (@a binary OUTPUT) AS BEGIN SET @a=0x121; Select @a as a; END; +#exec sp_test12 0x122 +#Declare @a binary;Set @a=0x121; exec sp_test12 @a;select @a as a; +#storedproc#!#prep#!#sp_test12#!#binary|-|a|-|0x122|-|output +#storedproc#!#prep#!#sp_test12#!#binary|-|a|-|0x122|-|inputoutput +#DROP PROCEDURE sp_test12 +# varbinary BABEL-701 +#CREATE PROCEDURE sp_test13 (@a varbinary OUTPUT) AS BEGIN SET @a=0x121; Select @a as a; END; +#exec sp_test13 0x122 +#Declare @a varbinary;Set @a=0x122; exec sp_test13 @a;select @a as a; +#storedproc#!#prep#!#sp_test13#!#varbinary|-|a|-|0x122|-|output +#storedproc#!#prep#!#sp_test13#!#varbinary|-|a|-|0x122|-|inputoutput +#DROP PROCEDURE sp_test13 +# date +CREATE PROCEDURE sp_test14 (@a date output) AS BEGIN SET @a='1999-1-3'; Select @a as a; END; +#Declare @a DATE;Set @a='9999-12-31'; exec sp_test14 @a;select @a as a; +storedproc#!#prep#!#sp_test14#!#DATE|-|a|-|9999-12-31|-|output +storedproc#!#prep#!#sp_test14#!#DATE|-|a|-|9999-12-31|-|inputoutput +DROP PROCEDURE sp_test14 +# time +#CREATE PROCEDURE sp_test15 (@a time(4) OUTPUT) AS BEGIN SET @a='11:25:07.123'; Select @a as a; END; +#Declare @a Time;Set @a='12:45:37.123'; exec sp_test15 @a;select @a as a; +#storedproc#!#prep#!#sp_test15#!#Time|-|a|-|12:45:37.123|-|3|-|output +#storedproc#!#prep#!#sp_test15#!#Time|-|a|-|12:45:37.123|-|3|-|inputoutput +#DROP PROCEDURE sp_test15 +# dateime BABEL-694 +#CREATE PROCEDURE sp_test16 (@a datetime output) AS BEGIN SET @a='2004-05-18 13:59:59.995'; Select @a as a; END; +#Declare @a DATETIME;Set @a='2000-02-28 23:59:59.995'; exec sp_test16 @a;select @a as a; +#storedproc#!#prep#!#sp_test16#!#DATETIME|-|a|-|2000-02-28 23:59:59.995|-|output +#storedproc#!#prep#!#sp_test16#!#DATETIME|-|a|-|2000-02-28 23:59:59.995|-|inputoutput +#DROP PROCEDURE sp_test16 +# datetime2 +#CREATE PROCEDURE sp_test17 (@a datetime2(5) OUTPUT) AS BEGIN SET @a='2014-10-2 1:45:37.123456'; Select @a as a; END; +#Declare @a Datetime2;Set @a='2016-10-23 12:45:37.123456'; exec sp_test17 @a;select @a as a; +#storedproc#!#prep#!#sp_test17#!#Datetime2|-|a|-|2016-10-23 12:45:37.123456|-|5|-|output +#storedproc#!#prep#!#sp_test17#!#Datetime2|-|a|-|2016-10-23 12:45:37.123456|-|5|-|inputoutput +#DROP PROCEDURE sp_test17 +# smalldatetime BABEL-694 +#CREATE PROCEDURE sp_test18 (@a smalldatetime output) AS BEGIN SET @a='2010-02-03 12:58:23'; Select @a as a; END; +#Declare @a SMALLDATETIME;Set @a='2000-12-13 12:58:23'; exec sp_test18 @a;select @a as a; +#storedproc#!#prep#!#sp_test18#!#SMALLDATETIME|-|a|-|2000-12-13 12:58:23|-|output +#storedproc#!#prep#!#sp_test18#!#SMALLDATETIME|-|a|-|2000-12-13 12:58:23|-|inputoutput +#DROP PROCEDURE sp_test18 +# UID +#CREATE PROCEDURE sp_test19 (@a uniqueidentifier OUTPUT) AS BEGIN SET @a='ce8af10a-2709-43b0-9e4e-a02753929d17'; Select @a as a; END; +#Declare @a uniqueidentifier;Set @a='5b7c2e8d-6d90-411d-8e19-9a81067e6f6c'; exec sp_test19 @a;select @a as a; +#storedproc#!#prep#!#sp_test19#!#uniqueidentifier|-|a|-|5b7c2e8d-6d90-411d-8e19-9a81067e6f6c|-|output +#storedproc#!#prep#!#sp_test19#!#uniqueidentifier|-|a|-|5b7c2e8d-6d90-411d-8e19-9a81067e6f6c|-|inputoutput +#DROP PROCEDURE sp_test19 diff --git a/contrib/test/JDBC/input/tds_faultinjection.txt b/contrib/test/JDBC/input/tds_faultinjection.txt new file mode 100644 index 00000000000..882989216be --- /dev/null +++ b/contrib/test/JDBC/input/tds_faultinjection.txt @@ -0,0 +1,33 @@ +---- Inject an error in tds comm layer while reading the request +---- Test both SQL Batch and prepare exec +--SELECT inject_fault('tds_comm_throw_error', 2); +--SELECT 1; +--prepst#!# SELECT @a #!#BIGINT|-|a|-|0 + +---- test normal execution +--SELECT 1; + +-- Inject a pre-parsing error +-- Test both SQL Batch and prepare exec +SELECT inject_fault('pre_parsing_throw_error', 2); +SELECT 1; +prepst#!# SELECT @a #!#BIGINT|-|a|-|0 + +-- test normal execution +SELECT 1; + +-- Inject a post-parsing error +-- Test both SQL Batch and prepare exec +SELECT inject_fault('post_parsing_throw_error', 2); +SELECT 1; +prepst#!# SELECT @a #!#BIGINT|-|a|-|0 + +-- test normal execution +SELECT 1; + +-- Inject fault to tamper TDS request +-- Test both SQL Batch and prepare exec (but prepare exec is failing now) +SELECT inject_fault('pre_parsing_tamper_request', 1); +SELECT 1; +--prepst#!# SELECT @a #!#BIGINT|-|a|-|0 + diff --git a/contrib/test/JDBC/input/transactions/TestIsolationLevels.sql b/contrib/test/JDBC/input/transactions/TestIsolationLevels.sql new file mode 100644 index 00000000000..6c3ab561dda --- /dev/null +++ b/contrib/test/JDBC/input/transactions/TestIsolationLevels.sql @@ -0,0 +1,36 @@ +set transaction isolation level read uncommitted; +go + +set transaction isolation level read committed; +go + +set transaction isolation level repeatable read; +go + +set transaction isolation level snapshot; +go + +set transaction isolation level serializable; +go + +select set_config('default_transaction_isolation', 'read uncommitted', false); +go +select set_config('default_transaction_isolation', 'read committed', false); +go +select set_config('default_transaction_isolation', 'repeatable read', false); +go +select set_config('default_transaction_isolation', 'snapshot', false); +go +select set_config('default_transaction_isolation', 'serializable', false); +go + +select set_config('transaction_isolation', 'read uncommitted', false); +go +select set_config('transaction_isolation', 'read committed', false); +go +select set_config('transaction_isolation', 'repeatable read', false); +go +select set_config('transaction_isolation', 'snapshot', false); +go +select set_config('transaction_isolation', 'serializable', false); +go diff --git a/contrib/test/JDBC/input/transactions/TestTransactionName.sql b/contrib/test/JDBC/input/transactions/TestTransactionName.sql new file mode 100644 index 00000000000..22de0f0932e --- /dev/null +++ b/contrib/test/JDBC/input/transactions/TestTransactionName.sql @@ -0,0 +1,70 @@ +-- Tests for transaction name in babel + +CREATE TABLE TestTxnName(C1 INT); +GO + +-- Transaction name longer than 32 chars no allowed +BEGIN TRANSACTION longname11111111111111111111111111111111111111; +GO + +-- Transaction name longer than 32 truncated +DECLARE @txnName varchar(100) = 'a1111111111111111111111111111111truncatethis'; +BEGIN TRAN @txnName; +ROLLBACK TRAN a1111111111111111111111111111111 +GO + +SELECT @@trancount; +GO + +-- Transaction/savepoint names are case sensitive +DECLARE @txnName varchar(100) = 'Abc'; +DECLARE @spName varchar(100) = 'aBc'; + +BEGIN TRAN abc; +INSERT INTO TestTxnName VALUES(1); +SAVE TRAN @spName; +INSERT INTO TestTxnName VALUES(2); +SAVE TRAN abC; +INSERT INTO TestTxnName VALUES(3); +SAVE TRAN ABc; +INSERT INTO TestTxnName VALUES(4); +SAVE TRAN AbC; +INSERT INTO TestTxnName VALUES(5); +SAVE TRAN [aBC]; +INSERT INTO TestTxnName VALUES(6); +BEGIN TRAN @txnName; +GO + +SELECT C1 FROM TestTxnName ORDER BY C1; +GO + +ROLLBACK TRAN [AbC]; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO + +ROLLBACK TRAN ABc; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO + +ROLLBACK TRAN aBc; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO + +DECLARE @txnName varchar(100) = 'abc'; +ROLLBACK TRAN @txnName; +SELECT C1 FROM TestTxnName ORDER BY C1; +GO + +SELECT @@trancount; +GO + +DECLARE @txnName varchar(100) = 'abc'; +BEGIN TRAN @txnName; +COMMIT TRAN @txnName; +GO + +SELECT @@trancount; +GO + +DROP TABLE TestTxnName; +GO diff --git a/contrib/test/JDBC/input/transactions/TestTransactionSupportForProcedure.txt b/contrib/test/JDBC/input/transactions/TestTransactionSupportForProcedure.txt new file mode 100644 index 00000000000..a9a997b1c75 --- /dev/null +++ b/contrib/test/JDBC/input/transactions/TestTransactionSupportForProcedure.txt @@ -0,0 +1,260 @@ +#setup +create table txnproctable (c1 int not null, c2 varchar(100)) + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +create procedure txnproc1 as begin tran; insert into txnproctable values (1, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; commit tran; +select @@trancount; +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount; +select * from txnproctable order by c1; + + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK +drop procedure txnproc1; +create procedure txnproc1 as begin tran; insert into txnproctable values(2, 'xyz'); save tran sp1; delete from txnproctable; rollback tran sp1; rollback tran; +select @@trancount; +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount; +select * from txnproctable order by c1; + + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +drop procedure txnproc1 +create procedure txnproc1 as begin tran; insert into txnproctable values(3, 'dbd'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; +commit tran; +select @@trancount +select * from txnproctable order by c1; + + +# PROC +# BEGIN TRAN +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +drop procedure txnproc1 +create procedure txnproc1 as begin tran; insert into txnproctable values(4, 'sbd'); save tran sp1; update txnproctable set c1 = c1 + 1; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; +rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +commit tran; +select @@trancount +select * from txnproctable order by c1; + + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +begin tran; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(5, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; +commit tran; +select @@trancount +select * from txnproctable order by c1; + + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(6, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; +commit tran; +select @@trancount +select * from txnproctable order by c1; + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +begin tran; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(7, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; update txnproctable set c1 = c1 + 1; commit tran; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; + +# BEGIN TRAN +# PROC +# SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(8, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; update txnproctable set c1 = c1 + 1; rollback tran; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; + +# BEGIN TRAN +# START SAVEPOINT +# PROC +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +select * from txnproctable order by c1; +save tran sp1; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(9, 'abc'); update txnproctable set c1 = c1 + 1; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +rollback tran; +select @@trancount +select * from txnproctable order by c1; + +# BEGIN TRAN +# START SAVEPOINT +# PROC +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +select * from txnproctable order by c1; +save tran sp1; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(10, 'abc'); update txnproctable set c1 = c1 + 1; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +commit tran; +select @@trancount +select * from txnproctable order by c1; + +# BEGIN TRAN +# START SAVEPOINT +# PROC +# ROLLBACK SAVEPOINT +# ROLLBACK +begin tran; +select @@trancount +select * from txnproctable order by c1; +save tran sp1; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as insert into txnproctable values(11, 'abc'); rollback tran sp1; update txnproctable set c1 = c1 + 1; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +commit tran; +select @@trancount +select * from txnproctable order by c1; + +# PROC1 +# BEGIN TRAN +# PROC2 +# PROC3 +# BEGIN TRAN +# START SAVEPOINT +# ROLLBACK SAVEPOINT +# COMMIT +# COMMIT +create procedure txnProc3 as begin tran; insert into txnproctable values (16, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +create procedure txnProc2 as update txnproctable set c1 = c1 + 1; exec txnProc3; commit tran; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as delete from txnproctable; begin tran; exec txnProc2; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; +commit tran; +select @@trancount +select * from txnproctable order by c1; + +# PROC1 +# BEGIN TRAN +# PROC2 +# PROC3 +# BEGIN TRAN +# START SAVEPOINT +# ROLLBACK SAVEPOINT +# ROLLBACK TRAN +# COMMIT +drop procedure txnproc3 +create procedure txnProc3 as begin tran; insert into txnproctable values (20, 'abc'); save tran sp1; update txnproctable set c1 = c1 + 1; rollback tran sp1; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc2 +create procedure txnProc2 as update txnproctable set c1 = c1 + 1; exec txnProc3; rollback tran; +select @@trancount +select * from txnproctable order by c1; +drop procedure txnproc1 +create procedure txnproc1 as delete from txnproctable; begin tran; exec txnProc2; +select @@trancount +select * from txnproctable order by c1; +exec txnproc1; +select @@trancount +select * from txnproctable order by c1; + +#cleanup +drop procedure txnproc3 +drop procedure txnproc2 +drop procedure txnproc1 +drop table txnproctable \ No newline at end of file diff --git a/contrib/test/JDBC/input/transactions/TestTransactionsSQLBatch.txt b/contrib/test/JDBC/input/transactions/TestTransactionsSQLBatch.txt new file mode 100644 index 00000000000..c767fbbaead --- /dev/null +++ b/contrib/test/JDBC/input/transactions/TestTransactionsSQLBatch.txt @@ -0,0 +1,189 @@ +create table TxnTable(c1 int); + +# Begin transaction -> commit transaction +begin transaction; +select @@trancount; +begin transaction; +select @@trancount; +set transaction isolation level read committed; +#show transaction_isolation; +#show default_transaction_isolation; +insert into TxnTable values(1); +commit transaction; +select @@trancount; +commit transaction; +select @@trancount; +select c1 from TxnTable; + +# Begin transaction -> rollback transaction +begin transaction; +insert into TxnTable values(2); +rollback transaction; +select c1 from TxnTable; + +#Begin tran -> commit tran +begin tran; +insert into TxnTable values(2); +commit tran; +select c1 from TxnTable; + +# Begin tran -> rollback tran +begin tran; +select @@trancount; +begin tran; +set transaction isolation level read uncommitted; +#show transaction_isolation; +#show default_transaction_isolation; +insert into TxnTable values(3); +select @@trancount; +rollback tran; +select @@trancount; +select c1 from TxnTable; + +set transaction isolation level repeatable read; +#show transaction_isolation; +#show default_transaction_isolation; + +# Begin transaction -> commit +begin transaction; +insert into TxnTable values(4); +commit; +select c1 from TxnTable; + +# Begin transaction -> commit work +begin transaction; +insert into TxnTable values(5); +commit work; +select c1 from TxnTable; + +# Begin transaction -> rollback +begin transaction; +insert into TxnTable values(6); +rollback; +select c1 from TxnTable; + +# Begin transaction -> rollback work +begin transaction; +insert into TxnTable values(7); +rollback work; +select c1 from TxnTable; + +# Begin transaction name -> commit transaction name +begin transaction txn1; +insert into TxnTable values(8); +commit transaction txn1; +select c1 from TxnTable; + +# Begin transaction name -> rollback transaction name +begin transaction txn1; +insert into TxnTable values(9); +rollback transaction txn1; +select c1 from TxnTable; + +# Begin tran name -> commit tran name +begin tran txn1; +insert into TxnTable values(10); +commit tran txn1; +select c1 from TxnTable; + +# Begin tran name -> rollback tran name +begin tran txn1; +insert into TxnTable values(10); +rollback tran txn1; +select c1 from TxnTable; + +truncate table TxnTable; + +# save tran name -> rollback tran name +set transaction isolation level snapshot; +#show transaction_isolation; +#show default_transaction_isolation; +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +select @@trancount; +insert into TxnTable values(2); +save tran sp2; +insert into TxnTable values(3); +save tran sp2; +select @@trancount; +insert into TxnTable values(4); +select c1 from TxnTable; +rollback tran sp2; +select @@trancount; +select c1 from TxnTable; +rollback tran sp2; +select @@trancount; +select c1 from TxnTable; +rollback tran sp1; +select @@trancount; +select c1 from TxnTable; +rollback tran txn1; +select @@trancount; +select c1 from TxnTable; + +# begin transaction name -> save transaction name -> rollback to first savepoint +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +insert into TxnTable values(2); +save transaction sp2; +insert into TxnTable values(3); +save transaction sp3; +insert into TxnTable values(4); +rollback tran sp1; +#rollback tran sp1; -- this will give an error +rollback tran; +select c1 from TxnTable; + +# begin transaction name -> save transaction name -> rollback tran name, Rollback whole transaction +set transaction isolation level serializable; +#show transaction_isolation; +#show default_transaction_isolation; +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +begin transaction txn1; +insert into TxnTable values(2); +save transaction sp1; +insert into TxnTable values(3); +select @@trancount; +rollback tran txn1; +select @@trancount; +select c1 from TxnTable; + +# begin transaction -> save transaction name -> rollback to savepoint, commit transaction +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +insert into TxnTable values(2); +select c1 from TxnTable; +rollback tran sp1; +commit transaction; +select c1 from TxnTable; + +# begin transaction -> save transaction name -> rollback to savepoint +# save transaction name -> commit transaction +begin transaction txn1; +insert into TxnTable values(3); +save transaction sp1; +insert into TxnTable values(4); +select c1 from TxnTable; +rollback tran sp1; +save transaction sp2; +insert into TxnTable values(5); +commit transaction; +select c1 from TxnTable; + +# begin transaction -> save transaction name -> error -> rollback to savepoint +# commit transaction +begin transaction txn1; +insert into TxnTable values(6); +save transaction sp1; +insert into TxnTable values(7); +#select c1 frm TxnTable; -- error +rollback tran sp1; +commit transaction; +select c1 from TxnTable; + +Drop table TxnTable; \ No newline at end of file diff --git a/contrib/test/JDBC/input/transactions/TestTriggers.sql b/contrib/test/JDBC/input/transactions/TestTriggers.sql new file mode 100644 index 00000000000..04a9ad44a72 --- /dev/null +++ b/contrib/test/JDBC/input/transactions/TestTriggers.sql @@ -0,0 +1,197 @@ +create table triggerTab1(c1 int, c2 varchar(30)) +go + +create table triggerTab2(c1 int) +go + +create table triggerTab3(c1 int) +go + +insert into triggerTab1 values(1, 'first') +go + +insert into triggerTab2 values(1) +go + +insert into triggerTab3 values(1) +go + +create trigger txnTrig1 on triggerTab1 for insert as +begin tran; +update triggerTab2 set c1 = 2; +commit; +select * from triggerTab2 order by c1; +select * from inserted; +go + +create trigger txnTrig2 on triggerTab2 for update as +save tran sp1; +save tran sp2; +delete from triggerTab3; +rollback tran sp1; +go + +create trigger txnTrig3 on triggerTab3 for delete as +select * from triggerTab3 order by c1; +insert into triggerTab3 values(1); +select * from deleted; +rollback tran sp2; +go + +insert into triggerTab1 values(2, 'second'); +go + +begin tran; +go +insert into triggerTab1 values(3, 'third'); +go +commit; +go + +drop trigger txnTrig1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +commit; +update triggerTab2 set c1 = 2; +select * from triggerTab2 order by c1; +go + +insert into triggerTab1 values(2, 'second'); +go + +begin tran; +go + +insert into triggerTab1 values(2, 'second'); +go + +drop trigger txnTrig1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +rollback; +update triggerTab2 set c1 = 3; +select * from triggerTab2 order by c1; +go + +insert into triggerTab1 values(2, 'second'); +go + +begin tran; +go + +insert into triggerTab1 values(2, 'second'); +go + +create procedure triggerProc1 as +save tran sp1; +insert into triggerTab1 values(3, 'third'); +rollback tran sp1; +go + +create procedure triggerProc2 as +begin tran; +exec triggerProc1; +insert into triggerTab1 values(3, 'third'); +commit; +go + +exec triggerProc2; +go + +create table triggerErrorTab(c1 int not null); +go + +create trigger triggerErr on triggerErrorTab for insert as +insert into triggerErrorTab values(NULL); +insert into invalidTab values(1); +go + +insert into triggerErrorTab values(1); +go + + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop trigger txnTrig1 +go + + +create procedure triggerProc1 as +commit; +go + +create procedure triggerProc2 as +exec triggerProc1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +exec triggerProc2; +go + +insert into triggerTab1 values(1, 'value1'); +go + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop trigger txnTrig1 +go + + +create procedure triggerProc1 as +rollback; +go + +create procedure triggerProc2 as +exec triggerProc1; +go + +create trigger txnTrig1 on triggerTab1 after insert as +exec triggerProc2; +go + +insert into triggerTab1 values(1, 'value2'); +go + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop trigger txnTrig1 +go + + +create procedure triggerProc1 as +rollback tran sp1; +go + +create procedure triggerProc2 as +exec triggerProc1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +save tran sp1; +exec triggerProc2; +go + +insert into triggerTab1 values(1, 'value3'); +go + + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop table triggerTab1; +go +drop table triggerTab2; +go +drop table triggerTab3; +go +drop table triggerErrorTab +go diff --git a/contrib/test/JDBC/jdbc_schedule b/contrib/test/JDBC/jdbc_schedule new file mode 100644 index 00000000000..eabc1bbad9f --- /dev/null +++ b/contrib/test/JDBC/jdbc_schedule @@ -0,0 +1,12 @@ +# Schedule File for JDBC Test Framework for local run +# 1. Lines starting with '#' will be treated as comments +# 2. To run a postgres command: cmd#!#postgresql#!# +# 3. To run a T-SQL command: cmd#!#sqlserver#!# +# 4. Keyword "all" is equivalent to running all test files in +# input folder +# 5. To add a test, add test name (without extension Eg. BABEL-TEST) on a +# new line +# 6. If you want the framework to not run certain files, use: ignore#!# + +all + diff --git a/contrib/test/JDBC/pom.xml b/contrib/test/JDBC/pom.xml new file mode 100644 index 00000000000..cbe83b011e9 --- /dev/null +++ b/contrib/test/JDBC/pom.xml @@ -0,0 +1,87 @@ + + 4.0.0 + com.sqlsamples + JDBC-testsuite + jar + 1.0.0 + JDBC-testsuite + http://maven.apache.org + + + org.junit.jupiter + junit-jupiter + 5.7.0 + + + org.junit.jupiter + junit-jupiter-params + 5.5.2 + test + + + commons-io + commons-io + 2.6 + + + org.apache.commons + commons-math3 + 3.6.1 + + + org.apache.commons + commons-csv + 1.4 + + + org.apache.logging.log4j + log4j-api + 2.16.0 + + + org.apache.logging.log4j + log4j-core + 2.16.0 + + + + com.microsoft.sqlserver + mssql-jdbc + 8.2.2.jre8 + + + org.postgresql + postgresql + 42.2.18 + + + + + + + maven-surefire-plugin + 3.0.0-M5 + + + false + 3.0 + true + true + true + true + + + + + maven-failsafe-plugin + 2.22.2 + + + + + + 1.8 + 1.8 + + \ No newline at end of file diff --git a/contrib/test/JDBC/sql_expected/1034_1.out b/contrib/test/JDBC/sql_expected/1034_1.out new file mode 100644 index 00000000000..87bb7a12708 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1034_1.out @@ -0,0 +1,71 @@ + +-- simple batch start +CREATE TABLE t1034 +( + c1 int +,c2 int +) +GO + +GO + +CREATE TRIGGER tr1034 +ON t1034 +FOR UPDATE, UPDATE +AS +SELECT 1 +GO +~~ERROR (Code: 1034)~~ + +~~ERROR (Message: Syntax error: Duplicate specification of the action "UPDATE" in the trigger declaration.)~~ + +DROP TABLE t1034 +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO +CREATE TABLE t1034 +( + c1 int +,c2 int +) +GO + +GO + +CREATE TRIGGER tr1034 +ON t1034 +FOR UPDATE, UPDATE +AS +SELECT 1 +GO +~~ERROR (Code: 1034)~~ + +~~ERROR (Message: Syntax error: Duplicate specification of the action "UPDATE" in the trigger declaration.)~~ + + + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO +~~START~~ +text +Does not respect xact_abort flag +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1034 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1034', because it does not exist or you do not have permission.)~~ + + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/sql_expected/1049_1.out b/contrib/test/JDBC/sql_expected/1049_1.out new file mode 100644 index 00000000000..86d81f28928 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1049_1.out @@ -0,0 +1,406 @@ + +-- simple batch start +CREATE TABLE t1049 (c1 INT) +GO + + +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + +DROP TABLE t1049 +GO + + +begin transaction +GO +CREATE TABLE t1049 (c1 INT) +GO + + +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1049 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1049', because it does not exist or you do not have permission.)~~ + + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t1049 (c1 INT) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +end +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + + +DROP TABLE t1049 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t1049 (c1 INT) +GO + + +insert into error_mapping.temp2 values(1) +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + + +DROP TABLE t1049 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t1049 (c1 INT) +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +DROP TABLE t1049 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t1049 (c1 INT) +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +DROP TABLE t1049 +GO + + + +CREATE TABLE t1049 (c1 INT) +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1049 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1049 (c1 INT) +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1049 +GO + +set xact_abort OFF; +GO + + + +-- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t1049 (c1 INT) +--GO +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +--FOR SELECT c1 FROM t1049 +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t1049 +--GO +-- +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t1049 (c1 INT) +--GO +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +--FOR SELECT c1 FROM t1049 +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t1049 +--GO +-- +-- +-- +-- +--set xact_abort OFF; +--GO +-- Error classification is done -- +CREATE TABLE t1049 (c1 INT) +GO + + + + +begin try +select 1 +DECLARE cur1049 SCROLL CURSOR FAST_FORWARD READ_ONLY +FOR SELECT c1 FROM t1049 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 1049)~~ + +~~ERROR (Message: Mixing old and new syntax to specify cursor options is not allowed.)~~ + +DROP TABLE t1049 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/1051_1.out b/contrib/test/JDBC/sql_expected/1051_1.out new file mode 100644 index 00000000000..7237bb46e2d --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1051_1.out @@ -0,0 +1,75 @@ + +-- simple batch start +CREATE TABLE t1051 +( + c1 int +,c2 int NULL +) +GO + + + +CREATE PROC p1051_1 + @a CURSOR OUTPUT VARYING + AS +DECLARE c CURSOR + READ_ONLY +FOR +SELECT * FROM t1051 +GO +~~ERROR (Code: 1051)~~ + +~~ERROR (Message: Cursor parameters in a stored procedure must be declared with OUTPUT and VARYING options, and they must be specified in the order CURSOR VARYING OUTPUT.)~~ + +DROP TABLE t1051 +GO + + +SET XACT_ABORT ON; +GO + +begin transaction +GO +CREATE TABLE t1051 +( + c1 int +,c2 int NULL +) +GO + + + +CREATE PROC p1051_1 + @a CURSOR OUTPUT VARYING + AS +DECLARE c CURSOR + READ_ONLY +FOR +SELECT * FROM t1051 +GO +~~ERROR (Code: 1051)~~ + +~~ERROR (Message: Cursor parameters in a stored procedure must be declared with OUTPUT and VARYING options, and they must be specified in the order CURSOR VARYING OUTPUT.)~~ + + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO +~~START~~ +text +Does not respect xact_abort flag +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1051 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1051', because it does not exist or you do not have permission.)~~ + + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/sql_expected/10610_1.out b/contrib/test/JDBC/sql_expected/10610_1.out new file mode 100644 index 00000000000..476ba898456 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/10610_1.out @@ -0,0 +1,311 @@ +# Executing test ErrorHandling1 +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 10610)~~ + +~~ERROR (Message: Filtered index 'i10610' cannot be created on object 'v10610' because it is not a user table. Filtered indexes are only supported on tables. If you are trying to create a filtered index on a view, consider creating an indexed view with the filter expression incorporated in the view definition.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + + +create procedure ErrorHandling1 as +begin +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 10610)~~ + +~~ERROR (Message: Filtered index 'i10610' cannot be created on object 'v10610' because it is not a user table. Filtered indexes are only supported on tables. If you are trying to create a filtered index on a view, consider creating an indexed view with the filter expression incorporated in the view definition.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t10610( + c1 int, + c2 int +) +GO + +CREATE VIEW v10610 AS +SELECT c1, c2 +FROM t10610 +GO + + +begin try +select 1 +CREATE INDEX i10610 +ON v10610 (c1) +WHERE c2 = 1 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP VIEW v10610 +GO + +DROP TABLE t10610 +GO + + + diff --git a/contrib/test/JDBC/sql_expected/11700_1.out b/contrib/test/JDBC/sql_expected/11700_1.out new file mode 100644 index 00000000000..c5c8a6262c5 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11700_1.out @@ -0,0 +1,232 @@ +# Executing test ErrorHandling1 +GO + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +GO + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11700)~~ + +~~ERROR (Message: INCREMENT must not be zero)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11700)~~ + +~~ERROR (Message: INCREMENT must not be zero)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +GO + + + +begin try +select 1 +CREATE SEQUENCE s11700 + START WITH 1 + INCREMENT BY 0 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling2")~~ + + + diff --git a/contrib/test/JDBC/sql_expected/11701_1.out b/contrib/test/JDBC/sql_expected/11701_1.out new file mode 100644 index 00000000000..596b42822e1 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11701_1.out @@ -0,0 +1,245 @@ +create schema error_mapping; +GO +-- Executing test error_mapping.ErrorHandling1 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11701)~~ + +~~ERROR (Message: The absolute value of the increment for sequence object 's11701' must be less than or equal to the difference between the minimum and maximum value of the sequence object.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11701)~~ + +~~ERROR (Message: The absolute value of the increment for sequence object 's11701' must be less than or equal to the difference between the minimum and maximum value of the sequence object.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +begin try +select 1 +CREATE SEQUENCE s11701 INCREMENT BY 6 + MINVALUE 5 + MAXVALUE 10 + +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/11702_1.out b/contrib/test/JDBC/sql_expected/11702_1.out new file mode 100644 index 00000000000..8cafd718be0 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11702_1.out @@ -0,0 +1,331 @@ + +-- simple batch start +GO +CREATE SEQUENCE seq11702 AS bit +GO +~~ERROR (Code: 11702)~~ + +~~ERROR (Message: The sequence object 'seq11702' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, or any user-defined data type that is based on one of the above integer data types.)~~ + +GO + +begin transaction +GO +GO +CREATE SEQUENCE seq11702 AS bit +GO +~~ERROR (Code: 11702)~~ + +~~ERROR (Message: The sequence object 'seq11702' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, or any user-defined data type that is based on one of the above integer data types.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE SEQUENCE seq11702 AS bit +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO +insert into error_mapping.temp2 values(1) +CREATE SEQUENCE seq11702 AS bit +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 11702)~~ + +~~ERROR (Message: The sequence object 'seq11702' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, or any user-defined data type that is based on one of the above integer data types.)~~ + + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 11702)~~ + +~~ERROR (Message: The sequence object 'seq11702' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, or any user-defined data type that is based on one of the above integer data types.)~~ + + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11702)~~ + +~~ERROR (Message: The sequence object 'seq11702' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, or any user-defined data type that is based on one of the above integer data types.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE seq11702 AS bit +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11702)~~ + +~~ERROR (Message: The sequence object 'seq11702' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, or any user-defined data type that is based on one of the above integer data types.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +begin try +select 1 +CREATE SEQUENCE seq11702 AS bit +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/11703_1.out b/contrib/test/JDBC/sql_expected/11703_1.out new file mode 100644 index 00000000000..2c218a1d101 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11703_1.out @@ -0,0 +1,371 @@ + +-- simple batch start +GO + +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + +GO + +begin transaction +GO +GO + +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +begin try +select 1 +CREATE SEQUENCE s11703 + START WITH 11 + MINVALUE 5 + MAXVALUE 10 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/11703_2.out b/contrib/test/JDBC/sql_expected/11703_2.out new file mode 100644 index 00000000000..6d33548304c --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11703_2.out @@ -0,0 +1,371 @@ + +-- simple batch start +GO + +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + +GO + +begin transaction +GO +GO + +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +end +GO + +GO + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 11703)~~ + +~~ERROR (Message: The start value for sequence object 's11703' must be between the minimum and maximum value of the sequence object.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +begin try +select 1 +CREATE SEQUENCE s11703 + START WITH 1 + MINVALUE 5 + MAXVALUE 10 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/11705_1.out b/contrib/test/JDBC/sql_expected/11705_1.out new file mode 100644 index 00000000000..52d279bd657 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11705_1.out @@ -0,0 +1,239 @@ +# Executing test ErrorHandling1 +GO + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +GO + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11705)~~ + +~~ERROR (Message: The minimum value for sequence object 's11705' must be less than its maximum value.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11705)~~ + +~~ERROR (Message: The minimum value for sequence object 's11705' must be less than its maximum value.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +GO + + + +begin try +select 1 +CREATE SEQUENCE s11705 + increment by 1 + MINVALUE 10 + MAXVALUE 5 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + diff --git a/contrib/test/JDBC/sql_expected/11706_1.out b/contrib/test/JDBC/sql_expected/11706_1.out new file mode 100644 index 00000000000..4cb9ab10038 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11706_1.out @@ -0,0 +1,257 @@ +# Executing test ErrorHandling1 +GO + + + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +GO + + + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11706)~~ + +~~ERROR (Message: The cache size for sequence object 's11706' must be greater than 0.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + + + + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11706)~~ + +~~ERROR (Message: The cache size for sequence object 's11706' must be greater than 0.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +GO + + + + + +begin try +select 1 +CREATE SEQUENCE s11706 + AS INT + START WITH 5 + INCREMENT BY 1 + CACHE 0 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + diff --git a/contrib/test/JDBC/sql_expected/11708_1.out b/contrib/test/JDBC/sql_expected/11708_1.out new file mode 100644 index 00000000000..91aaa7d317e --- /dev/null +++ b/contrib/test/JDBC/sql_expected/11708_1.out @@ -0,0 +1,245 @@ +# Executing test ErrorHandling1 +GO + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +GO + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11708)~~ + +~~ERROR (Message: An invalid value was specified for argument 'MAXVALUE' for the given data type.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +GO + + +create procedure ErrorHandling1 as +begin +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 11708)~~ + +~~ERROR (Message: An invalid value was specified for argument 'MAXVALUE' for the given data type.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +GO + + +begin try +select 1 +CREATE SEQUENCE seq_11708 +AS [tinyint] +START WITH 1 +INCREMENT BY 1 +MINVALUE 1 +MAXVALUE 256 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + diff --git a/contrib/test/JDBC/sql_expected/132_1.out b/contrib/test/JDBC/sql_expected/132_1.out new file mode 100644 index 00000000000..215f7176d92 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/132_1.out @@ -0,0 +1,455 @@ + + + +-- simple batch start +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + +--------------------------------------------------------------------------- +--Post +GO + +begin transaction +GO + + +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +--------------------------------------------------------------------------- +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + + +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +end +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + + +--------------------------------------------------------------------------- +--Post +GO + +create table error_mapping.temp2 (a int) +GO + + + +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp2 values(1) +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + + +--------------------------------------------------------------------------- +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + + +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +--------------------------------------------------------------------------- +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +--------------------------------------------------------------------------- +--Post +GO + + + + +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--------------------------------------------------------------------------- +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + + +--132 Label already declared +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--------------------------------------------------------------------------- +--Post +GO +set xact_abort OFF; +GO + + + +-- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +----132 Label already declared +-- +----Could not find this PG error in excel file +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--lbl: +--lbl: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----132 Label already declared +-- +----Could not find this PG error in excel file +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--lbl: +--lbl: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +--132 Label already declared +--------------------------------------------------------------------------- +--Pre +GO + + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error +lbl: +lbl: +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +GO +~~ERROR (Code: 132)~~ + +~~ERROR (Message: The label 'lbl' has already been declared. Label names must be unique within a query batch or stored procedure.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/133_1.out b/contrib/test/JDBC/sql_expected/133_1.out new file mode 100644 index 00000000000..c3303084ac7 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/133_1.out @@ -0,0 +1,457 @@ + + + +-- simple batch start +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + +--------------------------------------------------------------------------- +--Post +GO + +begin transaction +GO + + +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +--------------------------------------------------------------------------- +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + + +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +end +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + + +--------------------------------------------------------------------------- +--Post +GO + +create table error_mapping.temp2 (a int) +GO + + + +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp2 values(1) +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + + +--------------------------------------------------------------------------- +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + + +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +--------------------------------------------------------------------------- +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +--------------------------------------------------------------------------- +--Post +GO + + + + +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--------------------------------------------------------------------------- +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + + +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--------------------------------------------------------------------------- +--Post +GO +set xact_abort OFF; +GO + + + + + + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--GOTO lbl2 +--blb3: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----133 Reference non-existing label in GOTO +-- +----Could not find this PG error in excel file +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +--GOTO lbl2 +--blb3: +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +--133 Reference non-existing label in GOTO +--Could not find this PG error in excel file +--------------------------------------------------------------------------- +--Pre +GO + + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error +GOTO lbl2 +blb3: +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----------------------------------------------------------------------------- +----Post +--GO +-- +-- +-- +GO +~~ERROR (Code: 133)~~ + +~~ERROR (Message: A GOTO statement references the label 'lbl2' but the label has not been declared.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/134_1.out b/contrib/test/JDBC/sql_expected/134_1.out new file mode 100644 index 00000000000..ec5d3c03708 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/134_1.out @@ -0,0 +1,444 @@ + + +-- simple batch start +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO + +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + +--Post +GO + +begin transaction +GO + +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO + +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +end +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + + +--Post +GO + +create table error_mapping.temp2 (a int) +GO + + +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO + +insert into error_mapping.temp2 values(1) +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + + +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +--Post +GO + + + +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + + + + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +-- +----CHECK constraint conflict, when adding constraint +--DECLARE @a int; +--DECLARE @a int; +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----134 Declare same variable name twice +-- +----------------------------------------------------------------------------- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----------------------------------------------------------------------------- +----Generate the error +-- +----CHECK constraint conflict, when adding constraint +--DECLARE @a int; +--DECLARE @a int; +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +--134 Declare same variable name twice +--------------------------------------------------------------------------- +--Pre +GO + + + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error +--CHECK constraint conflict, when adding constraint +DECLARE @a int; +DECLARE @a int; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +GO +~~ERROR (Code: 134)~~ + +~~ERROR (Message: The variable name '@a' has already been declared. Variable names must be unique within a query batch or stored procedure.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/135_1.out b/contrib/test/JDBC/sql_expected/135_1.out new file mode 100644 index 00000000000..c3bb9a936a5 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/135_1.out @@ -0,0 +1,415 @@ + + + +-- simple batch start +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +--Generate the error +BREAK +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + +--Post +GO + +begin transaction +GO + + +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +--Generate the error +BREAK +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + + +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Generate the error +BREAK +end +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + + +--Post +GO + +create table error_mapping.temp2 (a int) +GO + + + +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +insert into error_mapping.temp2 values(1) +--Generate the error +BREAK +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + + +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + + +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +BREAK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +--Post +GO + + + + +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +BREAK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + + +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +BREAK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + + + + + + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--BREAK +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----135 BREAK when not in WHILE loop +-- +----Could not find this error in excel file +-- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--BREAK +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +--135 BREAK when not in WHILE loop +--Could not find this error in excel file +--Pre +GO + + +begin try +select 1 +--Generate the error +BREAK +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +GO +~~ERROR (Code: 135)~~ + +~~ERROR (Message: Cannot use a BREAK statement outside the scope of a WHILE statement.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/136_1.out b/contrib/test/JDBC/sql_expected/136_1.out new file mode 100644 index 00000000000..145ac1a28b2 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/136_1.out @@ -0,0 +1,415 @@ + + + +-- simple batch start +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +--Generate the error +CONTINUE +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + +--Post +GO + +begin transaction +GO + + +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +--Generate the error +CONTINUE +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +--Post +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + + +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Generate the error +CONTINUE +end +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + + +--Post +GO + +create table error_mapping.temp2 (a int) +GO + + + +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +insert into error_mapping.temp2 values(1) +--Generate the error +CONTINUE +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + + +--Post +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + + +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +--Post +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +CONTINUE +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +--Post +GO + + + + +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +CONTINUE +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + + +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +CONTINUE +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + + + + + + + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--CONTINUE +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----136 CONTINUE when not in WHILE loop +-- +----Could not find this error in excel file +-- +----Pre +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--CONTINUE +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +--136 CONTINUE when not in WHILE loop +--Could not find this error in excel file +--Pre +GO + + +begin try +select 1 +--Generate the error +CONTINUE +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--GO +-- +-- +-- +GO +~~ERROR (Code: 136)~~ + +~~ERROR (Message: Cannot use a CONTINUE statement outside the scope of a WHILE statement.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/141_1.out b/contrib/test/JDBC/sql_expected/141_1.out new file mode 100644 index 00000000000..91767248ec7 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/141_1.out @@ -0,0 +1,426 @@ + + +-- simple batch start +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + +--Post +DROP TABLE t141; +GO + +begin transaction +GO + +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +--Post +DROP TABLE t141; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't141', because it does not exist or you do not have permission.)~~ + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + + +--Post +DROP TABLE t141; +GO + +create table error_mapping.temp2 (a int) +GO + + +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +insert into error_mapping.temp2 values(1) +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + + +--Post +DROP TABLE t141; +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +--Post +DROP TABLE t141; +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t141; +GO + + + +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t141; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t141; +GO +set xact_abort OFF; +GO + + + +-- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +----141 Inline variable assignment for only some columns +-- +----Pre +--CREATE TABLE t141(c1 int, c2 int); +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--DECLARE @a int; +--SELECT @a = c1, c2 FROM t141 +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--DROP TABLE t141; +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +----141 Inline variable assignment for only some columns +-- +----Pre +--CREATE TABLE t141(c1 int, c2 int); +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +----Generate the error +--DECLARE @a int; +--SELECT @a = c1, c2 FROM t141 +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +--DROP TABLE t141; +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +--141 Inline variable assignment for only some columns +--Pre +CREATE TABLE t141(c1 int, c2 int); +GO + + +begin try +select 1 +--Generate the error +DECLARE @a int; +SELECT @a = c1, c2 FROM t141 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +----Post +DROP TABLE t141; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.)~~ + + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/142_1.out b/contrib/test/JDBC/sql_expected/142_1.out new file mode 100644 index 00000000000..c331f1df083 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/142_1.out @@ -0,0 +1,365 @@ + +-- simple batch start +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + +DROP TABLE someTable +GO + +begin transaction +GO +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE someTable +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 'someTable', because it does not exist or you do not have permission.)~~ + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +end +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + + +DROP TABLE someTable +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +insert into error_mapping.temp2 values(1) +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + + +DROP TABLE someTable +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +DROP TABLE someTable +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +DROP TABLE someTable +GO + + +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE someTable +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE someTable +GO +set xact_abort OFF; +GO + + +---- comiple time error portion end -- +---- Next portion is for runtime error -- +-- +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE someTable(c1 int PRIMARY KEY) +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE someTable +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE someTable(c1 int PRIMARY KEY) +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE someTable +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +---- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE someTable(c1 int PRIMARY KEY) +GO + + +begin try +select 1 +CREATE TABLE t142(id_c int, REFERENCES someTable(c1)) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE someTable +--GO +-- +-- +-- +GO +~~ERROR (Code: 142)~~ + +~~ERROR (Message: Incorrect syntax for definition of the 'TABLE' constraint.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/1505_1.out b/contrib/test/JDBC/sql_expected/1505_1.out new file mode 100644 index 00000000000..3891445df4a --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1505_1.out @@ -0,0 +1,267 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +~~ROW COUNT: 2~~ + + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t1505; +GO + + +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +~~ROW COUNT: 2~~ + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +--Post +DROP TABLE t1505; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +~~ROW COUNT: 2~~ + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +--Post +DROP TABLE t1505; +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +~~ROW COUNT: 2~~ + + + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1505)~~ + +~~ERROR (Message: could not create unique index "xt15059dd4e461268c8034f5c8564e155c67a6")~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t1505; +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +~~ROW COUNT: 2~~ + + + +create procedure ErrorHandling1 as +begin +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1505)~~ + +~~ERROR (Message: could not create unique index "xt15059dd4e461268c8034f5c8564e155c67a6")~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t1505; +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t1505(c1 int); +INSERT INTO t1505 VALUES(1), (1); +GO +~~ROW COUNT: 2~~ + + + +begin try +select 1 +--Generate the error +CREATE UNIQUE INDEX x ON t1505(c1); +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling2")~~ + +--Post +DROP TABLE t1505; +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; +GO diff --git a/contrib/test/JDBC/sql_expected/16948_1.out b/contrib/test/JDBC/sql_expected/16948_1.out new file mode 100644 index 00000000000..fdbe53590d5 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/16948_1.out @@ -0,0 +1,601 @@ + +-- simple batch start +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + + + + + +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + +DROP TABLE t16948 +GO + + +begin transaction +GO +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + + + + + +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t16948 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't16948', because it does not exist or you do not have permission.)~~ + + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +end +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + + +DROP TABLE t16948 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + + + + + +insert into error_mapping.temp2 values(1) +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + + +DROP TABLE t16948 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +DROP TABLE t16948 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + + + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +DROP TABLE t16948 +GO + + + +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + +begin transaction +GO + + + + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t16948 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + +begin transaction +GO + + + + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t16948 +GO + +set xact_abort OFF; +GO + + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t16948(c1 int) +--GO +--INSERT INTO t16948 (c1) +--VALUES +--(10), +--(3), +--(8) +--GO +-- +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE @notCursor INT +-- +--DECLARE @c1 INT +--DECLARE tcursor CURSOR FOR +--SELECT c1 +--FROM t16948 +-- +--OPEN tcursor +--FETCH NEXT FROM @notCursor INTO @c1 +-- +--CLOSE tcursor +--DEALLOCATE tcursor +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t16948 +--GO +-- +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--CREATE TABLE t16948(c1 int) +--GO +--INSERT INTO t16948 (c1) +--VALUES +--(10), +--(3), +--(8) +--GO +-- +-- +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--DECLARE @notCursor INT +-- +--DECLARE @c1 INT +--DECLARE tcursor CURSOR FOR +--SELECT c1 +--FROM t16948 +-- +--OPEN tcursor +--FETCH NEXT FROM @notCursor INTO @c1 +-- +--CLOSE tcursor +--DEALLOCATE tcursor +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t16948 +--GO +-- +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- +CREATE TABLE t16948(c1 int) +GO +INSERT INTO t16948 (c1) +VALUES +(10), +(3), +(8) +GO +~~ROW COUNT: 3~~ + + + + + + + + + +begin try +select 1 +DECLARE @notCursor INT +DECLARE @c1 INT +DECLARE tcursor CURSOR FOR +SELECT c1 +FROM t16948 +OPEN tcursor +FETCH NEXT FROM @notCursor INTO @c1 +CLOSE tcursor +DEALLOCATE tcursor +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--DROP TABLE t16948 +--GO +-- +-- +-- +-- +GO +~~ERROR (Code: 16948)~~ + +~~ERROR (Message: The variable '@notCursor' is not a cursor variable, but it is used in a place where a cursor variable is expected.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/16950_1.out b/contrib/test/JDBC/sql_expected/16950_1.out new file mode 100644 index 00000000000..b66185abb50 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/16950_1.out @@ -0,0 +1,398 @@ + +-- simple batch start +GO + + + +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +GO +~~ERROR (Code: 16950)~~ + +~~ERROR (Message: The variable '@cursor1' does not currently have a cursor allocated to it.)~~ + +GO + + +begin transaction +GO +GO + + + +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +GO +~~ERROR (Code: 16950)~~ + +~~ERROR (Message: The variable '@cursor1' does not currently have a cursor allocated to it.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + + + +insert into error_mapping.temp2 values(1) +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 16950)~~ + +~~ERROR (Message: The variable '@cursor1' does not currently have a cursor allocated to it.)~~ + + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 16950)~~ + +~~ERROR (Message: The variable '@cursor1' does not currently have a cursor allocated to it.)~~ + + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO + + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO + + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 16950)~~ + +~~ERROR (Message: The variable '@cursor1' does not currently have a cursor allocated to it.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 16950)~~ + +~~ERROR (Message: The variable '@cursor1' does not currently have a cursor allocated to it.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + + +begin try +select 1 +DECLARE @c1 INT +DECLARE @cursor1 CURSOR +FETCH NEXT FROM @cursor1 INTO @c1 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/1752_1750_2.out b/contrib/test/JDBC/sql_expected/1752_1750_2.out new file mode 100644 index 00000000000..cbad2bf2883 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1752_1750_2.out @@ -0,0 +1,236 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1752 +GO + + +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1752 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1752 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: Column 'c3' in table 't1752' is invalid for creating a default constraint.)~~ + +~~START~~ +text +CURRENT BATCH TERMINATING ERROR +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1752 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: Column 'c3' in table 't1752' is invalid for creating a default constraint.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t1752 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t1752(c1 INT, c2 INT, c3 as c1*c2) +GO + + + + +begin try +select 1 +ALTER TABLE t1752 +ADD CONSTRAINT constraint1752 DEFAULT 1 FOR c3 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: Column 'c3' in table 't1752' is invalid for creating a default constraint.)~~ + +DROP TABLE t1752 +GO + + diff --git a/contrib/test/JDBC/sql_expected/1765_1750_1.out b/contrib/test/JDBC/sql_expected/1765_1750_1.out new file mode 100644 index 00000000000..f2b1db6d277 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1765_1750_1.out @@ -0,0 +1,371 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1765_2', because it does not exist or you do not have permission.)~~ + + + +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + +begin transaction +GO +# Executing test ErrorHandling1 + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1765_2', because it does not exist or you do not have permission.)~~ + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + +begin transaction +GO +# Executing test ErrorHandling1 + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1765_2', because it does not exist or you do not have permission.)~~ + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1765)~~ + +~~ERROR (Message: Foreign key 'FK__t1765_2__c2__2D00F5CE' creation failed. Only NO ACTION and CASCADE referential delete actions are allowed for referencing computed column 'c2'.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1765_2', because it does not exist or you do not have permission.)~~ + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + + + + +create procedure ErrorHandling1 as +begin +--SET QUOTED_IDENTIFIER ON +--GO +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1765)~~ + +~~ERROR (Message: Foreign key 'FK__t1765_2__c2__33ADF35D' creation failed. Only NO ACTION and CASCADE referential delete actions are allowed for referencing computed column 'c2'.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1765_2', because it does not exist or you do not have permission.)~~ + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t1765_1 +( + c1 int PRIMARY KEY +,c2 int +) +GO + + + + + + + + +begin try +select 1 +--SET QUOTED_IDENTIFIER ON +--GO +CREATE TABLE t1765_2 +( + c1 int PRIMARY KEY +,c2 AS c1 + 1 PERSISTED +, FOREIGN KEY (c2) + REFERENCES t1765_1(c1) ON DELETE SET NULL) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t1765_1 +GO + +DROP TABLE t1765_2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1765_2', because it does not exist or you do not have permission.)~~ + + + diff --git a/contrib/test/JDBC/sql_expected/1768_1750_1.out b/contrib/test/JDBC/sql_expected/1768_1750_1.out new file mode 100644 index 00000000000..81a4fcc8194 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1768_1750_1.out @@ -0,0 +1,335 @@ +# Executing test ErrorHandling1 +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1768)~~ + +~~ERROR (Message: Foreign key 'fk1768_2' references object 'v1768' which is not a user table.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + + +create procedure ErrorHandling1 as +begin +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 1768)~~ + +~~ERROR (Message: Foreign key 'fk1768_2' references object 'v1768' which is not a user table.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t1768_1( + c1 int NOT NULL +,c2 int NOT NULL +,PRIMARY KEY (c1)) +GO + +CREATE VIEW v1768 +AS +SELECT c1, c2 +FROM t1768_1 +GO + + +begin try +select 1 +CREATE TABLE t1768_2 ( + c1 int +,c2 int +,CONSTRAINT fk1768_2 FOREIGN KEY (c1) REFERENCES v1768 +) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP VIEW v1768 +GO + +DROP TABLE t1768_1 +GO + + + + diff --git a/contrib/test/JDBC/sql_expected/180_1.out b/contrib/test/JDBC/sql_expected/180_1.out new file mode 100644 index 00000000000..01509ef97a8 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/180_1.out @@ -0,0 +1,4259 @@ + + +-- simple batch start +GO + +CREATE FUNCTION fn180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +RETURNS INT +AS +BEGIN + RETURN 100 +END +GO +~~ERROR (Code: 180)~~ + +~~ERROR (Message: There are too many parameters in this CREATE FUNCTION statement. The maximum number is 2100.)~~ + + +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO + +GO + +CREATE FUNCTION fn180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +RETURNS INT +AS +BEGIN + RETURN 100 +END +GO +~~ERROR (Code: 180)~~ + +~~ERROR (Message: There are too many parameters in this CREATE FUNCTION statement. The maximum number is 2100.)~~ + + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO +~~START~~ +text +Does not respect xact_abort flag +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/sql_expected/180_2.out b/contrib/test/JDBC/sql_expected/180_2.out new file mode 100644 index 00000000000..a6fb35843c4 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/180_2.out @@ -0,0 +1,4255 @@ + + +-- simple batch start +GO + +CREATE PROC p180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +AS +BEGIN + RETURN 100 +END +GO +~~ERROR (Code: 180)~~ + +~~ERROR (Message: There are too many parameters in this CREATE PROCEDURE statement. The maximum number is 2100.)~~ + + +GO + +SET XACT_ABORT ON; +GO + +begin transaction +GO + +GO + +CREATE PROC p180( + @p1 int +,@p2 int +,@p3 int +,@p4 int +,@p5 int +,@p6 int +,@p7 int +,@p8 int +,@p9 int +,@p10 int +,@p11 int +,@p12 int +,@p13 int +,@p14 int +,@p15 int +,@p16 int +,@p17 int +,@p18 int +,@p19 int +,@p20 int +,@p21 int +,@p22 int +,@p23 int +,@p24 int +,@p25 int +,@p26 int +,@p27 int +,@p28 int +,@p29 int +,@p30 int +,@p31 int +,@p32 int +,@p33 int +,@p34 int +,@p35 int +,@p36 int +,@p37 int +,@p38 int +,@p39 int +,@p40 int +,@p41 int +,@p42 int +,@p43 int +,@p44 int +,@p45 int +,@p46 int +,@p47 int +,@p48 int +,@p49 int +,@p50 int +,@p51 int +,@p52 int +,@p53 int +,@p54 int +,@p55 int +,@p56 int +,@p57 int +,@p58 int +,@p59 int +,@p60 int +,@p61 int +,@p62 int +,@p63 int +,@p64 int +,@p65 int +,@p66 int +,@p67 int +,@p68 int +,@p69 int +,@p70 int +,@p71 int +,@p72 int +,@p73 int +,@p74 int +,@p75 int +,@p76 int +,@p77 int +,@p78 int +,@p79 int +,@p80 int +,@p81 int +,@p82 int +,@p83 int +,@p84 int +,@p85 int +,@p86 int +,@p87 int +,@p88 int +,@p89 int +,@p90 int +,@p91 int +,@p92 int +,@p93 int +,@p94 int +,@p95 int +,@p96 int +,@p97 int +,@p98 int +,@p99 int +,@p100 int +,@p101 int +,@p102 int +,@p103 int +,@p104 int +,@p105 int +,@p106 int +,@p107 int +,@p108 int +,@p109 int +,@p110 int +,@p111 int +,@p112 int +,@p113 int +,@p114 int +,@p115 int +,@p116 int +,@p117 int +,@p118 int +,@p119 int +,@p120 int +,@p121 int +,@p122 int +,@p123 int +,@p124 int +,@p125 int +,@p126 int +,@p127 int +,@p128 int +,@p129 int +,@p130 int +,@p131 int +,@p132 int +,@p133 int +,@p134 int +,@p135 int +,@p136 int +,@p137 int +,@p138 int +,@p139 int +,@p140 int +,@p141 int +,@p142 int +,@p143 int +,@p144 int +,@p145 int +,@p146 int +,@p147 int +,@p148 int +,@p149 int +,@p150 int +,@p151 int +,@p152 int +,@p153 int +,@p154 int +,@p155 int +,@p156 int +,@p157 int +,@p158 int +,@p159 int +,@p160 int +,@p161 int +,@p162 int +,@p163 int +,@p164 int +,@p165 int +,@p166 int +,@p167 int +,@p168 int +,@p169 int +,@p170 int +,@p171 int +,@p172 int +,@p173 int +,@p174 int +,@p175 int +,@p176 int +,@p177 int +,@p178 int +,@p179 int +,@p180 int +,@p181 int +,@p182 int +,@p183 int +,@p184 int +,@p185 int +,@p186 int +,@p187 int +,@p188 int +,@p189 int +,@p190 int +,@p191 int +,@p192 int +,@p193 int +,@p194 int +,@p195 int +,@p196 int +,@p197 int +,@p198 int +,@p199 int +,@p200 int +,@p201 int +,@p202 int +,@p203 int +,@p204 int +,@p205 int +,@p206 int +,@p207 int +,@p208 int +,@p209 int +,@p210 int +,@p211 int +,@p212 int +,@p213 int +,@p214 int +,@p215 int +,@p216 int +,@p217 int +,@p218 int +,@p219 int +,@p220 int +,@p221 int +,@p222 int +,@p223 int +,@p224 int +,@p225 int +,@p226 int +,@p227 int +,@p228 int +,@p229 int +,@p230 int +,@p231 int +,@p232 int +,@p233 int +,@p234 int +,@p235 int +,@p236 int +,@p237 int +,@p238 int +,@p239 int +,@p240 int +,@p241 int +,@p242 int +,@p243 int +,@p244 int +,@p245 int +,@p246 int +,@p247 int +,@p248 int +,@p249 int +,@p250 int +,@p251 int +,@p252 int +,@p253 int +,@p254 int +,@p255 int +,@p256 int +,@p257 int +,@p258 int +,@p259 int +,@p260 int +,@p261 int +,@p262 int +,@p263 int +,@p264 int +,@p265 int +,@p266 int +,@p267 int +,@p268 int +,@p269 int +,@p270 int +,@p271 int +,@p272 int +,@p273 int +,@p274 int +,@p275 int +,@p276 int +,@p277 int +,@p278 int +,@p279 int +,@p280 int +,@p281 int +,@p282 int +,@p283 int +,@p284 int +,@p285 int +,@p286 int +,@p287 int +,@p288 int +,@p289 int +,@p290 int +,@p291 int +,@p292 int +,@p293 int +,@p294 int +,@p295 int +,@p296 int +,@p297 int +,@p298 int +,@p299 int +,@p300 int +,@p301 int +,@p302 int +,@p303 int +,@p304 int +,@p305 int +,@p306 int +,@p307 int +,@p308 int +,@p309 int +,@p310 int +,@p311 int +,@p312 int +,@p313 int +,@p314 int +,@p315 int +,@p316 int +,@p317 int +,@p318 int +,@p319 int +,@p320 int +,@p321 int +,@p322 int +,@p323 int +,@p324 int +,@p325 int +,@p326 int +,@p327 int +,@p328 int +,@p329 int +,@p330 int +,@p331 int +,@p332 int +,@p333 int +,@p334 int +,@p335 int +,@p336 int +,@p337 int +,@p338 int +,@p339 int +,@p340 int +,@p341 int +,@p342 int +,@p343 int +,@p344 int +,@p345 int +,@p346 int +,@p347 int +,@p348 int +,@p349 int +,@p350 int +,@p351 int +,@p352 int +,@p353 int +,@p354 int +,@p355 int +,@p356 int +,@p357 int +,@p358 int +,@p359 int +,@p360 int +,@p361 int +,@p362 int +,@p363 int +,@p364 int +,@p365 int +,@p366 int +,@p367 int +,@p368 int +,@p369 int +,@p370 int +,@p371 int +,@p372 int +,@p373 int +,@p374 int +,@p375 int +,@p376 int +,@p377 int +,@p378 int +,@p379 int +,@p380 int +,@p381 int +,@p382 int +,@p383 int +,@p384 int +,@p385 int +,@p386 int +,@p387 int +,@p388 int +,@p389 int +,@p390 int +,@p391 int +,@p392 int +,@p393 int +,@p394 int +,@p395 int +,@p396 int +,@p397 int +,@p398 int +,@p399 int +,@p400 int +,@p401 int +,@p402 int +,@p403 int +,@p404 int +,@p405 int +,@p406 int +,@p407 int +,@p408 int +,@p409 int +,@p410 int +,@p411 int +,@p412 int +,@p413 int +,@p414 int +,@p415 int +,@p416 int +,@p417 int +,@p418 int +,@p419 int +,@p420 int +,@p421 int +,@p422 int +,@p423 int +,@p424 int +,@p425 int +,@p426 int +,@p427 int +,@p428 int +,@p429 int +,@p430 int +,@p431 int +,@p432 int +,@p433 int +,@p434 int +,@p435 int +,@p436 int +,@p437 int +,@p438 int +,@p439 int +,@p440 int +,@p441 int +,@p442 int +,@p443 int +,@p444 int +,@p445 int +,@p446 int +,@p447 int +,@p448 int +,@p449 int +,@p450 int +,@p451 int +,@p452 int +,@p453 int +,@p454 int +,@p455 int +,@p456 int +,@p457 int +,@p458 int +,@p459 int +,@p460 int +,@p461 int +,@p462 int +,@p463 int +,@p464 int +,@p465 int +,@p466 int +,@p467 int +,@p468 int +,@p469 int +,@p470 int +,@p471 int +,@p472 int +,@p473 int +,@p474 int +,@p475 int +,@p476 int +,@p477 int +,@p478 int +,@p479 int +,@p480 int +,@p481 int +,@p482 int +,@p483 int +,@p484 int +,@p485 int +,@p486 int +,@p487 int +,@p488 int +,@p489 int +,@p490 int +,@p491 int +,@p492 int +,@p493 int +,@p494 int +,@p495 int +,@p496 int +,@p497 int +,@p498 int +,@p499 int +,@p500 int +,@p501 int +,@p502 int +,@p503 int +,@p504 int +,@p505 int +,@p506 int +,@p507 int +,@p508 int +,@p509 int +,@p510 int +,@p511 int +,@p512 int +,@p513 int +,@p514 int +,@p515 int +,@p516 int +,@p517 int +,@p518 int +,@p519 int +,@p520 int +,@p521 int +,@p522 int +,@p523 int +,@p524 int +,@p525 int +,@p526 int +,@p527 int +,@p528 int +,@p529 int +,@p530 int +,@p531 int +,@p532 int +,@p533 int +,@p534 int +,@p535 int +,@p536 int +,@p537 int +,@p538 int +,@p539 int +,@p540 int +,@p541 int +,@p542 int +,@p543 int +,@p544 int +,@p545 int +,@p546 int +,@p547 int +,@p548 int +,@p549 int +,@p550 int +,@p551 int +,@p552 int +,@p553 int +,@p554 int +,@p555 int +,@p556 int +,@p557 int +,@p558 int +,@p559 int +,@p560 int +,@p561 int +,@p562 int +,@p563 int +,@p564 int +,@p565 int +,@p566 int +,@p567 int +,@p568 int +,@p569 int +,@p570 int +,@p571 int +,@p572 int +,@p573 int +,@p574 int +,@p575 int +,@p576 int +,@p577 int +,@p578 int +,@p579 int +,@p580 int +,@p581 int +,@p582 int +,@p583 int +,@p584 int +,@p585 int +,@p586 int +,@p587 int +,@p588 int +,@p589 int +,@p590 int +,@p591 int +,@p592 int +,@p593 int +,@p594 int +,@p595 int +,@p596 int +,@p597 int +,@p598 int +,@p599 int +,@p600 int +,@p601 int +,@p602 int +,@p603 int +,@p604 int +,@p605 int +,@p606 int +,@p607 int +,@p608 int +,@p609 int +,@p610 int +,@p611 int +,@p612 int +,@p613 int +,@p614 int +,@p615 int +,@p616 int +,@p617 int +,@p618 int +,@p619 int +,@p620 int +,@p621 int +,@p622 int +,@p623 int +,@p624 int +,@p625 int +,@p626 int +,@p627 int +,@p628 int +,@p629 int +,@p630 int +,@p631 int +,@p632 int +,@p633 int +,@p634 int +,@p635 int +,@p636 int +,@p637 int +,@p638 int +,@p639 int +,@p640 int +,@p641 int +,@p642 int +,@p643 int +,@p644 int +,@p645 int +,@p646 int +,@p647 int +,@p648 int +,@p649 int +,@p650 int +,@p651 int +,@p652 int +,@p653 int +,@p654 int +,@p655 int +,@p656 int +,@p657 int +,@p658 int +,@p659 int +,@p660 int +,@p661 int +,@p662 int +,@p663 int +,@p664 int +,@p665 int +,@p666 int +,@p667 int +,@p668 int +,@p669 int +,@p670 int +,@p671 int +,@p672 int +,@p673 int +,@p674 int +,@p675 int +,@p676 int +,@p677 int +,@p678 int +,@p679 int +,@p680 int +,@p681 int +,@p682 int +,@p683 int +,@p684 int +,@p685 int +,@p686 int +,@p687 int +,@p688 int +,@p689 int +,@p690 int +,@p691 int +,@p692 int +,@p693 int +,@p694 int +,@p695 int +,@p696 int +,@p697 int +,@p698 int +,@p699 int +,@p700 int +,@p701 int +,@p702 int +,@p703 int +,@p704 int +,@p705 int +,@p706 int +,@p707 int +,@p708 int +,@p709 int +,@p710 int +,@p711 int +,@p712 int +,@p713 int +,@p714 int +,@p715 int +,@p716 int +,@p717 int +,@p718 int +,@p719 int +,@p720 int +,@p721 int +,@p722 int +,@p723 int +,@p724 int +,@p725 int +,@p726 int +,@p727 int +,@p728 int +,@p729 int +,@p730 int +,@p731 int +,@p732 int +,@p733 int +,@p734 int +,@p735 int +,@p736 int +,@p737 int +,@p738 int +,@p739 int +,@p740 int +,@p741 int +,@p742 int +,@p743 int +,@p744 int +,@p745 int +,@p746 int +,@p747 int +,@p748 int +,@p749 int +,@p750 int +,@p751 int +,@p752 int +,@p753 int +,@p754 int +,@p755 int +,@p756 int +,@p757 int +,@p758 int +,@p759 int +,@p760 int +,@p761 int +,@p762 int +,@p763 int +,@p764 int +,@p765 int +,@p766 int +,@p767 int +,@p768 int +,@p769 int +,@p770 int +,@p771 int +,@p772 int +,@p773 int +,@p774 int +,@p775 int +,@p776 int +,@p777 int +,@p778 int +,@p779 int +,@p780 int +,@p781 int +,@p782 int +,@p783 int +,@p784 int +,@p785 int +,@p786 int +,@p787 int +,@p788 int +,@p789 int +,@p790 int +,@p791 int +,@p792 int +,@p793 int +,@p794 int +,@p795 int +,@p796 int +,@p797 int +,@p798 int +,@p799 int +,@p800 int +,@p801 int +,@p802 int +,@p803 int +,@p804 int +,@p805 int +,@p806 int +,@p807 int +,@p808 int +,@p809 int +,@p810 int +,@p811 int +,@p812 int +,@p813 int +,@p814 int +,@p815 int +,@p816 int +,@p817 int +,@p818 int +,@p819 int +,@p820 int +,@p821 int +,@p822 int +,@p823 int +,@p824 int +,@p825 int +,@p826 int +,@p827 int +,@p828 int +,@p829 int +,@p830 int +,@p831 int +,@p832 int +,@p833 int +,@p834 int +,@p835 int +,@p836 int +,@p837 int +,@p838 int +,@p839 int +,@p840 int +,@p841 int +,@p842 int +,@p843 int +,@p844 int +,@p845 int +,@p846 int +,@p847 int +,@p848 int +,@p849 int +,@p850 int +,@p851 int +,@p852 int +,@p853 int +,@p854 int +,@p855 int +,@p856 int +,@p857 int +,@p858 int +,@p859 int +,@p860 int +,@p861 int +,@p862 int +,@p863 int +,@p864 int +,@p865 int +,@p866 int +,@p867 int +,@p868 int +,@p869 int +,@p870 int +,@p871 int +,@p872 int +,@p873 int +,@p874 int +,@p875 int +,@p876 int +,@p877 int +,@p878 int +,@p879 int +,@p880 int +,@p881 int +,@p882 int +,@p883 int +,@p884 int +,@p885 int +,@p886 int +,@p887 int +,@p888 int +,@p889 int +,@p890 int +,@p891 int +,@p892 int +,@p893 int +,@p894 int +,@p895 int +,@p896 int +,@p897 int +,@p898 int +,@p899 int +,@p900 int +,@p901 int +,@p902 int +,@p903 int +,@p904 int +,@p905 int +,@p906 int +,@p907 int +,@p908 int +,@p909 int +,@p910 int +,@p911 int +,@p912 int +,@p913 int +,@p914 int +,@p915 int +,@p916 int +,@p917 int +,@p918 int +,@p919 int +,@p920 int +,@p921 int +,@p922 int +,@p923 int +,@p924 int +,@p925 int +,@p926 int +,@p927 int +,@p928 int +,@p929 int +,@p930 int +,@p931 int +,@p932 int +,@p933 int +,@p934 int +,@p935 int +,@p936 int +,@p937 int +,@p938 int +,@p939 int +,@p940 int +,@p941 int +,@p942 int +,@p943 int +,@p944 int +,@p945 int +,@p946 int +,@p947 int +,@p948 int +,@p949 int +,@p950 int +,@p951 int +,@p952 int +,@p953 int +,@p954 int +,@p955 int +,@p956 int +,@p957 int +,@p958 int +,@p959 int +,@p960 int +,@p961 int +,@p962 int +,@p963 int +,@p964 int +,@p965 int +,@p966 int +,@p967 int +,@p968 int +,@p969 int +,@p970 int +,@p971 int +,@p972 int +,@p973 int +,@p974 int +,@p975 int +,@p976 int +,@p977 int +,@p978 int +,@p979 int +,@p980 int +,@p981 int +,@p982 int +,@p983 int +,@p984 int +,@p985 int +,@p986 int +,@p987 int +,@p988 int +,@p989 int +,@p990 int +,@p991 int +,@p992 int +,@p993 int +,@p994 int +,@p995 int +,@p996 int +,@p997 int +,@p998 int +,@p999 int +,@p1000 int +,@p1001 int +,@p1002 int +,@p1003 int +,@p1004 int +,@p1005 int +,@p1006 int +,@p1007 int +,@p1008 int +,@p1009 int +,@p1010 int +,@p1011 int +,@p1012 int +,@p1013 int +,@p1014 int +,@p1015 int +,@p1016 int +,@p1017 int +,@p1018 int +,@p1019 int +,@p1020 int +,@p1021 int +,@p1022 int +,@p1023 int +,@p1024 int +,@p1025 int +,@p1026 int +,@p1027 int +,@p1028 int +,@p1029 int +,@p1030 int +,@p1031 int +,@p1032 int +,@p1033 int +,@p1034 int +,@p1035 int +,@p1036 int +,@p1037 int +,@p1038 int +,@p1039 int +,@p1040 int +,@p1041 int +,@p1042 int +,@p1043 int +,@p1044 int +,@p1045 int +,@p1046 int +,@p1047 int +,@p1048 int +,@p1049 int +,@p1050 int +,@p1051 int +,@p1052 int +,@p1053 int +,@p1054 int +,@p1055 int +,@p1056 int +,@p1057 int +,@p1058 int +,@p1059 int +,@p1060 int +,@p1061 int +,@p1062 int +,@p1063 int +,@p1064 int +,@p1065 int +,@p1066 int +,@p1067 int +,@p1068 int +,@p1069 int +,@p1070 int +,@p1071 int +,@p1072 int +,@p1073 int +,@p1074 int +,@p1075 int +,@p1076 int +,@p1077 int +,@p1078 int +,@p1079 int +,@p1080 int +,@p1081 int +,@p1082 int +,@p1083 int +,@p1084 int +,@p1085 int +,@p1086 int +,@p1087 int +,@p1088 int +,@p1089 int +,@p1090 int +,@p1091 int +,@p1092 int +,@p1093 int +,@p1094 int +,@p1095 int +,@p1096 int +,@p1097 int +,@p1098 int +,@p1099 int +,@p1100 int +,@p1101 int +,@p1102 int +,@p1103 int +,@p1104 int +,@p1105 int +,@p1106 int +,@p1107 int +,@p1108 int +,@p1109 int +,@p1110 int +,@p1111 int +,@p1112 int +,@p1113 int +,@p1114 int +,@p1115 int +,@p1116 int +,@p1117 int +,@p1118 int +,@p1119 int +,@p1120 int +,@p1121 int +,@p1122 int +,@p1123 int +,@p1124 int +,@p1125 int +,@p1126 int +,@p1127 int +,@p1128 int +,@p1129 int +,@p1130 int +,@p1131 int +,@p1132 int +,@p1133 int +,@p1134 int +,@p1135 int +,@p1136 int +,@p1137 int +,@p1138 int +,@p1139 int +,@p1140 int +,@p1141 int +,@p1142 int +,@p1143 int +,@p1144 int +,@p1145 int +,@p1146 int +,@p1147 int +,@p1148 int +,@p1149 int +,@p1150 int +,@p1151 int +,@p1152 int +,@p1153 int +,@p1154 int +,@p1155 int +,@p1156 int +,@p1157 int +,@p1158 int +,@p1159 int +,@p1160 int +,@p1161 int +,@p1162 int +,@p1163 int +,@p1164 int +,@p1165 int +,@p1166 int +,@p1167 int +,@p1168 int +,@p1169 int +,@p1170 int +,@p1171 int +,@p1172 int +,@p1173 int +,@p1174 int +,@p1175 int +,@p1176 int +,@p1177 int +,@p1178 int +,@p1179 int +,@p1180 int +,@p1181 int +,@p1182 int +,@p1183 int +,@p1184 int +,@p1185 int +,@p1186 int +,@p1187 int +,@p1188 int +,@p1189 int +,@p1190 int +,@p1191 int +,@p1192 int +,@p1193 int +,@p1194 int +,@p1195 int +,@p1196 int +,@p1197 int +,@p1198 int +,@p1199 int +,@p1200 int +,@p1201 int +,@p1202 int +,@p1203 int +,@p1204 int +,@p1205 int +,@p1206 int +,@p1207 int +,@p1208 int +,@p1209 int +,@p1210 int +,@p1211 int +,@p1212 int +,@p1213 int +,@p1214 int +,@p1215 int +,@p1216 int +,@p1217 int +,@p1218 int +,@p1219 int +,@p1220 int +,@p1221 int +,@p1222 int +,@p1223 int +,@p1224 int +,@p1225 int +,@p1226 int +,@p1227 int +,@p1228 int +,@p1229 int +,@p1230 int +,@p1231 int +,@p1232 int +,@p1233 int +,@p1234 int +,@p1235 int +,@p1236 int +,@p1237 int +,@p1238 int +,@p1239 int +,@p1240 int +,@p1241 int +,@p1242 int +,@p1243 int +,@p1244 int +,@p1245 int +,@p1246 int +,@p1247 int +,@p1248 int +,@p1249 int +,@p1250 int +,@p1251 int +,@p1252 int +,@p1253 int +,@p1254 int +,@p1255 int +,@p1256 int +,@p1257 int +,@p1258 int +,@p1259 int +,@p1260 int +,@p1261 int +,@p1262 int +,@p1263 int +,@p1264 int +,@p1265 int +,@p1266 int +,@p1267 int +,@p1268 int +,@p1269 int +,@p1270 int +,@p1271 int +,@p1272 int +,@p1273 int +,@p1274 int +,@p1275 int +,@p1276 int +,@p1277 int +,@p1278 int +,@p1279 int +,@p1280 int +,@p1281 int +,@p1282 int +,@p1283 int +,@p1284 int +,@p1285 int +,@p1286 int +,@p1287 int +,@p1288 int +,@p1289 int +,@p1290 int +,@p1291 int +,@p1292 int +,@p1293 int +,@p1294 int +,@p1295 int +,@p1296 int +,@p1297 int +,@p1298 int +,@p1299 int +,@p1300 int +,@p1301 int +,@p1302 int +,@p1303 int +,@p1304 int +,@p1305 int +,@p1306 int +,@p1307 int +,@p1308 int +,@p1309 int +,@p1310 int +,@p1311 int +,@p1312 int +,@p1313 int +,@p1314 int +,@p1315 int +,@p1316 int +,@p1317 int +,@p1318 int +,@p1319 int +,@p1320 int +,@p1321 int +,@p1322 int +,@p1323 int +,@p1324 int +,@p1325 int +,@p1326 int +,@p1327 int +,@p1328 int +,@p1329 int +,@p1330 int +,@p1331 int +,@p1332 int +,@p1333 int +,@p1334 int +,@p1335 int +,@p1336 int +,@p1337 int +,@p1338 int +,@p1339 int +,@p1340 int +,@p1341 int +,@p1342 int +,@p1343 int +,@p1344 int +,@p1345 int +,@p1346 int +,@p1347 int +,@p1348 int +,@p1349 int +,@p1350 int +,@p1351 int +,@p1352 int +,@p1353 int +,@p1354 int +,@p1355 int +,@p1356 int +,@p1357 int +,@p1358 int +,@p1359 int +,@p1360 int +,@p1361 int +,@p1362 int +,@p1363 int +,@p1364 int +,@p1365 int +,@p1366 int +,@p1367 int +,@p1368 int +,@p1369 int +,@p1370 int +,@p1371 int +,@p1372 int +,@p1373 int +,@p1374 int +,@p1375 int +,@p1376 int +,@p1377 int +,@p1378 int +,@p1379 int +,@p1380 int +,@p1381 int +,@p1382 int +,@p1383 int +,@p1384 int +,@p1385 int +,@p1386 int +,@p1387 int +,@p1388 int +,@p1389 int +,@p1390 int +,@p1391 int +,@p1392 int +,@p1393 int +,@p1394 int +,@p1395 int +,@p1396 int +,@p1397 int +,@p1398 int +,@p1399 int +,@p1400 int +,@p1401 int +,@p1402 int +,@p1403 int +,@p1404 int +,@p1405 int +,@p1406 int +,@p1407 int +,@p1408 int +,@p1409 int +,@p1410 int +,@p1411 int +,@p1412 int +,@p1413 int +,@p1414 int +,@p1415 int +,@p1416 int +,@p1417 int +,@p1418 int +,@p1419 int +,@p1420 int +,@p1421 int +,@p1422 int +,@p1423 int +,@p1424 int +,@p1425 int +,@p1426 int +,@p1427 int +,@p1428 int +,@p1429 int +,@p1430 int +,@p1431 int +,@p1432 int +,@p1433 int +,@p1434 int +,@p1435 int +,@p1436 int +,@p1437 int +,@p1438 int +,@p1439 int +,@p1440 int +,@p1441 int +,@p1442 int +,@p1443 int +,@p1444 int +,@p1445 int +,@p1446 int +,@p1447 int +,@p1448 int +,@p1449 int +,@p1450 int +,@p1451 int +,@p1452 int +,@p1453 int +,@p1454 int +,@p1455 int +,@p1456 int +,@p1457 int +,@p1458 int +,@p1459 int +,@p1460 int +,@p1461 int +,@p1462 int +,@p1463 int +,@p1464 int +,@p1465 int +,@p1466 int +,@p1467 int +,@p1468 int +,@p1469 int +,@p1470 int +,@p1471 int +,@p1472 int +,@p1473 int +,@p1474 int +,@p1475 int +,@p1476 int +,@p1477 int +,@p1478 int +,@p1479 int +,@p1480 int +,@p1481 int +,@p1482 int +,@p1483 int +,@p1484 int +,@p1485 int +,@p1486 int +,@p1487 int +,@p1488 int +,@p1489 int +,@p1490 int +,@p1491 int +,@p1492 int +,@p1493 int +,@p1494 int +,@p1495 int +,@p1496 int +,@p1497 int +,@p1498 int +,@p1499 int +,@p1500 int +,@p1501 int +,@p1502 int +,@p1503 int +,@p1504 int +,@p1505 int +,@p1506 int +,@p1507 int +,@p1508 int +,@p1509 int +,@p1510 int +,@p1511 int +,@p1512 int +,@p1513 int +,@p1514 int +,@p1515 int +,@p1516 int +,@p1517 int +,@p1518 int +,@p1519 int +,@p1520 int +,@p1521 int +,@p1522 int +,@p1523 int +,@p1524 int +,@p1525 int +,@p1526 int +,@p1527 int +,@p1528 int +,@p1529 int +,@p1530 int +,@p1531 int +,@p1532 int +,@p1533 int +,@p1534 int +,@p1535 int +,@p1536 int +,@p1537 int +,@p1538 int +,@p1539 int +,@p1540 int +,@p1541 int +,@p1542 int +,@p1543 int +,@p1544 int +,@p1545 int +,@p1546 int +,@p1547 int +,@p1548 int +,@p1549 int +,@p1550 int +,@p1551 int +,@p1552 int +,@p1553 int +,@p1554 int +,@p1555 int +,@p1556 int +,@p1557 int +,@p1558 int +,@p1559 int +,@p1560 int +,@p1561 int +,@p1562 int +,@p1563 int +,@p1564 int +,@p1565 int +,@p1566 int +,@p1567 int +,@p1568 int +,@p1569 int +,@p1570 int +,@p1571 int +,@p1572 int +,@p1573 int +,@p1574 int +,@p1575 int +,@p1576 int +,@p1577 int +,@p1578 int +,@p1579 int +,@p1580 int +,@p1581 int +,@p1582 int +,@p1583 int +,@p1584 int +,@p1585 int +,@p1586 int +,@p1587 int +,@p1588 int +,@p1589 int +,@p1590 int +,@p1591 int +,@p1592 int +,@p1593 int +,@p1594 int +,@p1595 int +,@p1596 int +,@p1597 int +,@p1598 int +,@p1599 int +,@p1600 int +,@p1601 int +,@p1602 int +,@p1603 int +,@p1604 int +,@p1605 int +,@p1606 int +,@p1607 int +,@p1608 int +,@p1609 int +,@p1610 int +,@p1611 int +,@p1612 int +,@p1613 int +,@p1614 int +,@p1615 int +,@p1616 int +,@p1617 int +,@p1618 int +,@p1619 int +,@p1620 int +,@p1621 int +,@p1622 int +,@p1623 int +,@p1624 int +,@p1625 int +,@p1626 int +,@p1627 int +,@p1628 int +,@p1629 int +,@p1630 int +,@p1631 int +,@p1632 int +,@p1633 int +,@p1634 int +,@p1635 int +,@p1636 int +,@p1637 int +,@p1638 int +,@p1639 int +,@p1640 int +,@p1641 int +,@p1642 int +,@p1643 int +,@p1644 int +,@p1645 int +,@p1646 int +,@p1647 int +,@p1648 int +,@p1649 int +,@p1650 int +,@p1651 int +,@p1652 int +,@p1653 int +,@p1654 int +,@p1655 int +,@p1656 int +,@p1657 int +,@p1658 int +,@p1659 int +,@p1660 int +,@p1661 int +,@p1662 int +,@p1663 int +,@p1664 int +,@p1665 int +,@p1666 int +,@p1667 int +,@p1668 int +,@p1669 int +,@p1670 int +,@p1671 int +,@p1672 int +,@p1673 int +,@p1674 int +,@p1675 int +,@p1676 int +,@p1677 int +,@p1678 int +,@p1679 int +,@p1680 int +,@p1681 int +,@p1682 int +,@p1683 int +,@p1684 int +,@p1685 int +,@p1686 int +,@p1687 int +,@p1688 int +,@p1689 int +,@p1690 int +,@p1691 int +,@p1692 int +,@p1693 int +,@p1694 int +,@p1695 int +,@p1696 int +,@p1697 int +,@p1698 int +,@p1699 int +,@p1700 int +,@p1701 int +,@p1702 int +,@p1703 int +,@p1704 int +,@p1705 int +,@p1706 int +,@p1707 int +,@p1708 int +,@p1709 int +,@p1710 int +,@p1711 int +,@p1712 int +,@p1713 int +,@p1714 int +,@p1715 int +,@p1716 int +,@p1717 int +,@p1718 int +,@p1719 int +,@p1720 int +,@p1721 int +,@p1722 int +,@p1723 int +,@p1724 int +,@p1725 int +,@p1726 int +,@p1727 int +,@p1728 int +,@p1729 int +,@p1730 int +,@p1731 int +,@p1732 int +,@p1733 int +,@p1734 int +,@p1735 int +,@p1736 int +,@p1737 int +,@p1738 int +,@p1739 int +,@p1740 int +,@p1741 int +,@p1742 int +,@p1743 int +,@p1744 int +,@p1745 int +,@p1746 int +,@p1747 int +,@p1748 int +,@p1749 int +,@p1750 int +,@p1751 int +,@p1752 int +,@p1753 int +,@p1754 int +,@p1755 int +,@p1756 int +,@p1757 int +,@p1758 int +,@p1759 int +,@p1760 int +,@p1761 int +,@p1762 int +,@p1763 int +,@p1764 int +,@p1765 int +,@p1766 int +,@p1767 int +,@p1768 int +,@p1769 int +,@p1770 int +,@p1771 int +,@p1772 int +,@p1773 int +,@p1774 int +,@p1775 int +,@p1776 int +,@p1777 int +,@p1778 int +,@p1779 int +,@p1780 int +,@p1781 int +,@p1782 int +,@p1783 int +,@p1784 int +,@p1785 int +,@p1786 int +,@p1787 int +,@p1788 int +,@p1789 int +,@p1790 int +,@p1791 int +,@p1792 int +,@p1793 int +,@p1794 int +,@p1795 int +,@p1796 int +,@p1797 int +,@p1798 int +,@p1799 int +,@p1800 int +,@p1801 int +,@p1802 int +,@p1803 int +,@p1804 int +,@p1805 int +,@p1806 int +,@p1807 int +,@p1808 int +,@p1809 int +,@p1810 int +,@p1811 int +,@p1812 int +,@p1813 int +,@p1814 int +,@p1815 int +,@p1816 int +,@p1817 int +,@p1818 int +,@p1819 int +,@p1820 int +,@p1821 int +,@p1822 int +,@p1823 int +,@p1824 int +,@p1825 int +,@p1826 int +,@p1827 int +,@p1828 int +,@p1829 int +,@p1830 int +,@p1831 int +,@p1832 int +,@p1833 int +,@p1834 int +,@p1835 int +,@p1836 int +,@p1837 int +,@p1838 int +,@p1839 int +,@p1840 int +,@p1841 int +,@p1842 int +,@p1843 int +,@p1844 int +,@p1845 int +,@p1846 int +,@p1847 int +,@p1848 int +,@p1849 int +,@p1850 int +,@p1851 int +,@p1852 int +,@p1853 int +,@p1854 int +,@p1855 int +,@p1856 int +,@p1857 int +,@p1858 int +,@p1859 int +,@p1860 int +,@p1861 int +,@p1862 int +,@p1863 int +,@p1864 int +,@p1865 int +,@p1866 int +,@p1867 int +,@p1868 int +,@p1869 int +,@p1870 int +,@p1871 int +,@p1872 int +,@p1873 int +,@p1874 int +,@p1875 int +,@p1876 int +,@p1877 int +,@p1878 int +,@p1879 int +,@p1880 int +,@p1881 int +,@p1882 int +,@p1883 int +,@p1884 int +,@p1885 int +,@p1886 int +,@p1887 int +,@p1888 int +,@p1889 int +,@p1890 int +,@p1891 int +,@p1892 int +,@p1893 int +,@p1894 int +,@p1895 int +,@p1896 int +,@p1897 int +,@p1898 int +,@p1899 int +,@p1900 int +,@p1901 int +,@p1902 int +,@p1903 int +,@p1904 int +,@p1905 int +,@p1906 int +,@p1907 int +,@p1908 int +,@p1909 int +,@p1910 int +,@p1911 int +,@p1912 int +,@p1913 int +,@p1914 int +,@p1915 int +,@p1916 int +,@p1917 int +,@p1918 int +,@p1919 int +,@p1920 int +,@p1921 int +,@p1922 int +,@p1923 int +,@p1924 int +,@p1925 int +,@p1926 int +,@p1927 int +,@p1928 int +,@p1929 int +,@p1930 int +,@p1931 int +,@p1932 int +,@p1933 int +,@p1934 int +,@p1935 int +,@p1936 int +,@p1937 int +,@p1938 int +,@p1939 int +,@p1940 int +,@p1941 int +,@p1942 int +,@p1943 int +,@p1944 int +,@p1945 int +,@p1946 int +,@p1947 int +,@p1948 int +,@p1949 int +,@p1950 int +,@p1951 int +,@p1952 int +,@p1953 int +,@p1954 int +,@p1955 int +,@p1956 int +,@p1957 int +,@p1958 int +,@p1959 int +,@p1960 int +,@p1961 int +,@p1962 int +,@p1963 int +,@p1964 int +,@p1965 int +,@p1966 int +,@p1967 int +,@p1968 int +,@p1969 int +,@p1970 int +,@p1971 int +,@p1972 int +,@p1973 int +,@p1974 int +,@p1975 int +,@p1976 int +,@p1977 int +,@p1978 int +,@p1979 int +,@p1980 int +,@p1981 int +,@p1982 int +,@p1983 int +,@p1984 int +,@p1985 int +,@p1986 int +,@p1987 int +,@p1988 int +,@p1989 int +,@p1990 int +,@p1991 int +,@p1992 int +,@p1993 int +,@p1994 int +,@p1995 int +,@p1996 int +,@p1997 int +,@p1998 int +,@p1999 int +,@p2000 int +,@p2001 int +,@p2002 int +,@p2003 int +,@p2004 int +,@p2005 int +,@p2006 int +,@p2007 int +,@p2008 int +,@p2009 int +,@p2010 int +,@p2011 int +,@p2012 int +,@p2013 int +,@p2014 int +,@p2015 int +,@p2016 int +,@p2017 int +,@p2018 int +,@p2019 int +,@p2020 int +,@p2021 int +,@p2022 int +,@p2023 int +,@p2024 int +,@p2025 int +,@p2026 int +,@p2027 int +,@p2028 int +,@p2029 int +,@p2030 int +,@p2031 int +,@p2032 int +,@p2033 int +,@p2034 int +,@p2035 int +,@p2036 int +,@p2037 int +,@p2038 int +,@p2039 int +,@p2040 int +,@p2041 int +,@p2042 int +,@p2043 int +,@p2044 int +,@p2045 int +,@p2046 int +,@p2047 int +,@p2048 int +,@p2049 int +,@p2050 int +,@p2051 int +,@p2052 int +,@p2053 int +,@p2054 int +,@p2055 int +,@p2056 int +,@p2057 int +,@p2058 int +,@p2059 int +,@p2060 int +,@p2061 int +,@p2062 int +,@p2063 int +,@p2064 int +,@p2065 int +,@p2066 int +,@p2067 int +,@p2068 int +,@p2069 int +,@p2070 int +,@p2071 int +,@p2072 int +,@p2073 int +,@p2074 int +,@p2075 int +,@p2076 int +,@p2077 int +,@p2078 int +,@p2079 int +,@p2080 int +,@p2081 int +,@p2082 int +,@p2083 int +,@p2084 int +,@p2085 int +,@p2086 int +,@p2087 int +,@p2088 int +,@p2089 int +,@p2090 int +,@p2091 int +,@p2092 int +,@p2093 int +,@p2094 int +,@p2095 int +,@p2096 int +,@p2097 int +,@p2098 int +,@p2099 int +,@p2100 int +,@p2101 int +) +AS +BEGIN + RETURN 100 +END +GO +~~ERROR (Code: 180)~~ + +~~ERROR (Message: There are too many parameters in this CREATE PROCEDURE statement. The maximum number is 2100.)~~ + + +if @@trancount > 0 select cast('Does not respoect xact_abort flag' as TEXT) else select cast('Respects xact_abort flag' as text); +GO +~~START~~ +text +Does not respoect xact_abort flag +~~END~~ + + +if @@trancount > 0 rollback tran +GO + +SET XACT_ABORT OFF; +GO + diff --git a/contrib/test/JDBC/sql_expected/1946_1.out b/contrib/test/JDBC/sql_expected/1946_1.out new file mode 100644 index 00000000000..fe3da4f7ad2 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/1946_1.out @@ -0,0 +1,598 @@ + +-- simple batch start +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +GO +~~ERROR (Code: 1946)~~ + +~~ERROR (Message: Operation failed. The index entry of length 3004 bytes for the index 'ix1946' exceeds the maximum length of 900 bytes.)~~ + +DROP TABLE t1946 +GO + +begin transaction +GO +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +GO +~~ERROR (Code: 1946)~~ + +~~ERROR (Message: Operation failed. The index entry of length 3004 bytes for the index 'ix1946' exceeds the maximum length of 900 bytes.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t1946 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't1946', because it does not exist or you do not have permission.)~~ + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +end +GO + +DROP TABLE t1946 +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + +insert into error_mapping.temp2 values(1) +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1946)~~ + +~~ERROR (Message: Operation failed. The index entry of length 3004 bytes for the index 'ix1946' exceeds the maximum length of 900 bytes.)~~ + + +DROP TABLE t1946 +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1946)~~ + +~~ERROR (Message: Operation failed. The index entry of length 3004 bytes for the index 'ix1946' exceeds the maximum length of 900 bytes.)~~ + + +DROP TABLE t1946 +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t1946 +GO + + +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +begin transaction +GO + + + + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1946 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO +begin transaction +GO + + + + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t1946 +GO +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 1946)~~ + +~~ERROR (Message: Operation failed. The index entry of length 3004 bytes for the index 'ix1946' exceeds the maximum length of 900 bytes.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t1946 +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 1946)~~ + +~~ERROR (Message: Operation failed. The index entry of length 3004 bytes for the index 'ix1946' exceeds the maximum length of 900 bytes.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t1946 +GO + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t1946 +(c1 int IDENTITY +,c2 varchar(500) +,c3 varchar(500) +,c4 varchar(500) +,c5 varchar(500) +,c6 varchar(500) +,c7 varchar(500) +) +GO + +CREATE INDEX ix1946 ON t1946(c1, c2, c3, c4, c5, c6, c7) +GO + + + + + + +begin try +select 1 +INSERT INTO t1946(c2, c3, c4, c5, c6, c7) +VALUES( + REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +,REPLICATE('a', 500) +) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t1946 +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/217_1.out b/contrib/test/JDBC/sql_expected/217_1.out new file mode 100644 index 00000000000..632fee0c246 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/217_1.out @@ -0,0 +1,329 @@ +# Executing test ErrorHandling1 + + +--217 NESTLEVEL exceeded +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + + + +--217 NESTLEVEL exceeded +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + + +--217 NESTLEVEL exceeded +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + + +-- Next portion is for runtime error -- +--217 NESTLEVEL exceeded +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 217)~~ + +~~ERROR (Message: Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 + + +--217 NESTLEVEL exceeded +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + + +create procedure ErrorHandling1 as +begin +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 217)~~ + +~~ERROR (Message: Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + + +-- Error classification is done -- +--217 NESTLEVEL exceeded +--Bbf has the incorrect max nestlevel. Should be 32, but it is 1040. +--Known, as BABEL-610 +--------------------------------------------------------------------------- +--Pre +CREATE PROC p217 AS +PRINT @@NESTLEVEL +--IF @@NESTLEVEL = 33 +--BEGIN +-- RETURN +--END +EXEC p217; +GO + + +begin try +select 1 +--------------------------------------------------------------------------- +--Generate the error +EXEC p217; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--------------------------------------------------------------------------- +--Post +DROP PROC p217 +GO + + diff --git a/contrib/test/JDBC/sql_expected/219_1.out b/contrib/test/JDBC/sql_expected/219_1.out new file mode 100644 index 00000000000..16ffcb4a894 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/219_1.out @@ -0,0 +1,227 @@ +# Executing test ErrorHandling1 +CREATE TYPE type_218 from INT +GO + + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TYPE type_218 +GO + + +CREATE TYPE type_218 from INT +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TYPE type_218 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TYPE type_218 from INT +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TYPE type_218 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TYPE type_218 from INT +GO + + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 219)~~ + +~~ERROR (Message: The type 'type_218' already exists, or you do not have permission to create it.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TYPE type_218 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TYPE type_218 from INT +GO + + +create procedure ErrorHandling1 as +begin +CREATE TYPE type_218 from INT +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 219)~~ + +~~ERROR (Message: The type 'type_218' already exists, or you do not have permission to create it.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TYPE type_218 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TYPE type_218 from INT +GO + + +begin try +select 1 +CREATE TYPE type_218 from INT +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TYPE type_218 +GO + + diff --git a/contrib/test/JDBC/sql_expected/220_1.out b/contrib/test/JDBC/sql_expected/220_1.out new file mode 100644 index 00000000000..eaaa90133b1 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/220_1.out @@ -0,0 +1,268 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type tinyint, value = 1000.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +DECLARE @a tinyint = 1000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type tinyint, value = 1000.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +begin try +select 1 +--Generate the error +DECLARE @a tinyint = 1000; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/sql_expected/220_2.out b/contrib/test/JDBC/sql_expected/220_2.out new file mode 100644 index 00000000000..4009a6ce944 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/220_2.out @@ -0,0 +1,262 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type smallint, value = 45000.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +DECLARE @a smallint = 45000; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type smallint, value = 45000.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +begin try +select 1 +DECLARE @a smallint = 45000; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/sql_expected/220_3.out b/contrib/test/JDBC/sql_expected/220_3.out new file mode 100644 index 00000000000..6619721852d --- /dev/null +++ b/contrib/test/JDBC/sql_expected/220_3.out @@ -0,0 +1,262 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type tinyint, value = 1000.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_1(c1) VALUES(1000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type tinyint, value = 1000.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +begin try +select 1 +INSERT INTO t220_1(c1) VALUES(1000); +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/sql_expected/220_4.out b/contrib/test/JDBC/sql_expected/220_4.out new file mode 100644 index 00000000000..8182368d663 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/220_4.out @@ -0,0 +1,262 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type smallint, value = 45000.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +create procedure ErrorHandling1 as +begin +INSERT INTO t220_2(c1) VALUES(45000); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: Arithmetic overflow error for data type smallint, value = 45000.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t220_1(c1 tinyint); +CREATE TABLE t220_2(c1 smallint); +GO + + +begin try +select 1 +INSERT INTO t220_2(c1) VALUES(45000); +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t220_1 +GO +DROP TABLE t220_2 +GO + + diff --git a/contrib/test/JDBC/sql_expected/2732_1.out b/contrib/test/JDBC/sql_expected/2732_1.out new file mode 100644 index 00000000000..2a58222a5dd --- /dev/null +++ b/contrib/test/JDBC/sql_expected/2732_1.out @@ -0,0 +1,358 @@ + +-- simple batch start +GO + +RAISERROR(5005, 10, 1, N'ErrorMessage') +GO +~~ERROR (Code: 2732)~~ + +~~ERROR (Message: Error number 5005 is invalid. The number must be from 13000 through 2147483647 and it cannot be 50000.)~~ + +GO + + +begin transaction +GO +GO + +RAISERROR(5005, 10, 1, N'ErrorMessage') +GO +~~ERROR (Code: 2732)~~ + +~~ERROR (Message: Error number 5005 is invalid. The number must be from 13000 through 2147483647 and it cannot be 50000.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +RAISERROR(5005, 10, 1, N'ErrorMessage') +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +RAISERROR(5005, 10, 1, N'ErrorMessage') +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2732)~~ + +~~ERROR (Message: Error number 5005 is invalid. The number must be from 13000 through 2147483647 and it cannot be 50000.)~~ + + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2732)~~ + +~~ERROR (Message: Error number 5005 is invalid. The number must be from 13000 through 2147483647 and it cannot be 50000.)~~ + + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 2732)~~ + +~~ERROR (Message: Error number 5005 is invalid. The number must be from 13000 through 2147483647 and it cannot be 50000.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR(5005, 10, 1, N'ErrorMessage') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 2732)~~ + +~~ERROR (Message: Error number 5005 is invalid. The number must be from 13000 through 2147483647 and it cannot be 50000.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +begin try +select 1 +RAISERROR(5005, 10, 1, N'ErrorMessage') +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/2747_1.out b/contrib/test/JDBC/sql_expected/2747_1.out new file mode 100644 index 00000000000..a1bbc698d86 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/2747_1.out @@ -0,0 +1,379 @@ + +-- simple batch start +GO + +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + +GO + +begin transaction +GO +GO + +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +end +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + + +GO + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2812)~~ + +~~ERROR (Message: Could not find stored procedure 'error_mapping.ErrorHandling1'.)~~ + + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO +~~START~~ +text +parse analysis phase error +~~END~~ + + +drop procedure error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +Compile time error +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +GO + + +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH ONLY TERMINATING +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO +set xact_abort OFF; +GO + + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +--,1, +--1, +--3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--GO +-- +-- +--set xact_abort ON; +--GO +---- Executing test error_mapping.ErrorHandling10000000 +--GO +-- +--create procedure error_mapping.ErrorHandling1 as +--begin +--RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +--,1, +--1, +--3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +-- +--if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +--select @@trancount; +--end +-- +--GO +-- +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--GO +--create procedure error_mapping.ErrorHandling2 as +--begin +--exec error_mapping.ErrorHandling1; +--if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +--end +-- +--GO +-- +--begin transaction; +--GO +--exec error_mapping.ErrorHandling2; +--GO +--declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +--if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--GO +-- +-- +-- +--set xact_abort OFF; +--GO +-- +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +begin try +select 1 +RAISERROR('%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d' +,1, +1, +3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +--drop procedure error_mapping.ErrorHandling1; +--drop procedure error_mapping.ErrorHandling2; +--set xact_abort OFF; +--set implicit_transactions OFF; +--GO +--GO +-- +-- +-- +GO +~~ERROR (Code: 2747)~~ + +~~ERROR (Message: Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.)~~ + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/2787_1.out b/contrib/test/JDBC/sql_expected/2787_1.out new file mode 100644 index 00000000000..453a6a1691f --- /dev/null +++ b/contrib/test/JDBC/sql_expected/2787_1.out @@ -0,0 +1,363 @@ + + +-- simple batch start +GO +RAISERROR('Hello %q', 16, 1, 'as') +GO +~~ERROR (Code: 2787)~~ + +~~ERROR (Message: Invalid format specification: '%q'.)~~ + + +GO + +begin transaction +GO + +GO +RAISERROR('Hello %q', 16, 1, 'as') +GO +~~ERROR (Code: 2787)~~ + +~~ERROR (Message: Invalid format specification: '%q'.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + + +GO + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + + +GO +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +RAISERROR('Hello %q', 16, 1, 'as') +end +GO + + +GO + +create table error_mapping.temp2 (a int) +GO + + +GO +insert into error_mapping.temp2 values(1) +RAISERROR('Hello %q', 16, 1, 'as') +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2787)~~ + +~~ERROR (Message: Invalid format specification: '%q'.)~~ + + + +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + + +GO +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2787)~~ + +~~ERROR (Message: Invalid format specification: '%q'.)~~ + + + +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +GO +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +GO +set xact_abort OFF; +GO + + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 2787)~~ + +~~ERROR (Message: Invalid format specification: '%q'.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + +set xact_abort ON; +GO + +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +RAISERROR('Hello %q', 16, 1, 'as') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 2787)~~ + +~~ERROR (Message: Invalid format specification: '%q'.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +set xact_abort OFF; +GO + + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + +begin try +select 1 +RAISERROR('Hello %q', 16, 1, 'as') +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + + +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/293_1.out b/contrib/test/JDBC/sql_expected/293_1.out new file mode 100644 index 00000000000..17132a1686a --- /dev/null +++ b/contrib/test/JDBC/sql_expected/293_1.out @@ -0,0 +1,491 @@ + +-- simple batch start +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +GO +~~START~~ +smallmoney +~~ERROR (Code: 293)~~ + +~~ERROR (Message: Cannot convert char value to smallmoney. The char value has incorrect syntax.)~~ + +DROP TABLE t293 +GO + + +begin transaction +GO +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +GO +~~START~~ +smallmoney +~~ERROR (Code: 293)~~ + +~~ERROR (Message: Cannot convert char value to smallmoney. The char value has incorrect syntax.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t293 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't293', because it does not exist or you do not have permission.)~~ + + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +end +GO + +DROP TABLE t293 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + +insert into error_mapping.temp2 values(1) +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +GO +~~ROW COUNT: 1~~ + +~~START~~ +smallmoney +~~ERROR (Code: 293)~~ + +~~ERROR (Message: Cannot convert char value to smallmoney. The char value has incorrect syntax.)~~ + + +DROP TABLE t293 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallmoney +~~ERROR (Code: 293)~~ + +~~ERROR (Message: Cannot convert char value to smallmoney. The char value has incorrect syntax.)~~ + + +DROP TABLE t293 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t293 +GO + + + +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t293 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t293 +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~START~~ +smallmoney +~~ERROR (Code: 293)~~ + +~~ERROR (Message: Cannot convert char value to smallmoney. The char value has incorrect syntax.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t293 +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~START~~ +smallmoney +~~ERROR (Code: 293)~~ + +~~ERROR (Message: Cannot convert char value to smallmoney. The char value has incorrect syntax.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t293 +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t293(c1 varchar(255)) +GO + +INSERT INTO t293(c1) +VALUES ('string'); +GO +~~ROW COUNT: 1~~ + + + + + +begin try +select 1 +SELECT CAST( + (SELECT * FROM t293) + AS SMALLMONEY) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallmoney +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t293 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/3609_1.out b/contrib/test/JDBC/sql_expected/3609_1.out new file mode 100644 index 00000000000..0fe42b2bd6f --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3609_1.out @@ -0,0 +1,474 @@ + +-- simple batch start +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + +INSERT INTO t3609 values (1) +GO +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + +DROP TABLE t3609 +Go + +begin transaction +GO +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + +INSERT INTO t3609 values (1) +GO +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t3609 +Go + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +INSERT INTO t3609 values (1) +end +GO + +DROP TABLE t3609 +Go + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + +insert into error_mapping.temp2 values(1) +INSERT INTO t3609 values (1) +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +DROP TABLE t3609 +Go + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +DROP TABLE t3609 +Go + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3609 +Go + + +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t3609 +Go + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t3609 +Go +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3609 +Go + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3609 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3609 +Go + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t3609(id int); +GO + + +CREATE TRIGGER t3609Trigger +ON t3609 +AFTER INSERT +AS +BEGIN + COMMIT TRANSACTION +END +GO + + + +begin try +select 1 +INSERT INTO t3609 values (1) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t3609 +Go + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/3701_3.out b/contrib/test/JDBC/sql_expected/3701_3.out new file mode 100644 index 00000000000..921d2020aa2 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3701_3.out @@ -0,0 +1,243 @@ +# Executing test ErrorHandling1 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'p3701', because it does not exist or you do not have permission.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Procedure doesn't exist +DROP PROC p3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'p3701', because it does not exist or you do not have permission.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +GO + + +begin try +select 1 +--Procedure doesn't exist +DROP PROC p3701; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +GO + + diff --git a/contrib/test/JDBC/sql_expected/3701_4.out b/contrib/test/JDBC/sql_expected/3701_4.out new file mode 100644 index 00000000000..b91c0556847 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3701_4.out @@ -0,0 +1,243 @@ +# Executing test ErrorHandling1 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the trigger 'tr3701', because it does not exist or you do not have permission.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Trigger doesn't exist +DROP TRIGGER tr3701 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the trigger 'tr3701', because it does not exist or you do not have permission.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +GO + + +begin try +select 1 +--Trigger doesn't exist +DROP TRIGGER tr3701 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +GO + + diff --git a/contrib/test/JDBC/sql_expected/3701_5.out b/contrib/test/JDBC/sql_expected/3701_5.out new file mode 100644 index 00000000000..1ab3e8be8e7 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3701_5.out @@ -0,0 +1,243 @@ +# Executing test ErrorHandling1 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the function 'fn3701', because it does not exist or you do not have permission.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Function doesn't exist +DROP FUNCTION fn3701; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the function 'fn3701', because it does not exist or you do not have permission.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +GO + + +begin try +select 1 +--Function doesn't exist +DROP FUNCTION fn3701; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +GO + + diff --git a/contrib/test/JDBC/sql_expected/3728_3727_1.out b/contrib/test/JDBC/sql_expected/3728_3727_1.out new file mode 100644 index 00000000000..847a67d02b1 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3728_3727_1.out @@ -0,0 +1,305 @@ +# Executing test ErrorHandling1 +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3728 +GO + + + +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t3728 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t3728 +GO + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3728)~~ + +~~ERROR (Message: 'NonExistingConstraint' is not a constraint.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3728 +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3728)~~ + +~~ERROR (Message: 'NonExistingConstraint' is not a constraint.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3728 +GO + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t3728(c1 int, c2 int) +GO +INSERT INTO t3728 VALUES(1, 11) +INSERT INTO t3728 VALUES(1, 111) +INSERT INTO t3728 VALUES(2, 22) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + + +begin try +select 1 +ALTER TABLE t3728 + DROP CONSTRAINT NonExistingConstraint +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t3728 +GO + + + diff --git a/contrib/test/JDBC/sql_expected/3729_1.out b/contrib/test/JDBC/sql_expected/3729_1.out new file mode 100644 index 00000000000..09ce7e63ed5 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3729_1.out @@ -0,0 +1,326 @@ +USE master +GO + +# Executing test ErrorHandling1 +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3729)~~ + +~~ERROR (Message: cannot drop function master_s3729.f3729() because other objects depend on it)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + + +create procedure ErrorHandling1 as +begin +DROP FUNCTION s3729.f3729 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 3729)~~ + +~~ERROR (Message: cannot drop function master_s3729.f3729() because other objects depend on it)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE SCHEMA s3729 +GO + +CREATE FUNCTION s3729.f3729 () +RETURNS INT +AS +BEGIN + RETURN 123 ; +END; +GO + +CREATE TABLE t3729(c1 int default s3729.f3729()) +GO + + + +begin try +select 1 +DROP FUNCTION s3729.f3729 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling2")~~ + +DROP TABLE t3729 +GO +DROP FUNCTION s3729.f3729 +GO +DROP SCHEMA s3729 +GO + + diff --git a/contrib/test/JDBC/sql_expected/3902_1.out b/contrib/test/JDBC/sql_expected/3902_1.out new file mode 100644 index 00000000000..c9a81164794 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3902_1.out @@ -0,0 +1,261 @@ +# Executing test ErrorHandling1 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +int +0 +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +~~START~~ +text +CURRENT BATCH TERMINATING ERROR +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +COMMIT; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +int +0 +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +~~START~~ +text +CURRENT BATCH TERMINATING ERROR +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +GO + + +begin try +select 1 +--Generate the error +COMMIT; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +GO + + diff --git a/contrib/test/JDBC/sql_expected/3903_1.out b/contrib/test/JDBC/sql_expected/3903_1.out new file mode 100644 index 00000000000..4d6cf02d505 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3903_1.out @@ -0,0 +1,267 @@ +# Executing test ErrorHandling1 +--Pre +GO + + + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + +--Pre +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +GO + + + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +int +0 +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +~~START~~ +text +CURRENT BATCH TERMINATING ERROR +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + + + +create procedure ErrorHandling1 as +begin +--Generate the error +ROLLBACK +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +int +0 +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +~~START~~ +text +CURRENT BATCH TERMINATING ERROR +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +GO + + + +begin try +select 1 +--Generate the error +ROLLBACK +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +GO + + diff --git a/contrib/test/JDBC/sql_expected/3930_1.out b/contrib/test/JDBC/sql_expected/3930_1.out new file mode 100644 index 00000000000..c3da5148b65 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/3930_1.out @@ -0,0 +1,330 @@ +# Executing test ErrorHandling1 +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + + +GO +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 3930)~~ + +~~ERROR (Message: The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + + +create procedure ErrorHandling1 as +begin +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 3930)~~ + +~~ERROR (Message: The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t3930_1(c1 int primary key, c2 int) +GO +CREATE TABLE t3930_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t3930_1(c1)) +GO +CREATE TABLE t3930 (a int) +GO + + +begin try +select 1 +begin tran + begin try + TRUNCATE TABLE t3930_1; + end try + begin catch + select xact_state(); + drop table t3930; + end catch +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling1")~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "errorhandling2")~~ + +DROP TABLE t3930_2 +GO +DROP TABLE t3930_1 +GO +DROP TABLE t3930 +GO diff --git a/contrib/test/JDBC/sql_expected/4708_1.out b/contrib/test/JDBC/sql_expected/4708_1.out new file mode 100644 index 00000000000..913051df70a --- /dev/null +++ b/contrib/test/JDBC/sql_expected/4708_1.out @@ -0,0 +1,293 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 4708)~~ + +~~ERROR (Message: Could not truncate object 'v4708' because it is not a table.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + + + +create procedure ErrorHandling1 as +begin +TRUNCATE TABLE v4708 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 4708)~~ + +~~ERROR (Message: Could not truncate object 'v4708' because it is not a table.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t4708(c1 int) +GO +CREATE VIEW v4708 +AS +SELECT c1 FROM t4708 +GO + + + + + +begin try +select 1 +TRUNCATE TABLE v4708 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP VIEW v4708 +GO +DROP TABLE t4708 +GO + + + + diff --git a/contrib/test/JDBC/sql_expected/4712_1.out b/contrib/test/JDBC/sql_expected/4712_1.out new file mode 100644 index 00000000000..deab64dc201 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/4712_1.out @@ -0,0 +1,311 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 4712)~~ + +~~ERROR (Message: Cannot truncate table 't4712_1' because it is being referenced by a FOREIGN KEY constraint.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + +create procedure ErrorHandling1 as +begin +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 4712)~~ + +~~ERROR (Message: Cannot truncate table 't4712_1' because it is being referenced by a FOREIGN KEY constraint.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t4712_1(c1 int primary key, c2 int) +GO +CREATE TABLE t4712_2(c1 int primary key, c2 int, constraint fk foreign key (c2) references t4712_1(c1)) +GO +INSERT INTO t4712_1 VALUES(1, 10) +INSERT INTO t4712_2 VALUES(100, 1) +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + + +begin try +select 1 +--CHECK constraint conflict, when adding constraint +TRUNCATE TABLE t4712_1 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t4712_2 +GO +DROP TABLE t4712_1 +GO + + diff --git a/contrib/test/JDBC/sql_expected/4901_1.out b/contrib/test/JDBC/sql_expected/4901_1.out new file mode 100644 index 00000000000..58b9d621384 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/4901_1.out @@ -0,0 +1,423 @@ + +-- simple batch start +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + +ALTER TABLE t4901 ADD c2 int NOT NULL +GO +~~ERROR (Code: 4901)~~ + +~~ERROR (Message: ALTER TABLE only allows columns to be added that can contain nulls, or have a DEFAULT definition specified, or the column being added is an identity or timestamp column, or alternatively if none of the previous conditions are satisfied the table must be empty to allow addition of this column. Column 'c2' cannot be added to non-empty table 't4901' because it does not satisfy these conditions.)~~ + +DROP TABLE t4901 +GO + + +begin transaction +GO +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + +ALTER TABLE t4901 ADD c2 int NOT NULL +GO +~~ERROR (Code: 4901)~~ + +~~ERROR (Message: ALTER TABLE only allows columns to be added that can contain nulls, or have a DEFAULT definition specified, or the column being added is an identity or timestamp column, or alternatively if none of the previous conditions are satisfied the table must be empty to allow addition of this column. Column 'c2' cannot be added to non-empty table 't4901' because it does not satisfy these conditions.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t4901 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't4901', because it does not exist or you do not have permission.)~~ + + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +ALTER TABLE t4901 ADD c2 int NOT NULL +end +GO + +DROP TABLE t4901 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + +insert into error_mapping.temp2 values(1) +ALTER TABLE t4901 ADD c2 int NOT NULL +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 4901)~~ + +~~ERROR (Message: ALTER TABLE only allows columns to be added that can contain nulls, or have a DEFAULT definition specified, or the column being added is an identity or timestamp column, or alternatively if none of the previous conditions are satisfied the table must be empty to allow addition of this column. Column 'c2' cannot be added to non-empty table 't4901' because it does not satisfy these conditions.)~~ + + +DROP TABLE t4901 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 4901)~~ + +~~ERROR (Message: ALTER TABLE only allows columns to be added that can contain nulls, or have a DEFAULT definition specified, or the column being added is an identity or timestamp column, or alternatively if none of the previous conditions are satisfied the table must be empty to allow addition of this column. Column 'c2' cannot be added to non-empty table 't4901' because it does not satisfy these conditions.)~~ + + +DROP TABLE t4901 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + + + +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4901 +GO + + + +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t4901 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t4901 +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + + + +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 4901)~~ + +~~ERROR (Message: ALTER TABLE only allows columns to be added that can contain nulls, or have a DEFAULT definition specified, or the column being added is an identity or timestamp column, or alternatively if none of the previous conditions are satisfied the table must be empty to allow addition of this column. Column 'c2' cannot be added to non-empty table 't4901' because it does not satisfy these conditions.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t4901 +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + + + +create procedure error_mapping.ErrorHandling1 as +begin +ALTER TABLE t4901 ADD c2 int NOT NULL +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 4901)~~ + +~~ERROR (Message: ALTER TABLE only allows columns to be added that can contain nulls, or have a DEFAULT definition specified, or the column being added is an identity or timestamp column, or alternatively if none of the previous conditions are satisfied the table must be empty to allow addition of this column. Column 'c2' cannot be added to non-empty table 't4901' because it does not satisfy these conditions.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t4901 +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t4901(c1 varchar(23)) +GO +INSERT INTO t4901 VALUES('a') +GO +~~ROW COUNT: 1~~ + + + + +begin try +select 1 +ALTER TABLE t4901 ADD c2 int NOT NULL +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t4901 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/4920_1.out b/contrib/test/JDBC/sql_expected/4920_1.out new file mode 100644 index 00000000000..ffa68792989 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/4920_1.out @@ -0,0 +1,287 @@ +# Executing test ErrorHandling1 +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t4920 +GO + + + + +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t4920 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t4920 +GO + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 4920)~~ + +~~ERROR (Message: ALTER TABLE failed because trigger 'NonExisting' on table 't4920' does not exist.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t4920 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + + +create procedure ErrorHandling1 as +begin +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 4920)~~ + +~~ERROR (Message: ALTER TABLE failed because trigger 'NonExisting' on table 't4920' does not exist.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t4920 +GO + + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t4920(c1 int) +GO + +CREATE TRIGGER tr_4920 ON t4920 +AFTER INSERT +AS +SELECT 1 +GO + + + +begin try +select 1 +ALTER TABLE t4920 + DISABLE TRIGGER NonExisting +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t4920 +GO + + + + diff --git a/contrib/test/JDBC/sql_expected/512_1.out b/contrib/test/JDBC/sql_expected/512_1.out new file mode 100644 index 00000000000..47439f4815c --- /dev/null +++ b/contrib/test/JDBC/sql_expected/512_1.out @@ -0,0 +1,276 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +~~ROW COUNT: 2~~ + + + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t512; +GO + + +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +~~ROW COUNT: 2~~ + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t512; +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +~~ROW COUNT: 2~~ + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t512; +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +~~ROW COUNT: 2~~ + + + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +int +~~ERROR (Code: 512)~~ + +~~ERROR (Message: Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t512; +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +~~ROW COUNT: 2~~ + + + +create procedure ErrorHandling1 as +begin +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~START~~ +int +~~ERROR (Code: 512)~~ + +~~ERROR (Message: Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t512; +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t512(c1 int); +INSERT INTO t512 VALUES(1), (5); +GO +~~ROW COUNT: 2~~ + + + +begin try +select 1 +--Generate the error +SELECT * FROM t512 WHERE c1 = (SELECT c1 FROM t512); +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t512; +GO + + diff --git a/contrib/test/JDBC/sql_expected/515_1.out b/contrib/test/JDBC/sql_expected/515_1.out new file mode 100644 index 00000000000..c855423f725 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/515_1.out @@ -0,0 +1,250 @@ +# Executing test ErrorHandling1 +--Pre +CREATE TABLE t515(c1 int not null); +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t515 +GO + + +--Pre +CREATE TABLE t515(c1 int not null); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t515 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +CREATE TABLE t515(c1 int not null); +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +DROP TABLE t515 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +CREATE TABLE t515(c1 int not null); +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 515)~~ + +~~ERROR (Message: Cannot insert the value NULL into column 'c1', table 'AdventureWorks2014.dbo.t515'; column does not allow nulls. INSERT fails.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +DROP TABLE t515 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +CREATE TABLE t515(c1 int not null); +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +INSERT INTO t515 VALUES(null); +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 515)~~ + +~~ERROR (Message: Cannot insert the value NULL into column 'c1', table 'AdventureWorks2014.dbo.t515'; column does not allow nulls. INSERT fails.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +DROP TABLE t515 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +CREATE TABLE t515(c1 int not null); +GO + + +begin try +select 1 +--Generate the error +INSERT INTO t515 VALUES(null); +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +DROP TABLE t515 +GO + + diff --git a/contrib/test/JDBC/sql_expected/517_1.out b/contrib/test/JDBC/sql_expected/517_1.out new file mode 100644 index 00000000000..63bd8885383 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/517_1.out @@ -0,0 +1,374 @@ + +-- simple batch start +GO + +SELECT DATEADD(YY,-300,getdate()) +GO +~~START~~ +datetime +~~ERROR (Code: 517)~~ + +~~ERROR (Message: Adding a value to a 'datetime' column caused an overflow.)~~ + +GO + + +begin transaction +GO +GO + +SELECT DATEADD(YY,-300,getdate()) +GO +~~START~~ +datetime +~~ERROR (Code: 517)~~ + +~~ERROR (Message: Adding a value to a 'datetime' column caused an overflow.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SELECT DATEADD(YY,-300,getdate()) +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + +insert into error_mapping.temp2 values(1) +SELECT DATEADD(YY,-300,getdate()) +GO +~~ROW COUNT: 1~~ + +~~START~~ +datetime +~~ERROR (Code: 517)~~ + +~~ERROR (Message: Adding a value to a 'datetime' column caused an overflow.)~~ + + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +datetime +~~ERROR (Code: 517)~~ + +~~ERROR (Message: Adding a value to a 'datetime' column caused an overflow.)~~ + + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~START~~ +datetime +~~ERROR (Code: 517)~~ + +~~ERROR (Message: Adding a value to a 'datetime' column caused an overflow.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT DATEADD(YY,-300,getdate()) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~START~~ +datetime +~~ERROR (Code: 517)~~ + +~~ERROR (Message: Adding a value to a 'datetime' column caused an overflow.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + +begin try +select 1 +SELECT DATEADD(YY,-300,getdate()) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +datetime +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/545_1.out b/contrib/test/JDBC/sql_expected/545_1.out new file mode 100644 index 00000000000..a58e8bbdc1b --- /dev/null +++ b/contrib/test/JDBC/sql_expected/545_1.out @@ -0,0 +1,310 @@ +# Executing test ErrorHandling1 + +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) +GO + + + + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON +INSERT INTO t545 (c2) VALUES(2) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +DROP TABLE t545 +GO + + + + + +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON +INSERT INTO t545 (c2) VALUES(2) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +DROP TABLE t545 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON +INSERT INTO t545 (c2) VALUES(2) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +DROP TABLE t545 +GO + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + +-- Next portion is for runtime error -- +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) +GO + + + + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON +INSERT INTO t545 (c2) VALUES(2) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 545)~~ + +~~ERROR (Message: Explicit value must be specified for identity column in table 't545' either when IDENTITY_INSERT is set to ON or when a replication user is inserting into a NOT FOR REPLICATION identity column.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +DROP TABLE t545 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 + +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) +GO + + + + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t545 ON +INSERT INTO t545 (c2) VALUES(2) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 545)~~ + +~~ERROR (Message: Explicit value must be specified for identity column in table 't545' either when IDENTITY_INSERT is set to ON or when a replication user is inserting into a NOT FOR REPLICATION identity column.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + + +DROP TABLE t545 +GO + + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + +-- Error classification is done -- +CREATE TABLE t545 +( + c1 int IDENTITY +,c2 int +) +GO + + + + + + +begin try +select 1 +SET IDENTITY_INSERT t545 ON +INSERT INTO t545 (c2) VALUES(2) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + + +DROP TABLE t545 +GO + + + + diff --git a/contrib/test/JDBC/sql_expected/550_1.out b/contrib/test/JDBC/sql_expected/550_1.out new file mode 100644 index 00000000000..754039a6a77 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/550_1.out @@ -0,0 +1,328 @@ +# Executing test ErrorHandling1 +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 550)~~ + +~~ERROR (Message: The attempted insert or update failed because the target view either specifies WITH CHECK OPTION or spans a view that specifies WITH CHECK OPTION and one or more rows resulting from the operation did not qualify under the CHECK OPTION constraint.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + + +create procedure ErrorHandling1 as +begin +INSERT INTO v550 (c2) VALUES(20) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 550)~~ + +~~ERROR (Message: The attempted insert or update failed because the target view either specifies WITH CHECK OPTION or spans a view that specifies WITH CHECK OPTION and one or more rows resulting from the operation did not qualify under the CHECK OPTION constraint.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t550 +( + c1 int IDENTITY +,c2 int +) +GO + +CREATE VIEW v550 +AS +SELECT c1, c2 FROM t550 WHERE c2 < 10 +WITH CHECK OPTION +GO + + + +begin try +select 1 +INSERT INTO v550 (c2) VALUES(20) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP VIEW v550 +GO + +DROP TABLE t550 +GO + + + + diff --git a/contrib/test/JDBC/sql_expected/556_1.out b/contrib/test/JDBC/sql_expected/556_1.out new file mode 100644 index 00000000000..bdbaabc4c11 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/556_1.out @@ -0,0 +1,591 @@ + +-- simple batch start +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +begin transaction +GO +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 208)~~ + +~~ERROR (Message: Invalid object name 't556'.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP PROC proc_556 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'proc_556', because it does not exist or you do not have permission.)~~ + +DROP TABLE t556 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't556', because it does not exist or you do not have permission.)~~ + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +INSERT INTO t556(id, results) +EXECUTE proc_556; +end +GO + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +insert into error_mapping.temp2 values(1) +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + +begin transaction +GO + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP PROC proc_556 +GO +DROP TABLE t556 +GO +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + + + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t556(id, results) +EXECUTE proc_556; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t556 ( + id int NOT NULL + , results varchar(50) NOT NULL +); +GO + +CREATE PROCEDURE proc_556 +AS +BEGIN + ALTER TABLE t556 ADD test_type INT; +END; +GO +INSERT INTO t556(id, results) +EXECUTE proc_556; +GO +~~ERROR (Code: 556)~~ + +~~ERROR (Message: INSERT EXEC failed because the stored procedure altered the schema of the target table.)~~ + + + + +begin try +select 1 +INSERT INTO t556(id, results) +EXECUTE proc_556; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP PROC proc_556 +GO +DROP TABLE t556 +GO + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/6401_1.out b/contrib/test/JDBC/sql_expected/6401_1.out new file mode 100644 index 00000000000..3487ba0697b --- /dev/null +++ b/contrib/test/JDBC/sql_expected/6401_1.out @@ -0,0 +1,275 @@ +# Executing test ErrorHandling1 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +--Post +GO + + + +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +--Pre +GO +begin transaction +GO +# Executing test ErrorHandling1 + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +--Post +GO + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 6401)~~ + +~~ERROR (Message: Cannot roll back myMispelledTran. No transaction or savepoint of that name was found.)~~ + +~~START~~ +int +0 +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +~~START~~ +text +CURRENT BATCH TERMINATING ERROR +~~END~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +--Pre +GO + + +create procedure ErrorHandling1 as +begin +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 6401)~~ + +~~ERROR (Message: Cannot roll back myMispelledTran. No transaction or savepoint of that name was found.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +--Post +GO + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +--Pre +GO + + +begin try +select 1 +--Generate the error +BEGIN TRAN; +SAVE TRAN mytran; +ROLLBACK TRAN myMispelledTran; +ROLLBACK; +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +1 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +--Post +GO + + + diff --git a/contrib/test/JDBC/sql_expected/8106_1.out b/contrib/test/JDBC/sql_expected/8106_1.out new file mode 100644 index 00000000000..f7368f629b1 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8106_1.out @@ -0,0 +1,389 @@ + +-- simple batch start +CREATE TABLE t8106(a int) +GO + + +SET IDENTITY_INSERT t8106 ON +GO +~~ERROR (Code: 8106)~~ + +~~ERROR (Message: Table 't8106' does not have the identity property. Cannot perform SET operation.)~~ + +DROP TABLE t8106 +GO + + +begin transaction +GO +CREATE TABLE t8106(a int) +GO + + +SET IDENTITY_INSERT t8106 ON +GO +~~ERROR (Code: 8106)~~ + +~~ERROR (Message: Table 't8106' does not have the identity property. Cannot perform SET operation.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +runtime error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP TABLE t8106 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the table 't8106', because it does not exist or you do not have permission.)~~ + + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +CREATE TABLE t8106(a int) +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SET IDENTITY_INSERT t8106 ON +end +GO + +DROP TABLE t8106 +GO + + +create table error_mapping.temp2 (a int) +GO + +CREATE TABLE t8106(a int) +GO + + +insert into error_mapping.temp2 values(1) +SET IDENTITY_INSERT t8106 ON +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 8106)~~ + +~~ERROR (Message: Table 't8106' does not have the identity property. Cannot perform SET operation.)~~ + + +DROP TABLE t8106 +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +CREATE TABLE t8106(a int) +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 8106)~~ + +~~ERROR (Message: Table 't8106' does not have the identity property. Cannot perform SET operation.)~~ + + +DROP TABLE t8106 +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +CREATE TABLE t8106(a int) +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8106 +GO + + + +CREATE TABLE t8106(a int) +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t8106 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t8106(a int) +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t8106 +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t8106(a int) +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 8106)~~ + +~~ERROR (Message: Table 't8106' does not have the identity property. Cannot perform SET operation.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t8106 +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t8106(a int) +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SET IDENTITY_INSERT t8106 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 8106)~~ + +~~ERROR (Message: Table 't8106' does not have the identity property. Cannot perform SET operation.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t8106 +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +CREATE TABLE t8106(a int) +GO + + + + +begin try +select 1 +SET IDENTITY_INSERT t8106 ON +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t8106 +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/8107_1.out b/contrib/test/JDBC/sql_expected/8107_1.out new file mode 100644 index 00000000000..f2592c2fba5 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8107_1.out @@ -0,0 +1,347 @@ +# Executing test ErrorHandling1 +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO +~~ROW COUNT: 1~~ + + + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO +~~ROW COUNT: 1~~ + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO +~~ROW COUNT: 1~~ + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO +~~ROW COUNT: 1~~ + + + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8107)~~ + +~~ERROR (Message: IDENTITY_INSERT is already ON for table 'AdventureWorks2014.dbo.t8107_1'. Cannot perform SET operation for table 't8107_2'.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO +~~ROW COUNT: 1~~ + + + + + +create procedure ErrorHandling1 as +begin +SET IDENTITY_INSERT t8107_2 ON +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8107)~~ + +~~ERROR (Message: IDENTITY_INSERT is already ON for table 'AdventureWorks2014.dbo.t8107_1'. Cannot perform SET operation for table 't8107_2'.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t8107_1(a int identity) +GO + +CREATE TABLE t8107_2(a int identity) +GO + +SET IDENTITY_INSERT t8107_1 ON +GO + +INSERT INTO t8107_1(a) VALUES(1) +GO +~~ROW COUNT: 1~~ + + + + + +begin try +select 1 +SET IDENTITY_INSERT t8107_2 ON +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +SET IDENTITY_INSERT t8107_1 OFF +GO + +DROP TABLE t8107_1 +GO + +DROP TABLE t8107_2 +GO + + + diff --git a/contrib/test/JDBC/sql_expected/8143_1.out b/contrib/test/JDBC/sql_expected/8143_1.out new file mode 100644 index 00000000000..922ce699ee8 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8143_1.out @@ -0,0 +1,250 @@ +# Executing test ErrorHandling1 + +GO + + + + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + +-- Next portion is for runtime error -- +GO + + + + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8143)~~ + +~~ERROR (Message: Parameter '@Resource' was supplied multiple times.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 + +GO + + + + + +create procedure ErrorHandling1 as +begin +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8143)~~ + +~~ERROR (Message: Parameter '@Resource' was supplied multiple times.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + + +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + +-- Error classification is done -- +GO + + + + + +begin try +select 1 +EXEC sp_releaseapplock @Resource = 'SomeResource', @Resource = 'SomeResource' +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + + +GO + + diff --git a/contrib/test/JDBC/sql_expected/8143_2.out b/contrib/test/JDBC/sql_expected/8143_2.out new file mode 100644 index 00000000000..c476e50d99a --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8143_2.out @@ -0,0 +1,250 @@ +# Executing test ErrorHandling1 + +GO + + + + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO + +GO + + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO + +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + +-- Next portion is for runtime error -- +GO + + + + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8143)~~ + +~~ERROR (Message: Parameter '@Resource' was supplied multiple times.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO + +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 + +GO + + + + + +create procedure ErrorHandling1 as +begin +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8143)~~ + +~~ERROR (Message: Parameter '@Resource' was supplied multiple times.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + + +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 + +-- Error classification is done -- +GO + + + + + +begin try +select 1 +EXEC sp_getapplock @Resource = 'SomeResource', @Resource = 'SomeResource', @LockMode = 'Shared' +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + + +GO + + diff --git a/contrib/test/JDBC/sql_expected/8152_1.out b/contrib/test/JDBC/sql_expected/8152_1.out new file mode 100644 index 00000000000..307bddc9a40 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8152_1.out @@ -0,0 +1,256 @@ +# Executing test ErrorHandling1 +CREATE TABLE t8152(a CHAR(5)) +GO + + + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8152 +GO + + + + +CREATE TABLE t8152(a CHAR(5)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t8152 +GO + + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t8152(a CHAR(5)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t8152 +GO + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t8152(a CHAR(5)) +GO + + + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8152)~~ + +~~ERROR (Message: String or binary data would be truncated.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t8152 +GO + + + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t8152(a CHAR(5)) +GO + + + + +create procedure ErrorHandling1 as +begin +INSERT INTO t8152(a) VALUES('123456') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8152)~~ + +~~ERROR (Message: String or binary data would be truncated.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t8152 +GO + + + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t8152(a CHAR(5)) +GO + + + + +begin try +select 1 +INSERT INTO t8152(a) VALUES('123456') +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +DROP TABLE t8152 +GO + + + + diff --git a/contrib/test/JDBC/sql_expected/8152_2.out b/contrib/test/JDBC/sql_expected/8152_2.out new file mode 100644 index 00000000000..c046e483a89 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8152_2.out @@ -0,0 +1,244 @@ +# Executing test ErrorHandling1 +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t2628 +GO + + +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t2628 +GO + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + +begin transaction +GO +# Executing test ErrorHandling1 + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +DROP TABLE t2628 +GO +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Next portion is for runtime error -- +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8152)~~ + +~~ERROR (Message: String or binary data would be truncated.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +DROP TABLE t2628 +GO + + +set xact_abort ON; +GO +# Executing test ErrorHandling10000000 +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + + + +create procedure ErrorHandling1 as +begin +INSERT INTO t2628 VALUES('1234') +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure ErrorHandling2 as +begin +exec ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec ErrorHandling2; +GO +~~ERROR (Code: 8152)~~ + +~~ERROR (Message: String or binary data would be truncated.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +DROP TABLE t2628 +GO + + + +set xact_abort OFF; +GO + +# Executing test ErrorHandling10000000 +-- Error classification is done -- +CREATE TABLE t2628(c1 int IDENTITY, c2 varchar(3)) +GO + + + + +begin try +select 1 +INSERT INTO t2628 VALUES('1234') +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure ErrorHandling1; +drop procedure ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'ErrorHandling2', because it does not exist or you do not have permission.)~~ + +DROP TABLE t2628 +GO + + diff --git a/contrib/test/JDBC/sql_expected/8159_1.out b/contrib/test/JDBC/sql_expected/8159_1.out new file mode 100644 index 00000000000..03c66025687 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8159_1.out @@ -0,0 +1,61 @@ + +-- simple batch start +GO + + +CREATE VIEW v8159(c1, c2) +AS +SELECT + 'a' AS c1 +GO +~~ERROR (Code: 8159)~~ + +~~ERROR (Message: 'v8159' has fewer columns than were specified in the column list.)~~ + +DROP VIEW v8159 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the view 'v8159', because it does not exist or you do not have permission.)~~ + + +SET XACT_ABORT ON; +GO + +begin transaction +GO +GO + + +CREATE VIEW v8159(c1, c2) +AS +SELECT + 'a' AS c1 +GO +~~ERROR (Code: 8159)~~ + +~~ERROR (Message: 'v8159' has fewer columns than were specified in the column list.)~~ + + +if (@@trancount > 0) select cast('Does not respect xact_abort flag' as text) else select cast('Respects xact_abort flag' as text) +GO +~~START~~ +text +Respects xact_abort flag +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +DROP VIEW v8159 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the view 'v8159', because it does not exist or you do not have permission.)~~ + + +SET XACT_ABORT OFF; +GO + + diff --git a/contrib/test/JDBC/sql_expected/8179_1.out b/contrib/test/JDBC/sql_expected/8179_1.out new file mode 100644 index 00000000000..f46cf0cdaf7 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/8179_1.out @@ -0,0 +1,378 @@ + +-- simple batch start +GO + + +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +GO +~~ERROR (Code: 8179)~~ + +~~ERROR (Message: Could not find prepared statement with handle 12345.)~~ + +GO + + +begin transaction +GO +GO + + +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +GO +~~ERROR (Code: 8179)~~ + +~~ERROR (Message: Could not find prepared statement with handle 12345.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + + +insert into error_mapping.temp2 values(1) +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 8179)~~ + +~~ERROR (Message: Could not find prepared statement with handle 12345.)~~ + + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 8179)~~ + +~~ERROR (Message: Could not find prepared statement with handle 12345.)~~ + + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 8179)~~ + +~~ERROR (Message: Could not find prepared statement with handle 12345.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~ERROR (Code: 8179)~~ + +~~ERROR (Message: Could not find prepared statement with handle 12345.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + +begin try +select 1 +DECLARE @P1 INT = 12345 +EXEC sp_execute @P1, 1 +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/9809_1.out b/contrib/test/JDBC/sql_expected/9809_1.out new file mode 100644 index 00000000000..2e6f6871e99 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/9809_1.out @@ -0,0 +1,384 @@ + +-- simple batch start +GO + + +SELECT CONVERT(DATE, '10-10-10', 140) +GO +~~START~~ +date +~~ERROR (Code: 9809)~~ + +~~ERROR (Message: The style 140 is not supported for conversions from varchar to date.)~~ + +GO + + +begin transaction +GO +GO + + +SELECT CONVERT(DATE, '10-10-10', 140) +GO +~~START~~ +date +~~ERROR (Code: 9809)~~ + +~~ERROR (Message: The style 140 is not supported for conversions from varchar to date.)~~ + + + +-- Below output is only applicable if query is special case. for example, CREATE/ALTER TRIGGER, CREATE/ALTER FUNCTION, CREATE/ALTER PROC, CREATE/ALTER VIEW etc +if (@@trancount > 0) select cast('compile time error' as text) else select cast('runtime error' as text) +GO +~~START~~ +text +compile time error +~~END~~ + + +if (@@trancount > 0) rollback tran +GO + +GO + + +-- simple batch end +GO + +create schema error_mapping; +GO +-- Next portion is to check if error is being raised during parse analysis phase +GO + +create table error_mapping.temp1 (a int) +GO + +GO + + +create procedure error_mapping.ErrorHandling1 as +begin +insert into error_mapping.temp1 values(1) +SELECT CONVERT(DATE, '10-10-10', 140) +end +GO + +GO + + +create table error_mapping.temp2 (a int) +GO + +GO + + +insert into error_mapping.temp2 values(1) +SELECT CONVERT(DATE, '10-10-10', 140) +GO +~~ROW COUNT: 1~~ + +~~START~~ +date +~~ERROR (Code: 9809)~~ + +~~ERROR (Message: The style 140 is not supported for conversions from varchar to date.)~~ + + +GO + + +-- Here we are assuming that error_mapping.ErrorHandling1 is created with no error +GO + +create table error_mapping.temp3 (a int) +GO + +GO + +insert into error_mapping.temp3 values(1) +exec error_mapping.ErrorHandling1; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +date +~~ERROR (Code: 9809)~~ + +~~ERROR (Message: The style 140 is not supported for conversions from varchar to date.)~~ + + +GO + + +if ((select count(*) from error_mapping.temp1) = 0 and (select count(*) from error_mapping.temp2) = 0 and (select count(*) from error_mapping.temp3) > 0) select cast('parse analysis phase error' as text) +GO + +drop procedure error_mapping.ErrorHandling1; +GO +drop table error_mapping.temp1; +drop table error_mapping.temp2; +drop table error_mapping.temp3; +GO + +-- Parse analysis phase end +GO + + +-- compile time error portion +-- Executing test error_mapping.ErrorHandling1 +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('Compile time error' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + + + +-- Checking xact_abort_flag for compile time error -- +set xact_abort ON; +GO +GO + +begin transaction +GO + + +-- Executing test error_mapping.ErrorHandling1 +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + + +GO +GO + +set xact_abort OFF; +GO + + +-- comiple time error portion end -- +-- Next portion is for runtime error -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~START~~ +date +~~ERROR (Code: 9809)~~ + +~~ERROR (Message: The style 140 is not supported for conversions from varchar to date.)~~ + +~~START~~ +text +STATEMENT TERMINATING ERROR +~~END~~ + +~~START~~ +int +1 +~~END~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +GO + + + +set xact_abort ON; +GO +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + +create procedure error_mapping.ErrorHandling1 as +begin +SELECT CONVERT(DATE, '10-10-10', 140) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +select @@trancount; +end +GO + +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +GO + +create procedure error_mapping.ErrorHandling2 as +begin +exec error_mapping.ErrorHandling1; +if @@error > 0 select cast('CURRENT BATCH TERMINATING ERROR' as text); +end +GO + +begin transaction; +GO +exec error_mapping.ErrorHandling2; +GO +~~START~~ +date +~~ERROR (Code: 9809)~~ + +~~ERROR (Message: The style 140 is not supported for conversions from varchar to date.)~~ + +declare @err int = @@error; if (@err > 0 and @@trancount > 0) select cast('BATCH ONLY TERMINATING' as text) else if @err > 0 select cast('BATCH TERMINATING\ txn rolledback' as text); +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +text +BATCH TERMINATING\ txn rolledback +~~END~~ + +GO + + + + +set xact_abort OFF; +GO + + +-- Error classification is done -- +-- Executing test error_mapping.ErrorHandling10000000 +GO + + + + +begin try +select 1 +SELECT CONVERT(DATE, '10-10-10', 140) +end try +begin catch + select xact_state(); +end catch +if @@trancount > 0 rollback transaction; +drop procedure error_mapping.ErrorHandling1; +drop procedure error_mapping.ErrorHandling2; +set xact_abort OFF; +set implicit_transactions OFF; +GO +~~START~~ +int +1 +~~END~~ + +~~START~~ +date +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling1', because it does not exist or you do not have permission.)~~ + +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: Cannot drop the procedure 'error_mapping.ErrorHandling2', because it does not exist or you do not have permission.)~~ + +GO + + + + +GO +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/BABEL-1944.out b/contrib/test/JDBC/sql_expected/BABEL-1944.out new file mode 100644 index 00000000000..dfd9363ecf1 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-1944.out @@ -0,0 +1,312 @@ +create table t1(a int); +go +create table t2(a int); +go +create procedure sp_trancount as +select @@trancount; +GO +create procedure sp_commit_no_begin as +insert into t1 values(3); +commit; +select * from t1; +GO +create procedure sp_rollback_no_begin as +insert into t1 values(4); +rollback; +select * from t1; +GO +create procedure sp_rollback_with_begin as +begin tran; +insert into t1 values(4); +rollback; +select * from t1; +go + +select @@trancount; +go +~~START~~ +int +0 +~~END~~ + +-- trancount inside normal EXEC should be same as outside +exec sp_trancount; +go +~~START~~ +int +0 +~~END~~ + +-- trancount inside INSERT-EXEC should be 1 more than outside +insert into t1 exec sp_trancount; +go +~~ROW COUNT: 1~~ + +select * from t1; +go +~~START~~ +int +1 +~~END~~ + +delete t1; +go +~~ROW COUNT: 1~~ + + +-- zero level - normal EXEC should succeed with warning for no BEGIN TRAN on the +-- COMMIT +exec sp_commit_no_begin; +go +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +~~START~~ +int +3 +~~END~~ + +-- zero level - INSERT-EXEC should fail with error message about the COMMIT +-- inside INSERT-EXEC must have a BEGIN TRAN +delete t1; +go +~~ROW COUNT: 1~~ + +insert into t2 exec sp_commit_no_begin; +go +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Cannot use the COMMIT statement within an INSERT-EXEC statement unless BEGIN TRANSACTION is used first.)~~ + +select count(*) from t1; +select count(*) from t2; +go +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- one level - should fail and abort that level of transaction +begin tran; +go +select @@trancount; +go +~~START~~ +int +1 +~~END~~ + +insert into t2 execute sp_commit_no_begin; +go +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Cannot use the COMMIT statement within an INSERT-EXEC statement unless BEGIN TRANSACTION is used first.)~~ + +select @@trancount; +go +~~START~~ +int +0 +~~END~~ + +select count(*) from t1; +select count(*) from t2; +go +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- previous level aborted, this should be the same as before +begin tran; +go +select @@trancount; +go +~~START~~ +int +1 +~~END~~ + +insert into t2 execute sp_commit_no_begin; +go +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Cannot use the COMMIT statement within an INSERT-EXEC statement unless BEGIN TRANSACTION is used first.)~~ + +select count(*) from t1; +select count(*) from t2; +go +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- normal EXEC with COMMIT is allowed with one level - should succeed with +-- unbalanced level warning from 1 to 0 +begin tran; +go +select @@trancount; +go +~~START~~ +int +1 +~~END~~ + +execute sp_commit_no_begin; +go +~~ROW COUNT: 1~~ + +~~START~~ +int +3 +~~END~~ + +~~ERROR (Code: 33554436)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +select count(*) from t1; +select count(*) from t2; +go +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- two levels - should succeed with unbalanced level warning from 2 to 1, and +-- should not send any Row tokens +select @@trancount; +go +~~START~~ +int +0 +~~END~~ + +begin tran; +go +begin tran; +go +select @@trancount; +go +~~START~~ +int +2 +~~END~~ + +insert into t2 execute sp_commit_no_begin; +go +~~ERROR (Code: 33554436)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 2 current count 1)~~ + +select count(*) from t1; +select count(*) from t2; +go +~~START~~ +int +2 +~~END~~ + +~~START~~ +int +2 +~~END~~ + +select @@trancount; +go +~~START~~ +int +1 +~~END~~ + +commit; +go + +-- INSERT-EXEC with ROLLBACK in one level of transaction - should fail and abort +-- that level of transaction +begin tran; +go +select @@trancount; +go +~~START~~ +int +1 +~~END~~ + +insert into t2 exec sp_rollback_no_begin; +go +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Cannot use the ROLLBACK statement within an INSERT-EXEC statement.)~~ + +select @@trancount; +go +~~START~~ +int +0 +~~END~~ + + +begin tran; +go +select @@trancount; +go +~~START~~ +int +1 +~~END~~ + +insert into t2 exec sp_rollback_with_begin; +go +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Cannot use the ROLLBACK statement within an INSERT-EXEC statement.)~~ + +select @@trancount; +go +~~START~~ +int +0 +~~END~~ + + +-- cleanup +drop table t1; +go +drop table t2; +go +drop procedure sp_trancount; +go +drop procedure sp_commit_no_begin; +go +drop procedure sp_rollback_no_begin; +go +drop procedure sp_rollback_with_begin; +go diff --git a/contrib/test/JDBC/sql_expected/BABEL-2079.out b/contrib/test/JDBC/sql_expected/BABEL-2079.out new file mode 100644 index 00000000000..bc385ad6b0c --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-2079.out @@ -0,0 +1,52 @@ +create schema error_mapping; +GO + +CREATE TABLE t3616(id int); +GO +CREATE TRIGGER t3616Trigger +ON t3616 +AFTER INSERT +AS +BEGIN + BEGIN TRY + BEGIN TRAN + INSERT INTO t3616 VALUES (2) + COMMIT TRAN + END TRY + BEGIN CATCH + END CATCH +END +GO + +create procedure error_mapping.ErrorHandling1 as +begin +INSERT INTO t3616 values (1) +if @@error > 0 select cast('STATEMENT TERMINATING ERROR' as text); +end +GO + +SET NOCOUNT ON +GO + +exec error_mapping.ErrorHandling1; +GO +~~ERROR (Code: 3616)~~ + +~~ERROR (Message: An error was raised during trigger execution. The batch has been aborted and the user transaction, if any, has been rolled back.)~~ + + +select * from t3616; +GO +~~START~~ +int +~~END~~ + + +drop table t3616; +GO + +drop procedure error_mapping.ErrorHandling1; +GO + +drop schema error_mapping; +GO diff --git a/contrib/test/JDBC/sql_expected/BABEL-2354.out b/contrib/test/JDBC/sql_expected/BABEL-2354.out new file mode 100644 index 00000000000..0935a7889bd --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-2354.out @@ -0,0 +1,23 @@ +CREATE TABLE employeeData( ID INT IDENTITY (1,1) PRIMARY KEY,Emp_First_name VARCHAR (50),Emp_Last_name VARCHAR (50),Emp_Salary INT) +GO + +CREATE TRIGGER updEmployeeData ON employeeData AFTER UPDATE AS + IF (COLUMNS_UPDATED() & 14) > 0 + BEGIN + PRINT 'Columns 3, 5 and 9 updated'; + END; +GO +~~ERROR (Code: 1088)~~ + +~~ERROR (Message: 'COLUMNS_UPDATED FUNC IN TRIGGER' is not currently supported in Babelfish)~~ + + +drop trigger updEmployeeData +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: trigger "updemployeedata" does not exist)~~ + + +drop table employeeData +GO diff --git a/contrib/test/JDBC/sql_expected/BABEL-2419.out b/contrib/test/JDBC/sql_expected/BABEL-2419.out new file mode 100644 index 00000000000..07d2e8b94fa --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-2419.out @@ -0,0 +1,37 @@ + +create table t_2419 (a varchar(10)); +insert into t_2419 values ('correct'); +select a 'alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij' +from t_2419 +order by alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij +go +~~ROW COUNT: 1~~ + +~~START~~ +varchar +correct +~~END~~ + + +select a 'alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij' +from t_2419 +order by [alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] +go +~~START~~ +varchar +correct +~~END~~ + + +select a 'alias_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij' +from t_2419 +order by [ALIAS_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] +go +~~START~~ +varchar +correct +~~END~~ + + +drop table t_2419; +go diff --git a/contrib/test/JDBC/sql_expected/BABEL-2432.out b/contrib/test/JDBC/sql_expected/BABEL-2432.out new file mode 100644 index 00000000000..49197a0abbd --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-2432.out @@ -0,0 +1,51 @@ +-- mixed-case column name. select via lowercase name +create table t2432 ([COL_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] int); +insert into t2432 values (1); +GO +~~ROW COUNT: 1~~ + + +select [col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] from t2432; +GO +~~START~~ +int +1 +~~END~~ + + +select col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij from t2432; +GO +~~START~~ +int +1 +~~END~~ + + +drop table t2432; +GO + +-- lowercase column name. select via mixed-case name +create table t2432_2 ([col_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] int); +insert into t2432_2 values (1); +GO +~~ROW COUNT: 1~~ + + +select [COL_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij] from t2432_2; +GO +~~START~~ +int +1 +~~END~~ + + +select COL_longer_than_63_0abcdefgij1abcdefgij2abcdefgij3abcdefgij4abcdefgij5abcdefgij6abcdefgij7abcdefgij8abcdefghij9abcdefghij from t2432_2; +GO +~~START~~ +int +1 +~~END~~ + + +drop table t2432_2; +GO diff --git a/contrib/test/JDBC/sql_expected/BABEL-2437.out b/contrib/test/JDBC/sql_expected/BABEL-2437.out new file mode 100644 index 00000000000..454980175f4 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-2437.out @@ -0,0 +1,16 @@ +use master; +go + +create schema babel_2437_schema; +go + +create table babel_2437_schema.t1 (a int primary key); +go + +create table babel_2437_schema.t2 (b int, FOREIGN KEY (b) REFERENCES babel_2437_schema.t1 (a) ON DELETE CASCADE ON UPDATE CASCADE); +go + +drop table if exists babel_2437_schema.t2; +drop table if exists babel_2437_schema.t1; +drop schema babel_2437_schema; +go diff --git a/contrib/test/JDBC/sql_expected/BABEL-EXTENDEDPROPERTY.out b/contrib/test/JDBC/sql_expected/BABEL-EXTENDEDPROPERTY.out new file mode 100644 index 00000000000..c68de5857ac --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-EXTENDEDPROPERTY.out @@ -0,0 +1,29 @@ +create table t1 (a int) +go + +-- For now, will always return empty result set because sys.extended_properties +-- is always empty before the support of sp_[add/drop/update]extendedproperty (BABEL-280) +select * FROM fn_listextendedproperty('COLUMN', 'schema', N'dbo', 'table', N't1', 'column', N'a'); +go +~~START~~ +varchar#!#varchar#!#varchar#!#sql_variant +~~END~~ + + +select * FROM fn_listextendedproperty(NULL, 'schema', N'dbo', 'table', N't1', NULL, NULL); +go +~~START~~ +varchar#!#varchar#!#varchar#!#sql_variant +~~END~~ + + +-- Failed query in BABEL-1784 +exec [sys].sp_columns_100 N't23',N'dbo',NULL,NULL,@ODBCVer=3,@fUsePattern=1; +go +~~START~~ +varchar#!#varchar#!#varchar#!#varchar#!#smallint#!#varchar#!#int#!#int#!#smallint#!#smallint#!#smallint#!#varchar#!#nvarchar#!#smallint#!#smallint#!#int#!#int#!#varchar#!#smallint#!#smallint#!#smallint#!#smallint#!#varchar#!#varchar#!#varchar#!#varchar#!#varchar#!#varchar#!#tinyint +~~END~~ + + +drop table t1 +go diff --git a/contrib/test/JDBC/sql_expected/BABEL-IDENTITY.out b/contrib/test/JDBC/sql_expected/BABEL-IDENTITY.out new file mode 100644 index 00000000000..cea0a23895d --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-IDENTITY.out @@ -0,0 +1,458 @@ +USE MASTER; +GO + +CREATE TABLE dbo.test_table1 (test_id INT IDENTITY, test_col1 INT); +go + +CREATE PROCEDURE insert_test_table1 + @id INT, + @val INT +AS + INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (@id, @val); +go + +SELECT @@IDENTITY; +go +~~START~~ +numeric + +~~END~~ + +SELECT SCOPE_IDENTITY(); +go +~~START~~ +numeric + +~~END~~ + +INSERT INTO dbo.test_table1 (test_col1) VALUES (10); +go +~~ROW COUNT: 1~~ + +SELECT @@IDENTITY; +go +~~START~~ +numeric +1 +~~END~~ + +SELECT SCOPE_IDENTITY(); +go +~~START~~ +numeric +1 +~~END~~ + +SELECT @@SERVERNAME; +go +~~START~~ +nvarchar +BABELFISH +~~END~~ + +-- Expect an error +INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (2, 10); +go +~~ERROR (Code: 156008580)~~ + +~~ERROR (Message: cannot insert into column "test_id")~~ + + +SET IDENTITY_INSERT dbo.test_table1 ON; +go + +-- Test custom insert +EXECUTE insert_test_table1 2, 10; +go +~~ROW COUNT: 1~~ + + +-- Insert a non-sequential max identity value +EXECUTE insert_test_table1 10, 10; +go +~~ROW COUNT: 1~~ + + +-- Insert a lesser identity value +EXECUTE insert_test_table1 5, 10; +go +~~ROW COUNT: 1~~ + + +-- Set to off. Notice we're not specifying the schema this time +SET IDENTITY_INSERT test_table1 OFF; +go + +-- Verify the identity sequence value is updated to the max value +INSERT INTO dbo.test_table1 (test_col1) VALUES (11); +go +~~ROW COUNT: 1~~ + +INSERT INTO dbo.test_table1 (test_col1) VALUES (12); +go +~~ROW COUNT: 1~~ + + +SELECT * FROM dbo.test_table1; +go +~~START~~ +int#!#int +1#!#10 +2#!#10 +10#!#10 +5#!#10 +11#!#11 +12#!#12 +~~END~~ + + +-- Expect an error. Verify IDENTITY_INSERT set off +INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (2, 10); +go +~~ERROR (Code: 156008580)~~ + +~~ERROR (Message: cannot insert into column "test_id")~~ + + +-- Set to table then drop it. Should implicitly turn IDENTITY_INSERT off +SET IDENTITY_INSERT dbo.test_table1 ON; +go +DROP TABLE test_table1; +go + +-- Create a table with the same name +CREATE TABLE dbo.test_table1 (test_id INT IDENTITY, test_col1 INT); +go + +-- Try to insert. Expect an error. Same name but different OID +INSERT INTO dbo.test_table1 (test_id, test_col1) VALUES (2, 10); +go +~~ERROR (Code: 156008580)~~ + +~~ERROR (Message: cannot insert into column "test_id")~~ + + +-- Expect errors +SET IDENTITY_INSERT test_table2 ON; +go +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "test_table2" does not exist)~~ + +SET IDENTITY_INSERT fake_schema.test_table1 ON; +go +~~ERROR (Code: 1411)~~ + +~~ERROR (Message: schema "fake_schema" does not exist)~~ + +SET IDENTITY_INSERT fake_db.dbo.test_table1 ON; +go +~~ERROR (Code: 1088)~~ + +~~ERROR (Message: cross-database references are not implemented: "fake_db.dbo.test_table1")~~ + + +CREATE TABLE dbo.test_table2 (test_id INT IDENTITY(7,2), test_col1 INT); +go + +-- Expect error. Set IDENTITY_INSERT to a table then try setting it to another +SET IDENTITY_INSERT dbo.test_table1 ON; +go +SET IDENTITY_INSERT dbo.test_table2 ON; +go +~~ERROR (Code: 16777410)~~ + +~~ERROR (Message: IDENTITY_INSERT is already ON for table 'jdbc_testdb.dbo.test_table1')~~ + +SET IDENTITY_INSERT dbo.test_table1 OFF; +go +INSERT INTO dbo.test_table2 (test_col1) VALUES (13); +go +~~ROW COUNT: 1~~ + +INSERT INTO dbo.test_table2 (test_col1) VALUES (108); +go +~~ROW COUNT: 1~~ + +SELECT @@IDENTITY; +go +~~START~~ +numeric +9 +~~END~~ + +SELECT SCOPE_IDENTITY(); +go +~~START~~ +numeric +9 +~~END~~ + + +SELECT * FROM dbo.test_table2; +go +~~START~~ +int#!#int +7#!#13 +9#!#108 +~~END~~ + + +-- Expect error. Cannot set IDENTITY_INSERT to table without identity property +CREATE TABLE dbo.test_table3 (test_id INT, test_col1 INT); +go + +SET IDENTITY_INSERT dbo.test_table3 ON; +go +~~ERROR (Code: 50360452)~~ + +~~ERROR (Message: Table 'dbo.test_table3' does not have the identity property. Cannot perform SET operation.)~~ + + +-- Test INSERT with default target list that omits identity columns +CREATE TABLE dbo.employees +(person_id int IDENTITY PRIMARY KEY, firstname nvarchar(20), lastname nvarchar(30), salary money); +go + +INSERT INTO employees VALUES (N'Neil', N'Armstrong', 11236.9898); +go +~~ROW COUNT: 1~~ + + +SELECT * FROM dbo.employees; +go +~~START~~ +int#!#nvarchar#!#nvarchar#!#money +1#!#Neil#!#Armstrong#!#11236.9898 +~~END~~ + + +-- Test identity insert with multiple columns +SET IDENTITY_INSERT dbo.employees ON; +go + +CREATE PROCEDURE insert_employees + @id INT, + @first TEXT, + @last TEXT, + @salary NUMERIC(18,4) +AS + INSERT INTO dbo.employees (person_id, firstname, lastname, salary) VALUES (@id, @first, @last, @salary); +go + +EXEC insert_employees 5, N'Buzz', N'Aldrin', 11236.9898; +go +~~ROW COUNT: 1~~ + + +SELECT @@IDENTITY; +go +~~START~~ +numeric +5 +~~END~~ + + +-- Expect Errors. Cannot insert without explicit identity column value +INSERT INTO employees VALUES (N'Michael', N'Collins', 11236.9898); +go +~~ERROR (Code: 50360452)~~ + +~~ERROR (Message: Explicit value must be specified for identity column in table 'employees' when IDENTITY_INSERT is set to ON)~~ + + +INSERT INTO employees (firstname, lastname, salary) VALUES (N'Michael', N'Collins', 11236.9898); +go +~~ERROR (Code: 50360452)~~ + +~~ERROR (Message: Explicit value must be specified for identity column in table 'employees' when IDENTITY_INSERT is set to ON)~~ + + +SET IDENTITY_INSERT dbo.employees OFF; +go + +INSERT INTO employees VALUES (N'Michael', N'Collins', 11236.9898); +go +~~ROW COUNT: 1~~ + + +SELECT * FROM dbo.employees; +go +~~START~~ +int#!#nvarchar#!#nvarchar#!#money +1#!#Neil#!#Armstrong#!#11236.9898 +5#!#Buzz#!#Aldrin#!#11236.9898 +6#!#Michael#!#Collins#!#11236.9898 +~~END~~ + + +-- Test Camel Case +CREATE TABLE [dbo].[Test_Table1]([Test_Id] INT IDENTITY, test_col1 INT); +go +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "test_table1" already exists)~~ + + +SET IDENTITY_INSERT [Test_Table1] ON; +go + +CREATE PROCEDURE insert_test_table_c + @id INT, + @val INT +AS + INSERT INTO [dbo].[Test_Table1] ([Test_Id], test_col1) VALUES (@id, @val); +go + +CREATE PROCEDURE insert_test_table_c_default + @val INT +AS + INSERT INTO [dbo].[Test_Table1] (test_col1) VALUES (@val); +go + +EXEC insert_test_table_c 1, 10; +go +~~ROW COUNT: 1~~ + + +EXEC insert_test_table_c 5, 20; +go +~~ROW COUNT: 1~~ + + +-- Expect error. Insert restriction +EXEC insert_test_table_c_default 30; +go +~~ERROR (Code: 50360452)~~ + +~~ERROR (Message: Explicit value must be specified for identity column in table 'test_table1' when IDENTITY_INSERT is set to ON)~~ + +-- Expect errors. Not matching case +SET IDENTITY_INSERT Test_Table1 ON; +go +SET IDENTITY_INSERT [tEst_tAble1] ON; +go +SET IDENTITY_INSERT [dbo].[Test_Table1] ON; +go +INSERT INTO [dbo].[Test_Table1] (test_id, test_col1) VALUES (10, 30); +go +~~ROW COUNT: 1~~ + + +-- Set to off and verify table +SET IDENTITY_INSERT [dbo].[Test_Table1] OFF; +go + +EXEC insert_test_table_c_default 30; +go +~~ROW COUNT: 1~~ + + +SELECT * FROM [Test_Table1]; +go +~~START~~ +int#!#int +1#!#10 +5#!#20 +10#!#30 +11#!#30 +~~END~~ + + +-- Test updating negative increment +CREATE TABLE dbo.t_neg_inc_1(id INT IDENTITY(1, -1), col1 INT); +go + +CREATE PROCEDURE insert_default_neg_inc_1 + @val INT +AS BEGIN + INSERT INTO dbo.t_neg_inc_1(col1) VALUES (@val); +END; +go + +CREATE PROCEDURE insert_id_neg_inc_1 + @id INT, + @val INT +AS BEGIN + SET IDENTITY_INSERT t_neg_inc_1 ON; + INSERT INTO dbo.t_neg_inc_1(id, col1) VALUES (@id, @val); + SET IDENTITY_INSERT t_neg_inc_1 OFF; +END; +go + +EXEC insert_default_neg_inc_1 10; +go +~~ROW COUNT: 1~~ + + +EXEC insert_default_neg_inc_1 20; +go +~~ROW COUNT: 1~~ + + +EXEC insert_id_neg_inc_1 -5, 30; +go +~~ROW COUNT: 1~~ + + +EXEC insert_default_neg_inc_1 40; +go +~~ROW COUNT: 1~~ + + +EXEC insert_id_neg_inc_1 5, 50; +go +~~ROW COUNT: 1~~ + + +EXEC insert_default_neg_inc_1 60; +go +~~ROW COUNT: 1~~ + + +SELECT * FROM t_neg_inc_1; +go +~~START~~ +int#!#int +1#!#10 +0#!#20 +-5#!#30 +-6#!#40 +5#!#50 +-7#!#60 +~~END~~ + + +-- Test get id max/min helper functions +SELECT sys.get_min_id_from_table(';drop table master_dbo.test_table1;', 'master_dbo', 'test_table1'); +GO +~~START~~ +bigint +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: column ";drop table master_dbo.test_table1;" does not exist)~~ + + +SELECT sys.get_max_id_from_table('test_id', ';drop table master_dbo', 'test_table1;'); +GO +~~START~~ +bigint +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: relation ";drop table master_dbo.test_table1;" does not exist)~~ + + +-- Clean up +DROP PROCEDURE insert_test_table1, +insert_employees, +insert_test_table_c, +insert_test_table_c_default, +insert_default_neg_inc_1, +insert_id_neg_inc_1; +go +DROP TABLE dbo.test_table1, +dbo.test_table2, +dbo.test_table3, +dbo.employees, +dbo.t_neg_inc_1; +go diff --git a/contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN-PREPEXEC.out b/contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN-PREPEXEC.out new file mode 100644 index 00000000000..b9d3b095199 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN-PREPEXEC.out @@ -0,0 +1,128 @@ +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +CREATE TABLE impl_txn_prepexec_table (a varchar(15) UNIQUE, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) + +prepst#!#INSERT INTO impl_txn_prepexec_table VALUES (?, ?, ?, ?, ?, ?, ?)#!#varchar|-|a|-|Apple#!#nvarchar|-|b|-|red#!#int|-|c|-|1#!#char|-|d|-|Delhi#!#nchar|-|e|-|Sad😞#!#datetime|-|f|-|2000-12-13 12:58:23.123#!#numeric|-|g|-|123.1|-|4|-|1 +~~ROW COUNT: 1~~ + + +SET IMPLICIT_TRANSACTIONS ON + +SELECT @@TRANCOUNT +~~START~~ +int +0 +~~END~~ + +prepst#!#SELECT * FROM impl_txn_prepexec_table WHERE a = ? AND b = ? AND c = ?#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|1 +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +SELECT @@TRANCOUNT +~~START~~ +int +1 +~~END~~ + +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +~~START~~ +int +0 +~~END~~ + +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|2 +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +SELECT @@TRANCOUNT +~~START~~ +int +1 +~~END~~ + +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +~~START~~ +int +0 +~~END~~ + +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|green#!#int|-|p3|-|1 +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +SELECT @@TRANCOUNT +~~START~~ +int +1 +~~END~~ + +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +~~START~~ +int +0 +~~END~~ + +prepst#!#SELECT 12, 34, 56 FROM (SELECT * FROM impl_txn_prepexec_table WHERE a = ? AND b = ? AND c = ?) AS dummy_table#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|1 +~~START~~ +int#!#int#!#int +12#!#34#!#56 +~~END~~ + +SELECT @@TRANCOUNT +~~START~~ +int +1 +~~END~~ + +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +~~START~~ +int +0 +~~END~~ + +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|red#!#int|-|p3|-|2 +~~START~~ +int#!#int#!#int +~~END~~ + +SELECT @@TRANCOUNT +~~START~~ +int +1 +~~END~~ + +IF @@TRANCOUNT > 0 COMMIT + +SELECT @@TRANCOUNT +~~START~~ +int +0 +~~END~~ + +prepst#!#exec#!#varchar|-|p1|-|Apple#!#nvarchar|-|p2|-|green#!#int|-|p3|-|1 +~~START~~ +int#!#int#!#int +~~END~~ + +SELECT @@TRANCOUNT +~~START~~ +int +1 +~~END~~ + +IF @@TRANCOUNT > 0 COMMIT + +SET IMPLICIT_TRANSACTIONS OFF +DROP TABLE impl_txn_prepexec_table +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; diff --git a/contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN.out b/contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN.out new file mode 100644 index 00000000000..6e2551bfab0 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN.out @@ -0,0 +1,564 @@ +-- Setup +CREATE TABLE implicit_tran_table (a int) +GO + +INSERT INTO implicit_tran_table VALUES (10) +GO +~~ROW COUNT: 1~~ + + +SET IMPLICIT_TRANSACTIONS ON +GO + +-- Select from table should start implicit transaction +SELECT @@TRANCOUNT +SELECT * FROM implicit_tran_table +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +10 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +SELECT @@TRANCOUNT +DECLARE @implicit_tran_table_var int +SELECT @implicit_tran_table_var = a FROM implicit_tran_table +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Select from table variable should start implicit transaction +SELECT @@TRANCOUNT +DECLARE @implicit_tran_table_var TABLE (col1 VARCHAR(10)); +SELECT * FROM @implicit_tran_table_var +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +varchar +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Select not from a table should not start implicit transaction +SELECT @@TRANCOUNT +SELECT 123 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +123 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +SELECT @@TRANCOUNT +DECLARE @implicit_tran_table_var int +SELECT @implicit_tran_table_var = 1234 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- BABEL-1869 +-- 2-Column select should not start implicit transaction +SELECT @@TRANCOUNT +SELECT 1, 2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +SELECT @@TRANCOUNT +SELECT 1, 2, 3 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int#!#int#!#int +1#!#2#!#3 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- Select from table in subquery should start implicit transaction +SELECT @@TRANCOUNT +SELECT (select count(*) from implicit_tran_table) +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +SELECT @@TRANCOUNT +SELECT 1, 2 FROM (SELECT * FROM implicit_tran_table) as dummy_table +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int#!#int +1#!#2 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Select to call a function should not start implicit transaction +SELECT @@TRANCOUNT +SELECT @@ERROR +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +/* + * DMLs should start implicit transaction + * Note: Did not add test for MERGE since + * we do not support it (BABEL-877) + */ +SELECT @@TRANCOUNT +INSERT INTO implicit_tran_table VALUES (11) +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + + +SELECT @@TRANCOUNT +UPDATE implicit_tran_table SET a = 100 WHERE a = 10 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + + +SELECT @@TRANCOUNT +DELETE FROM implicit_tran_table WHERE a = 100 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + + +-- Create table should start implicit transaction +SELECT @@TRANCOUNT +CREATE TABLE implicit_tran_table2 (c smallint) +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- BABEL-1870 +-- SELECT ... INTO should start implicit transaction +-- Note: We internally convert this to CREATE TABLE AS +SELECT @@TRANCOUNT +SELECT * INTO dummy_table FROM implicit_tran_table2 +SELECT @@TRANCOUNT +DROP TABLE dummy_table +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Alter table should start implicit transaction +SELECT @@TRANCOUNT; +ALTER TABLE implicit_tran_table2 ADD CONSTRAINT default_c DEFAULT 99 FOR c +SELECT @@TRANCOUNT; +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- truncate table should start implicit transaction +SELECT @@TRANCOUNT +TRUNCATE TABLE implicit_tran_table2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Drop table should start implicit transaction +SELECT @@TRANCOUNT +DROP TABLE implicit_tran_table2 +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Create procedure should start implicit transaction +SELECT @@TRANCOUNT +GO +~~START~~ +int +0 +~~END~~ + +CREATE PROCEDURE implicit_tran_proc + AS + BEGIN + SELECT 'Select inside a procedure' + END +GO +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +1 +~~END~~ + + + + +/* + * Alter procedure should start implicit transaction + * Note: Did not add test for ALTER PROCEDURE since + * we do not support it (BABEL-442) + */ +-- Drop procedure should start implicit transaction +SELECT @@TRANCOUNT +DROP PROCEDURE implicit_tran_proc +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Begin transaction should start implicit transaction +SELECT @@TRANCOUNT +BEGIN TRANSACTION +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +2 +~~END~~ + + +-- Create database should not start implicit transaction +SELECT @@TRANCOUNT +CREATE DATABASE implicit_tran_db +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- Drop database should not start implicit transaction +SELECT @@TRANCOUNT +DROP DATABASE implicit_tran_db +SELECT @@TRANCOUNT +IF @@TRANCOUNT > 0 COMMIT +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +/* + * Declare cursor should start an implicit transaction + */ +SELECT @@TRANCOUNT; +DECLARE implicit_tran_cursor CURSOR FOR SELECT * FROM implicit_tran_table; +SELECT @@TRANCOUNT; +DEALLOCATE implicit_tran_cursor; +IF @@TRANCOUNT > 0 COMMIT; +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +SELECT @@TRANCOUNT; +DECLARE implicit_tran_cursor CURSOR FOR SELECT 9876; +SELECT @@TRANCOUNT; +DEALLOCATE implicit_tran_cursor; +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + + +-- Open and fetch should start implicit transaction +-- Close and deallocate should not start implicit transaction +DECLARE implicit_tran_cursor CURSOR FOR SELECT * FROM implicit_tran_table; +DECLARE @val INT; +IF @@TRANCOUNT > 0 COMMIT; +SELECT @@TRANCOUNT; +OPEN implicit_tran_cursor; +SELECT @@TRANCOUNT; +IF @@TRANCOUNT > 0 COMMIT; +SELECT @@TRANCOUNT; +FETCH FROM implicit_tran_cursor INTO @val; +SELECT @@TRANCOUNT; +IF @@TRANCOUNT > 0 COMMIT; +SELECT @@TRANCOUNT; +CLOSE implicit_tran_cursor; +SELECT @@TRANCOUNT; +DEALLOCATE implicit_tran_cursor; +SELECT @@TRANCOUNT; +GO +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +1 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + +~~START~~ +int +0 +~~END~~ + + +-- Cleanup +SET IMPLICIT_TRANSACTIONS OFF +GO + +DROP TABLE implicit_tran_table +GO diff --git a/contrib/test/JDBC/sql_expected/TestAuth.out b/contrib/test/JDBC/sql_expected/TestAuth.out new file mode 100644 index 00000000000..343b762932b --- /dev/null +++ b/contrib/test/JDBC/sql_expected/TestAuth.out @@ -0,0 +1,40 @@ +#database name, username and password should not exceed 128 characters +java_auth#!#database|-|11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +~~ERROR (Code: 1283)~~ + +~~ERROR (Message: database "111111111111111111111111111111111111111111111111111111111111111" does not exist ClientConnectionId:26480caa-93c1-42d0-9a24-38f36eeebb5e)~~ + +java_auth#!#database|-|111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 +~~ERROR (Code: 0)~~ + +~~ERROR (Message: The databaseName property exceeds the maximum number of 128 characters.)~~ + +java_auth#!#user|-|11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +~~ERROR (Code: 514)~~ + +~~ERROR (Message: role "111111111111111111111111111111111111111111111111111111111111111" does not exist ClientConnectionId:9914082d-a9a3-4857-959b-029461beae5f)~~ + +java_auth#!#user|-|111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 +~~ERROR (Code: 0)~~ + +~~ERROR (Message: The user property exceeds the maximum number of 128 characters.)~~ + +#not sure why any password is accepted during authentication through cloud desktop +#This test should throw error but from cloud desktop a connection is successfully established +#java_auth#!#password|-|11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +java_auth#!#password|-|111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 +~~ERROR (Code: 0)~~ + +~~ERROR (Message: The password property exceeds the maximum number of 128 characters.)~~ + +java_auth#!#others|-|packetSize=0 +~~SUCCESS~~ +java_auth#!#others|-|packetSize=-1 +~~SUCCESS~~ +java_auth#!#others|-|packetSize=4096 +~~SUCCESS~~ +java_auth#!#database|-|test1 SELECT 1 +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: database "test1 SELECT 1" does not exist ClientConnectionId:02c14f23-c21c-435f-8c00-f2b823b83437)~~ + diff --git a/contrib/test/JDBC/sql_expected/TestErrorsWithTryCatch.out b/contrib/test/JDBC/sql_expected/TestErrorsWithTryCatch.out new file mode 100644 index 00000000000..0f8b51ca03e --- /dev/null +++ b/contrib/test/JDBC/sql_expected/TestErrorsWithTryCatch.out @@ -0,0 +1,1702 @@ +CREATE TABLE ErrorWithTryCatchTable (a varchar(15) UNIQUE NOT NULL, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) +GO + +-- setup for "invalid characters found: cannot cast value "%s" to money" error +CREATE TABLE t293_1(a money, b int); +GO + +INSERT INTO t293_1(a, b) values ($100, 1), ($101, 2); +GO +~~ROW COUNT: 2~~ + + +-- setup for error "column \"%s\" of relation \"%s\" is a generated column" error +CREATE TABLE t1752_2(c1 INT, c2 INT, c3 as c1*c2) +GO + +-- setup for "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" error +CREATE TABLE t141_2(c1 int, c2 int); +GO + +-- Error: duplicate key value violates unique constraint +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: duplicate key value violates unique constraint +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + + + + +-- Error: check constraint violation +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: check constraint violation +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET g = 101.4 WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + + + + + + + +-- Error: not null constraint violation +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: not null constraint violation +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE ErrorWithTryCatchTable SET c = NULL WHERE a = 1; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + + + + + + + +-- Error: creating an existing table +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: creating an existing table +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE ErrorWithTryCatchTable (a int); + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + + + + + + + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_2" is a generated column)~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_2" is a generated column)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_2 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + + +-- Error: "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_2; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_2; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: "value for domain tinyint violates check constraint "tinyint_check"" +-- Simple error inside try-catch +BEGIN TRY + SELECT xact_state(); + DECLARE @a tinyint = 1000; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~START~~ +smallint +0 +~~END~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Simple batch with try catch +BEGIN TRY + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Simple batch with transaction inside try-catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Simple procedure inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;';; +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +0 +~~END~~ + +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Transaction inside try-catch but not inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Transaction inside procedure but not inside try-catch +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; + COMMIT TRAN +END +GO +BEGIN TRY + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +0 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- Transaction inside try-catch and inside procedure +CREATE PROCEDURE errorWithTryCatchProc1 +AS +BEGIN + BEGIN TRAN + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE t293_1 SET a = convert(money, ''string'') WHERE b > 1;'; + COMMIT TRAN +END +GO +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + EXEC errorWithTryCatchProc1; +END TRY +BEGIN CATCH + SELECT xact_state(); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +-1 +~~END~~ + +~~ERROR (Code: 4)~~ + +~~ERROR (Message: Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.)~~ + +IF @@trancount > 0 ROLLBACK TRAN; +GO +DROP PROCEDURE errorWithTryCatchProc1 +GO +select * from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- valid INSERT inside catch after an error +-- Simple batch with try catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Orange', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select a from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar +Apple +Orange +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + +-- invalid INSERT inside catch after an error +-- Simple batch with try catch +BEGIN TRY + BEGIN TRAN + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END TRY +BEGIN CATCH + SELECT xact_state(); + INSERT INTO ErrorWithTryCatchTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +END CATCH; +GO +~~START~~ +smallint +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +smallint +1 +~~END~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "errorwithtrycatchtable_a_key")~~ + +select a from ErrorWithTryCatchTable ORDER BY c +GO +~~START~~ +varchar +Apple +~~END~~ + +truncate table ErrorWithTryCatchTable +GO + + +DROP TABLE ErrorWithTryCatchTable +GO + +-- cleanup for "invalid characters found: cannot cast value "%s" to money" error +DROP TABLE t293_1; +GO + +-- cleanup for error "column \"%s\" of relation \"%s\" is a generated column" error +DROP TABLE t1752_2 +GO + +-- cleanup for "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" error +DROP TABLE t141_2; +GO + +while (@@trancount > 0) commit tran; +GO + diff --git a/contrib/test/JDBC/sql_expected/TestSimpleErrors.out b/contrib/test/JDBC/sql_expected/TestSimpleErrors.out new file mode 100644 index 00000000000..dfbbf262fd2 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/TestSimpleErrors.out @@ -0,0 +1,4844 @@ +CREATE TABLE simpleErrorTable (a varchar(15) UNIQUE NOT NULL, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) +GO + +-- setup for "data out of range for datetime" error +CREATE TABLE t517_1(a datetime); +GO + +-- setup for "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" error +CREATE TABLE t141_1(c1 int, c2 int); +GO + +-- setup for "column \"%s\" of relation \"%s\" is a generated column" +CREATE TABLE t1752_1(c1 INT, c2 INT, c3 as c1*c2) +GO + +if @@trancount > 0 commit tran; +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1 +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 0 current count 1)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + declare @err int = @@error; if @err = 0 select 0 else select 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 0 current count 1)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE simpleErrorTable SET c = NULL WHERE c = 1; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 0 current count 1)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 1 current count 0)~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: creating an existing table +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +CREATE TABLE simpleErrorTable (a int); +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +commit tran; +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +rollback tran; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +rollback tran sp1; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK TO SAVEPOINT can only be used in transaction blocks)~~ + +rollback tran; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran; +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +commit tran; +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 266)~~ + +~~ERROR (Message: Transaction count after execution indicates a mismatch number of BEGIN and COMMIT statements. Previous count 0 current count 1)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran sp1; +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK TO SAVEPOINT can only be used in transaction blocks)~~ + +rollback tran; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +begin tran +GO +DECLARE @a tinyint = 1000; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +begin tran +GO +DECLARE @a tinyint = 1000; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +select @@trancount; +GO +~~START~~ +int +0 +~~END~~ + + +if @@trancount > 0 commit tran; +GO + +-- Error: value for domain tinyint violates check constraint "tinyint_check" +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DECLARE @a tinyint = 1000; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran sp1; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK TO SAVEPOINT can only be used in transaction blocks)~~ + +rollback tran; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +commit tran; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +~~ROW COUNT: 1~~ + +commit tran; +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- cleanup for "data out of range for datetime" error +DROP TABLE t517_1; +GO + +-- clean up for "A SELECT statement that assigns a value to a variable must not +-- be combined with data-retrieval operations" error +DROP TABLE t141_1; +GO + +-- setup for "column \"%s\" of relation \"%s\" is a generated column" +DROP TABLE t1752_1; +GO + +drop table simpleErrorTable +GO + +while (@@trancount > 0) commit tran; +GO + diff --git a/contrib/test/JDBC/sql_expected/TestSimpleErrorsWithImplicitTran.out b/contrib/test/JDBC/sql_expected/TestSimpleErrorsWithImplicitTran.out new file mode 100644 index 00000000000..862a2096141 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/TestSimpleErrorsWithImplicitTran.out @@ -0,0 +1,4918 @@ +#Setup +SET IMPLICIT_TRANSACTIONS ON + +#Run error handling tests +CREATE TABLE simpleErrorTable (a varchar(15) UNIQUE NOT NULL, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) +GO + +-- setup for "data out of range for datetime" error +CREATE TABLE t517_1(a datetime); +GO + +-- setup for "A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations" error +CREATE TABLE t141_1(c1 int, c2 int); +GO + +-- setup for "column \"%s\" of relation \"%s\" is a generated column" +CREATE TABLE t1752_1(c1 INT, c2 INT, c3 as c1*c2) +GO + +if @@trancount > 0 commit tran; +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + declare @err int = @@error; if @err = 0 select 0 else select 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1 +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + declare @err int = @@error; if @err = 0 select 0 else select 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: data out of range for datetime +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO t517_1 VALUES (DATEADD(YY,-300,getdate())); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 517)~~ + +~~ERROR (Message: data out of range for datetime)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE simpleErrorTable SET c = NULL WHERE c = 1; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + declare @err int = @@error; if @err = 0 select 0 else select 1; +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~START~~ +int +0 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + + +-- Error: creating an existing table +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +CREATE TABLE simpleErrorTable (a int); +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +commit tran; +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +rollback tran; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +rollback tran sp1; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK TO SAVEPOINT can only be used in transaction blocks)~~ + +rollback tran; +GO +~~ERROR (Code: 3903)~~ + +~~ERROR (Message: ROLLBACK can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran; +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +commit tran; +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +drop procedure simpleErrorProc3 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc3")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +3 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +3 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "column \"%s\" of relation \"%s\" is a generated column" +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + ALTER TABLE t1752_1 ADD CONSTRAINT constraint1752 DEFAULT 'test' FOR c3; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 1752)~~ + +~~ERROR (Message: column "c3" of relation "t1752_1" is a generated column)~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran sp1; +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 6401)~~ + +~~ERROR (Message: savepoint "sp1" does not exist)~~ + +rollback tran; +GO +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +begin tran +GO +DECLARE @a tinyint = 1000; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +begin tran +GO +DECLARE @a tinyint = 1000; +GO +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +2 +~~END~~ + +commit tran +GO +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +Cherry#!#indigo#!#8#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: "A SELECT statement that assigns a value to a variable must not be +-- combined with data-retrieval operations" +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a int; SELECT @a = c1, c2 FROM t141_1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +~~ERROR (Code: 141)~~ + +~~ERROR (Message: A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations)~~ + +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + DECLARE @a tinyint = 1000; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +select @@trancount; +GO +~~START~~ +int +1 +~~END~~ + + +if @@trancount > 0 commit tran; +GO + +-- Error: value for domain tinyint violates check constraint "tinyint_check" +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DECLARE @a tinyint = 1000; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 220)~~ + +~~ERROR (Message: value for domain tinyint violates check constraint "tinyint_check")~~ + +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_pkey")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran sp1; +GO +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_pkey")~~ + +rollback tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +commit tran; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_pkey")~~ + +commit tran; +GO +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +0 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +drop procedure simpleErrorProc3 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc3")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +declare @err int = @@error; if @err = 0 select 0 else select 1; +GO +~~START~~ +int +1 +~~END~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Orange#!##!#3#!#Whoops! #!#Happy😀 #!#1900-02-28 23:59:59.99#!#342.5 +~~END~~ + +select @@trancount +GO +~~START~~ +int +1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc2")~~ + +truncate table simpleErrorTable +GO + +-- cleanup for "data out of range for datetime" error +DROP TABLE t517_1; +GO + +-- clean up for "A SELECT statement that assigns a value to a variable must not +-- be combined with data-retrieval operations" error +DROP TABLE t141_1; +GO + +-- setup for "column \"%s\" of relation \"%s\" is a generated column" +DROP TABLE t1752_1; +GO + +drop table simpleErrorTable +GO + +while (@@trancount > 0) commit tran; +GO + + +#Cleanup +IF @@trancount > 0 ROLLBACK +SET IMPLICIT_TRANSACTIONS OFF diff --git a/contrib/test/JDBC/sql_expected/TestSimpleErrorsWithXactAbort.out b/contrib/test/JDBC/sql_expected/TestSimpleErrorsWithXactAbort.out new file mode 100644 index 00000000000..e194f7dd702 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/TestSimpleErrorsWithXactAbort.out @@ -0,0 +1,4022 @@ +SET XACT_ABORT ON +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'ignore'; +GO + +CREATE TABLE simpleErrorTable (a varchar(15) UNIQUE, b nvarchar(25), c int PRIMARY KEY, d char(15) DEFAULT 'Whoops!', e nchar(25), f datetime, g numeric(4,1) CHECK (g >= 103.5)) +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1 +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: duplicate key value violates unique constraint +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + INSERT INTO simpleErrorTable VALUES ('Apple', N'blue', 2, 'Chennai', N'Neutral😐', '2006-11-11 22:47:23.128', 512.4); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2627)~~ + +~~ERROR (Message: duplicate key value violates unique constraint "simpleerrortable_a_key")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: check constraint violation +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: check constraint violation +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET g = 101.4 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "simpleerrortable" violates check constraint "simpleerrortable_g_check")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: not null constraint violation +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE simpleErrorTable SET c = NULL WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: not null constraint violation +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c" of relation "simpleerrortable" violates not-null constraint)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: creating an existing table +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +CREATE TABLE simpleErrorTable (a int); +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + CREATE TABLE simpleErrorTable (a int); + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: creating an existing table +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + CREATE TABLE simpleErrorTable (a int); + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "simpleerrortable" already exists)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: deleting values from a table that does not exist +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +DELETE FROM simpleErrorTable1 WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + DELETE FROM simpleErrorTable1 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + DELETE FROM simpleErrorTable1 WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: deleting values from a table that does not exist +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + DELETE FROM simpleErrorTable1 WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 208)~~ + +~~ERROR (Message: relation "simpleerrortable1" does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: syntax error +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 4 and character position 8)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 5 and character position 12)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 5 and character position 12)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 6 and character position 12)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 7 and character position 12)~~ + +exec simpleErrorProc1 +GO +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 6 and character position 16)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 9 and character position 24)~~ + +exec simpleErrorProc1 +GO +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 8 and character position 16)~~ + +exec simpleErrorProc1 +GO +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 7 and character position 12)~~ + +begin tran +GO +exec simpleErrorProc1 +GO +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 7 and character position 12)~~ + +begin tran +GO +exec simpleErrorProc1 +GO +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 7 and character position 12)~~ + +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 7 and character position 12)~~ + +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc3 +GO +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 8 and character position 16)~~ + +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: syntax error +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + UPDATE1 simpleErrorTable SET c = NULL WHERE c = 1; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +~~ERROR (Code: 16801924)~~ + +~~ERROR (Message: syntax error near 'simpleErrorTable' at line 8 and character position 16)~~ + +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 52461700)~~ + +~~ERROR (Message: procedure simpleerrorproc1() does not exist)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "simpleerrorproc1")~~ + +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch +INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; +INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with commit transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with rollback transaction and rollback to savepoint +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + save tran sp1; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran sp1; +rollback tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple batch with nested transaction +begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +commit tran; +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + begin tran; + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + commit tran; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started inside procedure but ended outside procedure +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +commit tran +GO +~~ERROR (Code: 3902)~~ + +~~ERROR (Message: COMMIT can only be used in transaction blocks)~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through commit +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- simple procedure with transaction started outside procedure but ended inside procedure through rollback +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +begin tran +GO +exec simpleErrorProc1 +GO +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 2) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure (level 3) +create procedure simpleErrorProc1 +as +begin + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); +end +GO +create procedure simpleErrorProc2 +as +begin + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); +end +GO +create procedure simpleErrorProc3 +as +begin + SELECT 1; + exec simpleErrorProc2; + SELECT 2; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +Apple#!#red#!#1#!#Delhi #!#Sad😞 #!#2000-12-13 12:58:23.123#!#123.1 +Pineapple#!#pink#!#7#!#Surat #!#Frown😠 #!#2000-12-13 12:58:23.123#!#123.1 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +drop procedure simpleErrorProc3 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with commit transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + commit tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + commit tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- Error: conversion error and executing stored procedure using sp_executesql +-- nested procedure with rollback transaction +create procedure simpleErrorProc1 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Apple', N'red', 1, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + EXECUTE sp_executesql N'UPDATE simpleErrorTable SET a = convert(int, ''abc'') WHERE c = 1;'; + INSERT INTO simpleErrorTable(a, b, c, e, f, g) VALUES ('Orange', NULL, 3, N'Happy😀', '1900-02-28 23:59:59.989', 342.5); + rollback tran; +end +GO +create procedure simpleErrorProc2 +as +begin + begin tran; + INSERT INTO simpleErrorTable VALUES ('Pineapple', N'pink', 7, 'Surat', N'Frown😠', '2000-12-13 12:58:23.123', 123.1); + exec simpleErrorProc1; + INSERT INTO simpleErrorTable VALUES ('Cherry', N'indigo', 8, 'Delhi', N'Sad😞', '2000-12-13 12:58:23.123', 123.1); + rollback tran; +end +GO +exec simpleErrorProc2 +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ERROR (Code: 33685634)~~ + +~~ERROR (Message: invalid input syntax for type integer: "abc")~~ + +select * from simpleErrorTable ORDER BY c +GO +~~START~~ +varchar#!#nvarchar#!#int#!#char#!#nchar#!#datetime#!#numeric +~~END~~ + +select @@trancount +GO +~~START~~ +int +0 +~~END~~ + +drop procedure simpleErrorProc1 +GO +drop procedure simpleErrorProc2 +GO +truncate table simpleErrorTable +GO + +-- cleanup +SET XACT_ABORT OFF; +GO + +drop table simpleErrorTable +GO + +EXEC sp_babelfish_configure 'babelfishpg_tsql.escape_hatch_unique_constraint', 'strict'; +GO diff --git a/contrib/test/JDBC/sql_expected/TestTriggers.out b/contrib/test/JDBC/sql_expected/TestTriggers.out new file mode 100644 index 00000000000..88577bbdcc0 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/TestTriggers.out @@ -0,0 +1,391 @@ +create table triggerTab1(c1 int, c2 varchar(30)) +go + +create table triggerTab2(c1 int) +go + +create table triggerTab3(c1 int) +go + +insert into triggerTab1 values(1, 'first') +go +~~ROW COUNT: 1~~ + + +insert into triggerTab2 values(1) +go +~~ROW COUNT: 1~~ + + +insert into triggerTab3 values(1) +go +~~ROW COUNT: 1~~ + + +create trigger txnTrig1 on triggerTab1 for insert as +begin tran; +update triggerTab2 set c1 = 2; +commit; +select * from triggerTab2 order by c1; +select * from inserted; +go + +create trigger txnTrig2 on triggerTab2 for update as +save tran sp1; +save tran sp2; +delete from triggerTab3; +rollback tran sp1; +go + +create trigger txnTrig3 on triggerTab3 for delete as +select * from triggerTab3 order by c1; +insert into triggerTab3 values(1); +select * from deleted; +rollback tran sp2; +go + +insert into triggerTab1 values(2, 'second'); +go +~~START~~ +int +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +~~START~~ +int#!#varchar +2#!#second +~~END~~ + +~~ROW COUNT: 1~~ + + +begin tran; +go +insert into triggerTab1 values(3, 'third'); +go +~~START~~ +int +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +~~START~~ +int#!#varchar +3#!#third +~~END~~ + +~~ROW COUNT: 1~~ + +commit; +go + +drop trigger txnTrig1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +commit; +update triggerTab2 set c1 = 2; +select * from triggerTab2 order by c1; +go + +insert into triggerTab1 values(2, 'second'); +go +~~START~~ +int +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +begin tran; +go + +insert into triggerTab1 values(2, 'second'); +go +~~START~~ +int +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +2 +~~END~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +drop trigger txnTrig1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +rollback; +update triggerTab2 set c1 = 3; +select * from triggerTab2 order by c1; +go + +insert into triggerTab1 values(2, 'second'); +go +~~START~~ +int +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +3 +~~END~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +begin tran; +go + +insert into triggerTab1 values(2, 'second'); +go +~~START~~ +int +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +3 +~~END~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +create procedure triggerProc1 as +save tran sp1; +insert into triggerTab1 values(3, 'third'); +rollback tran sp1; +go + +create procedure triggerProc2 as +begin tran; +exec triggerProc1; +insert into triggerTab1 values(3, 'third'); +commit; +go + +exec triggerProc2; +go +~~START~~ +int +~~END~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +1 +~~END~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~START~~ +int +3 +~~END~~ + +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +create table triggerErrorTab(c1 int not null); +go + +create trigger triggerErr on triggerErrorTab for insert as +insert into triggerErrorTab values(NULL); +insert into invalidTab values(1); +go + +insert into triggerErrorTab values(1); +go +~~ERROR (Code: 515)~~ + +~~ERROR (Message: null value in column "c1" of relation "triggererrortab" violates not-null constraint)~~ + + + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop trigger txnTrig1 +go + + +create procedure triggerProc1 as +commit; +go + +create procedure triggerProc2 as +exec triggerProc1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +exec triggerProc2; +go + +insert into triggerTab1 values(1, 'value1'); +go +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop trigger txnTrig1 +go + + +create procedure triggerProc1 as +rollback; +go + +create procedure triggerProc2 as +exec triggerProc1; +go + +create trigger txnTrig1 on triggerTab1 after insert as +exec triggerProc2; +go + +insert into triggerTab1 values(1, 'value2'); +go +~~ERROR (Code: 3609)~~ + +~~ERROR (Message: The transaction ended in the trigger. The batch has been aborted.)~~ + + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop trigger txnTrig1 +go + + +create procedure triggerProc1 as +rollback tran sp1; +go + +create procedure triggerProc2 as +exec triggerProc1; +go + +create trigger txnTrig1 on triggerTab1 for insert as +save tran sp1; +exec triggerProc2; +go + +insert into triggerTab1 values(1, 'value3'); +go +~~ROW COUNT: 1~~ + + + +drop procedure triggerProc1; +go +drop procedure triggerProc2; +go +drop table triggerTab1; +go +drop table triggerTab2; +go +drop table triggerTab3; +go +drop table triggerErrorTab +go diff --git a/contrib/test/JDBC/sql_expected/temp-tables.out b/contrib/test/JDBC/sql_expected/temp-tables.out new file mode 100644 index 00000000000..5b29e6123d2 --- /dev/null +++ b/contrib/test/JDBC/sql_expected/temp-tables.out @@ -0,0 +1,410 @@ +USE master +GO + + + +-- +-- Tests for T-SQL style temp tables +-- +-- Basic temp table create/insert/select using tsql dialect +CREATE TABLE #local_tempt(col int); +GO + +INSERT INTO #local_tempt VALUES (1); +GO +~~ROW COUNT: 1~~ + + +SELECT * FROM #local_tempt; +GO +~~START~~ +int +1 +~~END~~ + + +CREATE TABLE ##global_tempt(col int); +GO +~~ERROR (Code: 1088)~~ + +~~ERROR (Message: 'GLOBAL TEMPORARY TABLE' is not currently supported in Babelfish)~~ + + +CREATE SCHEMA temp_tables_test; +GO + +CREATE TABLE temp_tables_test.#local_tempt_withschema(col int); +GO + +INSERT INTO temp_tables_test.#local_tempt_withschema VALUES (1); +GO +~~ROW COUNT: 1~~ + + +SELECT * FROM temp_tables_test.#local_tempt_withschema; +GO +~~START~~ +int +1 +~~END~~ + + +DROP SCHEMA temp_tables_test; +GO + + +-- Implicitly creating temp tables +CREATE TABLE tt_test_t1 (col int); +GO + +INSERT INTO tt_test_t1 values (1); +GO +~~ROW COUNT: 1~~ + + +INSERT INTO tt_test_t1 values (NULL); +GO +~~ROW COUNT: 1~~ + + +SELECT * INTO #local_tempt2 FROM tt_test_t1; +GO + +SELECT * FROM #local_tempt2; +GO +~~START~~ +int +1 + +~~END~~ + + + +-- Implicitly creating temp tables in procedure +CREATE PROCEDURE temp_table_sp AS +BEGIN + SELECT * INTO #tt_sp_local FROM tt_test_t1; + INSERT INTO #tt_sp_local VALUES(2); +END; +GO + +EXEC temp_table_sp; +GO +~~ROW COUNT: 1~~ + + +-- BABEL-903: create temp table named #[digit][string] +create procedure babel903 AS +BEGIN + create table #903 (a int); + select col into #903tt from tt_test_t1; + insert into #903 values(1); + insert into #903tt values(1); +END +GO + +exec babel903; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +-- BABEL-904: drop temp table +CREATE PROCEDURE babel904 AS +BEGIN + create table #t (a int); + create table #tt (a int); + drop table #t; + drop table #tt; +END +go + +exec babel904; +GO + + +-- Visibility tests +create table #tt (a int); +go +insert into #tt values(0); +go +~~ROW COUNT: 1~~ + + +CREATE procedure temp_table_nested_sp_1st AS +BEGIN + CREATE TABLE #tt_1st (a int); + insert into #tt values(1); + insert into #tt_1st values(1); + insert into #tt_2nd values(1); + insert into #tt_3rd values(1); +END; +GO + +CREATE procedure temp_table_nested_sp_2nd AS +BEGIN + CREATE TABLE #tt_2nd (a int); + EXEC temp_table_nested_sp_1st; + insert into #tt values(2); + insert into #tt_2nd values(2); + insert into #tt_3rd values(2); +END; +GO + +CREATE procedure temp_table_nested_sp_3rd AS +BEGIN + CREATE TABLE #tt_3rd (a int); + EXEC temp_table_nested_sp_2nd; + insert into #tt values(3); + insert into #tt_3rd values(3); +END; +GO + +EXEC temp_table_nested_sp_3rd; +GO +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + + +-- should fail to find these tables +select * from #tt_1st; +go +~~ERROR (Code: 16908420)~~ + +~~ERROR (Message: relation "#tt_1st" does not exist)~~ + +select * from #tt_2nd; +go +~~ERROR (Code: 16908420)~~ + +~~ERROR (Message: relation "#tt_2nd" does not exist)~~ + +select * from #tt_3rd; +go +~~ERROR (Code: 16908420)~~ + +~~ERROR (Message: relation "#tt_3rd" does not exist)~~ + +-- This should print 0, 1, 2 and 3 +select * from #tt; +go +~~START~~ +int +0 +1 +2 +3 +~~END~~ + + +DROP PROCEDURE temp_table_nested_sp_1st; +go +DROP PROCEDURE temp_table_nested_sp_2nd; +go +DROP PROCEDURE temp_table_nested_sp_3rd; +go +DROP TABLE #tt; +go + +-- creating temp tables with duplicated names. +create table #tt (a int); +go +insert into #tt values(1); +go +~~ROW COUNT: 1~~ + + +CREATE procedure temp_table_nested_sp_inner AS +BEGIN + CREATE TABLE #tt (a int); -- same name as the top-level, allowed + CREATE TABLE #tt_sp_outer (a int); -- same name as the outer procedure, allowed + insert into #tt values(3); +END; +GO + +CREATE procedure temp_table_nested_sp_outer AS +BEGIN + CREATE TABLE #tt (a int); -- same name as the top-level, allowed + CREATE TABLE #tt_sp_outer (a int); + insert into #tt values(2); + EXEC temp_table_nested_sp_inner; +END; +GO + +EXEC temp_table_nested_sp_outer; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +select * from #tt; -- should only print value '1' +go +~~START~~ +int +1 +~~END~~ + +drop table #tt; +go + +-- procedure with exception +CREATE procedure temp_table_sp_exception AS +BEGIN + CREATE TABLE #tt (a int); + CREATE TABLE #tt (a int); -- throws error +END; +GO +EXEC temp_table_sp_exception; +GO +~~ERROR (Code: 2714)~~ + +~~ERROR (Message: relation "#tt" already exists)~~ + +select * from #tt; -- can't find the table +go +~~ERROR (Code: 16908420)~~ + +~~ERROR (Message: relation "#tt" does not exist)~~ + + +-- drop/alter tables +CREATE procedure temp_table_sp_alter AS +BEGIN + CREATE TABLE #tt (a int); + CREATE TABLE #tt2 (a int); + DROP TABLE #tt2; + ALTER TABLE #tt ADD b char; + insert into #tt values(1, 'x'); +END; +GO + +EXEC temp_table_sp_alter; +GO +~~ROW COUNT: 1~~ + + + +-- constraints +create table #tt_con(a int CHECK (a > 10)); +go +insert into #tt_con values(1); -- errorneous +go +~~ERROR (Code: 67391682)~~ + +~~ERROR (Message: new row for relation "#tt_con" violates check constraint "#tt_con_a_check")~~ + +CREATE PROCEDURE temp_table_sp_constraint AS +BEGIN + create table #tt (a int CHECK (a > 10)); + insert into #tt values(11); + insert into #tt_con(a) select a from #tt; +END +go +exec temp_table_sp_constraint; +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +select * from #tt_con; -- should print 11 +go +~~START~~ +int +11 +~~END~~ + + + +-- statistic +create table #tt_stat(a int, b int); +go +insert into #tt_stat values(1, 2); +go +~~ROW COUNT: 1~~ + +-- valid T-SQL create-statitstics is not supported yet +--CREATE STATISTICS s1 on #tt_stat(a, b); +--go +drop table #tt_stat; +go + + +-- BABEL-322: '#' in column name is allowed in tsql +CREATE TABLE #babel322(#col int, ##col int); +GO +DROP TABLE #babel322; +GO + +-- BABEL-1637: rollback within procedure makes top-level temp table gone +create proc sp_babel1637 as + create table #tt_1637 (a int) + begin tran + insert into #tt_1637 values (123) + rollback + select * from #tt_1637 +go +create table #tt_1637 (a int) +insert into #tt_1637 values (456) +select * from #tt_1637 +go +~~ROW COUNT: 1~~ + +~~START~~ +int +456 +~~END~~ + +exec sp_babel1637 +go +~~ROW COUNT: 1~~ + +~~START~~ +int +~~END~~ + +select * from #tt_1637 +go +~~START~~ +int +456 +~~END~~ + +drop table #tt_1637 +go + + +-- cleanup +DROP PROCEDURE temp_table_sp; +GO +DROP PROCEDURE babel903; +GO +DROP PROCEDURE babel904; +GO +DROP PROCEDURE temp_table_nested_sp_inner; +GO +DROP PROCEDURE temp_table_nested_sp_outer; +GO +DROP PROCEDURE temp_table_sp_exception; +GO +DROP PROCEDURE temp_table_sp_alter; +GO +DROP PROCEDURE temp_table_sp_constraint; +GO +DROP TABLE tt_test_t1; +GO diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/CompareResults.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/CompareResults.java new file mode 100644 index 00000000000..ff3d85e0e0c --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/CompareResults.java @@ -0,0 +1,296 @@ +package com.sqlsamples; + +import org.junit.jupiter.api.Assertions; +import org.opentest4j.AssertionFailedError; +import microsoft.sql.DateTimeOffset; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.sql.*; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.time.LocalTime; +import java.util.Locale; + +import static com.sqlsamples.Config.outputColumnName; +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; +import static java.sql.Types.*; +import static java.util.Objects.isNull; + +public class CompareResults { + + // function to write result set into a file + public static void writeResultSetToFile(BufferedWriter bw, ResultSet rs, Logger logger) { + try { + bw.write("~~START~~"); + bw.newLine(); + + ResultSetMetaData rsmd = rs.getMetaData(); + int cols = rsmd.getColumnCount(); + + if (outputColumnName) { + for (int i = 1; i <= cols; i++) { + bw.write(rsmd.getColumnName(i)); + if (i != cols) bw.write("#!#"); + } + bw.newLine(); + } + + for (int i = 1; i <= cols; i++) { + bw.write(rsmd.getColumnTypeName(i)); + if (i != cols) bw.write("#!#"); + } + bw.newLine(); + + while (rs.next()) { + for (int i = 1; i <= cols; i++) { + if(isNull(rs.getObject(i))){ + bw.write(""); + } else { + String str = rs.getString(i); + str = str.replaceAll("[\r\n]+", ""); + bw.write(str); + } + + if (i != cols) bw.write("#!#"); + } + bw.newLine(); + } + + bw.write("~~END~~"); + bw.newLine(); + bw.newLine(); + + rs.close(); + + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + // function to write the tuple, result set cursor is pointing to, into a file + public static void writeCursorResultSetToFile(BufferedWriter bw, ResultSet cursor, Logger logger) { + try { + bw.write("~~START~~"); + bw.newLine(); + + ResultSetMetaData rsmd = cursor.getMetaData(); + int cols = rsmd.getColumnCount(); + + if (outputColumnName) { + for (int i = 1; i <= cols; i++) { + bw.write(rsmd.getColumnName(i)); + if (i != cols) bw.write("#!#"); + } + bw.newLine(); + } + + for (int i = 1; i <= cols; i++) { + bw.write(rsmd.getColumnTypeName(i)); + if (i != cols) bw.write("#!#"); + } + + bw.newLine(); + + for (int i = 1; i <= cols; i++) { + if(isNull(cursor.getObject(i))){ + bw.write(""); + } else { + bw.write(cursor.getObject(i).toString()); + } + if (i != cols) bw.write("#!#"); + } + bw.newLine(); + bw.write("~~END~~"); + bw.newLine(); + bw.newLine(); + + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + // processes all the results sequentially that we get from executing a JDBC Statement + static void processResults(Statement stmt, BufferedWriter bw, int resultsProcessed, boolean resultSetExist, Logger logger) { + int updateCount = -9; // initialize to impossible value + + while (true) { + boolean exceptionOccurred = true; + do { + try { + if (resultsProcessed > 0) { + resultSetExist = stmt.getMoreResults(); + } + exceptionOccurred = false; + updateCount = stmt.getUpdateCount(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + resultsProcessed++; + } while (exceptionOccurred); + + if ((!resultSetExist) && (updateCount == -1)) { + break; + } + + if (resultSetExist) { + try (ResultSet rs = stmt.getResultSet()) { + writeResultSetToFile(bw, rs, logger); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } else { + if (updateCount > 0) { + try { + bw.write("~~ROW COUNT: " + updateCount + "~~"); + bw.newLine(); + bw.newLine(); + } catch (IOException e) { + logger.error("IO Exception: " + e.getMessage(), e); + } + } + } + } + } + + // function to map SQL data type to JDBC data types + static int SQLtoJDBCDataTypeMapping(String sqlDataType) { + + if(sqlDataType.equalsIgnoreCase("bigint")) { + return BIGINT; + } else if (sqlDataType.equalsIgnoreCase("binary") + || sqlDataType.equalsIgnoreCase("timestamp")) { + return BINARY; + } else if (sqlDataType.equalsIgnoreCase("bit")) { + return BIT; + } else if (sqlDataType.equalsIgnoreCase("char") + || sqlDataType.equalsIgnoreCase("uniqueidentifier")) { + return CHAR; + } else if (sqlDataType.equalsIgnoreCase("date")) { + return DATE; + } else if (sqlDataType.equalsIgnoreCase("datetime") + || sqlDataType.equalsIgnoreCase("datetime2") + || sqlDataType.equalsIgnoreCase("smalldatetime")) { + return TIMESTAMP; + } else if (sqlDataType.equalsIgnoreCase("datetimeoffset")) { + return microsoft.sql.Types.DATETIMEOFFSET; + } else if (sqlDataType.equalsIgnoreCase("decimal") + || sqlDataType.equalsIgnoreCase("money") + || sqlDataType.equalsIgnoreCase("smallmoney")) { + return DECIMAL; + } else if (sqlDataType.equalsIgnoreCase("float")) { + return DOUBLE; + } else if (sqlDataType.equalsIgnoreCase("image")) { + return LONGVARBINARY; + } else if (sqlDataType.equalsIgnoreCase("int")) { + return INTEGER; + } else if (sqlDataType.equalsIgnoreCase("nchar")) { + return NCHAR; + } else if (sqlDataType.equalsIgnoreCase("nvarchar") + || sqlDataType.equalsIgnoreCase("nvarcharmax")) { + return NVARCHAR; + } else if (sqlDataType.equalsIgnoreCase("ntext") + || sqlDataType.equalsIgnoreCase("xml")) { + return LONGNVARCHAR; + } else if (sqlDataType.equalsIgnoreCase("numeric")) { + return NUMERIC; + } else if (sqlDataType.equalsIgnoreCase("real")) { + return REAL; + } else if (sqlDataType.equalsIgnoreCase("smallint")) { + return SMALLINT; + } else if (sqlDataType.equalsIgnoreCase("text")) { + return LONGVARCHAR; + } else if (sqlDataType.equalsIgnoreCase("time")) { + return TIME; + } else if (sqlDataType.equalsIgnoreCase("tinyint")) { + return TINYINT; + } else if (sqlDataType.equalsIgnoreCase("udt") + || sqlDataType.equalsIgnoreCase("varbinary") + || sqlDataType.equalsIgnoreCase("varbinarymax") + || sqlDataType.equalsIgnoreCase("geometry") + || sqlDataType.equalsIgnoreCase("geography")) { + return VARBINARY; + } else if (sqlDataType.equalsIgnoreCase("varchar") + || sqlDataType.equalsIgnoreCase("varcharmax")) { + return VARCHAR; + } else if (sqlDataType.equalsIgnoreCase("sqlvariant")) { + return microsoft.sql.Types.SQL_VARIANT; + } else return 0; + } + + // function to parse SQL data type to Java data type + static Object parse_data(String result, String datatype, Logger logger) { + + try { + if(result.equals("")){ + return null; + } + + /* TODO: Add more data types here as we support them */ + if (datatype.equalsIgnoreCase("int")) { + return Integer.parseInt(result); + } else if (datatype.equalsIgnoreCase("string") + || datatype.equalsIgnoreCase("char") + || datatype.equalsIgnoreCase("nchar") + || datatype.equalsIgnoreCase("varchar") + || datatype.equalsIgnoreCase("nvarchar") + || datatype.equalsIgnoreCase("uniqueidentifier") + || datatype.equalsIgnoreCase("varcharmax") + || datatype.equalsIgnoreCase("nvarcharmax")) { + return result; + } else if (datatype.equalsIgnoreCase("boolean") + || datatype.equalsIgnoreCase("bit")) { + return Boolean.parseBoolean(result); + } else if (datatype.equalsIgnoreCase("long") + || datatype.equalsIgnoreCase("bigint")) { + return Long.parseLong(result); + } else if (datatype.equalsIgnoreCase("double") + || datatype.equalsIgnoreCase("float")) { + return Double.parseDouble(result); + } else if (datatype.equalsIgnoreCase("unsigned_char") + || datatype.equalsIgnoreCase("smallint") + || datatype.equalsIgnoreCase("tinyint")) { + return Short.parseShort(result); + } else if (datatype.equalsIgnoreCase("real")) { + return Float.parseFloat(result); + } else if (datatype.equalsIgnoreCase("byte")) { + return Byte.parseByte(result); + } else if (datatype.equalsIgnoreCase("binary") + || datatype.equalsIgnoreCase("varbinary") + || datatype.equalsIgnoreCase("timestamp") + || datatype.equalsIgnoreCase("udt")) { + return result; + } else if (datatype.equalsIgnoreCase("decimal") + || datatype.equalsIgnoreCase("money") + || datatype.equalsIgnoreCase("smallmoney") + || datatype.equalsIgnoreCase("numeric")) { + DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(Locale.US); + format.setParseBigDecimal(true); + return format.parse(result); + } else if (datatype.equalsIgnoreCase("datetime") + || datatype.equalsIgnoreCase("datetime2") + || datatype.equalsIgnoreCase("smalldatetime")){ + return Timestamp.valueOf(result); + } else if (datatype.equalsIgnoreCase("date")){ + return Date.valueOf(result); + } else if (datatype.equalsIgnoreCase("time")){ + return Time.valueOf(LocalTime.parse(result)); + } else if (datatype.equalsIgnoreCase("text") + || datatype.equalsIgnoreCase("ntext")){ + return result.replaceAll("", System.lineSeparator()); + } else if (datatype.equalsIgnoreCase("datetimeoffset")){ + return DateTimeOffset.valueOf(Timestamp.valueOf(result), 0); + } + } catch (ParseException pe) { + logger.error("Parse Exception: " + pe.getMessage(), pe); + } + + return null; + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/Config.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/Config.java new file mode 100644 index 00000000000..3c6e22b8ec4 --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/Config.java @@ -0,0 +1,145 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.*; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.appender.*; +import org.apache.logging.log4j.core.config.AppenderRef; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.layout.PatternLayout; + +import java.io.*; +import java.util.Map; +import java.util.Properties; + +import static java.util.Objects.isNull; + +public class Config { + + static Properties properties = readConfig(); + static String inputFilesDirectoryPath = properties.getProperty("inputFilesPath"); + static boolean printLogsToConsole = Boolean.parseBoolean(properties.getProperty("printLogsToConsole")); + static String JDBCDriver = properties.getProperty("driver"); + static boolean performanceTest = Boolean.parseBoolean(properties.getProperty("performanceTest")); + static boolean outputColumnName = Boolean.parseBoolean(properties.getProperty("outputColumnName")); + static boolean outputErrorCode = Boolean.parseBoolean(properties.getProperty("outputErrorCode")); + static String scheduleFileName = properties.getProperty("scheduleFile"); + static String testFileRoot = properties.getProperty("testFileRoot"); + + static String connectionString = constructConnectionString(); + + // read configuration from text file "config.txt" and load it as properties + static Properties readConfig() { + Properties prop = new Properties(); + String filePath = System.getProperty("babel-config-file"); + if (filePath == null){ + filePath = "resources/config.txt"; + } + + try { + File file = new File(filePath); + FileInputStream fis; + + if (file.isFile()) { + fis = new FileInputStream(file); + } else { + // try alternate path + fis = new FileInputStream("src/main/resources/config.txt"); + } + + prop.load(fis); + + // override configuration if system environment variables are defined + String env, property; + + for(Map.Entry entry : prop.entrySet()){ + property = entry.getKey().toString(); + env = System.getenv(property); + if (isNull(env)) { + env = System.getProperty(property); + } + + if(!isNull(env)) { + prop.put(property, env); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + + return prop; + } + + // configure properties of logger + public static void configureLogger(String logFileName, Logger logger) throws IOException { + LoggerContext context = LoggerContext.getContext(false); + Configuration config = context.getConfiguration(); + + PatternLayout pattern = PatternLayout.newBuilder().withPattern("%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p - %m%n").build(); + FileAppender fileAppender = FileAppender.newBuilder().setName("fileAppender").setLayout(pattern).withFileName(logFileName+ ".log").build(); + + AppenderRef ref = AppenderRef.createAppenderRef("fileAppender", null, null); + AppenderRef[] refs = new AppenderRef[] { ref }; + LoggerConfig loggerConfig = LoggerConfig.createLogger(false, Level.ERROR, logger.getName(), null, refs, null, config, null); + loggerConfig.addAppender(fileAppender, null, null); + + config.addLogger(logger.getName(), loggerConfig); /* 2 */ + context.updateLoggers(); + } + + // configure properties of summary logger + public static void configureSummaryLogger(String logFileName, Logger summaryLogger) throws IOException { + Logger rootLogger = LogManager.getRootLogger(); + Configurator.setLevel(rootLogger.getName(), Level.DEBUG); + + LoggerContext context = LoggerContext.getContext(false); + Configuration config = context.getConfiguration(); + + PatternLayout pattern = PatternLayout.newBuilder().withPattern("%m%n").build(); + FileAppender fileAppender = FileAppender.newBuilder().setName("summaryFileAppender").setLayout(pattern).withFileName(logFileName+ ".log").build(); + ConsoleAppender consoleAppender = ConsoleAppender.newBuilder().setName("summaryConsoleAppender").setLayout(pattern).build(); + + AppenderRef fileRef = AppenderRef.createAppenderRef("summaryFileAppender", null, null); + AppenderRef consoleRef = AppenderRef.createAppenderRef("summaryConsoleAppender", null, null); + + AppenderRef[] refs = new AppenderRef[] { fileRef, consoleRef }; + LoggerConfig loggerConfig = LoggerConfig.createLogger(false, summaryLogger.getLevel(), summaryLogger.getName(), null, refs, null, config, null); + loggerConfig.addAppender(fileAppender, null, null); + loggerConfig.addAppender(consoleAppender, null, null); + + config.addLogger(summaryLogger.getName(), loggerConfig); + context.updateLoggers(); + } + + static String createSQLServerConnectionString(String URL, String port, String databaseName, String user, String password) { + return "jdbc:sqlserver://" + URL + ":" + port + ";" + "databaseName=" + + databaseName + ";" + "user=" + user + ";" + "password=" + password; + } + + static String createPostgreSQLConnectionString(String URL, String port, String databaseName, String user, String password) { + return "jdbc:postgresql://" + URL + ":" + port + "/" + + databaseName + "?" + "user=" + user + "&" + "password=" + password; + } + + private static String constructConnectionString() { + + String URL = properties.getProperty("URL"); + String port = properties.getProperty("port"); + String databaseName = properties.getProperty("databaseName"); + String physicalDatabaseName = properties.getProperty("physicalDatabaseName"); + String user = properties.getProperty("user"); + String password = properties.getProperty("password"); + + // return connection strings + if (JDBCDriver.equalsIgnoreCase("sqlserver")) { + String tsql_port = properties.getProperty("tsql_port"); + return createSQLServerConnectionString(URL, tsql_port, databaseName, user, password); + } else if (JDBCDriver.equalsIgnoreCase("postgresql")) { + String psql_port = properties.getProperty("psql_port"); + return createPostgreSQLConnectionString(URL, psql_port, physicalDatabaseName, user, password); + } else System.out.println("Incorrect driver specified in config.txt . Please specify either \"sqlserver\" or \"postgresql\""); + + return null; + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/ExportResults.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/ExportResults.java new file mode 100644 index 00000000000..379fa4fcc0f --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/ExportResults.java @@ -0,0 +1,33 @@ +package com.sqlsamples; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; + +public class ExportResults { + + public static void createCSVFile(String CSVfileName, HashMap statisticsHashMap) { + + String[] HEADERS = { "URI", "Min exec time", "Max exec time", "Mean exec time", "Median exec time", "Std. dev"}; + + try { + FileWriter out = new FileWriter(CSVfileName + ".csv"); + CSVPrinter printer = new CSVPrinter(out, CSVFormat.DEFAULT.withHeader(HEADERS)); + statisticsHashMap.forEach((URI, statistics) -> { + try { + printer.printRecord(URI, statistics.minimum(), statistics.maximum(), statistics.mean(), statistics.median(), statistics.stddev()); + } catch (IOException e) { + e.printStackTrace(); + } + }); + + printer.close(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/HandleException.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/HandleException.java new file mode 100644 index 00000000000..cebeeddff3a --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/HandleException.java @@ -0,0 +1,49 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.*; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.sql.SQLException; + +import org.postgresql.util.PSQLException; + +import static com.sqlsamples.Config.outputErrorCode; + +public class HandleException { + + // function to handle SQL exception + // writes error message to a file + static void handleSQLExceptionWithFile(SQLException e, BufferedWriter bw, Logger logger){ + try { + if (outputErrorCode) { + bw.write("~~ERROR (Code: " + e.getErrorCode() + ")~~"); + bw.newLine(); + bw.newLine(); + + // Ensure SQLState is printed as part of pg error message + if (e instanceof PSQLException) { + String errorMsg = e.getMessage(); + + // Do not print error location as part of error message + int index = errorMsg.indexOf("Location:"); + + if (index != -1) { + errorMsg = errorMsg.substring(0, index); + } else { + errorMsg += "\n "; + } + bw.write("~~ERROR (Message: "+ errorMsg + " Server SQLState: " + e.getSQLState() + ")~~"); + } else { + bw.write("~~ERROR (Message: "+ e.getMessage() + ")~~"); + } + } else { + bw.write("~~ERROR~~"); + } + bw.newLine(); + bw.newLine(); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCAuthentication.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCAuthentication.java new file mode 100644 index 00000000000..a135f900f83 --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCAuthentication.java @@ -0,0 +1,85 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +import static com.sqlsamples.Config.*; +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; + +public class JDBCAuthentication { + + void javaAuthentication(String strLine, BufferedWriter bw, Logger logger) { + + // Convert .NET input file format for authentication to JDBC + String[] result = strLine.split("#!#"); + + Properties connectionPropertiesBabel = new Properties(); + + // get default values from current connection + connectionPropertiesBabel.put("serverName", properties.getProperty("URL")); + + if (JDBCDriver.equalsIgnoreCase("sqlserver")) { + connectionPropertiesBabel.put("portNumber", properties.getProperty("tsql_port")); + } + if (JDBCDriver.equalsIgnoreCase("postgresql")) { + connectionPropertiesBabel.put("portNumber", properties.getProperty("psql_port")); + } + + connectionPropertiesBabel.put("database", properties.getProperty("databaseName")); + connectionPropertiesBabel.put("user", properties.getProperty("user")); + connectionPropertiesBabel.put("password", properties.getProperty("password")); + + String port = ""; + + if (JDBCDriver.equalsIgnoreCase("sqlserver")) { + port = properties.getProperty("tsql_port"); + } + if (JDBCDriver.equalsIgnoreCase("postgresql")) { + port = properties.getProperty("psql_port"); + } + + connectionPropertiesBabel.put("url", "jdbc:sqlserver://" + properties.getProperty("URL") + ":" + port); + + String other_prop = ""; + + String connectionString_babel = createConnectionString(result, connectionPropertiesBabel, other_prop); + + try { + bw.write(strLine); + bw.newLine(); + + // establish connection using connection string + DriverManager.getConnection(connectionString_babel); + + bw.write("~~SUCCESS~~"); + bw.newLine(); + + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + private String createConnectionString(String[] result, Properties connectionPropertiesBabel, String other_prop) { + for (int i = 1; i < result.length; i++) { + if (result[i].startsWith("others")) { + other_prop = result[i].replaceFirst("others\\|-\\|", ""); + } else { + String[] property = result[i].split("\\|-\\|", -1); + connectionPropertiesBabel.put(property[0], property[1]); + } + } + + return connectionPropertiesBabel.get("url") + + ";" + "databaseName=" + connectionPropertiesBabel.get("database") + + ";" + "user=" + connectionPropertiesBabel.get("user") + + ";" + "password=" + connectionPropertiesBabel.get("password") + + ";" + other_prop; + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCallableStatement.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCallableStatement.java new file mode 100644 index 00000000000..5baf2ebeddf --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCallableStatement.java @@ -0,0 +1,196 @@ +package com.sqlsamples; + +import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; +import com.microsoft.sqlserver.jdbc.SQLServerDataTable; +import microsoft.sql.DateTimeOffset; +import org.apache.logging.log4j.Logger; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.math.BigDecimal; +import java.sql.*; +import java.sql.Date; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.time.LocalTime; +import java.util.*; + +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; + +public class JDBCCallableStatement { + + CallableStatement cstmt_bbl; + + void createCallableStatements(Connection con_bbl, String SQL, BufferedWriter bw, Logger logger) { + try { + cstmt_bbl = con_bbl.prepareCall(SQL); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void closeCallableStatements(BufferedWriter bw, Logger logger) { + try { + if (cstmt_bbl != null) cstmt_bbl.close(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + // function to write output of executed callable statement to a file + void testCallableStatementWithFile(String[] result, BufferedWriter bw, String strLine, Logger logger){ + try { + bw.write(strLine); + bw.newLine(); + set_bind_values(result, cstmt_bbl, bw, logger); + + boolean resultSetExist = false; + int resultsProcessed = 0; + try { + resultSetExist = cstmt_bbl.execute(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + resultsProcessed++; + } + CompareResults.processResults(cstmt_bbl, bw, resultsProcessed, resultSetExist, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + + // method to set values of bind variables in a callable statement + private void set_bind_values(String[] result, CallableStatement cstmt, BufferedWriter bw, Logger logger) { + + for (int j = 3; j < (result.length); j++) { + String[] parameter = result[j].split("\\|-\\|", -1); + parameter[0] = parameter[0].toLowerCase(); + try{ + if (parameter[parameter.length - 1].toLowerCase().contains("out")) { + cstmt.registerOutParameter(j - 2, CompareResults.SQLtoJDBCDataTypeMapping(parameter[0])); + } + if (parameter[parameter.length - 1].toLowerCase().contains("in")) { + /* TODO: Add more data types here as we support them */ + if (parameter[2].equalsIgnoreCase("")) { + cstmt.setNull(j - 2, CompareResults.SQLtoJDBCDataTypeMapping(parameter[0])); + } else if (parameter[0].equalsIgnoreCase("int")) { + // if there is decimal point, remove everything after the point + if (parameter[2].indexOf('.') != -1) parameter[2] = parameter[2].substring(0, parameter[2].indexOf('.') - 1); + cstmt.setInt(j - 2, Integer.parseInt(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("string") + || parameter[0].equalsIgnoreCase("char") + || parameter[0].equalsIgnoreCase("nchar") + || parameter[0].equalsIgnoreCase("varchar") + || parameter[0].equalsIgnoreCase("nvarchar") + || parameter[0].equalsIgnoreCase("uniqueidentifier") + || parameter[0].equalsIgnoreCase("varcharmax") + || parameter[0].equalsIgnoreCase("nvarcharmax")) { + cstmt.setString(j - 2, parameter[2]); + } else if (parameter[0].equalsIgnoreCase("boolean") + || parameter[0].equalsIgnoreCase("bit")) { + cstmt.setBoolean(j - 2, Boolean.parseBoolean(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("long") + || parameter[0].equalsIgnoreCase("bigint")) { + cstmt.setLong(j - 2, Long.parseLong(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("double") + || parameter[0].equalsIgnoreCase("float")) { + cstmt.setDouble(j - 2, Double.parseDouble(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("unsigned_char") + || parameter[0].equalsIgnoreCase("smallint") + || parameter[0].equalsIgnoreCase("tinyint")) { + cstmt.setShort(j - 2, Short.parseShort(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("real")) { + cstmt.setFloat(j - 2, Float.parseFloat(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("byte")) { + cstmt.setByte(j - 2, Byte.parseByte(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("binary") + || parameter[0].equalsIgnoreCase("varbinary") + || parameter[0].equalsIgnoreCase("timestamp") + || parameter[0].equalsIgnoreCase("udt")) { + byte[] byteArray = parameter[2].getBytes(); + cstmt.setBytes(j - 2, byteArray); + } else if (parameter[0].equalsIgnoreCase("decimal") + || parameter[0].equalsIgnoreCase("money") + || parameter[0].equalsIgnoreCase("smallmoney") + || parameter[0].equalsIgnoreCase("numeric")) { + DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(Locale.US); + format.setParseBigDecimal(true); + // remove dollar sign else parsing decimal from this will throw exception + String decimalString = parameter[2].replace("$", ""); + BigDecimal number = (BigDecimal) format.parse(decimalString); + cstmt.setBigDecimal(j - 2, number); + } else if (parameter[0].equalsIgnoreCase("datetime") + || parameter[0].equalsIgnoreCase("datetime2") + || parameter[0].equalsIgnoreCase("smalldatetime")) { + cstmt.setTimestamp(j - 2, Timestamp.valueOf(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("date")) { + cstmt.setDate(j - 2, Date.valueOf(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("time")) { + cstmt.setTime(j - 2, Time.valueOf(LocalTime.parse(parameter[2]))); + } else if (parameter[0].equalsIgnoreCase("datetimeoffset")) { + SQLServerCallableStatement ssCstmt = (SQLServerCallableStatement) cstmt; + ssCstmt.setDateTimeOffset(j - 1, DateTimeOffset.valueOf(Timestamp.valueOf(parameter[2]), 0)); + cstmt = ssCstmt; + } else if (parameter[0].equalsIgnoreCase("text") + || parameter[0].equalsIgnoreCase("ntext")) { + Reader r = new FileReader(parameter[2]); + cstmt.setCharacterStream(j - 2, r); + } else if (parameter[0].equalsIgnoreCase("image")) { + File file = new File(parameter[2]); + BufferedImage bImage = ImageIO.read(file); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ImageIO.write(bImage, "jpg", bos); + byte[] byteArray = bos.toByteArray(); + cstmt.setBytes(j - 2, byteArray); + } else if (parameter[0].equalsIgnoreCase("xml")) { + SQLXML sqlxml = cstmt.getConnection().createSQLXML(); + sqlxml.setString(parameter[2]); + cstmt.setSQLXML(j - 2, sqlxml); + } else if (parameter[0].equalsIgnoreCase("tvp")) { + FileInputStream fstream = new FileInputStream(parameter[2]); + DataInputStream in = new DataInputStream(fstream); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + SQLServerDataTable sourceDataTable = new SQLServerDataTable(); + + // first line of file has table columns and their data types + String strLine = br.readLine(); + + String[] columnMetaData = strLine.split(","); + + for (String columnMetaDatum : columnMetaData) { + String[] column = columnMetaDatum.split("-"); + sourceDataTable.addColumnMetadata(column[0], CompareResults.SQLtoJDBCDataTypeMapping(column[1])); + } + + // process and add all rows to data table + while ((strLine = br.readLine()) != null) { + String[] row = strLine.split(","); + ArrayList rowTuple = new ArrayList<>(); + + for(int i = 0; i < row.length; i++) { + rowTuple.add(CompareResults.parse_data(row[i], columnMetaData[i], logger)); + } + sourceDataTable.addRow(rowTuple); + } + + // setStructured API is only applicable to SQLServerCallableStatement + SQLServerCallableStatement ssCstmt = (SQLServerCallableStatement) cstmt; + ssCstmt.setStructured(j - 1, parameter[1], sourceDataTable); + cstmt = ssCstmt; + } + } + } catch (ParseException pe) { + logger.error("Parse Exception: " + pe.getMessage(), pe); + } catch (SQLException se) { + handleSQLExceptionWithFile(se, bw, logger); + } catch (FileNotFoundException e) { + logger.error("File Not Found Exception: " + e.getMessage(), e); + } catch (IOException e) { + logger.error("IO Exception: " + e.getMessage(), e); + } + } + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCrossDialect.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCrossDialect.java new file mode 100644 index 00000000000..4eb1701e771 --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCrossDialect.java @@ -0,0 +1,170 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.*; + +import java.util.*; +import java.io.BufferedWriter; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +import static com.sqlsamples.Config.*; +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; + +public class JDBCCrossDialect { + + String URL = properties.getProperty("URL"); + String tsql_port = properties.getProperty("tsql_port"); + String psql_port = properties.getProperty("psql_port"); + String databaseName = properties.getProperty("databaseName"); + String physicalDatabaseName = properties.getProperty("physicalDatabaseName"); + String user = properties.getProperty("user"); + String password = properties.getProperty("password"); + + // Key is the username, password and database name concatenated + // Value is the connection object created using the above 3 attributes + HashMap tsqlConnectionMap = new HashMap<>(); + HashMap psqlConnectionMap = new HashMap<>(); + + String newUser = null; + String newPassword = null; + String newDatabase = null; + String newPhysicalDatabase = null; + String searchPath = null; + + JDBCCrossDialect (Connection connection) { + + // Add already established connection to appropriate hash map + if (JDBCDriver.equalsIgnoreCase("sqlserver")) + tsqlConnectionMap.put(user, connection); + + if (JDBCDriver.equalsIgnoreCase("postgresql")) + psqlConnectionMap.put(user, connection); + } + + void getConnectionAttributes (String strLine) { + // Extract username, password and database from string + String[] connAttributes = strLine.split("\\s+"); + + // First two elements of array will be "--" and ("tsql" or "psql") + for (int i = 2; i < connAttributes.length; i++) { + String connAttribute = connAttributes[i]; + + if (connAttribute.contains("user=")) { + newUser = connAttribute.split("=")[1]; + } else if (connAttribute.contains("password=")) { + newPassword = connAttribute.split("=")[1]; + } else if (connAttribute.contains("database=")) { + newDatabase = connAttribute.split("=")[1]; + } else if (connAttribute.contains("currentSchema=")) { + searchPath = connAttribute.split("=")[1]; + } + } + + // If a connection attribute is not provided, we take the value from config file + if (newUser == null) + newUser = user; + + if (newPassword == null) + newPassword = password; + + if (newDatabase == null) + newDatabase = databaseName; + + if (newPhysicalDatabase == null) + newPhysicalDatabase = physicalDatabaseName; + } + + void resetConnectionAttributes () { + newUser = null; + newPassword = null; + newDatabase = null; + } + + Connection getTsqlConnection (String strLine, BufferedWriter bw, Logger logger) { + + Connection tsqlConnection = null; + + getConnectionAttributes(strLine); + + try { + // Use sqlserver JDBC driver + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + // if we already have opened a tsql connection then reuse that + tsqlConnection = tsqlConnectionMap.get(newUser + newPassword + newDatabase); + + if (tsqlConnection == null) { + // Create a new connection on tsql port and use that + String connectionString = createSQLServerConnectionString(URL, tsql_port, newDatabase, newUser, newPassword); + tsqlConnection = DriverManager.getConnection(connectionString); + + tsqlConnectionMap.put(newUser + newPassword + newDatabase, tsqlConnection); + } + + } catch (ClassNotFoundException e) { + logger.error("Class Not Found Exception: " + e.getMessage(), e); + } catch (SQLException se) { + handleSQLExceptionWithFile(se, bw, logger); + } + + resetConnectionAttributes(); + + return tsqlConnection; + } + + Connection getPsqlConnection (String strLine, BufferedWriter bw, Logger logger) { + + Connection psqlConnection = null; + + getConnectionAttributes(strLine); + + try { + // Use postgresql JDBC driver + Class.forName("org.postgresql.Driver"); + + // if we already have opened a psql connection then reuse that + psqlConnection = psqlConnectionMap.get(newUser + newPassword + newPhysicalDatabase + searchPath); + + // Create a new connection on psql port and use that + if (psqlConnection == null) { + String connectionString = createPostgreSQLConnectionString(URL, psql_port, newPhysicalDatabase, newUser, newPassword); + + if (searchPath != null) { + connectionString += ("¤tSchema=" + searchPath); + } + psqlConnection = DriverManager.getConnection(connectionString); + + psqlConnectionMap.put(newUser + newPassword + newPhysicalDatabase + searchPath, psqlConnection); + } + + } catch (ClassNotFoundException e) { + logger.error("Class Not Found Exception: " + e.getMessage(), e); + } catch (SQLException se) { + handleSQLExceptionWithFile(se, bw, logger); + } + + resetConnectionAttributes(); + + return psqlConnection; + } + + void closeConnectionsUtil (HashMap connectionMap, BufferedWriter bw, Logger logger) { + connectionMap.forEach( + (connectionAttribute, connection) -> { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + } + ); + } + + void closeConnections (BufferedWriter bw, Logger logger) { + closeConnectionsUtil(tsqlConnectionMap, bw, logger); + closeConnectionsUtil(psqlConnectionMap, bw, logger); + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCursor.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCursor.java new file mode 100644 index 00000000000..c54aa353b26 --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCursor.java @@ -0,0 +1,192 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; + +public class JDBCCursor { + + ResultSet cursor_bbl; + PreparedStatement pstmt_bbl; + + int[] setCursorOptions(int i, String[] result, Logger logger) { + int type = ResultSet.TYPE_FORWARD_ONLY; + int concur = ResultSet.CONCUR_READ_ONLY; + int holdability = 0; + + for(; i < result.length; i++) { + if (result[i].equalsIgnoreCase("TYPE_FORWARD_ONLY")) { + logger.info("Setting cursor type: forward-only"); + type = ResultSet.TYPE_FORWARD_ONLY; + } else if (result[i].equalsIgnoreCase("TYPE_SCROLL_SENSITIVE")) { + logger.info("Setting cursor type: scroll sensitive"); + type = ResultSet.TYPE_SCROLL_SENSITIVE; + } else if (result[i].equalsIgnoreCase("TYPE_SCROLL_INSENSITIVE")) { + logger.info("Setting cursor type: scroll insensitive"); + type = ResultSet.TYPE_SCROLL_INSENSITIVE; + } else if (result[i].equalsIgnoreCase("CONCUR_READ_ONLY")) { + logger.info("Setting cursor concurrency: read-only"); + concur = ResultSet.CONCUR_READ_ONLY; + } else if (result[i].equalsIgnoreCase("CONCUR_UPDATABLE")) { + logger.info("Setting cursor concurrency: updatable"); + concur = ResultSet.CONCUR_UPDATABLE; + } else if (result[i].equalsIgnoreCase("HOLD_CURSORS_OVER_COMMIT")) { + logger.info("Setting cursor holdability: hold cursors over commit"); + holdability = ResultSet.HOLD_CURSORS_OVER_COMMIT; + } else if (result[i].equalsIgnoreCase("CLOSE_CURSORS_AT_COMMIT")) { + logger.info("Setting cursor holdability: close cursors at commit"); + holdability = ResultSet.CLOSE_CURSORS_AT_COMMIT; + } else { + logger.error("Invalid Cursor attribute!"); + } + } + + return new int[]{type, concur, holdability}; + } + + void openCursor(Connection con_bbl, Logger logger, String[] result, String strLine, BufferedWriter bw) { + logger.info("Opening Cursor"); + + if (result[2].toLowerCase().startsWith("prepst")) { + openCursorOnPreparedStatement(con_bbl, logger, result, bw); + } else { + openCursorOnStatement(con_bbl, bw, logger, result); + } + } + + void openCursorOnPreparedStatement(Connection con_bbl, Logger logger, String[] result, BufferedWriter bw) { + // array with prepared statement relevant contents + List contents = new ArrayList<>(); + contents.add(result[2]); + contents.add(result[3]); + + int i; + + // while the result array contains prepared statement relevant delimiter + // add those array elements to contents' list + for (i = 4; i < result.length && result[i].contains("|-|"); i++) { + contents.add(result[i]); + } + + String[] contentsArray = contents.toArray(new String[0]); + + // set cursor options + int[] cursorOptions = setCursorOptions(i, result, logger); + int type = cursorOptions[0]; + int concur = cursorOptions[1]; + int holdability = cursorOptions[2]; + + String SQL = contentsArray[1]; + + try { + con_bbl.setHoldability(holdability); + if (!result[3].toLowerCase().startsWith("exec")) { + if (pstmt_bbl != null) pstmt_bbl.close(); + pstmt_bbl = con_bbl.prepareStatement(SQL, type, concur); + } + + JDBCPreparedStatement.set_bind_values(contentsArray, pstmt_bbl, bw, logger); + cursor_bbl = pstmt_bbl.executeQuery(); + bw.write("~~SUCCESS~~"); + bw.newLine(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + void openCursorOnStatement(Connection con_bbl, BufferedWriter bw, Logger logger, String[] result) { + // set cursor options + int[] cursorOptions = setCursorOptions(3, result, logger); + int type = cursorOptions[0]; + int concur = cursorOptions[1]; + int holdability = cursorOptions[2]; + + String SQL = result[2]; + Statement stmt_sql = null, stmt_bbl = null; + + try { + con_bbl.setHoldability(holdability); + stmt_bbl = con_bbl.createStatement(type, concur); + cursor_bbl = stmt_bbl.executeQuery(SQL); + bw.write("~~SUCCESS~~"); + bw.newLine(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + void cursorFetch(BufferedWriter bw, Logger logger, String[] result) { + + try { + if (cursor_bbl == null) { + bw.write("~~ERROR~~"); + bw.newLine(); + } else if (result[2].toLowerCase().startsWith("beforefirst")) { + cursor_bbl.beforeFirst(); + bw.write("~~SUCCESS~~"); + bw.newLine(); + } else if (result[2].toLowerCase().startsWith("afterlast")) { + cursor_bbl.afterLast(); + bw.write("~~SUCCESS~~"); + bw.newLine(); + } else if (result[2].toLowerCase().startsWith("first")) { + cursor_bbl.first(); + CompareResults.writeCursorResultSetToFile(bw, cursor_bbl, logger); + } else if (result[2].toLowerCase().startsWith("last")) { + cursor_bbl.last(); + CompareResults.writeCursorResultSetToFile(bw, cursor_bbl, logger); + } else if (result[2].toLowerCase().startsWith("next")) { + cursor_bbl.next(); + CompareResults.writeCursorResultSetToFile(bw, cursor_bbl, logger); + } else if (result[2].toLowerCase().startsWith("prev")) { + cursor_bbl.previous(); + CompareResults.writeCursorResultSetToFile(bw, cursor_bbl, logger); + } else if (result[2].toLowerCase().startsWith("abs") || result[2].toLowerCase().startsWith("rel")) { + if (result.length < 4 || result[3].length() == 0 || result[3].matches("[ \\t]+")) { + bw.write("~~ERROR~~"); + bw.newLine(); + } else { + int pos = Integer.parseInt(result[3]); + + if (result[2].toLowerCase().startsWith("abs")) { + cursor_bbl.absolute(pos); + } else { + cursor_bbl.relative(pos); + } + CompareResults.writeCursorResultSetToFile(bw, cursor_bbl, logger); + } + } else { + bw.write("~~ERROR~~"); + bw.newLine(); + } + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + void cursorClose(BufferedWriter bw, Logger logger) { + logger.info("Closing cursors"); + + try { + if (cursor_bbl != null) cursor_bbl.close(); + bw.write("~~SUCCESS~~"); + bw.newLine(); + } catch(SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCPreparedStatement.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCPreparedStatement.java new file mode 100644 index 00000000000..ae0a01155e4 --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCPreparedStatement.java @@ -0,0 +1,191 @@ +package com.sqlsamples; + +import com.microsoft.sqlserver.jdbc.SQLServerDataTable; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import microsoft.sql.DateTimeOffset; +import org.apache.logging.log4j.Logger; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.math.BigDecimal; +import java.sql.*; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Locale; + +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; + +public class JDBCPreparedStatement { + + PreparedStatement pstmt_bbl; + + void createPreparedStatements(Connection con_bbl, String SQL, BufferedWriter bw, Logger logger) { + try { + pstmt_bbl = con_bbl.prepareStatement(SQL); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void closePreparedStatements(BufferedWriter bw, Logger logger) { + try { + if (pstmt_bbl != null) pstmt_bbl.close(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + // function to write output of executed prepared statement to a file + void testPreparedStatementWithFile(String[] result, BufferedWriter bw, String strLine, Logger logger){ + try { + bw.write(strLine); + bw.newLine(); + set_bind_values(result, pstmt_bbl, bw, logger); + + boolean resultSetExist = false; + int resultsProcessed = 0; + try { + resultSetExist = pstmt_bbl.execute(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + resultsProcessed++; + } + CompareResults.processResults(pstmt_bbl, bw, resultsProcessed, resultSetExist, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } + + // method to set values of bind variables in a prepared statement + static void set_bind_values(String[] result, PreparedStatement pstmt, BufferedWriter bw, Logger logger) { + + for (int j = 2; j < (result.length); j++) { + String[] parameter = result[j].split("\\|-\\|", -1); + + try{ + /* TODO: Add more data types here as we support them */ + if(parameter[2].equalsIgnoreCase("")){ + pstmt.setNull(j - 1, CompareResults.SQLtoJDBCDataTypeMapping(parameter[0])); + } else if (parameter[0].equalsIgnoreCase("int")) { + // if there is decimal point, remove everything after the point + if (parameter[2].indexOf('.') != -1) parameter[2] = parameter[2].substring(0, parameter[2].indexOf('.') - 1); + pstmt.setInt(j - 1, Integer.parseInt(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("string") + || parameter[0].equalsIgnoreCase("char") + || parameter[0].equalsIgnoreCase("nchar") + || parameter[0].equalsIgnoreCase("varchar") + || parameter[0].equalsIgnoreCase("nvarchar") + || parameter[0].equalsIgnoreCase("uniqueidentifier") + || parameter[0].equalsIgnoreCase("varcharmax") + || parameter[0].equalsIgnoreCase("nvarcharmax")) { + pstmt.setString(j - 1, parameter[2]); + } else if (parameter[0].equalsIgnoreCase("boolean") + || parameter[0].equalsIgnoreCase("bit")) { + pstmt.setBoolean(j - 1, Boolean.parseBoolean(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("long") + || parameter[0].equalsIgnoreCase("bigint")) { + pstmt.setLong(j - 1, Long.parseLong(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("double") + || parameter[0].equalsIgnoreCase("float")) { + pstmt.setDouble(j - 1, Double.parseDouble(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("unsigned_char") + || parameter[0].equalsIgnoreCase("smallint") + || parameter[0].equalsIgnoreCase("tinyint")) { + pstmt.setShort(j - 1, Short.parseShort(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("real")) { + pstmt.setFloat(j - 1, Float.parseFloat(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("byte")) { + pstmt.setByte(j - 1, Byte.parseByte(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("binary") + || parameter[0].equalsIgnoreCase("varbinary") + || parameter[0].equalsIgnoreCase("timestamp") + || parameter[0].equalsIgnoreCase("udt")) { + byte[] byteArray = parameter[2].getBytes(); + pstmt.setBytes(j - 1, byteArray); + } else if (parameter[0].equalsIgnoreCase("decimal") + || parameter[0].equalsIgnoreCase("money") + || parameter[0].equalsIgnoreCase("smallmoney") + || parameter[0].equalsIgnoreCase("numeric")) { + DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(Locale.US); + format.setParseBigDecimal(true); + // remove dollar sign else parsing decimal from this will throw exception + String decimalString = parameter[2].replace("$", ""); + BigDecimal number = (BigDecimal) format.parse(decimalString); + pstmt.setBigDecimal(j - 1, number); + } else if (parameter[0].equalsIgnoreCase("datetime") + || parameter[0].equalsIgnoreCase("datetime2") + || parameter[0].equalsIgnoreCase("smalldatetime")) { + pstmt.setTimestamp(j - 1, Timestamp.valueOf(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("date")) { + pstmt.setDate(j - 1, Date.valueOf(parameter[2])); + } else if (parameter[0].equalsIgnoreCase("time")) { + pstmt.setTime(j - 1, Time.valueOf(LocalTime.parse(parameter[2]))); + } else if (parameter[0].equalsIgnoreCase("datetimeoffset")) { + SQLServerPreparedStatement ssPstmt = (SQLServerPreparedStatement) pstmt; + ssPstmt.setDateTimeOffset(j - 1, DateTimeOffset.valueOf(Timestamp.valueOf(parameter[2]), 0)); + pstmt = ssPstmt; + } else if (parameter[0].equalsIgnoreCase("text") + || parameter[0].equalsIgnoreCase("ntext")) { + Reader r = new FileReader(parameter[2]); + pstmt.setCharacterStream(j - 1, r); + } else if (parameter[0].equalsIgnoreCase("image")) { + File file = new File(parameter[2]); + BufferedImage bImage = ImageIO.read(file); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ImageIO.write(bImage, "jpg", bos ); + byte [] byteArray = bos.toByteArray(); + pstmt.setBytes(j - 1, byteArray); + } else if (parameter[0].equalsIgnoreCase("xml")) { + SQLXML sqlxml = pstmt.getConnection().createSQLXML(); + sqlxml.setString(parameter[2]); + pstmt.setSQLXML(j - 1, sqlxml); + } else if (parameter[0].equalsIgnoreCase("tvp")) { + FileInputStream fstream = new FileInputStream(parameter[2]); + DataInputStream in = new DataInputStream(fstream); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + + SQLServerDataTable sourceDataTable = new SQLServerDataTable(); + + // first line of file has table columns and their data types + String strLine = br.readLine(); + + String[] columnMetaData = strLine.split(","); + + for (String columnMetaDatum : columnMetaData) { + String[] column = columnMetaDatum.split("-"); + sourceDataTable.addColumnMetadata(column[0], CompareResults.SQLtoJDBCDataTypeMapping(column[1])); + } + + // process and add all rows to data table + while ((strLine = br.readLine()) != null) { + String[] row = strLine.split(","); + ArrayList rowTuple = new ArrayList<>(); + + for(int i = 0; i < row.length; i++) { + String columnDataType = columnMetaData[i].split("-")[1]; + Object value = CompareResults.parse_data(row[i], columnDataType, logger); + rowTuple.add(value); + } + sourceDataTable.addRow(rowTuple.toArray()); + } + + SQLServerPreparedStatement ssPstmt = (SQLServerPreparedStatement) pstmt; + ssPstmt.setStructured(j - 1, parameter[1], sourceDataTable); + pstmt = ssPstmt; + } + } catch (SQLException se) { + handleSQLExceptionWithFile(se, bw, logger); + } catch (FileNotFoundException e) { + logger.error("File Not Found Exception: " + e.getMessage(), e); + } catch (IOException e) { + logger.error("IO Exception: " + e.getMessage(), e); + } catch (ParseException e) { + logger.error("Parse Exception: " + e.getMessage(), e); + } + } + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCStatement.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCStatement.java new file mode 100644 index 00000000000..4f012c29793 --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCStatement.java @@ -0,0 +1,53 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; + +public class JDBCStatement { + + Statement stmt_bbl; + + void createStatements(Connection con_bbl, BufferedWriter bw, Logger logger) { + try { + stmt_bbl = con_bbl.createStatement(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void closeStatements(BufferedWriter bw, Logger logger) { + try { + if (stmt_bbl != null) stmt_bbl.close(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + // function to write output of executed statement to a file + void testStatementWithFile(String SQL, BufferedWriter bw, String strLine, Logger logger){ + try { + bw.write(strLine); + bw.newLine(); + + boolean resultSetExist = false; + int resultsProcessed = 0; + try { + resultSetExist = stmt_bbl.execute(SQL); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + resultsProcessed++; + } + CompareResults.processResults(stmt_bbl, bw, resultsProcessed, resultSetExist, logger); + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCTransaction.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCTransaction.java new file mode 100644 index 00000000000..24cf748d7bb --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCTransaction.java @@ -0,0 +1,121 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Savepoint; +import java.util.*; + +import static com.sqlsamples.HandleException.handleSQLExceptionWithFile; +import static java.sql.Connection.*; + +public class JDBCTransaction { + + private HashMap> savepointMap = new HashMap<>(); + + void beginTransaction(Connection con_bbl, BufferedWriter bw, Logger logger) { + logger.info("Beginning transaction"); + + try { + con_bbl.setAutoCommit(false); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void transactionCommit(Connection con_bbl, BufferedWriter bw, Logger logger) { + logger.info("Committing transaction"); + + try { + con_bbl.commit(); + con_bbl.setAutoCommit(true); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void transactionRollback(Connection con_bbl, BufferedWriter bw, Logger logger) { + logger.info("Rolling back transaction"); + + try { + con_bbl.rollback(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void transactionRollbackToSavepoint(Connection con_bbl, String[] result, BufferedWriter bw, Logger logger) { + logger.info("Rolling back to savepoint " + result[2]); + if (!savepointMap.containsKey(result[2])) { + logger.error("Savepoint with the name " + result[2] + " does not exist!"); + } else { + try { + con_bbl.rollback(savepointMap.get(result[2]).getKey()); + con_bbl.setAutoCommit(true); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + } + + + void setTransactionSavepoint(Connection con_bbl, BufferedWriter bw, Logger logger){ + // unnamed savepoint + logger.info("Setting unnamed savepoint"); + + try { + con_bbl.setSavepoint(); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void setTransactionNamedSavepoint(Connection con_bbl, String[] result, BufferedWriter bw, Logger logger) { + // named savepoint + logger.info("Setting savepoint " + result[2]); + + try { + Savepoint bblSavepoint = con_bbl.setSavepoint(result[2]); + savepointMap.put(result[2], new AbstractMap.SimpleEntry<>(bblSavepoint, null)); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void setTransactionIsolationLevelUtil(Connection con, int isolationLevel, BufferedWriter bw, Logger logger) { + try { + con.setTransactionIsolation(isolationLevel); + } catch (SQLException e) { + handleSQLExceptionWithFile(e, bw, logger); + } + } + + void setTransactionIsolationLevel(Connection con_bbl, String[] result, BufferedWriter bw, Logger logger) { + if (result.length < 3 || result[2].length() == 0 || result[2].matches("[ \\t]+")) { + logger.error("No isolation level specified!"); + } else { + String isolationLevel = result[2]; + + if (isolationLevel.toLowerCase().startsWith("ru")) { + logger.info("Transaction isolation level set to TRANSACTION_READ_UNCOMMITTED"); + setTransactionIsolationLevelUtil(con_bbl, TRANSACTION_READ_UNCOMMITTED, bw, logger); + } else if (isolationLevel.toLowerCase().startsWith("rc")) { + logger.info("Transaction isolation level set to TRANSACTION_READ_COMMITTED"); + setTransactionIsolationLevelUtil(con_bbl, TRANSACTION_READ_COMMITTED, bw, logger); + } else if (isolationLevel.toLowerCase().startsWith("rr")) { + logger.info("Transaction isolation level set to TRANSACTION_REPEATABLE_READ"); + setTransactionIsolationLevelUtil(con_bbl, TRANSACTION_REPEATABLE_READ, bw, logger); + } else if (isolationLevel.toLowerCase().startsWith("se")) { + logger.info("Transaction isolation level set to TRANSACTION_SERIALIZABLE"); + setTransactionIsolationLevelUtil(con_bbl, TRANSACTION_SERIALIZABLE, bw, logger); + } else if (isolationLevel.toLowerCase().startsWith("sn")) { + logger.info("Transaction isolation level set to TRANSACTION_SNAPSHOT"); + setTransactionIsolationLevelUtil(con_bbl, TRANSACTION_READ_COMMITTED + 4094, bw, logger); + } else { + logger.error("Invalid Transaction isolation level! Set from ru, rc, rr or s"); + } + } + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/Statistics.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/Statistics.java new file mode 100644 index 00000000000..270938480ba --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/Statistics.java @@ -0,0 +1,34 @@ +package com.sqlsamples; + +import java.util.*; + +import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; + +public class Statistics { + DescriptiveStatistics descriptiveStatistics; + static ArrayList exec_times = new ArrayList<>(); + + public Statistics(double[] values) { + descriptiveStatistics = new DescriptiveStatistics(values); + } + + public double mean() { + return descriptiveStatistics.getMean(); + } + + public double median() { + return descriptiveStatistics.getPercentile(50); + } + + public double stddev() { + return descriptiveStatistics.getStandardDeviation(); + } + + public double minimum() { + return descriptiveStatistics.getMin(); + } + + public double maximum() { + return descriptiveStatistics.getMax(); + } +} diff --git a/contrib/test/JDBC/src/main/java/com/sqlsamples/batch_run.java b/contrib/test/JDBC/src/main/java/com/sqlsamples/batch_run.java new file mode 100644 index 00000000000..057e6733eee --- /dev/null +++ b/contrib/test/JDBC/src/main/java/com/sqlsamples/batch_run.java @@ -0,0 +1,282 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.Logger; + +import java.io.*; +import java.sql.*; +import java.util.*; + +import static java.util.Objects.isNull; + +import static com.sqlsamples.Config.*; +import static com.sqlsamples.Statistics.exec_times; + +public class batch_run { + + // method that iterates through an input file and writes output to corresponding .out file. + static void batch_run_sql(Connection con_bbl, BufferedWriter bw, String testFilePath, Logger logger) { + + boolean isSQLFile = testFilePath.contains(".sql"); + boolean isCrossDialectFile = false; + boolean tsqlDialect = false; + boolean psqlDialect = false; + + if (testFilePath.contains(".mix")) { + isCrossDialectFile = true; + isSQLFile = true; // we want to treat GO as a batch separator in this case + } + + // initializing objects + JDBCAuthentication jdbcAuthentication = new JDBCAuthentication(); + JDBCCallableStatement jdbcCallableStatement = new JDBCCallableStatement(); + JDBCCursor jdbcCursor = new JDBCCursor(); + JDBCPreparedStatement jdbcPreparedStatement = new JDBCPreparedStatement(); + JDBCStatement jdbcStatement = new JDBCStatement(); + JDBCTransaction jdbcTransaction = new JDBCTransaction(); + JDBCCrossDialect jdbcCrossDialect = null; + + if (isCrossDialectFile) + jdbcCrossDialect = new JDBCCrossDialect(con_bbl); + + int passed = 0; + int failed = 0; + + try { + String SQL; // holds the SQL statement + StringBuilder sqlBatch = new StringBuilder(); + String strLine; // holds the current line which is getting executed + FileInputStream fstream; // stream of input file with the SQL batch queries to be tested + DataInputStream in; + BufferedReader br; + + fstream = new FileInputStream(testFilePath); + // get the object of DataInputStream + in = new DataInputStream(fstream); + br = new BufferedReader(new InputStreamReader(in)); + + // each iteration will process one line(SQL statement) from input file and compare result sets from SQL server and Babel instance + while ((strLine = br.readLine()) != null) { + // if line has no characters, or is commented out, or is a dotnet auth statement, proceed to next line + if (strLine.length() < 1 || strLine.startsWith("#") || strLine.startsWith("dotnet_auth")) { + if (strLine.length() < 1 || strLine.startsWith("#")) { + bw.write(strLine); + bw.newLine(); + } + continue; + } + + long startTime = System.nanoTime(); + + // if line starts with keyword "prepst", it means it is either a prep exec statement or an exec statement + if (strLine.startsWith("prepst")) { + + // Convert .NET input file format for prepared statement to JDBC + strLine = strLine.replaceAll("@[a-zA-Z0-9]+", "?"); + + String[] result; + // split wrt delimiter + result = strLine.split("#!#"); + // if an exec statement, set bind variables and execute query + if ((result[1].startsWith("exec")) || (result[1].startsWith("call"))) { + + StringBuilder params = new StringBuilder(); + // populate params with bind variables' types and values (used for logging only) + for (int i = 2; i < result.length; i++) { + params.append(result[i]); + if (i < result.length - 1) params.append(", "); + } + + logger.info("Executing an already prepared statement with the following bind variables: " + params.toString()); + jdbcPreparedStatement.testPreparedStatementWithFile(result, bw, strLine, logger); + + } else if (!result[1].equals("exec")) { + + jdbcPreparedStatement.closePreparedStatements(bw, logger); + + SQL = result[1]; + logger.info("Preparing the query: " + SQL); + + StringBuilder params = new StringBuilder(); + + for (int i = 2; i < result.length; i++) { + params.append(result[i]); + if (i < result.length - 1) params.append(", "); + } + + jdbcPreparedStatement.createPreparedStatements(con_bbl, SQL, bw, logger); + + logger.info("Executing with the bind variables " + params.toString()); + + jdbcPreparedStatement.testPreparedStatementWithFile(result, bw, strLine, logger); + } + + // if line starts with keyword "storedproc", it means it is a stored procedure + } else if (strLine.startsWith("storedproc")) { + + String[] result = strLine.split("#!#"); + + if (result[1].startsWith("prep")) { + + // close existing callable statements + jdbcCallableStatement.closeCallableStatements(bw, logger); + + SQL = result[2]; + StringBuilder bindparam = new StringBuilder(); + + for ( int i = 0; i <= result.length - 4; i++) { + if (i == 0) { + bindparam.append("("); + } + + if (i != result.length - 4) { + bindparam.append("?,"); + } else { + bindparam.append("?)"); + } + } + + // Convert .NET input file format for callable statement to JDBC + SQL = "{ call " + SQL + bindparam + " }"; + + logger.info("Preparing and executing call: " + SQL); + + jdbcCallableStatement.createCallableStatements(con_bbl, SQL, bw, logger); + jdbcCallableStatement.testCallableStatementWithFile(result, bw, strLine, logger); + + } else { + + logger.info("Executing an already prepared call"); + jdbcCallableStatement.testCallableStatementWithFile(result, bw, strLine, logger); + } + + // if line starts with keyword "txn", it means it is a transaction + } else if (strLine.startsWith("txn")) { + String[] result = strLine.split("#!#"); + + bw.write(strLine); + bw.newLine(); + + if (result[1].toLowerCase().startsWith("begin")) { + jdbcTransaction.beginTransaction(con_bbl, bw, logger); + } else if (result[1].toLowerCase().startsWith("commit")) { + jdbcTransaction.transactionCommit(con_bbl, bw, logger); + } else if (result[1].toLowerCase().startsWith("rollback")) { + if (result.length < 3 || result[2].length() == 0 || result[2].matches("[ \\t]+")) { + jdbcTransaction.transactionRollback(con_bbl, bw, logger); + } else { + // rolling back to a named savepoint + jdbcTransaction.transactionRollbackToSavepoint(con_bbl, result, bw, logger); + } + } else if (result[1].toLowerCase().startsWith("savepoint")) { + if (result.length < 3 || result[2].length() == 0 || result[2].matches("[ \\t]+")) { + jdbcTransaction.setTransactionSavepoint(con_bbl, bw, logger); + } else { + jdbcTransaction.setTransactionNamedSavepoint(con_bbl, result, bw, logger); + } + } else if (result[1].toLowerCase().startsWith("isolation")) { + jdbcTransaction.setTransactionIsolationLevel(con_bbl, result, bw, logger); + } else { + logger.error("Unrecognized Transaction! Either statement syntax is " + + "invalid or the test suite does not handle this query at the time"); + } + // assuming transactions don't return a result set + bw.write("~~SUCCESS~~"); + bw.newLine(); + + // if line starts with keyword "cursor", it means it is a cursor operation + } else if (strLine.startsWith("cursor")) { + + // Convert .NET input file format for prepared statement to JDBC + // Used if cursor opened on a result set from a prepared statement + if (strLine.contains("prepst")) { + strLine = strLine.replaceAll("@[a-zA-Z0-9]+", "?"); + } + + String[] result = strLine.split("#!#"); + + bw.write(strLine); + bw.newLine(); + + if (result[1].toLowerCase().startsWith("open")) { + jdbcCursor.openCursor(con_bbl, logger, result, strLine, bw); + + } else if (result[1].toLowerCase().startsWith("fetch")) { + jdbcCursor.cursorFetch(bw, logger, result); + } else if (result[1].toLowerCase().startsWith("close")) { + jdbcCursor.cursorClose(bw, logger); + } else { + logger.error("Unrecognized Cursor action! Either statement syntax is " + + "invalid or the test suite does not handle this action at the time"); + } + + } else if (strLine.startsWith("java_auth")) { + jdbcAuthentication.javaAuthentication(strLine, bw, logger); + + } else if (strLine.startsWith("include") && !isSQLFile) { + String[] result = strLine.split("#!#"); + String filePath = new File(result[1]).getAbsolutePath(); + + // Run the iterative parse and compare loop for test file + batch_run_sql(con_bbl, bw, filePath, logger); + + } else if (isCrossDialectFile && ( (tsqlDialect = strLine.toLowerCase().startsWith("-- tsql")) || + (psqlDialect = strLine.toLowerCase().startsWith("-- psql")))) { + // Cross dialect testing + + bw.write(strLine); + bw.newLine(); + + /* + * If tsql/psql connection with given username exists, reuse it. + * Else, create a new tsql/psql connection with that username and + * assign it to existing connection object. + */ + if (tsqlDialect) { + con_bbl = jdbcCrossDialect.getTsqlConnection(strLine, bw, logger); + } else if (psqlDialect) { + con_bbl = jdbcCrossDialect.getPsqlConnection(strLine, bw, logger); + } + + // Ensure connection object is not null + assert(con_bbl != null); + + } else { + // execute statement as a normal SQL statement + if (isSQLFile) { + if (!strLine.equalsIgnoreCase("GO")) { + sqlBatch.append(strLine).append(System.lineSeparator()); + continue; + } else { + SQL = sqlBatch.toString(); + sqlBatch = new StringBuilder(); + bw.write(SQL); + } + } else { + SQL = strLine; + } + + logger.info("Executing: " + SQL); + + jdbcStatement.closeStatements(bw, logger); + jdbcStatement.createStatements(con_bbl, bw, logger); + jdbcStatement.testStatementWithFile(SQL, bw, strLine, logger); + } + + long endTime = System.nanoTime(); + long duration = (endTime - startTime); + exec_times.add(duration); + } + } catch (IOException ioe) { + logger.error("IO Exception: " + ioe.getMessage(), ioe); + } + + // close existing statements if any + jdbcStatement.closeStatements(bw, logger); + jdbcPreparedStatement.closePreparedStatements(bw, logger); + jdbcCallableStatement.closeCallableStatements(bw, logger); + + // close connection used for cross dialect queries + if (jdbcCrossDialect != null) + jdbcCrossDialect.closeConnections(bw, logger); + } +} diff --git a/contrib/test/JDBC/src/main/resources/config.txt b/contrib/test/JDBC/src/main/resources/config.txt new file mode 100644 index 00000000000..fcb3a283a6f --- /dev/null +++ b/contrib/test/JDBC/src/main/resources/config.txt @@ -0,0 +1,40 @@ +# SET THIS CONNECTION STRING TO GENERATE THE EXPECTED OUTPUT FILE +URL = localhost +tsql_port = 1433 +psql_port = 5432 +databaseName = master +physicalDatabaseName = jdbc_testdb +user = jdbc_user +password = 12345678 + +# PATH TO INPUT TEST FILES FOLDER +inputFilesPath = input + +# SPECIFY IF YOU WISH TO PRINT ALL THE SUMMARY LOGS/DIFF TO CONSOLE +printLogsToConsole = false + +# SPECIFY WHICH JDBC DRIVER TO USE. CHOOSE FROM "postgresql" OR "sqlserver" +driver = sqlserver + +# SPECIFY IF YOU WANT TO GENERATE A PERFORMANCE REPORT +performanceTest = false + +# TO OVERCOME CERTAIN LIMITATIONS OF BABELFISH, INTRODUCED THE FOLLOWING 2 FLAGS +# SET THESE FLAGS TO "false" WHILE GENERATING OUTPUT FILES YOU WISH TO CHECK INSIDE +# THE "expected" FOLDER. THESE FLAGS ARE ONLY THERE FOR PURPOSE OF DEBUGGING + +# BABEL-415 AND BABEL-681 +# SPECIFY IF COLUMN NAMES SHOULD BE ACCOMPANYING RESULT SETS IN OUTPUT FILE +outputColumnName = false + +# BABEL-780 AND BABEL-1012 +# SPECIFY IF ERROR CODE SHOULD BE DISPLAYED IN OUTPUT FILE +outputErrorCode = true + +############################################ WHICH TEST TO RUN ############################################ + +# PLEASE LOOK AT THE jdbc_schedule FILE +scheduleFile = ./jdbc_schedule + +# Where to find the input, output, expected, etc. +testFileRoot = ./ diff --git a/contrib/test/JDBC/src/main/resources/junit-platform.properties b/contrib/test/JDBC/src/main/resources/junit-platform.properties new file mode 100644 index 00000000000..b776c389728 --- /dev/null +++ b/contrib/test/JDBC/src/main/resources/junit-platform.properties @@ -0,0 +1,2 @@ +junit.jupiter.execution.parallel.enabled = false +junit.jupiter.execution.parallel.mode.default = concurrent diff --git a/contrib/test/JDBC/src/test/java/com/sqlsamples/TestQueryFile.java b/contrib/test/JDBC/src/test/java/com/sqlsamples/TestQueryFile.java new file mode 100644 index 00000000000..8a8dad25044 --- /dev/null +++ b/contrib/test/JDBC/src/test/java/com/sqlsamples/TestQueryFile.java @@ -0,0 +1,356 @@ +package com.sqlsamples; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import java.io.*; +import java.sql.*; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.Date; +import java.util.stream.Stream; + +import static com.sqlsamples.Config.*; + +import static com.sqlsamples.Statistics.exec_times; + +public class TestQueryFile { + + static String timestamp = new SimpleDateFormat("dd-MM-yyyy'T'HH:mm:ss.SSS").format(new Date()); + static String generatedFilesDirectoryPath = testFileRoot + "/expected/"; + static String sqlServerGeneratedFilesDirectoryPath = testFileRoot + "/sql_expected/"; + static String outputFilesDirectoryPath = testFileRoot + "/output/"; + static Logger summaryLogger = LogManager.getLogger("testSummaryLogger"); //logger to write summary of tests executed + static Logger logger = LogManager.getLogger("eventLoggger"); //logger to log any test framework events + static ArrayList> summaryMap = new ArrayList<>(); //map to store test names and status + static ArrayList>> testCountMap = new ArrayList<>(); //map to store test names and number of tests passed + static ArrayList fileList = new ArrayList<>(); + static HashMap filePaths = new HashMap<>(); //map to store fileName and their paths + static ArrayList testsToRun = new ArrayList(); + static HashSet testsToIgnore = new HashSet(); + static File diffFile; + + String inputFileName; + Connection connection_bbl; // connection object for Babel instance + + public static void createTestFilesListUtil(String directory, String testToRun) { + File dir = new File(directory); + + File[] directoryListing = dir.listFiles(); + if (directoryListing != null) { + for (File file : directoryListing) { + + if (file.getName().startsWith(".")) { + continue; + } + + if (file.isDirectory()) { + createTestFilesListUtil(file.getAbsolutePath(), testToRun); + } else { + // append filename to arraylist and omit extension + String fileName = file.getName().replaceFirst("[.][^.]+$", ""); + + if (testToRun.equals("all") || testToRun.equals(fileName)) { + fileList.add(fileName); + filePaths.put(fileName, file.getAbsolutePath()); + } + } + } + } + } + + public static void createTestFilesList(String directory) { + for(String testToRun : testsToRun) { + /* prefix indicates it is a postgres command */ + if (testToRun.startsWith("cmd#!#")) { + fileList.add(testToRun); + } + else if (testToRun.startsWith("ignore#!#")) { + testsToIgnore.add(testToRun.split("#!#", -1)[1]); + } else { + createTestFilesListUtil(directory, testToRun); + } + } + } + + public static void execCommand(String cmd) throws ClassNotFoundException { + String[] command = cmd.split("#!#"); + + String URL = properties.getProperty("URL"); + String tsql_port = properties.getProperty("tsql_port"); + String psql_port = properties.getProperty("psql_port"); + String databaseName = properties.getProperty("databaseName"); + String physicalDatabaseName = properties.getProperty("physicalDatabaseName"); + String user = properties.getProperty("user"); + String password = properties.getProperty("password"); + String connectionString; + + if (command[1].equalsIgnoreCase("sqlserver")) { + /* if are trying to execute a t-sql command but we are using postgres driver */ + if(JDBCDriver.equalsIgnoreCase("postgresql")) { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); //use sql server driver + } + connectionString = createSQLServerConnectionString(URL, tsql_port, databaseName, user, password); + summaryLogger.info("Execute T-SQL Command: " + command[2]); + } else if (command[1].equalsIgnoreCase("postgresql")) { + /* if are trying to execute a postgres command but we are using sqlserver driver */ + if(JDBCDriver.equalsIgnoreCase("sqlserver")) { + Class.forName("org.postgresql.Driver"); //use postgres driver + } + connectionString = createPostgreSQLConnectionString(URL, psql_port, physicalDatabaseName, user, password); + summaryLogger.info("Execute Postgres Command: " + command[2]); + } else { + summaryLogger.error("Invalid cmd type. Choose from \"sqlserver\" or \"postgresql\""); + return; + } + + try { + DriverManager.getConnection(connectionString).createStatement().executeQuery(command[2]); + } catch (SQLException e) { + summaryLogger.error(e.getMessage()); + } + + /* use the driver specified in config.txt */ + selectDriver(); + } + + static void selectDriver() throws ClassNotFoundException { + if (JDBCDriver.equalsIgnoreCase("postgresql")) { + Class.forName("org.postgresql.Driver"); + } else if (JDBCDriver.equalsIgnoreCase("sqlserver")) { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + } else throw new ClassNotFoundException("Driver not found for: " + JDBCDriver +". Choose from either 'sqlserver' or 'postgresql'"); + } + + // test data is seeded from here + static Stream inputFileNames() { + File dir = new File(inputFilesDirectoryPath); + File scheduleFile = new File(scheduleFileName); + + try (BufferedReader br = new BufferedReader(new FileReader(scheduleFile))) { + String line; + while ((line = br.readLine()) != null) { + if (!line.startsWith("#") && line.trim().length() > 0) + testsToRun.add(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + + createTestFilesList(dir.getAbsolutePath()); + return fileList.stream(); + } + + // configure logger for summary file and setup initial directory structure + @BeforeAll + public static void setup() throws IOException { + String testRunDir = "Info/" + timestamp + "/"; + new File(testRunDir).mkdirs(); + + new File(outputFilesDirectoryPath).mkdirs(); + diffFile = new File(testRunDir + timestamp + ".diff"); + diffFile.createNewFile(); + + String logSummaryFile = testRunDir + timestamp + "_runSummary"; + configureSummaryLogger(logSummaryFile, summaryLogger); + + String logFile = testRunDir + timestamp; + configureLogger(logFile, logger); + + summaryLogger.info("Started test suite. Now running tests..."); + } + + // close connections that are not null after every test + @AfterEach + public void closeConnections() throws SQLException { + if (connection_bbl != null) connection_bbl.close(); + } + + // write summary log after all tests have been executed + @AfterAll + public static void logSummary() { + int passed = 0; + int failed = 0; + int maxlen = 0; + String testStats = ""; + + summaryLogger.info("All tests executed successfully!"); + + summaryLogger.info("###########################################################################"); + summaryLogger.info("################################ SUMMARY ################################"); + summaryLogger.info("###########################################################################"); + + // get max length of test name (used for pretty print in logs) + for(AbstractMap.SimpleEntry set: summaryMap){ + String testMethodName = set.getKey(); + int len = testMethodName.length(); + if(len > maxlen) maxlen = len; + } + + // for every test in map, log test name and status + int i; + + for (i = 0; i < summaryMap.size(); i++) { + String testMethodName = summaryMap.get(i).getKey(); + + boolean status = summaryMap.get(i).getValue(); + int testsPassed = 0, totalTests = 0; + + if(status){ + //extra spaces for right side padding + summaryLogger.info((testMethodName + ":" + " ").substring(0, maxlen+2) + "Passed!" + testStats); + passed++; + } + else{ + //extra spaces for right side padding + summaryLogger.info((testMethodName + ":" + " ").substring(0, maxlen+2) + "Failed!" + testStats); + failed++; + } + } + + summaryLogger.info("###########################################################################"); + summaryLogger.info("TOTAL TESTS:\t" + (passed + failed)); + summaryLogger.info("TESTS PASSED:\t" + passed); + summaryLogger.info("TESTS FAILED:\t" + failed); + summaryLogger.info("###########################################################################"); + + if (performanceTest) { + performanceSummary(exec_times); + } + + // print absolute path to file containing diff + if(failed > 0) { + summaryLogger.info("Output diff can be found in '" + diffFile.getAbsolutePath() + "'"); + } + + // displays content of file holding diff to console + if(printLogsToConsole) { + System.out.println("############################# DIFF STARTS HERE #############################"); + try (BufferedReader br = new BufferedReader(new FileReader(diffFile))) { + String line; + while ((line = br.readLine()) != null) { + System.out.println(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + System.out.println("############################## DIFF ENDS HERE ##############################"); + } + } + + // generates performance report which has mean, median, mode of query execution times + static void performanceSummary(ArrayList exec_times) { + + com.sqlsamples.Statistics stats = new com.sqlsamples.Statistics(exec_times.stream().filter(Objects::nonNull).mapToDouble(i -> i).toArray()); + + HashMap statisticsHashMap = new HashMap() { + { + put(connectionString, stats); + } + }; + + String testRunDir = "Info/" + timestamp + "/"; + + String CSVFileName = testRunDir + timestamp + "_performanceReport"; + com.sqlsamples.ExportResults.createCSVFile(CSVFileName, statisticsHashMap); + } + + private static boolean compareOutFiles (File outputFile, File expectedFile) { + String outputFilePath = outputFile.getAbsolutePath(); + String expectedFilePath = expectedFile.getAbsolutePath(); + ProcessBuilder diffProcessBuilder; + + // if expected file is generated from SQL Server, do not compare error code and message + if (expectedFilePath.contains("sql_expected")) { + diffProcessBuilder = new ProcessBuilder("diff", "-a", "-u", "-I", "~~ERROR", expectedFilePath, outputFilePath); + } else { + diffProcessBuilder = new ProcessBuilder("diff", "-a", "-u", expectedFilePath, outputFilePath); + } + + try { + diffProcessBuilder.redirectError(ProcessBuilder.Redirect.appendTo(diffFile)); + diffProcessBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(diffFile)); + int exitCode = diffProcessBuilder.start().waitFor(); + + switch (exitCode) { + case 0: + return true; + + case 1: + return false; + + case 2: + System.out.println("There was some trouble when the diff command was executed!"); + return false; + + default: + System.out.println("Unknown exit code encountered while running diff!"); + return false; + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + return false; + } + + // parameterized test + @ParameterizedTest(name="{0}") + @MethodSource("inputFileNames") + public void TestQueryBatch(String inputFileName) throws SQLException, ClassNotFoundException, Throwable { + + // if it is a command and not a fileName + if (inputFileName.startsWith("cmd#!#")) { + execCommand(inputFileName); + return; + } else if (testsToIgnore.contains(inputFileName)) { + // if test is to be ignored, don't run it + return; + } else { + selectDriver(); + connection_bbl = DriverManager.getConnection(connectionString); + } + + summaryLogger.info("RUNNING " + inputFileName); + + logger.info("Running " + inputFileName + "..."); + + String testFilePath = filePaths.get(inputFileName); + + boolean result; // whether test passed or failed + int failed; + + File outputFile = new File(outputFilesDirectoryPath + inputFileName + ".out"); + + // generate buffer reader associated with the file + FileWriter fw = new FileWriter(outputFile); + BufferedWriter bw = new BufferedWriter(fw); + batch_run.batch_run_sql(connection_bbl, bw, testFilePath, logger); + bw.close(); + + File expectedFile = new File(generatedFilesDirectoryPath + inputFileName + ".out"); + File sqlExpectedFile = new File(sqlServerGeneratedFilesDirectoryPath + inputFileName + ".out"); + + if (expectedFile.exists()) { + // get the diff + result = compareOutFiles(outputFile, expectedFile); + } else if (sqlExpectedFile.exists()) { + // get the diff + result = compareOutFiles(outputFile, sqlExpectedFile); + } else { + result = false; + } + + summaryMap.add(new AbstractMap.SimpleEntry<>(inputFileName, result)); //add test name and result to map + + try { + Assertions.assertTrue(result); + } catch (AssertionError e) { + Throwable throwable = new Throwable(inputFileName + " FAILED! Output diff can be found in '" + diffFile.getAbsolutePath() + "'"); + throwable.setStackTrace(new StackTraceElement[0]); + throw throwable; + } + } +} diff --git a/contrib/test/JDBC/utils/Blank.jpeg b/contrib/test/JDBC/utils/Blank.jpeg new file mode 100644 index 00000000000..1cda9a53dc3 Binary files /dev/null and b/contrib/test/JDBC/utils/Blank.jpeg differ diff --git a/contrib/test/JDBC/utils/TxnErrorHandingCases.py b/contrib/test/JDBC/utils/TxnErrorHandingCases.py new file mode 100644 index 00000000000..01d43cb7b1c --- /dev/null +++ b/contrib/test/JDBC/utils/TxnErrorHandingCases.py @@ -0,0 +1,597 @@ +import random +import sys + + +prefix = 'ErrorHandling' +goTerm = 'GO\n' + +blockCount = 0 +tryList = [] +procList = [] +procOpen = [] +txnCnt = 0 +spList = [] + +testNum = 1 +testDict = {} +opNum = 0 +batchDone = False + +class Operation: + def __init__(self): + global opNum + self.inc = 0 + self.active = False; + self.opNum = opNum + self.batch = False + opNum = opNum + 1 + + def getSQL(self): + pass + + def getOpNum(self): + return str(self.opNum) + + def appendTerm(self, sql): + sql += ';\n' + if not self.batch: + sql += goTerm + return sql + + def appendOnlyGo(self, sql): + if not self.batch: + sql += 'GO\n' + return sql + + def include(self): + self.inc = self.inc + 1 + + def exclude(self): + self.inc = self.inc - 1 + + def isInclude(self): + return (self.inc > 0) + + def push(self, stmts): + if stmtDict.get("ERROR_COMMAND").isActive(): + return False + if blockCount > 0: + self.batch = True + self.active = True + return True + + def pop(self): + self.active = False + self.batch = False + + def isActive(self): + return self.active + + def validStmt(self): + global stmtDict, procList + validStmt = stmtDict.get("ERROR_COMMAND").isActive() + if not validStmt: + for proc in procList: + if stmtDict.get("EXEC_PROC" + str(proc)).isActive(): + validStmt = True + break + return validStmt + + +class XactAbort(Operation): + def __init__(self): + super().__init__() + self.params = ['ON', 'OFF'] + self.idx = 0 + + def getSQL(self): + res = [] + sql = 'set xact_abort ' + self.params[self.idx] + self.idx = (self.idx + 1)%2 + res.append(self.appendTerm(sql)) + return res + +class ImplTxns(Operation): + def __init__(self): + super().__init__() + self.params = ['ON', 'OFF'] + + def getSQL(self): + res = [] + sql = 'set implicit_transactions ' + self.params[random.randint(0,1)] + res.append(self.appendTerm(sql)) + return res + +class BeginTxn(Operation): + + def getSQL(self): + res = [self.appendTerm('begin transaction')] + return res + + def push(self, stmts): + global txnCnt + if stmtDict.get("ERROR_COMMAND").isActive(): + return False + txnCnt = txnCnt + 1 + if blockCount > 0: + self.batch = True + return True + + def pop(self): + global txnCnt + txnCnt = txnCnt - 1 + self.batch = False + +class CommitTxn(Operation): + + def getSQL(self): + res = [self.appendTerm('commit transaction')] + return res + + def push(self, stmts): + global txnCnt + if txnCnt == 0: + return False; + if not self.validStmt(): + return False + if blockCount > 0: + self.batch = True + txnCnt = txnCnt - 1 + return True + + def pop(self): + global txnCnt + txnCnt = txnCnt + 1 + self.batch = False + +class RollbackTxn(Operation): + def __init__(self): + super().__init__() + self.oldTxnCnt = 0 + + + def getSQL(self): + res = [self.appendTerm('rollback transaction')] + return res + + def push(self, stmts): + global txnCnt + if txnCnt == 0: + return False; + if not self.validStmt(): + return False + if blockCount > 0: + self.batch = True + self.oldTxnCnt = txnCnt + txnCnt = 0; + return True + + def pop(self): + global txnCnt + txnCnt = self.oldTxnCnt; + self.batch = False + +class SaveTxn(Operation): + def __init__(self): + super().__init__() + self.params = [prefix + 'sp'] + + def getSQL(self): + res = [self.appendTerm('save transaction ' + self.params[0])] + return res + + def push(self, stmts): + global spList + if txnCnt == 0: + return False; + if stmtDict.get("ERROR_COMMAND").isActive(): + return False + spList.append(self.params[0]) + if blockCount > 0: + self.batch = True + return True + + def pop(self): + global spList + spList.pop() + self.batch = False + +class RollbackSave(Operation): + def __init__(self): + super().__init__() + self.params = [prefix + 'sp'] + + def getSQL(self): + res = [self.appendTerm('rollback transaction ' + self.params[0])] + return res + + def push(self, stmts): + global spList + if len(spList) == 0: + return False + if not self.validStmt(): + return False + spList.pop() + if blockCount > 0: + self.batch = True + return True + + def pop(self): + global spList + spList.append(self.params[0]) + self.batch = False + +class Try(Operation): + def __init__(self, num): + super().__init__() + self.num = num; + + def getSQL(self): + res = ['begin try\n'] + return res; + + def push(self, stmts): + global blockCount, tryList + if stmtDict.get("ERROR_COMMAND").isActive(): + return False + if self.num > len(tryList) + 1: + return False + blockCount += 1 + tryList.append(self.num) + self.batch = True + return True + + def pop(self): + global blockCount, tryList + blockCount -= 1 + tryList.remove(self.num) + self.batch = False + +class Catch(Operation): + def __init__(self, num): + super().__init__() + self.num = num; + + def getSQL(self): + sql = 'end try\nbegin catch\n\tselect xact_state();\nend catch\n' + sql = self.appendOnlyGo(sql) + res = [sql] + return res; + + def push(self, stmts): + global blockCount, tryList + if len(tryList) == 0 or self.num != tryList[-1]: + return False + if not self.validStmt(): + return False + + blockCount -= 1 + tryList.remove(self.num) + if blockCount > 0: + self.batch = True + return True + + def pop(self): + global blockCount, tryList + blockCount += 1 + tryList.append(self.num) + self.batch = False + + +class BeginProc(Operation): + def __init__(self, num): + super().__init__() + self.num = num; + + def getSQL(self): + res = ['create procedure ' + prefix + str(self.num) + ' as\nbegin\n'] + return res + + def push(self, stmts): + global blockCount, procOpen + if blockCount > 0 or len(procOpen) > 0: + return False + if self.num > len(procList) + 1: + return False + + if len(procList) == 0 and stmtDict.get("ERROR_COMMAND").isActive(): + return False + + validStmt = not stmtDict.get("ERROR_COMMAND").isActive() + for proc in procList: + if not stmtDict.get("EXEC_PROC" + str(proc)).isActive(): + validStmt = True + break + if not validStmt: + return False + + blockCount += 1 + procOpen.append(self.num) + self.batch = True + return True + + def pop(self): + global blockCount, procOpen + blockCount -= 1 + procOpen.pop() + self.batch = False + + +class EndProc(Operation): + def __init__(self, num): + super().__init__() + self.num = num; + + def getSQL(self): + res = [self.appendOnlyGo('end\n')] + return res + + def push(self, stmts): + global blockCount, procOpen + if self.num not in procOpen or len(tryList) > 0: + return False + + stmtValid = False + for stmt in reversed(stmts): + if stmt == stmtDict.get("BEGIN_PROC" + str(self.num)): + break; + if stmt == stmtDict.get("ERROR_COMMAND"): + stmtValid = True + break + for proc in procList: + if stmt == stmtDict.get("EXEC_PROC" + str(proc)): + stmtValid = True + break + if stmtValid: + break + + if not stmtValid: + return False + + procList.append(self.num) + blockCount -= 1 + procOpen.pop() + self.batch = False + return True + + def pop(self): + global blockCount, procOpen + blockCount += 1 + procOpen.append(self.num) + procList.pop() + self.batch = False + + +class ExecProc(Operation): + def __init__(self, num): + super().__init__() + self.num = num; + + def getSQL(self): + sql = self.appendTerm('exec ' + prefix + str(self.num)); + res = [sql] + return res + + def push(self, stmts): + if self.num not in procList: + return False + if blockCount > 0: + self.batch = True + self.active = True + return True + +class AtAtTranCount(Operation): + def getSQL(self): + res = [self.appendTerm('select @@trancount')] + return res + +class PrintError(Operation): + def __init__(self, comment): + super().__init__() + self.done = False + self.comment = comment + + def getSQL(self): + self.done = True + msg = '' + if self.comment: + msg += "if @@error > 0 print '" + self.comment + "'" + elif batchDone: + msg += "declare @err int = @@error; if (@err > 0 and @@trancount > 0) print 'BATCH ONLY TERMINATING' else if @err > 0 print 'BATCH TERMINATING'" + else: + msg += "if @@error > 0 print 'STATEMENT TERMINATING ERROR'" + res = [msg + ";\n"] + return res + +class BatchStart(Operation): + def getSQL(self): + global batchDone + batchDone = False + return [''] + +class BatchEnd(Operation): + def getSQL(self): + global batchDone + batchDone = True + return [''] + +class ErrorCommand(Operation): + def __init__(self): + super().__init__() + + def getSQL(self): + res = [self.appendOnlyGo(commandQuery)] + return res + + +stmtDict = { + "XACT_ABORT": XactAbort(), + "BEGIN_TXN": BeginTxn(), + "COMMIT_TXN": CommitTxn(), + "ROLLBACK_TXN": RollbackTxn(), + "SAVE_TXN": SaveTxn(), + "ROLLBACK_SAVE": RollbackSave(), + "IMPL_TXNS": ImplTxns(), + "TRY1": Try(1), + "TRY2": Try(2), + "CATCH2": Catch(2), + "CATCH1": Catch(1), + "BEGIN_PROC1": BeginProc(1), + "EXEC_PROC2": ExecProc(2), + "END_PROC1": EndProc(1), + "BEGIN_PROC2": BeginProc(2), + "END_PROC2": EndProc(2), + "EXEC_PROC1": ExecProc(1), + "ERROR_COMMAND": ErrorCommand() + } + +def writeStmts(testFile, stmt): + sqls = stmt.getSQL() + for sql in sqls: + testFile.write(sql+"\n") + +def genBatch(testFile, stmtList, errCmd): + global testNum + for i in range(0, len(stmtList) + 1): + testFile.write('# Executing test ' + prefix + str(testNum) + '\n') + testFile.write(setupQuery+"\n") + j = 0 + for stmt in stmtList: + if (i == j): + writeStmts(testFile, errCmd) + writeStmts(testFile, stmt) + j += 1 + + if (i == j): + writeStmts(testFile, errCmd) + + testFile.write('if @@trancount > 0 rollback transaction\n') + testFile.write('set xact_abort OFF\n') + testFile.write('set implicit_transactions OFF\n') + testFile.write(celanupQuery) + testFile.write('\n\n\n') + testNum += 1 + +def genBatch(testFile, stmtList): + testFile.write('# Executing test ' + prefix + str(testNum) + '\n') + testFile.write(setupQuery+"\n") + for stmt in stmtList: + writeStmts(testFile, stmt) + + testFile.write('if @@trancount > 0 rollback transaction;\n') + for proc in procList: + testFile.write('drop procedure ' + prefix + str(proc) + ';\n') + testFile.write('set xact_abort OFF;\n') + testFile.write('set implicit_transactions OFF;\n') + testFile.write(celanupQuery) + testFile.write('\n\n\n') + +def genBasicCases(testFile): + global testNum, procList + atAtTranCount = AtAtTranCount(); + errorCommand = ErrorCommand(); + try1 = Try(1); + try2 = Try(2); + catch1 = Catch(1); + catch2 = Catch(2); + beginProc1 = BeginProc(1); + beginProc2 = BeginProc(2); + endProc1 = EndProc(1); + endProc2 = EndProc(2); + execProc1 = ExecProc(1); + execProc2 = ExecProc(2); + xactAbort = XactAbort(); + beginTxn = BeginTxn(); + commitTxn = CommitTxn(); + printError = PrintError(None); + printComplError = PrintError("CURRENT BATCH TERMINATING ERROR"); + + errorCommand.batch = True + atAtTranCount.batch = True + beginTxn.batch = True + commitTxn.batch = True + execProc1.batch = True + catch1.batch = True + """ + testNum = 10000000 + genBatch(testFile, [BatchStart(), beginTxn, errorCommand, + printError, atAtTranCount, + commitTxn, BatchEnd(), printError]) + testNum = 20000000 + genBatch(testFile, [try1, beginTxn, try2, errorCommand, atAtTranCount, catch1, catch2]) + """ + testNum = 10000000 + procList = [1, 2] + genBatch(testFile, [BatchStart(), beginProc1, errorCommand, + printError, atAtTranCount, + endProc1, beginProc2, execProc1, printComplError, endProc2, + beginTxn, execProc2, BatchEnd(), printError]) + """ + testNum = 40000000 + procList = [1, 2] + genBatch(testFile, [xactAbort, beginProc1, try1, errorCommand, + atAtTranCount, catch1, endProc1, beginProc2, execProc1, + endProc2, beginTxn, execProc2]) + procList.clear() + testNum = 1 + """ + +def swap(stmtList, i, j): + val = stmtList[i] + stmtList[i] = stmtList[j] + stmtList[j] = val + +def genValidCases(testFile, stmtDict, stmtList, curStmtList, idx): + global blockCount, testDict, procList, testNum + if testNum > testEnd: + return + procDone = True + for proc in procList: + if not stmtDict.get("EXEC_PROC" + str(proc)).isActive(): + procDone = False + break + + if procDone and stmtDict.get("ERROR_COMMAND").isActive() and blockCount == 0: + seq = '' + for stmt in curStmtList: + seq = seq + stmt.getOpNum() + if not testDict.get(seq, False): + if testNum < testStart: + testNum += 1 + return + testDict[seq] = True + genBatch(testFile, curStmtList) + testNum += 1 + return + + if (len(stmtList) == idx): + return + + if not stmtList[idx].isInclude(): + genValidCases(testFile, stmtDict, stmtList, curStmtList, idx + 1) + + stmtList[idx].include() + N = len(stmtList) + for i in range(idx, N): + if not stmtList[i].push(curStmtList): + continue + curStmtList.append(stmtList[i]) + swap(stmtList, idx, i) + genValidCases(testFile, stmtDict, stmtList, curStmtList, idx + 1) + swap(stmtList, idx, i) + curStmtList.pop() + stmtList[i].pop() + stmtList[idx].exclude() + +assert len(sys.argv) == 7, "Please provide setup, command and cleanup files together with test range as well as output file" + +setupQuery = open(sys.argv[1], "r").read() +commandQuery = open(sys.argv[2], "r").read() +celanupQuery = open(sys.argv[3], "r").read() +testStart = int(sys.argv[4]) +testEnd = int(sys.argv[5]) + +testFile = open(sys.argv[6], "w", buffering=1) +genBasicCases(testFile) +genValidCases(testFile, stmtDict, list(stmtDict.values()), [], 0) +testFile.close() diff --git a/contrib/test/JDBC/utils/blank.txt b/contrib/test/JDBC/utils/blank.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/contrib/test/JDBC/utils/devanagari.txt b/contrib/test/JDBC/utils/devanagari.txt new file mode 100644 index 00000000000..45f37b695da --- /dev/null +++ b/contrib/test/JDBC/utils/devanagari.txt @@ -0,0 +1 @@ +ऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसहऺऻ़ऽािीुूृॄॅॆेैॉॊोौ्ॎॏॐ॒॑॓॔ॕॖॗक़ख़ग़ज़ड़ढ़फ़य़ॠॡॢॣ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॹॺॻॼॽॾॿ \ No newline at end of file diff --git a/contrib/test/JDBC/utils/emojisText.txt b/contrib/test/JDBC/utils/emojisText.txt new file mode 100644 index 00000000000..117dfc03289 --- /dev/null +++ b/contrib/test/JDBC/utils/emojisText.txt @@ -0,0 +1 @@ +😀😃😁😎😒😞😍🙂😆😊😉 \ No newline at end of file diff --git a/contrib/test/JDBC/utils/flower.jpg b/contrib/test/JDBC/utils/flower.jpg new file mode 100644 index 00000000000..3e41ba4e61e Binary files /dev/null and b/contrib/test/JDBC/utils/flower.jpg differ diff --git a/contrib/test/JDBC/utils/sample.txt b/contrib/test/JDBC/utils/sample.txt new file mode 100644 index 00000000000..352a3fb9297 --- /dev/null +++ b/contrib/test/JDBC/utils/sample.txt @@ -0,0 +1,6 @@ +AAAAAAAAAAAAAAAAAAAA +BBBBBBBBBB +CCCCC +badksjvbajsdcbvjads +sejvhsdbfjhcgvasdhgcvsj +21639812365091264 \ No newline at end of file diff --git a/contrib/test/JDBC/utils/utf16.txt b/contrib/test/JDBC/utils/utf16.txt new file mode 100644 index 00000000000..ee839d6a00c --- /dev/null +++ b/contrib/test/JDBC/utils/utf16.txt @@ -0,0 +1 @@ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿̴̵̶̷̸̡̢̧̨̛̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣͤͥͦͧͨͩͪͫͬͭͮͯ͘͜͟͢͝͞͠͡ͰͱͲͳʹ͵Ͷͷͺͻͼͽ;΄΅Ά·ΈΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿҀҁ҂҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖՙ՚՛՜՝՞՟աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև։֊֏ְֱֲֳִֵֶַָֹֺֻּֽ֑֖֛֢֣֤֥֦֧֪֚֭֮֒֓֔֕֗֘֙֜֝֞֟֠֡֨֩֫֬֯־ֿ׀ׁׂ׃ׅׄ׆ׇאבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ׳״؀؁؂؃؄؆؇؈؉؊؋،؍؎؏ؘؙؚؐؑؒؓؔؕؖؗ؛؞؟ؠءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّْٕٖٜٟٓٔٗ٘ٙٚٛٝٞ٠١٢٣٤٥٦٧٨٩٪٫٬٭ٮٯٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿڀځڂڃڄڅچڇڈډڊڋڌڍڎڏڐڑڒړڔڕږڗژڙښڛڜڝڞڟڠڡڢڣڤڥڦڧڨکڪګڬڭڮگڰڱڲڳڴڵڶڷڸڹںڻڼڽھڿۀہۂۃۄۅۆۇۈۉۊۋیۍێۏېۑےۓ۔ەۖۗۘۙۚۛۜ۝۞ۣ۟۠ۡۢۤۥۦۧۨ۩۪ۭ۫۬ۮۯ۰۱۲۳۴۵۶۷۸۹ۺۻۼ۽۾ۿ܀܁܂܃܄܅܆܇܈܉܊܋܌܍܏ܐܑܒܓܔܕܖܗܘܙܚܛܜܝܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯܱܴܷܸܹܻܼܾ݂݄݆݈ܰܲܳܵܶܺܽܿ݀݁݃݅݇݉݊ݍݎݏݐݑݒݓݔݕݖݗݘݙݚݛݜݝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿހށނރބޅކއވމފދތލގޏސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަާިީުޫެޭޮޯްޱ߀߁߂߃߄߅߆߇߈߉ߊߋߌߍߎߏߐߑߒߓߔߕߖߗߘߙߚߛߜߝߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ߶߷߸߹ߺࠀࠁࠂࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌࠍࠎࠏࠐࠑࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜࠝࠞࠟࠠࠡࠢࠣࠤࠥࠦࠧࠨࠩࠪࠫࠬ࠭࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾ࡀࡁࡂࡃࡄࡅࡆࡇࡈࡉࡊࡋࡌࡍࡎࡏࡐࡑࡒࡓࡔࡕࡖࡗࡘ࡙࡚࡛࡞ࢠࢢࢣࢤࢥࢦࢧࢨࢩࢪࢫࢬࣰࣱࣲࣦࣩ࣭࣮࣯ࣶࣹࣺࣤࣥࣧࣨ࣪࣫࣬ࣳࣴࣵࣷࣸࣻࣼࣽࣾऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसहऺऻ़ऽािीुूृॄॅॆेैॉॊोौ्ॎॏॐ॒॑॓॔ॕॖॗक़ख़ग़ज़ड़ढ़फ़य़ॠॡॢॣ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॹॺॻॼॽॾॿঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻ਁਂਃਅਆਇਈਉਊਏਐਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਲ਼ਵਸ਼ਸਹ਼ਾਿੀੁੂੇੈੋੌ੍ੑਖ਼ਗ਼ਜ਼ੜਫ਼੦੧੨੩੪੫੬੭੮੯ੰੱੲੳੴੵઁંઃઅઆઇઈઉઊઋઌઍએઐઑઓઔકખગઘઙચછજઝઞટઠડઢણતથદધનપફબભમયરલળવશષસહ઼ઽાિીુૂૃૄૅેૈૉોૌ્ૐૠૡૢૣ૦૧૨૩૪૫૬૭૮૯૰૱ଁଂଃଅଆଇଈଉଊଋଌଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଳଵଶଷସହ଼ଽାିୀୁୂୃୄେୈୋୌ୍ୖୗଡ଼ଢ଼ୟୠୡୢୣ୦୧୨୩୪୫୬୭୮୯୰ୱ୲୳୴୵୶୷ஂஃஅஆஇஈஉஊஎஏஐஒஓஔகஙசஜஞடணதநனபமயரறலளழவஶஷஸஹாிீுூெேைொோௌ்ௐௗ௦௧௨௩௪௫௬௭௮௯௰௱௲௳௴௵௶௷௸௹௺ఁంఃఅఆఇఈఉఊఋఌఎఏఐఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధనపఫబభమయరఱలళవశషసహఽాిీుూృౄెేైొోౌ్ౕౖౘౙౠౡౢౣ౦౧౨౩౪౫౬౭౮౯౸౹౺౻౼౽౾౿ಂಃಅಆಇಈಉಊಋಌಎಏಐಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನಪಫಬಭಮಯರಱಲಳವಶಷಸಹ಼ಽಾಿೀುೂೃೄೆೇೈೊೋೌ್ೕೖೞೠೡೢೣ೦೧೨೩೪೫೬೭೮೯ೱೲംഃഅആഇഈഉഊഋഌഎഏഐഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനഩപഫബഭമയരറലളഴവശഷസഹഺഽാിീുൂൃൄെേൈൊോൌ്ൎൗൠൡൢൣ൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵൹ൺൻർൽൾൿංඃඅආඇඈඉඊඋඌඍඎඏඐඑඒඓඔඕඖකඛගඝඞඟචඡජඣඤඥඦටඨඩඪණඬතථදධනඳපඵබභමඹයරලවශෂසහළෆ්ාැෑිීුූෘෙේෛොෝෞෟෲෳ෴กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛ກຂຄງຈຊຍດຕຖທນບປຜຝພຟມຢຣລວສຫອຮຯະັາຳິີຶືຸູົຼຽເແໂໃໄໆ່້໊໋໌ໍ໐໑໒໓໔໕໖໗໘໙ໜໝໞໟༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအဢဣဤဥဦဧဨဩဪါာိီုူေဲဳဴဵံ့း္်ျြွှဿ၀၁၂၃၄၅၆၇၈၉၊။၌၍၎၏ၐၑၒၓၔၕၖၗၘၙၚၛၜၝၞၟၠၡၢၣၤၥၦၧၨၩၪၫၬၭၮၯၰၱၲၳၴၵၶၷၸၹၺၻၼၽၾၿႀႁႂႃႄႅႆႇႈႉႊႋႌႍႎႏ႐႑႒႓႔႕႖႗႘႙ႚႛႜႝ႞႟ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅჇჍაბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ჻ჼჽჾჿᄀᄁᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜᄝᄞᄟᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿᅀᅁᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌᅍᅎᅏᅐᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜᅝᅞᅟᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀᆁᆂᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌᆍᆎᆏᆐᆑᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜᆝᆞᆟᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀᇁᇂᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌᇍᇎᇏᇐᇑᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜᇝᇞᇟᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿሀሁሂሃሄህሆሇለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿቀቁቂቃቄቅቆቇቈቊቋቌቍቐቑቒቓቔቕቖቘቚቛቜቝበቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿኀኁኂኃኄኅኆኇኈኊኋኌኍነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኯኰኲኳኴኵኸኹኺኻኼኽኾዀዂዃዄዅወዉዊዋዌውዎዏዐዑዒዓዔዕዖዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮዯደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿጀጁጂጃጄጅጆጇገጉጊጋጌግጎጏጐጒጓጔጕጘጙጚጛጜጝጞጟጠጡጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿፀፁፂፃፄፅፆፇፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፝፞፟፠፡።፣፤፥፦፧፨፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼ᎀᎁᎂᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌᎍᎎᎏ᎐᎑᎒᎓᎔᎕᎖᎗᎘᎙ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿᏀᏁᏂᏃᏄᏅᏆᏇᏈᏉᏊᏋᏌᏍᏎᏏᏐᏑᏒᏓᏔᏕᏖᏗᏘᏙᏚᏛᏜᏝᏞᏟᏠᏡᏢᏣᏤᏥᏦᏧᏨᏩᏪᏫᏬᏭᏮᏯᏰᏱᏲᏳᏴ᐀ᐁᐂᐃᐄᐅᐆᐇᐈᐉᐊᐋᐌᐍᐎᐏᐐᐑᐒᐓᐔᐕᐖᐗᐘᐙᐚᐛᐜᐝᐞᐟᐠᐡᐢᐣᐤᐥᐦᐧᐨᐩᐪᐫᐬᐭᐮᐯᐰᐱᐲᐳᐴᐵᐶᐷᐸᐹᐺᐻᐼᐽᐾᐿᑀᑁᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌᑍᑎᑏᑐᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜᑝᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿᒀᒁᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌᒍᒎᒏᒐᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜᒝᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿᓀᓁᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌᓍᓎᓏᓐᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜᓝᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿᔀᔁᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌᔍᔎᔏᔐᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜᔝᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿᕀᕁᕂᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌᕍᕎᕏᕐᕑᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜᕝᕞᕟᕠᕡᕢᕣᕤᕥᕦᕧᕨᕩᕪᕫᕬᕭᕮᕯᕰᕱᕲᕳᕴᕵᕶᕷᕸᕹᕺᕻᕼᕽᕾᕿᖀᖁᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌᖍᖎᖏᖐᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜᖝᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿᗀᗁᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌᗍᗎᗏᗐᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜᗝᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿᘀᘁᘂᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌᘍᘎᘏᘐᘑᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜᘝᘞᘟᘠᘡᘢᘣᘤᘥᘦᘧᘨᘩᘪᘫᘬᘭᘮᘯᘰᘱᘲᘳᘴᘵᘶᘷᘸᘹᘺᘻᘼᘽᘾᘿᙀᙁᙂᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌᙍᙎᙏᙐᙑᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜᙝᙞᙟᙠᙡᙢᙣᙤᙥᙦᙧᙨᙩᙪᙫᙬ᙭᙮ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔᚕᚖᚗᚘᚙᚚ᚛᚜ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰᜀᜁᜂᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜎᜏᜐᜑᜒᜓ᜔ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱᜲᜳ᜴᜵᜶ᝀᝁᝂᝃᝄᝅᝆᝇᝈᝉᝊᝋᝌᝍᝎᝏᝐᝑᝒᝓᝠᝡᝢᝣᝤᝥᝦᝧᝨᝩᝪᝫᝬᝮᝯᝰᝲᝳកខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអឣឤឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ឴឵ាិីឹឺុូួើឿៀេែៃោៅំះៈ៉៊់៌៍៎៏័៑្៓។៕៖ៗ៘៙៚៛ៜ៝០១២៣៤៥៦៧៨៩៰៱៲៳៴៵៶៷៸៹᠀᠁᠂᠃᠄᠅᠆᠇᠈᠉᠊᠋᠌᠍᠎᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙ᠠᠡᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿᡀᡁᡂᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜᡝᡞᡟᡠᡡᡢᡣᡤᡥᡦᡧᡨᡩᡪᡫᡬᡭᡮᡯᡰᡱᡲᡳᡴᡵᡶᡷᢀᢁᢂᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌᢍᢎᢏᢐᢑᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜᢝᢞᢟᢠᢡᢢᢣᢤᢥᢦᢧᢨᢩᢪᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀᣁᣂᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌᣍᣎᣏᣐᣑᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜᣝᣞᣟᣠᣡᣢᣣᣤᣥᣦᣧᣨᣩᣪᣫᣬᣭᣮᣯᣰᣱᣲᣳᣴᣵᤀᤁᤂᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌᤍᤎᤏᤐᤑᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤠᤡᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺᥀᥄᥅᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏ᥐᥑᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜᥝᥞᥟᥠᥡᥢᥣᥤᥥᥦᥧᥨᥩᥪᥫᥬᥭᥰᥱᥲᥳᥴᦀᦁᦂᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌᦍᦎᦏᦐᦑᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜᦝᦞᦟᦠᦡᦢᦣᦤᦥᦦᦧᦨᦩᦪᦫᦰᦱᦲᦳᦴᦵᦶᦷᦸᦹᦺᦻᦼᦽᦾᦿᧀᧁᧂᧃᧄᧅᧆᧇᧈᧉ᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᧚᧞᧟᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿ᨀᨁᨂᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌᨍᨎᨏᨐᨑᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ᨞᨟ᨠᨡᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿᩀᩁᩂᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌᩍᩎᩏᩐᩑᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜᩝᩞ᩠ᩡᩢᩣᩤᩥᩦᩧᩨᩩᩪᩫᩬᩭᩮᩯᩰᩱᩲᩳᩴ᩿᩵᩶᩷᩸᩹᩺᩻᩼᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᪠᪡᪢᪣᪤᪥᪦ᪧ᪨᪩᪪᪫᪬᪭ᬀᬁᬂᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌᬍᬎᬏᬐᬑᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜᬝᬞᬟᬠᬡᬢᬣᬤᬥᬦᬧᬨᬩᬪᬫᬬᬭᬮᬯᬰᬱᬲᬳ᬴ᬵᬶᬷᬸᬹᬺᬻᬼᬽᬾᬿᭀᭁᭂᭃ᭄ᭅᭆᭇᭈᭉᭊᭋ᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᭚᭛᭜᭝᭞᭟᭠᭡᭢᭣᭤᭥᭦᭧᭨᭩᭪᭬᭫᭭᭮᭯᭰᭱᭲᭳᭴᭵᭶᭷᭸᭹᭺᭻᭼ᮀᮁᮂᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌᮍᮎᮏᮐᮑᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜᮝᮞᮟᮠᮡᮢᮣᮤᮥᮦᮧᮨᮩ᮪᮫ᮬᮭᮮᮯ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹ᮺᮻᮼᮽᮾᮿᯀᯁᯂᯃᯄᯅᯆᯇᯈᯉᯊᯋᯌᯍᯎᯏᯐᯑᯒᯓᯔᯕᯖᯗᯘᯙᯚᯛᯜᯝᯞᯟᯠᯡᯢᯣᯤᯥ᯦ᯧᯨᯩᯪᯫᯬᯭᯮᯯᯰᯱ᯲᯳᯼᯽᯾᯿ᰀᰁᰂᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌᰍᰎᰏᰐᰑᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜᰝᰞᰟᰠᰡᰢᰣᰤᰥᰦᰧᰨᰩᰪᰫᰬᰭᰮᰯᰰᰱᰲᰳᰴᰵᰶ᰷᰻᰼᰽᰾᰿᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉ᱍᱎᱏ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜᱝᱞᱟᱠᱡᱢᱣᱤᱥᱦᱧᱨᱩᱪᱫᱬᱭᱮᱯᱰᱱᱲᱳᱴᱵᱶᱷᱸᱹᱺᱻᱼᱽ᱾᱿᳀᳁᳂᳃᳄᳅᳆᳇᳐᳑᳒᳓᳔᳕᳖᳗᳘᳙᳜᳝᳞᳟᳚᳛᳠᳡᳢᳣᳤᳥᳦᳧᳨ᳩᳪᳫᳬ᳭ᳮᳯᳰᳱᳲᳳ᳴ᳵᳶᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩᴪᴫᴬᴭᴮᴯᴰᴱᴲᴳᴴᴵᴶᴷᴸᴹᴺᴻᴼᴽᴾᴿᵀᵁᵂᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌᵍᵎᵏᵐᵑᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵸᵹᵺᵻᵼᵽᵾᵿᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜᶝᶞᶟᶠᶡᶢᶣᶤᶥᶦᶧᶨᶩᶪᶫᶬᶭᶮᶯᶰᶱᶲᶳᶴᶵᶶᶷᶸᶹᶺᶻᶼᶽᶾᶿ᷐᷎᷂᷊᷏᷽᷿᷀᷁᷃᷄᷅᷆᷇᷈᷉᷋᷌᷑᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜᷝᷞᷟᷠᷡᷢᷣᷤᷥᷦ᷾᷼᷍ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẜẝẞẟẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹỺỻỼỽỾỿἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕἘἙἚἛἜἝἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿὀὁὂὃὄὅὈὉὊὋὌὍὐὑὒὓὔὕὖὗὙὛὝὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴᾶᾷᾸᾹᾺΆᾼ᾽ι᾿῀῁ῂῃῄῆῇῈΈῊΉῌ῍῎῏ῐῑῒΐῖῗῘῙῚΊ῝῞῟ῠῡῢΰῤῥῦῧῨῩῪΎῬ῭΅`ῲῳῴῶῷῸΌῺΏῼ´῾           ​‌‍‎‏‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧

‪‭‮ ‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞ ⁠⁡⁢⁣⁤⁰ⁱ⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎ₐₑₒₓₔₕₖₗₘₙₚₛₜ₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃒⃓⃘⃙⃚⃐⃑⃔⃕⃖⃗⃛⃜⃝⃞⃟⃠⃡⃢⃣⃤⃥⃦⃪⃫⃨⃬⃭⃮⃯⃧⃩⃰℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠℡™℣ℤ℥Ω℧ℨ℩KÅℬℭ℮ℯℰℱℲℳℴℵℶℷℸℹ℺℻ℼℽℾℿ⅀⅁⅂⅃⅄ⅅⅆⅇⅈⅉ⅊⅋⅌⅍ⅎ⅏⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃↄↅↆↇↈ↉←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡⌢⌣⌤⌥⌦⌧⌨〈〉⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍼⍽⍾⍿⎀⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⎮⎯⎰⎱⎲⎳⎴⎵⎶⎷⎸⎹⎺⎻⎼⎽⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌⏍⏎⏏⏐⏑⏒⏓⏔⏕⏖⏗⏘⏙⏚⏛⏜⏝⏞⏟⏠⏡⏢⏣⏤⏥⏦⏧⏨⏩⏪⏫⏬⏭⏮⏯⏰⏱⏲⏳␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␢␣␤␥␦⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛎⛏⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟⛠⛡⛢⛣⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞❟❠❡❢❣❤❥❦❧❨❩❪❫❬❭❮❯❰❱❲❳❴❵❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔➕➖➗➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➰➱➲➳➴➵➶➷➸➹➺➻➼➽➾➿⟀⟁⟂⟃⟄⟅⟆⟇⟈⟉⟊⟋⟌⟍⟎⟏⟐⟑⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪⟫⟬⟭⟮⟯⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿⦀⦁⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜⦝⦞⦟⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿⧀⧁⧂⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜⧝⧞⧟⧠⧡⧢⧣⧤⧥⧦⧧⧨⧩⧪⧫⧬⧭⧮⧯⧰⧱⧲⧳⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻⨼⨽⨾⨿⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿⪀⪁⪂⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽⫾⫿⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜⬝⬞⬟⬠⬡⬢⬣⬤⬥⬦⬧⬨⬩⬪⬫⬬⬭⬮⬯⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿⭀⭁⭂⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌⭐⭑⭒⭓⭔⭕⭖⭗⭘⭙ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞⱠⱡⱢⱣⱤⱥⱦⱧⱨⱩⱪⱫⱬⱭⱮⱯⱰⱱⱲⱳⱴⱵⱶⱷⱸⱹⱺⱻⱼⱽⱾⱿⲀⲁⲂⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌⲍⲎⲏⲐⲑⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜⲝⲞⲟⲠⲡⲢⲣⲤⲥⲦⲧⲨⲩⲪⲫⲬⲭⲮⲯⲰⲱⲲⲳⲴⲵⲶⲷⲸⲹⲺⲻⲼⲽⲾⲿⳀⳁⳂⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌⳍⳎⳏⳐⳑⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜⳝⳞⳟⳠⳡⳢⳣⳤ⳥⳦⳧⳨⳩⳪ⳫⳬⳭⳮ⳯⳰⳱Ⳳⳳ⳹⳺⳻⳼⳽⳾⳿ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥⴧⴭⴰⴱⴲⴳⴴⴵⴶⴷⴸⴹⴺⴻⴼⴽⴾⴿⵀⵁⵂⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌⵍⵎⵏⵐⵑⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜⵝⵞⵟⵠⵡⵢⵣⵤⵥⵦⵧⵯ⵰⵿ⶀⶁⶂⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌⶍⶎⶏⶐⶑⶒⶓⶔⶕⶖⶠⶡⶢⶣⶤⶥⶦⶨⶩⶪⶫⶬⶭⶮⶰⶱⶲⶳⶴⶵⶶⶸⶹⶺⶻⶼⶽⶾⷀⷁⷂⷃⷄⷅⷆⷈⷉⷊⷋⷌⷍⷎⷐⷑⷒⷓⷔⷕⷖⷘⷙⷚⷛⷜⷝⷞⷠⷡⷢⷣⷤⷥⷦⷧⷨⷩⷪⷫⷬⷭⷮⷯⷰⷱⷲⷳⷴⷵⷶⷷⷸⷹⷺⷻⷼⷽⷾⷿ⸀⸁⸂⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌⸍⸎⸏⸐⸑⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜⸝⸞⸟⸠⸡⸢⸣⸤⸥⸦⸧⸨⸩⸪⸫⸬⸭⸮ⸯ⸰⸱⸲⸳⸴⸵⸶⸷⸸⸹⸺⸻⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜⺝⺞⺟⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻ 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟〠〡〢〣〤〥〦〧〨〩〪〭〮〯〫〬〰〱〲〳〴〵〶〷〸〹〺〻〼〽〾〿ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゙゚゛゜ゝゞゟ゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌㆍㆎ㆐㆑㆒㆓㆔㆕㆖㆗㆘㆙㆚㆛㆜㆝㆞㆟ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷㆸㆹㆺ㇀㇁㇂㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌㇍㇎㇏㇐㇑㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜㇝㇞㇟㇠㇡㇢㇣ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜㈝㈞㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀㉁㉂㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌㉍㉎㉏㉐㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌㋍㋎㋏㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㋿㌀㌁㌂㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌㌍㌎㌏㌐㌑㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜㌝㌞㌟㌠㌡㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿㍀㍁㍂㍃㍄㍅㍆㍇㍈㍉㍊㍋㍌㍍㍎㍏㍐㍑㍒㍓㍔㍕㍖㍗㍘㍙㍚㍛㍜㍝㍞㍟㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍱㍲㍳㍴㍵㍶㍷㍸㍹㍺㍻㍼㍽㍾㍿㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏋㏌㏍㏎㏏㏐㏑㏒㏓㏔㏕㏖㏗㏘㏙㏚㏛㏜㏝㏞㏟㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㏿㐀㐁㐂㐃㐄㐅㐆㐇㐈㐉㐊㐋㐌㐍㐎㐏㐐㐑㐒㐓㐔㐕㐖㐗㐘㐙㐚㐛㐜㐝㐞㐟㐠㐡㐢㐣㐤㐥㐦㐧㐨㐩㐪㐫㐬㐭㐮㐯㐰㐱㐲㐳㐴㐵㐶㐷㐸㐹㐺㐻㐼㐽㐾㐿㑀㑁㑂㑃㑄㑅㑆㑇㑈㑉㑊㑋㑌㑍㑎㑏㑐㑑㑒㑓㑔㑕㑖㑗㑘㑙㑚㑛㑜㑝㑞㑟㑠㑡㑢㑣㑤㑥㑦㑧㑨㑩㑪㑫㑬㑭㑮㑯㑰㑱㑲㑳㑴㑵㑶㑷㑸㑹㑺㑻㑼㑽㑾㑿㒀㒁㒂㒃㒄㒅㒆㒇㒈㒉㒊㒋㒌㒍㒎㒏㒐㒑㒒㒓㒔㒕㒖㒗㒘㒙㒚㒛㒜㒝㒞㒟㒠㒡㒢㒣㒤㒥㒦㒧㒨㒩㒪㒫㒬㒭㒮㒯㒰㒱㒲㒳㒴㒵㒶㒷㒸㒹㒺㒻㒼㒽㒾㒿㓀㓁㓂㓃㓄㓅㓆㓇㓈㓉㓊㓋㓌㓍㓎㓏㓐㓑㓒㓓㓔㓕㓖㓗㓘㓙㓚㓛㓜㓝㓞㓟㓠㓡㓢㓣㓤㓥㓦㓧㓨㓩㓪㓫㓬㓭㓮㓯㓰㓱㓲㓳㓴㓵㓶㓷㓸㓹㓺㓻㓼㓽㓾㓿㔀㔁㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌㔍㔎㔏㔐㔑㔒㔓㔔㔕㔖㔗㔘㔙㔚㔛㔜㔝㔞㔟㔠㔡㔢㔣㔤㔥㔦㔧㔨㔩㔪㔫㔬㔭㔮㔯㔰㔱㔲㔳㔴㔵㔶㔷㔸㔹㔺㔻㔼㔽㔾㔿㕀㕁㕂㕃㕄㕅㕆㕇㕈㕉㕊㕋㕌㕍㕎㕏㕐㕑㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜㕝㕞㕟㕠㕡㕢㕣㕤㕥㕦㕧㕨㕩㕪㕫㕬㕭㕮㕯㕰㕱㕲㕳㕴㕵㕶㕷㕸㕹㕺㕻㕼㕽㕾㕿㖀㖁㖂㖃㖄㖅㖆㖇㖈㖉㖊㖋㖌㖍㖎㖏㖐㖑㖒㖓㖔㖕㖖㖗㖘㖙㖚㖛㖜㖝㖞㖟㖠㖡㖢㖣㖤㖥㖦㖧㖨㖩㖪㖫㖬㖭㖮㖯㖰㖱㖲㖳㖴㖵㖶㖷㖸㖹㖺㖻㖼㖽㖾㖿㗀㗁㗂㗃㗄㗅㗆㗇㗈㗉㗊㗋㗌㗍㗎㗏㗐㗑㗒㗓㗔㗕㗖㗗㗘㗙㗚㗛㗜㗝㗞㗟㗠㗡㗢㗣㗤㗥㗦㗧㗨㗩㗪㗫㗬㗭㗮㗯㗰㗱㗲㗳㗴㗵㗶㗷㗸㗹㗺㗻㗼㗽㗾㗿㘀㘁㘂㘃㘄㘅㘆㘇㘈㘉㘊㘋㘌㘍㘎㘏㘐㘑㘒㘓㘔㘕㘖㘗㘘㘙㘚㘛㘜㘝㘞㘟㘠㘡㘢㘣㘤㘥㘦㘧㘨㘩㘪㘫㘬㘭㘮㘯㘰㘱㘲㘳㘴㘵㘶㘷㘸㘹㘺㘻㘼㘽㘾㘿㙀㙁㙂㙃㙄㙅㙆㙇㙈㙉㙊㙋㙌㙍㙎㙏㙐㙑㙒㙓㙔㙕㙖㙗㙘㙙㙚㙛㙜㙝㙞㙟㙠㙡㙢㙣㙤㙥㙦㙧㙨㙩㙪㙫㙬㙭㙮㙯㙰㙱㙲㙳㙴㙵㙶㙷㙸㙹㙺㙻㙼㙽㙾㙿㚀㚁㚂㚃㚄㚅㚆㚇㚈㚉㚊㚋㚌㚍㚎㚏㚐㚑㚒㚓㚔㚕㚖㚗㚘㚙㚚㚛㚜㚝㚞㚟㚠㚡㚢㚣㚤㚥㚦㚧㚨㚩㚪㚫㚬㚭㚮㚯㚰㚱㚲㚳㚴㚵㚶㚷㚸㚹㚺㚻㚼㚽㚾㚿㛀㛁㛂㛃㛄㛅㛆㛇㛈㛉㛊㛋㛌㛍㛎㛏㛐㛑㛒㛓㛔㛕㛖㛗㛘㛙㛚㛛㛜㛝㛞㛟㛠㛡㛢㛣㛤㛥㛦㛧㛨㛩㛪㛫㛬㛭㛮㛯㛰㛱㛲㛳㛴㛵㛶㛷㛸㛹㛺㛻㛼㛽㛾㛿㜀㜁㜂㜃㜄㜅㜆㜇㜈㜉㜊㜋㜌㜍㜎㜏㜐㜑㜒㜓㜔㜕㜖㜗㜘㜙㜚㜛㜜㜝㜞㜟㜠㜡㜢㜣㜤㜥㜦㜧㜨㜩㜪㜫㜬㜭㜮㜯㜰㜱㜲㜳㜴㜵㜶㜷㜸㜹㜺㜻㜼㜽㜾㜿㝀㝁㝂㝃㝄㝅㝆㝇㝈㝉㝊㝋㝌㝍㝎㝏㝐㝑㝒㝓㝔㝕㝖㝗㝘㝙㝚㝛㝜㝝㝞㝟㝠㝡㝢㝣㝤㝥㝦㝧㝨㝩㝪㝫㝬㝭㝮㝯㝰㝱㝲㝳㝴㝵㝶㝷㝸㝹㝺㝻㝼㝽㝾㝿㞀㞁㞂㞃㞄㞅㞆㞇㞈㞉㞊㞋㞌㞍㞎㞏㞐㞑㞒㞓㞔㞕㞖㞗㞘㞙㞚㞛㞜㞝㞞㞟㞠㞡㞢㞣㞤㞥㞦㞧㞨㞩㞪㞫㞬㞭㞮㞯㞰㞱㞲㞳㞴㞵㞶㞷㞸㞹㞺㞻㞼㞽㞾㞿㟀㟁㟂㟃㟄㟅㟆㟇㟈㟉㟊㟋㟌㟍㟎㟏㟐㟑㟒㟓㟔㟕㟖㟗㟘㟙㟚㟛㟜㟝㟞㟟㟠㟡㟢㟣㟤㟥㟦㟧㟨㟩㟪㟫㟬㟭㟮㟯㟰㟱㟲㟳㟴㟵㟶㟷㟸㟹㟺㟻㟼㟽㟾㟿㠀㠁㠂㠃㠄㠅㠆㠇㠈㠉㠊㠋㠌㠍㠎㠏㠐㠑㠒㠓㠔㠕㠖㠗㠘㠙㠚㠛㠜㠝㠞㠟㠠㠡㠢㠣㠤㠥㠦㠧㠨㠩㠪㠫㠬㠭㠮㠯㠰㠱㠲㠳㠴㠵㠶㠷㠸㠹㠺㠻㠼㠽㠾㠿㡀㡁㡂㡃㡄㡅㡆㡇㡈㡉㡊㡋㡌㡍㡎㡏㡐㡑㡒㡓㡔㡕㡖㡗㡘㡙㡚㡛㡜㡝㡞㡟㡠㡡㡢㡣㡤㡥㡦㡧㡨㡩㡪㡫㡬㡭㡮㡯㡰㡱㡲㡳㡴㡵㡶㡷㡸㡹㡺㡻㡼㡽㡾㡿㢀㢁㢂㢃㢄㢅㢆㢇㢈㢉㢊㢋㢌㢍㢎㢏㢐㢑㢒㢓㢔㢕㢖㢗㢘㢙㢚㢛㢜㢝㢞㢟㢠㢡㢢㢣㢤㢥㢦㢧㢨㢩㢪㢫㢬㢭㢮㢯㢰㢱㢲㢳㢴㢵㢶㢷㢸㢹㢺㢻㢼㢽㢾㢿㣀㣁㣂㣃㣄㣅㣆㣇㣈㣉㣊㣋㣌㣍㣎㣏㣐㣑㣒㣓㣔㣕㣖㣗㣘㣙㣚㣛㣜㣝㣞㣟㣠㣡㣢㣣㣤㣥㣦㣧㣨㣩㣪㣫㣬㣭㣮㣯㣰㣱㣲㣳㣴㣵㣶㣷㣸㣹㣺㣻㣼㣽㣾㣿㤀㤁㤂㤃㤄㤅㤆㤇㤈㤉㤊㤋㤌㤍㤎㤏㤐㤑㤒㤓㤔㤕㤖㤗㤘㤙㤚㤛㤜㤝㤞㤟㤠㤡㤢㤣㤤㤥㤦㤧㤨㤩㤪㤫㤬㤭㤮㤯㤰㤱㤲㤳㤴㤵㤶㤷㤸㤹㤺㤻㤼㤽㤾㤿㥀㥁㥂㥃㥄㥅㥆㥇㥈㥉㥊㥋㥌㥍㥎㥏㥐㥑㥒㥓㥔㥕㥖㥗㥘㥙㥚㥛㥜㥝㥞㥟㥠㥡㥢㥣㥤㥥㥦㥧㥨㥩㥪㥫㥬㥭㥮㥯㥰㥱㥲㥳㥴㥵㥶㥷㥸㥹㥺㥻㥼㥽㥾㥿㦀㦁㦂㦃㦄㦅㦆㦇㦈㦉㦊㦋㦌㦍㦎㦏㦐㦑㦒㦓㦔㦕㦖㦗㦘㦙㦚㦛㦜㦝㦞㦟㦠㦡㦢㦣㦤㦥㦦㦧㦨㦩㦪㦫㦬㦭㦮㦯㦰㦱㦲㦳㦴㦵㦶㦷㦸㦹㦺㦻㦼㦽㦾㦿㧀㧁㧂㧃㧄㧅㧆㧇㧈㧉㧊㧋㧌㧍㧎㧏㧐㧑㧒㧓㧔㧕㧖㧗㧘㧙㧚㧛㧜㧝㧞㧟㧠㧡㧢㧣㧤㧥㧦㧧㧨㧩㧪㧫㧬㧭㧮㧯㧰㧱㧲㧳㧴㧵㧶㧷㧸㧹㧺㧻㧼㧽㧾㧿㨀㨁㨂㨃㨄㨅㨆㨇㨈㨉㨊㨋㨌㨍㨎㨏㨐㨑㨒㨓㨔㨕㨖㨗㨘㨙㨚㨛㨜㨝㨞㨟㨠㨡㨢㨣㨤㨥㨦㨧㨨㨩㨪㨫㨬㨭㨮㨯㨰㨱㨲㨳㨴㨵㨶㨷㨸㨹㨺㨻㨼㨽㨾㨿㩀㩁㩂㩃㩄㩅㩆㩇㩈㩉㩊㩋㩌㩍㩎㩏㩐㩑㩒㩓㩔㩕㩖㩗㩘㩙㩚㩛㩜㩝㩞㩟㩠㩡㩢㩣㩤㩥㩦㩧㩨㩩㩪㩫㩬㩭㩮㩯㩰㩱㩲㩳㩴㩵㩶㩷㩸㩹㩺㩻㩼㩽㩾㩿㪀㪁㪂㪃㪄㪅㪆㪇㪈㪉㪊㪋㪌㪍㪎㪏㪐㪑㪒㪓㪔㪕㪖㪗㪘㪙㪚㪛㪜㪝㪞㪟㪠㪡㪢㪣㪤㪥㪦㪧㪨㪩㪪㪫㪬㪭㪮㪯㪰㪱㪲㪳㪴㪵㪶㪷㪸㪹㪺㪻㪼㪽㪾㪿㫀㫁㫂㫃㫄㫅㫆㫇㫈㫉㫊㫋㫌㫍㫎㫏㫐㫑㫒㫓㫔㫕㫖㫗㫘㫙㫚㫛㫜㫝㫞㫟㫠㫡㫢㫣㫤㫥㫦㫧㫨㫩㫪㫫㫬㫭㫮㫯㫰㫱㫲㫳㫴㫵㫶㫷㫸㫹㫺㫻㫼㫽㫾㫿㬀㬁㬂㬃㬄㬅㬆㬇㬈㬉㬊㬋㬌㬍㬎㬏㬐㬑㬒㬓㬔㬕㬖㬗㬘㬙㬚㬛㬜㬝㬞㬟㬠㬡㬢㬣㬤㬥㬦㬧㬨㬩㬪㬫㬬㬭㬮㬯㬰㬱㬲㬳㬴㬵㬶㬷㬸㬹㬺㬻㬼㬽㬾㬿㭀㭁㭂㭃㭄㭅㭆㭇㭈㭉㭊㭋㭌㭍㭎㭏㭐㭑㭒㭓㭔㭕㭖㭗㭘㭙㭚㭛㭜㭝㭞㭟㭠㭡㭢㭣㭤㭥㭦㭧㭨㭩㭪㭫㭬㭭㭮㭯㭰㭱㭲㭳㭴㭵㭶㭷㭸㭹㭺㭻㭼㭽㭾㭿㮀㮁㮂㮃㮄㮅㮆㮇㮈㮉㮊㮋㮌㮍㮎㮏㮐㮑㮒㮓㮔㮕㮖㮗㮘㮙㮚㮛㮜㮝㮞㮟㮠㮡㮢㮣㮤㮥㮦㮧㮨㮩㮪㮫㮬㮭㮮㮯㮰㮱㮲㮳㮴㮵㮶㮷㮸㮹㮺㮻㮼㮽㮾㮿㯀㯁㯂㯃㯄㯅㯆㯇㯈㯉㯊㯋㯌㯍㯎㯏㯐㯑㯒㯓㯔㯕㯖㯗㯘㯙㯚㯛㯜㯝㯞㯟㯠㯡㯢㯣㯤㯥㯦㯧㯨㯩㯪㯫㯬㯭㯮㯯㯰㯱㯲㯳㯴㯵㯶㯷㯸㯹㯺㯻㯼㯽㯾㯿㰀㰁㰂㰃㰄㰅㰆㰇㰈㰉㰊㰋㰌㰍㰎㰏㰐㰑㰒㰓㰔㰕㰖㰗㰘㰙㰚㰛㰜㰝㰞㰟㰠㰡㰢㰣㰤㰥㰦㰧㰨㰩㰪㰫㰬㰭㰮㰯㰰㰱㰲㰳㰴㰵㰶㰷㰸㰹㰺㰻㰼㰽㰾㰿㱀㱁㱂㱃㱄㱅㱆㱇㱈㱉㱊㱋㱌㱍㱎㱏㱐㱑㱒㱓㱔㱕㱖㱗㱘㱙㱚㱛㱜㱝㱞㱟㱠㱡㱢㱣㱤㱥㱦㱧㱨㱩㱪㱫㱬㱭㱮㱯㱰㱱㱲㱳㱴㱵㱶㱷㱸㱹㱺㱻㱼㱽㱾㱿㲀㲁㲂㲃㲄㲅㲆㲇㲈㲉㲊㲋㲌㲍㲎㲏㲐㲑㲒㲓㲔㲕㲖㲗㲘㲙㲚㲛㲜㲝㲞㲟㲠㲡㲢㲣㲤㲥㲦㲧㲨㲩㲪㲫㲬㲭㲮㲯㲰㲱㲲㲳㲴㲵㲶㲷㲸㲹㲺㲻㲼㲽㲾㲿㳀㳁㳂㳃㳄㳅㳆㳇㳈㳉㳊㳋㳌㳍㳎㳏㳐㳑㳒㳓㳔㳕㳖㳗㳘㳙㳚㳛㳜㳝㳞㳟㳠㳡㳢㳣㳤㳥㳦㳧㳨㳩㳪㳫㳬㳭㳮㳯㳰㳱㳲㳳㳴㳵㳶㳷㳸㳹㳺㳻㳼㳽㳾㳿㴀㴁㴂㴃㴄㴅㴆㴇㴈㴉㴊㴋㴌㴍㴎㴏㴐㴑㴒㴓㴔㴕㴖㴗㴘㴙㴚㴛㴜㴝㴞㴟㴠㴡㴢㴣㴤㴥㴦㴧㴨㴩㴪㴫㴬㴭㴮㴯㴰㴱㴲㴳㴴㴵㴶㴷㴸㴹㴺㴻㴼㴽㴾㴿㵀㵁㵂㵃㵄㵅㵆㵇㵈㵉㵊㵋㵌㵍㵎㵏㵐㵑㵒㵓㵔㵕㵖㵗㵘㵙㵚㵛㵜㵝㵞㵟㵠㵡㵢㵣㵤㵥㵦㵧㵨㵩㵪㵫㵬㵭㵮㵯㵰㵱㵲㵳㵴㵵㵶㵷㵸㵹㵺㵻㵼㵽㵾㵿㶀㶁㶂㶃㶄㶅㶆㶇㶈㶉㶊㶋㶌㶍㶎㶏㶐㶑㶒㶓㶔㶕㶖㶗㶘㶙㶚㶛㶜㶝㶞㶟㶠㶡㶢㶣㶤㶥㶦㶧㶨㶩㶪㶫㶬㶭㶮㶯㶰㶱㶲㶳㶴㶵㶶㶷㶸㶹㶺㶻㶼㶽㶾㶿㷀㷁㷂㷃㷄㷅㷆㷇㷈㷉㷊㷋㷌㷍㷎㷏㷐㷑㷒㷓㷔㷕㷖㷗㷘㷙㷚㷛㷜㷝㷞㷟㷠㷡㷢㷣㷤㷥㷦㷧㷨㷩㷪㷫㷬㷭㷮㷯㷰㷱㷲㷳㷴㷵㷶㷷㷸㷹㷺㷻㷼㷽㷾㷿㸀㸁㸂㸃㸄㸅㸆㸇㸈㸉㸊㸋㸌㸍㸎㸏㸐㸑㸒㸓㸔㸕㸖㸗㸘㸙㸚㸛㸜㸝㸞㸟㸠㸡㸢㸣㸤㸥㸦㸧㸨㸩㸪㸫㸬㸭㸮㸯㸰㸱㸲㸳㸴㸵㸶㸷㸸㸹㸺㸻㸼㸽㸾㸿㹀㹁㹂㹃㹄㹅㹆㹇㹈㹉㹊㹋㹌㹍㹎㹏㹐㹑㹒㹓㹔㹕㹖㹗㹘㹙㹚㹛㹜㹝㹞㹟㹠㹡㹢㹣㹤㹥㹦㹧㹨㹩㹪㹫㹬㹭㹮㹯㹰㹱㹲㹳㹴㹵㹶㹷㹸㹹㹺㹻㹼㹽㹾㹿㺀㺁㺂㺃㺄㺅㺆㺇㺈㺉㺊㺋㺌㺍㺎㺏㺐㺑㺒㺓㺔㺕㺖㺗㺘㺙㺚㺛㺜㺝㺞㺟㺠㺡㺢㺣㺤㺥㺦㺧㺨㺩㺪㺫㺬㺭㺮㺯㺰㺱㺲㺳㺴㺵㺶㺷㺸㺹㺺㺻㺼㺽㺾㺿㻀㻁㻂㻃㻄㻅㻆㻇㻈㻉㻊㻋㻌㻍㻎㻏㻐㻑㻒㻓㻔㻕㻖㻗㻘㻙㻚㻛㻜㻝㻞㻟㻠㻡㻢㻣㻤㻥㻦㻧㻨㻩㻪㻫㻬㻭㻮㻯㻰㻱㻲㻳㻴㻵㻶㻷㻸㻹㻺㻻㻼㻽㻾㻿㼀㼁㼂㼃㼄㼅㼆㼇㼈㼉㼊㼋㼌㼍㼎㼏㼐㼑㼒㼓㼔㼕㼖㼗㼘㼙㼚㼛㼜㼝㼞㼟㼠㼡㼢㼣㼤㼥㼦㼧㼨㼩㼪㼫㼬㼭㼮㼯㼰㼱㼲㼳㼴㼵㼶㼷㼸㼹㼺㼻㼼㼽㼾㼿㽀㽁㽂㽃㽄㽅㽆㽇㽈㽉㽊㽋㽌㽍㽎㽏㽐㽑㽒㽓㽔㽕㽖㽗㽘㽙㽚㽛㽜㽝㽞㽟㽠㽡㽢㽣㽤㽥㽦㽧㽨㽩㽪㽫㽬㽭㽮㽯㽰㽱㽲㽳㽴㽵㽶㽷㽸㽹㽺㽻㽼㽽㽾㽿㾀㾁㾂㾃㾄㾅㾆㾇㾈㾉㾊㾋㾌㾍㾎㾏㾐㾑㾒㾓㾔㾕㾖㾗㾘㾙㾚㾛㾜㾝㾞㾟㾠㾡㾢㾣㾤㾥㾦㾧㾨㾩㾪㾫㾬㾭㾮㾯㾰㾱㾲㾳㾴㾵㾶㾷㾸㾹㾺㾻㾼㾽㾾㾿㿀㿁㿂㿃㿄㿅㿆㿇㿈㿉㿊㿋㿌㿍㿎㿏㿐㿑㿒㿓㿔㿕㿖㿗㿘㿙㿚㿛㿜㿝㿞㿟㿠㿡㿢㿣㿤㿥㿦㿧㿨㿩㿪㿫㿬㿭㿮㿯㿰㿱㿲㿳㿴㿵㿶㿷㿸㿹㿺㿻㿼㿽㿾㿿䀀䀁䀂䀃䀄䀅䀆䀇䀈䀉䀊䀋䀌䀍䀎䀏䀐䀑䀒䀓䀔䀕䀖䀗䀘䀙䀚䀛䀜䀝䀞䀟䀠䀡䀢䀣䀤䀥䀦䀧䀨䀩䀪䀫䀬䀭䀮䀯䀰䀱䀲䀳䀴䀵䀶䀷䀸䀹䀺䀻䀼䀽䀾䀿䁀䁁䁂䁃䁄䁅䁆䁇䁈䁉䁊䁋䁌䁍䁎䁏䁐䁑䁒䁓䁔䁕䁖䁗䁘䁙䁚䁛䁜䁝䁞䁟䁠䁡䁢䁣䁤䁥䁦䁧䁨䁩䁪䁫䁬䁭䁮䁯䁰䁱䁲䁳䁴䁵䁶䁷䁸䁹䁺䁻䁼䁽䁾䁿䂀䂁䂂䂃䂄䂅䂆䂇䂈䂉䂊䂋䂌䂍䂎䂏䂐䂑䂒䂓䂔䂕䂖䂗䂘䂙䂚䂛䂜䂝䂞䂟䂠䂡䂢䂣䂤䂥䂦䂧䂨䂩䂪䂫䂬䂭䂮䂯䂰䂱䂲䂳䂴䂵䂶䂷䂸䂹䂺䂻䂼䂽䂾䂿䃀䃁䃂䃃䃄䃅䃆䃇䃈䃉䃊䃋䃌䃍䃎䃏䃐䃑䃒䃓䃔䃕䃖䃗䃘䃙䃚䃛䃜䃝䃞䃟䃠䃡䃢䃣䃤䃥䃦䃧䃨䃩䃪䃫䃬䃭䃮䃯䃰䃱䃲䃳䃴䃵䃶䃷䃸䃹䃺䃻䃼䃽䃾䃿䄀䄁䄂䄃䄄䄅䄆䄇䄈䄉䄊䄋䄌䄍䄎䄏䄐䄑䄒䄓䄔䄕䄖䄗䄘䄙䄚䄛䄜䄝䄞䄟䄠䄡䄢䄣䄤䄥䄦䄧䄨䄩䄪䄫䄬䄭䄮䄯䄰䄱䄲䄳䄴䄵䄶䄷䄸䄹䄺䄻䄼䄽䄾䄿䅀䅁䅂䅃䅄䅅䅆䅇䅈䅉䅊䅋䅌䅍䅎䅏䅐䅑䅒䅓䅔䅕䅖䅗䅘䅙䅚䅛䅜䅝䅞䅟䅠䅡䅢䅣䅤䅥䅦䅧䅨䅩䅪䅫䅬䅭䅮䅯䅰䅱䅲䅳䅴䅵䅶䅷䅸䅹䅺䅻䅼䅽䅾䅿䆀䆁䆂䆃䆄䆅䆆䆇䆈䆉䆊䆋䆌䆍䆎䆏䆐䆑䆒䆓䆔䆕䆖䆗䆘䆙䆚䆛䆜䆝䆞䆟䆠䆡䆢䆣䆤䆥䆦䆧䆨䆩䆪䆫䆬䆭䆮䆯䆰䆱䆲䆳䆴䆵䆶䆷䆸䆹䆺䆻䆼䆽䆾䆿䇀䇁䇂䇃䇄䇅䇆䇇䇈䇉䇊䇋䇌䇍䇎䇏䇐䇑䇒䇓䇔䇕䇖䇗䇘䇙䇚䇛䇜䇝䇞䇟䇠䇡䇢䇣䇤䇥䇦䇧䇨䇩䇪䇫䇬䇭䇮䇯䇰䇱䇲䇳䇴䇵䇶䇷䇸䇹䇺䇻䇼䇽䇾䇿䈀䈁䈂䈃䈄䈅䈆䈇䈈䈉䈊䈋䈌䈍䈎䈏䈐䈑䈒䈓䈔䈕䈖䈗䈘䈙䈚䈛䈜䈝䈞䈟䈠䈡䈢䈣䈤䈥䈦䈧䈨䈩䈪䈫䈬䈭䈮䈯䈰䈱䈲䈳䈴䈵䈶䈷䈸䈹䈺䈻䈼䈽䈾䈿䉀䉁䉂䉃䉄䉅䉆䉇䉈䉉䉊䉋䉌䉍䉎䉏䉐䉑䉒䉓䉔䉕䉖䉗䉘䉙䉚䉛䉜䉝䉞䉟䉠䉡䉢䉣䉤䉥䉦䉧䉨䉩䉪䉫䉬䉭䉮䉯䉰䉱䉲䉳䉴䉵䉶䉷䉸䉹䉺䉻䉼䉽䉾䉿䊀䊁䊂䊃䊄䊅䊆䊇䊈䊉䊊䊋䊌䊍䊎䊏䊐䊑䊒䊓䊔䊕䊖䊗䊘䊙䊚䊛䊜䊝䊞䊟䊠䊡䊢䊣䊤䊥䊦䊧䊨䊩䊪䊫䊬䊭䊮䊯䊰䊱䊲䊳䊴䊵䊶䊷䊸䊹䊺䊻䊼䊽䊾䊿䋀䋁䋂䋃䋄䋅䋆䋇䋈䋉䋊䋋䋌䋍䋎䋏䋐䋑䋒䋓䋔䋕䋖䋗䋘䋙䋚䋛䋜䋝䋞䋟䋠䋡䋢䋣䋤䋥䋦䋧䋨䋩䋪䋫䋬䋭䋮䋯䋰䋱䋲䋳䋴䋵䋶䋷䋸䋹䋺䋻䋼䋽䋾䋿䌀䌁䌂䌃䌄䌅䌆䌇䌈䌉䌊䌋䌌䌍䌎䌏䌐䌑䌒䌓䌔䌕䌖䌗䌘䌙䌚䌛䌜䌝䌞䌟䌠䌡䌢䌣䌤䌥䌦䌧䌨䌩䌪䌫䌬䌭䌮䌯䌰䌱䌲䌳䌴䌵䌶䌷䌸䌹䌺䌻䌼䌽䌾䌿䍀䍁䍂䍃䍄䍅䍆䍇䍈䍉䍊䍋䍌䍍䍎䍏䍐䍑䍒䍓䍔䍕䍖䍗䍘䍙䍚䍛䍜䍝䍞䍟䍠䍡䍢䍣䍤䍥䍦䍧䍨䍩䍪䍫䍬䍭䍮䍯䍰䍱䍲䍳䍴䍵䍶䍷䍸䍹䍺䍻䍼䍽䍾䍿䎀䎁䎂䎃䎄䎅䎆䎇䎈䎉䎊䎋䎌䎍䎎䎏䎐䎑䎒䎓䎔䎕䎖䎗䎘䎙䎚䎛䎜䎝䎞䎟䎠䎡䎢䎣䎤䎥䎦䎧䎨䎩䎪䎫䎬䎭䎮䎯䎰䎱䎲䎳䎴䎵䎶䎷䎸䎹䎺䎻䎼䎽䎾䎿䏀䏁䏂䏃䏄䏅䏆䏇䏈䏉䏊䏋䏌䏍䏎䏏䏐䏑䏒䏓䏔䏕䏖䏗䏘䏙䏚䏛䏜䏝䏞䏟䏠䏡䏢䏣䏤䏥䏦䏧䏨䏩䏪䏫䏬䏭䏮䏯䏰䏱䏲䏳䏴䏵䏶䏷䏸䏹䏺䏻䏼䏽䏾䏿䐀䐁䐂䐃䐄䐅䐆䐇䐈䐉䐊䐋䐌䐍䐎䐏䐐䐑䐒䐓䐔䐕䐖䐗䐘䐙䐚䐛䐜䐝䐞䐟䐠䐡䐢䐣䐤䐥䐦䐧䐨䐩䐪䐫䐬䐭䐮䐯䐰䐱䐲䐳䐴䐵䐶䐷䐸䐹䐺䐻䐼䐽䐾䐿䑀䑁䑂䑃䑄䑅䑆䑇䑈䑉䑊䑋䑌䑍䑎䑏䑐䑑䑒䑓䑔䑕䑖䑗䑘䑙䑚䑛䑜䑝䑞䑟䑠䑡䑢䑣䑤䑥䑦䑧䑨䑩䑪䑫䑬䑭䑮䑯䑰䑱䑲䑳䑴䑵䑶䑷䑸䑹䑺䑻䑼䑽䑾䑿䒀䒁䒂䒃䒄䒅䒆䒇䒈䒉䒊䒋䒌䒍䒎䒏䒐䒑䒒䒓䒔䒕䒖䒗䒘䒙䒚䒛䒜䒝䒞䒟䒠䒡䒢䒣䒤䒥䒦䒧䒨䒩䒪䒫䒬䒭䒮䒯䒰䒱䒲䒳䒴䒵䒶䒷䒸䒹䒺䒻䒼䒽䒾䒿䓀䓁䓂䓃䓄䓅䓆䓇䓈䓉䓊䓋䓌䓍䓎䓏䓐䓑䓒䓓䓔䓕䓖䓗䓘䓙䓚䓛䓜䓝䓞䓟䓠䓡䓢䓣䓤䓥䓦䓧䓨䓩䓪䓫䓬䓭䓮䓯䓰䓱䓲䓳䓴䓵䓶䓷䓸䓹䓺䓻䓼䓽䓾䓿䔀䔁䔂䔃䔄䔅䔆䔇䔈䔉䔊䔋䔌䔍䔎䔏䔐䔑䔒䔓䔔䔕䔖䔗䔘䔙䔚䔛䔜䔝䔞䔟䔠䔡䔢䔣䔤䔥䔦䔧䔨䔩䔪䔫䔬䔭䔮䔯䔰䔱䔲䔳䔴䔵䔶䔷䔸䔹䔺䔻䔼䔽䔾䔿䕀䕁䕂䕃䕄䕅䕆䕇䕈䕉䕊䕋䕌䕍䕎䕏䕐䕑䕒䕓䕔䕕䕖䕗䕘䕙䕚䕛䕜䕝䕞䕟䕠䕡䕢䕣䕤䕥䕦䕧䕨䕩䕪䕫䕬䕭䕮䕯䕰䕱䕲䕳䕴䕵䕶䕷䕸䕹䕺䕻䕼䕽䕾䕿䖀䖁䖂䖃䖄䖅䖆䖇䖈䖉䖊䖋䖌䖍䖎䖏䖐䖑䖒䖓䖔䖕䖖䖗䖘䖙䖚䖛䖜䖝䖞䖟䖠䖡䖢䖣䖤䖥䖦䖧䖨䖩䖪䖫䖬䖭䖮䖯䖰䖱䖲䖳䖴䖵䖶䖷䖸䖹䖺䖻䖼䖽䖾䖿䗀䗁䗂䗃䗄䗅䗆䗇䗈䗉䗊䗋䗌䗍䗎䗏䗐䗑䗒䗓䗔䗕䗖䗗䗘䗙䗚䗛䗜䗝䗞䗟䗠䗡䗢䗣䗤䗥䗦䗧䗨䗩䗪䗫䗬䗭䗮䗯䗰䗱䗲䗳䗴䗵䗶䗷䗸䗹䗺䗻䗼䗽䗾䗿䘀䘁䘂䘃䘄䘅䘆䘇䘈䘉䘊䘋䘌䘍䘎䘏䘐䘑䘒䘓䘔䘕䘖䘗䘘䘙䘚䘛䘜䘝䘞䘟䘠䘡䘢䘣䘤䘥䘦䘧䘨䘩䘪䘫䘬䘭䘮䘯䘰䘱䘲䘳䘴䘵䘶䘷䘸䘹䘺䘻䘼䘽䘾䘿䙀䙁䙂䙃䙄䙅䙆䙇䙈䙉䙊䙋䙌䙍䙎䙏䙐䙑䙒䙓䙔䙕䙖䙗䙘䙙䙚䙛䙜䙝䙞䙟䙠䙡䙢䙣䙤䙥䙦䙧䙨䙩䙪䙫䙬䙭䙮䙯䙰䙱䙲䙳䙴䙵䙶䙷䙸䙹䙺䙻䙼䙽䙾䙿䚀䚁䚂䚃䚄䚅䚆䚇䚈䚉䚊䚋䚌䚍䚎䚏䚐䚑䚒䚓䚔䚕䚖䚗䚘䚙䚚䚛䚜䚝䚞䚟䚠䚡䚢䚣䚤䚥䚦䚧䚨䚩䚪䚫䚬䚭䚮䚯䚰䚱䚲䚳䚴䚵䚶䚷䚸䚹䚺䚻䚼䚽䚾䚿䛀䛁䛂䛃䛄䛅䛆䛇䛈䛉䛊䛋䛌䛍䛎䛏䛐䛑䛒䛓䛔䛕䛖䛗䛘䛙䛚䛛䛜䛝䛞䛟䛠䛡䛢䛣䛤䛥䛦䛧䛨䛩䛪䛫䛬䛭䛮䛯䛰䛱䛲䛳䛴䛵䛶䛷䛸䛹䛺䛻䛼䛽䛾䛿䜀䜁䜂䜃䜄䜅䜆䜇䜈䜉䜊䜋䜌䜍䜎䜏䜐䜑䜒䜓䜔䜕䜖䜗䜘䜙䜚䜛䜜䜝䜞䜟䜠䜡䜢䜣䜤䜥䜦䜧䜨䜩䜪䜫䜬䜭䜮䜯䜰䜱䜲䜳䜴䜵䜶䜷䜸䜹䜺䜻䜼䜽䜾䜿䝀䝁䝂䝃䝄䝅䝆䝇䝈䝉䝊䝋䝌䝍䝎䝏䝐䝑䝒䝓䝔䝕䝖䝗䝘䝙䝚䝛䝜䝝䝞䝟䝠䝡䝢䝣䝤䝥䝦䝧䝨䝩䝪䝫䝬䝭䝮䝯䝰䝱䝲䝳䝴䝵䝶䝷䝸䝹䝺䝻䝼䝽䝾䝿䞀䞁䞂䞃䞄䞅䞆䞇䞈䞉䞊䞋䞌䞍䞎䞏䞐䞑䞒䞓䞔䞕䞖䞗䞘䞙䞚䞛䞜䞝䞞䞟䞠䞡䞢䞣䞤䞥䞦䞧䞨䞩䞪䞫䞬䞭䞮䞯䞰䞱䞲䞳䞴䞵䞶䞷䞸䞹䞺䞻䞼䞽䞾䞿䟀䟁䟂䟃䟄䟅䟆䟇䟈䟉䟊䟋䟌䟍䟎䟏䟐䟑䟒䟓䟔䟕䟖䟗䟘䟙䟚䟛䟜䟝䟞䟟䟠䟡䟢䟣䟤䟥䟦䟧䟨䟩䟪䟫䟬䟭䟮䟯䟰䟱䟲䟳䟴䟵䟶䟷䟸䟹䟺䟻䟼䟽䟾䟿䠀䠁䠂䠃䠄䠅䠆䠇䠈䠉䠊䠋䠌䠍䠎䠏䠐䠑䠒䠓䠔䠕䠖䠗䠘䠙䠚䠛䠜䠝䠞䠟䠠䠡䠢䠣䠤䠥䠦䠧䠨䠩䠪䠫䠬䠭䠮䠯䠰䠱䠲䠳䠴䠵䠶䠷䠸䠹䠺䠻䠼䠽䠾䠿䡀䡁䡂䡃䡄䡅䡆䡇䡈䡉䡊䡋䡌䡍䡎䡏䡐䡑䡒䡓䡔䡕䡖䡗䡘䡙䡚䡛䡜䡝䡞䡟䡠䡡䡢䡣䡤䡥䡦䡧䡨䡩䡪䡫䡬䡭䡮䡯䡰䡱䡲䡳䡴䡵䡶䡷䡸䡹䡺䡻䡼䡽䡾䡿䢀䢁䢂䢃䢄䢅䢆䢇䢈䢉䢊䢋䢌䢍䢎䢏䢐䢑䢒䢓䢔䢕䢖䢗䢘䢙䢚䢛䢜䢝䢞䢟䢠䢡䢢䢣䢤䢥䢦䢧䢨䢩䢪䢫䢬䢭䢮䢯䢰䢱䢲䢳䢴䢵䢶䢷䢸䢹䢺䢻䢼䢽䢾䢿䣀䣁䣂䣃䣄䣅䣆䣇䣈䣉䣊䣋䣌䣍䣎䣏䣐䣑䣒䣓䣔䣕䣖䣗䣘䣙䣚䣛䣜䣝䣞䣟䣠䣡䣢䣣䣤䣥䣦䣧䣨䣩䣪䣫䣬䣭䣮䣯䣰䣱䣲䣳䣴䣵䣶䣷䣸䣹䣺䣻䣼䣽䣾䣿䤀䤁䤂䤃䤄䤅䤆䤇䤈䤉䤊䤋䤌䤍䤎䤏䤐䤑䤒䤓䤔䤕䤖䤗䤘䤙䤚䤛䤜䤝䤞䤟䤠䤡䤢䤣䤤䤥䤦䤧䤨䤩䤪䤫䤬䤭䤮䤯䤰䤱䤲䤳䤴䤵䤶䤷䤸䤹䤺䤻䤼䤽䤾䤿䥀䥁䥂䥃䥄䥅䥆䥇䥈䥉䥊䥋䥌䥍䥎䥏䥐䥑䥒䥓䥔䥕䥖䥗䥘䥙䥚䥛䥜䥝䥞䥟䥠䥡䥢䥣䥤䥥䥦䥧䥨䥩䥪䥫䥬䥭䥮䥯䥰䥱䥲䥳䥴䥵䥶䥷䥸䥹䥺䥻䥼䥽䥾䥿䦀䦁䦂䦃䦄䦅䦆䦇䦈䦉䦊䦋䦌䦍䦎䦏䦐䦑䦒䦓䦔䦕䦖䦗䦘䦙䦚䦛䦜䦝䦞䦟䦠䦡䦢䦣䦤䦥䦦䦧䦨䦩䦪䦫䦬䦭䦮䦯䦰䦱䦲䦳䦴䦵䦶䦷䦸䦹䦺䦻䦼䦽䦾䦿䧀䧁䧂䧃䧄䧅䧆䧇䧈䧉䧊䧋䧌䧍䧎䧏䧐䧑䧒䧓䧔䧕䧖䧗䧘䧙䧚䧛䧜䧝䧞䧟䧠䧡䧢䧣䧤䧥䧦䧧䧨䧩䧪䧫䧬䧭䧮䧯䧰䧱䧲䧳䧴䧵䧶䧷䧸䧹䧺䧻䧼䧽䧾䧿䨀䨁䨂䨃䨄䨅䨆䨇䨈䨉䨊䨋䨌䨍䨎䨏䨐䨑䨒䨓䨔䨕䨖䨗䨘䨙䨚䨛䨜䨝䨞䨟䨠䨡䨢䨣䨤䨥䨦䨧䨨䨩䨪䨫䨬䨭䨮䨯䨰䨱䨲䨳䨴䨵䨶䨷䨸䨹䨺䨻䨼䨽䨾䨿䩀䩁䩂䩃䩄䩅䩆䩇䩈䩉䩊䩋䩌䩍䩎䩏䩐䩑䩒䩓䩔䩕䩖䩗䩘䩙䩚䩛䩜䩝䩞䩟䩠䩡䩢䩣䩤䩥䩦䩧䩨䩩䩪䩫䩬䩭䩮䩯䩰䩱䩲䩳䩴䩵䩶䩷䩸䩹䩺䩻䩼䩽䩾䩿䪀䪁䪂䪃䪄䪅䪆䪇䪈䪉䪊䪋䪌䪍䪎䪏䪐䪑䪒䪓䪔䪕䪖䪗䪘䪙䪚䪛䪜䪝䪞䪟䪠䪡䪢䪣䪤䪥䪦䪧䪨䪩䪪䪫䪬䪭䪮䪯䪰䪱䪲䪳䪴䪵䪶䪷䪸䪹䪺䪻䪼䪽䪾䪿䫀䫁䫂䫃䫄䫅䫆䫇䫈䫉䫊䫋䫌䫍䫎䫏䫐䫑䫒䫓䫔䫕䫖䫗䫘䫙䫚䫛䫜䫝䫞䫟䫠䫡䫢䫣䫤䫥䫦䫧䫨䫩䫪䫫䫬䫭䫮䫯䫰䫱䫲䫳䫴䫵䫶䫷䫸䫹䫺䫻䫼䫽䫾䫿䬀䬁䬂䬃䬄䬅䬆䬇䬈䬉䬊䬋䬌䬍䬎䬏䬐䬑䬒䬓䬔䬕䬖䬗䬘䬙䬚䬛䬜䬝䬞䬟䬠䬡䬢䬣䬤䬥䬦䬧䬨䬩䬪䬫䬬䬭䬮䬯䬰䬱䬲䬳䬴䬵䬶䬷䬸䬹䬺䬻䬼䬽䬾䬿䭀䭁䭂䭃䭄䭅䭆䭇䭈䭉䭊䭋䭌䭍䭎䭏䭐䭑䭒䭓䭔䭕䭖䭗䭘䭙䭚䭛䭜䭝䭞䭟䭠䭡䭢䭣䭤䭥䭦䭧䭨䭩䭪䭫䭬䭭䭮䭯䭰䭱䭲䭳䭴䭵䭶䭷䭸䭹䭺䭻䭼䭽䭾䭿䮀䮁䮂䮃䮄䮅䮆䮇䮈䮉䮊䮋䮌䮍䮎䮏䮐䮑䮒䮓䮔䮕䮖䮗䮘䮙䮚䮛䮜䮝䮞䮟䮠䮡䮢䮣䮤䮥䮦䮧䮨䮩䮪䮫䮬䮭䮮䮯䮰䮱䮲䮳䮴䮵䮶䮷䮸䮹䮺䮻䮼䮽䮾䮿䯀䯁䯂䯃䯄䯅䯆䯇䯈䯉䯊䯋䯌䯍䯎䯏䯐䯑䯒䯓䯔䯕䯖䯗䯘䯙䯚䯛䯜䯝䯞䯟䯠䯡䯢䯣䯤䯥䯦䯧䯨䯩䯪䯫䯬䯭䯮䯯䯰䯱䯲䯳䯴䯵䯶䯷䯸䯹䯺䯻䯼䯽䯾䯿䰀䰁䰂䰃䰄䰅䰆䰇䰈䰉䰊䰋䰌䰍䰎䰏䰐䰑䰒䰓䰔䰕䰖䰗䰘䰙䰚䰛䰜䰝䰞䰟䰠䰡䰢䰣䰤䰥䰦䰧䰨䰩䰪䰫䰬䰭䰮䰯䰰䰱䰲䰳䰴䰵䰶䰷䰸䰹䰺䰻䰼䰽䰾䰿䱀䱁䱂䱃䱄䱅䱆䱇䱈䱉䱊䱋䱌䱍䱎䱏䱐䱑䱒䱓䱔䱕䱖䱗䱘䱙䱚䱛䱜䱝䱞䱟䱠䱡䱢䱣䱤䱥䱦䱧䱨䱩䱪䱫䱬䱭䱮䱯䱰䱱䱲䱳䱴䱵䱶䱷䱸䱹䱺䱻䱼䱽䱾䱿䲀䲁䲂䲃䲄䲅䲆䲇䲈䲉䲊䲋䲌䲍䲎䲏䲐䲑䲒䲓䲔䲕䲖䲗䲘䲙䲚䲛䲜䲝䲞䲟䲠䲡䲢䲣䲤䲥䲦䲧䲨䲩䲪䲫䲬䲭䲮䲯䲰䲱䲲䲳䲴䲵䲶䲷䲸䲹䲺䲻䲼䲽䲾䲿䳀䳁䳂䳃䳄䳅䳆䳇䳈䳉䳊䳋䳌䳍䳎䳏䳐䳑䳒䳓䳔䳕䳖䳗䳘䳙䳚䳛䳜䳝䳞䳟䳠䳡䳢䳣䳤䳥䳦䳧䳨䳩䳪䳫䳬䳭䳮䳯䳰䳱䳲䳳䳴䳵䳶䳷䳸䳹䳺䳻䳼䳽䳾䳿䴀䴁䴂䴃䴄䴅䴆䴇䴈䴉䴊䴋䴌䴍䴎䴏䴐䴑䴒䴓䴔䴕䴖䴗䴘䴙䴚䴛䴜䴝䴞䴟䴠䴡䴢䴣䴤䴥䴦䴧䴨䴩䴪䴫䴬䴭䴮䴯䴰䴱䴲䴳䴴䴵䴶䴷䴸䴹䴺䴻䴼䴽䴾䴿䵀䵁䵂䵃䵄䵅䵆䵇䵈䵉䵊䵋䵌䵍䵎䵏䵐䵑䵒䵓䵔䵕䵖䵗䵘䵙䵚䵛䵜䵝䵞䵟䵠䵡䵢䵣䵤䵥䵦䵧䵨䵩䵪䵫䵬䵭䵮䵯䵰䵱䵲䵳䵴䵵䵶䵷䵸䵹䵺䵻䵼䵽䵾䵿䶀䶁䶂䶃䶄䶅䶆䶇䶈䶉䶊䶋䶌䶍䶎䶏䶐䶑䶒䶓䶔䶕䶖䶗䶘䶙䶚䶛䶜䶝䶞䶟䶠䶡䶢䶣䶤䶥䶦䶧䶨䶩䶪䶫䶬䶭䶮䶯䶰䶱䶲䶳䶴䶵䷀䷁䷂䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌䷍䷎䷏䷐䷑䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜䷝䷞䷟䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿一丁丂七丄丅丆万丈三上下丌不与丏丐丑丒专且丕世丗丘丙业丛东丝丞丟丠両丢丣两严並丧丨丩个丫丬中丮丯丰丱串丳临丵丶丷丸丹为主丼丽举丿乀乁乂乃乄久乆乇么义乊之乌乍乎乏乐乑乒乓乔乕乖乗乘乙乚乛乜九乞也习乡乢乣乤乥书乧乨乩乪乫乬乭乮乯买乱乲乳乴乵乶乷乸乹乺乻乼乽乾乿亀亁亂亃亄亅了亇予争亊事二亍于亏亐云互亓五井亖亗亘亙亚些亜亝亞亟亠亡亢亣交亥亦产亨亩亪享京亭亮亯亰亱亲亳亴亵亶亷亸亹人亻亼亽亾亿什仁仂仃仄仅仆仇仈仉今介仌仍从仏仐仑仒仓仔仕他仗付仙仚仛仜仝仞仟仠仡仢代令以仦仧仨仩仪仫们仭仮仯仰仱仲仳仴仵件价仸仹仺任仼份仾仿伀企伂伃伄伅伆伇伈伉伊伋伌伍伎伏伐休伒伓伔伕伖众优伙会伛伜伝伞伟传伡伢伣伤伥伦伧伨伩伪伫伬伭伮伯估伱伲伳伴伵伶伷伸伹伺伻似伽伾伿佀佁佂佃佄佅但佇佈佉佊佋佌位低住佐佑佒体佔何佖佗佘余佚佛作佝佞佟你佡佢佣佤佥佦佧佨佩佪佫佬佭佮佯佰佱佲佳佴併佶佷佸佹佺佻佼佽佾使侀侁侂侃侄侅來侇侈侉侊例侌侍侎侏侐侑侒侓侔侕侖侗侘侙侚供侜依侞侟侠価侢侣侤侥侦侧侨侩侪侫侬侭侮侯侰侱侲侳侴侵侶侷侸侹侺侻侼侽侾便俀俁係促俄俅俆俇俈俉俊俋俌俍俎俏俐俑俒俓俔俕俖俗俘俙俚俛俜保俞俟俠信俢俣俤俥俦俧俨俩俪俫俬俭修俯俰俱俲俳俴俵俶俷俸俹俺俻俼俽俾俿倀倁倂倃倄倅倆倇倈倉倊個倌倍倎倏倐們倒倓倔倕倖倗倘候倚倛倜倝倞借倠倡倢倣値倥倦倧倨倩倪倫倬倭倮倯倰倱倲倳倴倵倶倷倸倹债倻值倽倾倿偀偁偂偃偄偅偆假偈偉偊偋偌偍偎偏偐偑偒偓偔偕偖偗偘偙做偛停偝偞偟偠偡偢偣偤健偦偧偨偩偪偫偬偭偮偯偰偱偲偳側偵偶偷偸偹偺偻偼偽偾偿傀傁傂傃傄傅傆傇傈傉傊傋傌傍傎傏傐傑傒傓傔傕傖傗傘備傚傛傜傝傞傟傠傡傢傣傤傥傦傧储傩傪傫催傭傮傯傰傱傲傳傴債傶傷傸傹傺傻傼傽傾傿僀僁僂僃僄僅僆僇僈僉僊僋僌働僎像僐僑僒僓僔僕僖僗僘僙僚僛僜僝僞僟僠僡僢僣僤僥僦僧僨僩僪僫僬僭僮僯僰僱僲僳僴僵僶僷僸價僺僻僼僽僾僿儀儁儂儃億儅儆儇儈儉儊儋儌儍儎儏儐儑儒儓儔儕儖儗儘儙儚儛儜儝儞償儠儡儢儣儤儥儦儧儨儩優儫儬儭儮儯儰儱儲儳儴儵儶儷儸儹儺儻儼儽儾儿兀允兂元兄充兆兇先光兊克兌免兎兏児兑兒兓兔兕兖兗兘兙党兛兜兝兞兟兠兡兢兣兤入兦內全兩兪八公六兮兯兰共兲关兴兵其具典兹兺养兼兽兾兿冀冁冂冃冄内円冇冈冉冊冋册再冎冏冐冑冒冓冔冕冖冗冘写冚军农冝冞冟冠冡冢冣冤冥冦冧冨冩冪冫冬冭冮冯冰冱冲决冴况冶冷冸冹冺冻冼冽冾冿净凁凂凃凄凅准凇凈凉凊凋凌凍凎减凐凑凒凓凔凕凖凗凘凙凚凛凜凝凞凟几凡凢凣凤凥処凧凨凩凪凫凬凭凮凯凰凱凲凳凴凵凶凷凸凹出击凼函凾凿刀刁刂刃刄刅分切刈刉刊刋刌刍刎刏刐刑划刓刔刕刖列刘则刚创刜初刞刟删刡刢刣判別刦刧刨利刪别刬刭刮刯到刱刲刳刴刵制刷券刹刺刻刼刽刾刿剀剁剂剃剄剅剆則剈剉削剋剌前剎剏剐剑剒剓剔剕剖剗剘剙剚剛剜剝剞剟剠剡剢剣剤剥剦剧剨剩剪剫剬剭剮副剰剱割剳剴創剶剷剸剹剺剻剼剽剾剿劀劁劂劃劄劅劆劇劈劉劊劋劌劍劎劏劐劑劒劓劔劕劖劗劘劙劚力劜劝办功加务劢劣劤劥劦劧动助努劫劬劭劮劯劰励劲劳労劵劶劷劸効劺劻劼劽劾势勀勁勂勃勄勅勆勇勈勉勊勋勌勍勎勏勐勑勒勓勔動勖勗勘務勚勛勜勝勞募勠勡勢勣勤勥勦勧勨勩勪勫勬勭勮勯勰勱勲勳勴勵勶勷勸勹勺勻勼勽勾勿匀匁匂匃匄包匆匇匈匉匊匋匌匍匎匏匐匑匒匓匔匕化北匘匙匚匛匜匝匞匟匠匡匢匣匤匥匦匧匨匩匪匫匬匭匮匯匰匱匲匳匴匵匶匷匸匹区医匼匽匾匿區十卂千卄卅卆升午卉半卋卌卍华协卐卑卒卓協单卖南単卙博卛卜卝卞卟占卡卢卣卤卥卦卧卨卩卪卫卬卭卮卯印危卲即却卵卶卷卸卹卺卻卼卽卾卿厀厁厂厃厄厅历厇厈厉厊压厌厍厎厏厐厑厒厓厔厕厖厗厘厙厚厛厜厝厞原厠厡厢厣厤厥厦厧厨厩厪厫厬厭厮厯厰厱厲厳厴厵厶厷厸厹厺去厼厽厾县叀叁参參叄叅叆叇又叉及友双反収叏叐发叒叓叔叕取受变叙叚叛叜叝叞叟叠叡叢口古句另叧叨叩只叫召叭叮可台叱史右叴叵叶号司叹叺叻叼叽叾叿吀吁吂吃各吅吆吇合吉吊吋同名后吏吐向吒吓吔吕吖吗吘吙吚君吜吝吞吟吠吡吢吣吤吥否吧吨吩吪含听吭吮启吰吱吲吳吴吵吶吷吸吹吺吻吼吽吾吿呀呁呂呃呄呅呆呇呈呉告呋呌呍呎呏呐呑呒呓呔呕呖呗员呙呚呛呜呝呞呟呠呡呢呣呤呥呦呧周呩呪呫呬呭呮呯呰呱呲味呴呵呶呷呸呹呺呻呼命呾呿咀咁咂咃咄咅咆咇咈咉咊咋和咍咎咏咐咑咒咓咔咕咖咗咘咙咚咛咜咝咞咟咠咡咢咣咤咥咦咧咨咩咪咫咬咭咮咯咰咱咲咳咴咵咶咷咸咹咺咻咼咽咾咿哀品哂哃哄哅哆哇哈哉哊哋哌响哎哏哐哑哒哓哔哕哖哗哘哙哚哛哜哝哞哟哠員哢哣哤哥哦哧哨哩哪哫哬哭哮哯哰哱哲哳哴哵哶哷哸哹哺哻哼哽哾哿唀唁唂唃唄唅唆唇唈唉唊唋唌唍唎唏唐唑唒唓唔唕唖唗唘唙唚唛唜唝唞唟唠唡唢唣唤唥唦唧唨唩唪唫唬唭售唯唰唱唲唳唴唵唶唷唸唹唺唻唼唽唾唿啀啁啂啃啄啅商啇啈啉啊啋啌啍啎問啐啑啒啓啔啕啖啗啘啙啚啛啜啝啞啟啠啡啢啣啤啥啦啧啨啩啪啫啬啭啮啯啰啱啲啳啴啵啶啷啸啹啺啻啼啽啾啿喀喁喂喃善喅喆喇喈喉喊喋喌喍喎喏喐喑喒喓喔喕喖喗喘喙喚喛喜喝喞喟喠喡喢喣喤喥喦喧喨喩喪喫喬喭單喯喰喱喲喳喴喵営喷喸喹喺喻喼喽喾喿嗀嗁嗂嗃嗄嗅嗆嗇嗈嗉嗊嗋嗌嗍嗎嗏嗐嗑嗒嗓嗔嗕嗖嗗嗘嗙嗚嗛嗜嗝嗞嗟嗠嗡嗢嗣嗤嗥嗦嗧嗨嗩嗪嗫嗬嗭嗮嗯嗰嗱嗲嗳嗴嗵嗶嗷嗸嗹嗺嗻嗼嗽嗾嗿嘀嘁嘂嘃嘄嘅嘆嘇嘈嘉嘊嘋嘌嘍嘎嘏嘐嘑嘒嘓嘔嘕嘖嘗嘘嘙嘚嘛嘜嘝嘞嘟嘠嘡嘢嘣嘤嘥嘦嘧嘨嘩嘪嘫嘬嘭嘮嘯嘰嘱嘲嘳嘴嘵嘶嘷嘸嘹嘺嘻嘼嘽嘾嘿噀噁噂噃噄噅噆噇噈噉噊噋噌噍噎噏噐噑噒噓噔噕噖噗噘噙噚噛噜噝噞噟噠噡噢噣噤噥噦噧器噩噪噫噬噭噮噯噰噱噲噳噴噵噶噷噸噹噺噻噼噽噾噿嚀嚁嚂嚃嚄嚅嚆嚇嚈嚉嚊嚋嚌嚍嚎嚏嚐嚑嚒嚓嚔嚕嚖嚗嚘嚙嚚嚛嚜嚝嚞嚟嚠嚡嚢嚣嚤嚥嚦嚧嚨嚩嚪嚫嚬嚭嚮嚯嚰嚱嚲嚳嚴嚵嚶嚷嚸嚹嚺嚻嚼嚽嚾嚿囀囁囂囃囄囅囆囇囈囉囊囋囌囍囎囏囐囑囒囓囔囕囖囗囘囙囚四囜囝回囟因囡团団囤囥囦囧囨囩囪囫囬园囮囯困囱囲図围囵囶囷囸囹固囻囼国图囿圀圁圂圃圄圅圆圇圈圉圊國圌圍圎圏圐圑園圓圔圕圖圗團圙圚圛圜圝圞土圠圡圢圣圤圥圦圧在圩圪圫圬圭圮圯地圱圲圳圴圵圶圷圸圹场圻圼圽圾圿址坁坂坃坄坅坆均坈坉坊坋坌坍坎坏坐坑坒坓坔坕坖块坘坙坚坛坜坝坞坟坠坡坢坣坤坥坦坧坨坩坪坫坬坭坮坯坰坱坲坳坴坵坶坷坸坹坺坻坼坽坾坿垀垁垂垃垄垅垆垇垈垉垊型垌垍垎垏垐垑垒垓垔垕垖垗垘垙垚垛垜垝垞垟垠垡垢垣垤垥垦垧垨垩垪垫垬垭垮垯垰垱垲垳垴垵垶垷垸垹垺垻垼垽垾垿埀埁埂埃埄埅埆埇埈埉埊埋埌埍城埏埐埑埒埓埔埕埖埗埘埙埚埛埜埝埞域埠埡埢埣埤埥埦埧埨埩埪埫埬埭埮埯埰埱埲埳埴埵埶執埸培基埻埼埽埾埿堀堁堂堃堄堅堆堇堈堉堊堋堌堍堎堏堐堑堒堓堔堕堖堗堘堙堚堛堜堝堞堟堠堡堢堣堤堥堦堧堨堩堪堫堬堭堮堯堰報堲堳場堵堶堷堸堹堺堻堼堽堾堿塀塁塂塃塄塅塆塇塈塉塊塋塌塍塎塏塐塑塒塓塔塕塖塗塘塙塚塛塜塝塞塟塠塡塢塣塤塥塦塧塨塩塪填塬塭塮塯塰塱塲塳塴塵塶塷塸塹塺塻塼塽塾塿墀墁墂境墄墅墆墇墈墉墊墋墌墍墎墏墐墑墒墓墔墕墖増墘墙墚墛墜墝增墟墠墡墢墣墤墥墦墧墨墩墪墫墬墭墮墯墰墱墲墳墴墵墶墷墸墹墺墻墼墽墾墿壀壁壂壃壄壅壆壇壈壉壊壋壌壍壎壏壐壑壒壓壔壕壖壗壘壙壚壛壜壝壞壟壠壡壢壣壤壥壦壧壨壩壪士壬壭壮壯声壱売壳壴壵壶壷壸壹壺壻壼壽壾壿夀夁夂夃处夅夆备夈変夊夋夌复夎夏夐夑夒夓夔夕外夗夘夙多夛夜夝夞够夠夡夢夣夤夥夦大夨天太夫夬夭央夯夰失夲夳头夵夶夷夸夹夺夻夼夽夾夿奀奁奂奃奄奅奆奇奈奉奊奋奌奍奎奏奐契奒奓奔奕奖套奘奙奚奛奜奝奞奟奠奡奢奣奤奥奦奧奨奩奪奫奬奭奮奯奰奱奲女奴奵奶奷奸她奺奻奼好奾奿妀妁如妃妄妅妆妇妈妉妊妋妌妍妎妏妐妑妒妓妔妕妖妗妘妙妚妛妜妝妞妟妠妡妢妣妤妥妦妧妨妩妪妫妬妭妮妯妰妱妲妳妴妵妶妷妸妹妺妻妼妽妾妿姀姁姂姃姄姅姆姇姈姉姊始姌姍姎姏姐姑姒姓委姕姖姗姘姙姚姛姜姝姞姟姠姡姢姣姤姥姦姧姨姩姪姫姬姭姮姯姰姱姲姳姴姵姶姷姸姹姺姻姼姽姾姿娀威娂娃娄娅娆娇娈娉娊娋娌娍娎娏娐娑娒娓娔娕娖娗娘娙娚娛娜娝娞娟娠娡娢娣娤娥娦娧娨娩娪娫娬娭娮娯娰娱娲娳娴娵娶娷娸娹娺娻娼娽娾娿婀婁婂婃婄婅婆婇婈婉婊婋婌婍婎婏婐婑婒婓婔婕婖婗婘婙婚婛婜婝婞婟婠婡婢婣婤婥婦婧婨婩婪婫婬婭婮婯婰婱婲婳婴婵婶婷婸婹婺婻婼婽婾婿媀媁媂媃媄媅媆媇媈媉媊媋媌媍媎媏媐媑媒媓媔媕媖媗媘媙媚媛媜媝媞媟媠媡媢媣媤媥媦媧媨媩媪媫媬媭媮媯媰媱媲媳媴媵媶媷媸媹媺媻媼媽媾媿嫀嫁嫂嫃嫄嫅嫆嫇嫈嫉嫊嫋嫌嫍嫎嫏嫐嫑嫒嫓嫔嫕嫖嫗嫘嫙嫚嫛嫜嫝嫞嫟嫠嫡嫢嫣嫤嫥嫦嫧嫨嫩嫪嫫嫬嫭嫮嫯嫰嫱嫲嫳嫴嫵嫶嫷嫸嫹嫺嫻嫼嫽嫾嫿嬀嬁嬂嬃嬄嬅嬆嬇嬈嬉嬊嬋嬌嬍嬎嬏嬐嬑嬒嬓嬔嬕嬖嬗嬘嬙嬚嬛嬜嬝嬞嬟嬠嬡嬢嬣嬤嬥嬦嬧嬨嬩嬪嬫嬬嬭嬮嬯嬰嬱嬲嬳嬴嬵嬶嬷嬸嬹嬺嬻嬼嬽嬾嬿孀孁孂孃孄孅孆孇孈孉孊孋孌孍孎孏子孑孒孓孔孕孖字存孙孚孛孜孝孞孟孠孡孢季孤孥学孧孨孩孪孫孬孭孮孯孰孱孲孳孴孵孶孷學孹孺孻孼孽孾孿宀宁宂它宄宅宆宇守安宊宋完宍宎宏宐宑宒宓宔宕宖宗官宙定宛宜宝实実宠审客宣室宥宦宧宨宩宪宫宬宭宮宯宰宱宲害宴宵家宷宸容宺宻宼宽宾宿寀寁寂寃寄寅密寇寈寉寊寋富寍寎寏寐寑寒寓寔寕寖寗寘寙寚寛寜寝寞察寠寡寢寣寤寥實寧寨審寪寫寬寭寮寯寰寱寲寳寴寵寶寷寸对寺寻导寽対寿尀封専尃射尅将將專尉尊尋尌對導小尐少尒尓尔尕尖尗尘尙尚尛尜尝尞尟尠尡尢尣尤尥尦尧尨尩尪尫尬尭尮尯尰就尲尳尴尵尶尷尸尹尺尻尼尽尾尿局屁层屃屄居屆屇屈屉届屋屌屍屎屏屐屑屒屓屔展屖屗屘屙屚屛屜屝属屟屠屡屢屣層履屦屧屨屩屪屫屬屭屮屯屰山屲屳屴屵屶屷屸屹屺屻屼屽屾屿岀岁岂岃岄岅岆岇岈岉岊岋岌岍岎岏岐岑岒岓岔岕岖岗岘岙岚岛岜岝岞岟岠岡岢岣岤岥岦岧岨岩岪岫岬岭岮岯岰岱岲岳岴岵岶岷岸岹岺岻岼岽岾岿峀峁峂峃峄峅峆峇峈峉峊峋峌峍峎峏峐峑峒峓峔峕峖峗峘峙峚峛峜峝峞峟峠峡峢峣峤峥峦峧峨峩峪峫峬峭峮峯峰峱峲峳峴峵島峷峸峹峺峻峼峽峾峿崀崁崂崃崄崅崆崇崈崉崊崋崌崍崎崏崐崑崒崓崔崕崖崗崘崙崚崛崜崝崞崟崠崡崢崣崤崥崦崧崨崩崪崫崬崭崮崯崰崱崲崳崴崵崶崷崸崹崺崻崼崽崾崿嵀嵁嵂嵃嵄嵅嵆嵇嵈嵉嵊嵋嵌嵍嵎嵏嵐嵑嵒嵓嵔嵕嵖嵗嵘嵙嵚嵛嵜嵝嵞嵟嵠嵡嵢嵣嵤嵥嵦嵧嵨嵩嵪嵫嵬嵭嵮嵯嵰嵱嵲嵳嵴嵵嵶嵷嵸嵹嵺嵻嵼嵽嵾嵿嶀嶁嶂嶃嶄嶅嶆嶇嶈嶉嶊嶋嶌嶍嶎嶏嶐嶑嶒嶓嶔嶕嶖嶗嶘嶙嶚嶛嶜嶝嶞嶟嶠嶡嶢嶣嶤嶥嶦嶧嶨嶩嶪嶫嶬嶭嶮嶯嶰嶱嶲嶳嶴嶵嶶嶷嶸嶹嶺嶻嶼嶽嶾嶿巀巁巂巃巄巅巆巇巈巉巊巋巌巍巎巏巐巑巒巓巔巕巖巗巘巙巚巛巜川州巟巠巡巢巣巤工左巧巨巩巪巫巬巭差巯巰己已巳巴巵巶巷巸巹巺巻巼巽巾巿帀币市布帄帅帆帇师帉帊帋希帍帎帏帐帑帒帓帔帕帖帗帘帙帚帛帜帝帞帟帠帡帢帣帤帥带帧帨帩帪師帬席帮帯帰帱帲帳帴帵帶帷常帹帺帻帼帽帾帿幀幁幂幃幄幅幆幇幈幉幊幋幌幍幎幏幐幑幒幓幔幕幖幗幘幙幚幛幜幝幞幟幠幡幢幣幤幥幦幧幨幩幪幫幬幭幮幯幰幱干平年幵并幷幸幹幺幻幼幽幾广庀庁庂広庄庅庆庇庈庉床庋庌庍庎序庐庑庒库应底庖店庘庙庚庛府庝庞废庠庡庢庣庤庥度座庨庩庪庫庬庭庮庯庰庱庲庳庴庵庶康庸庹庺庻庼庽庾庿廀廁廂廃廄廅廆廇廈廉廊廋廌廍廎廏廐廑廒廓廔廕廖廗廘廙廚廛廜廝廞廟廠廡廢廣廤廥廦廧廨廩廪廫廬廭廮廯廰廱廲廳廴廵延廷廸廹建廻廼廽廾廿开弁异弃弄弅弆弇弈弉弊弋弌弍弎式弐弑弒弓弔引弖弗弘弙弚弛弜弝弞弟张弡弢弣弤弥弦弧弨弩弪弫弬弭弮弯弰弱弲弳弴張弶強弸弹强弻弼弽弾弿彀彁彂彃彄彅彆彇彈彉彊彋彌彍彎彏彐彑归当彔录彖彗彘彙彚彛彜彝彞彟彠彡形彣彤彥彦彧彨彩彪彫彬彭彮彯彰影彲彳彴彵彶彷彸役彺彻彼彽彾彿往征徂徃径待徆徇很徉徊律後徍徎徏徐徑徒従徔徕徖得徘徙徚徛徜徝從徟徠御徢徣徤徥徦徧徨復循徫徬徭微徯徰徱徲徳徴徵徶德徸徹徺徻徼徽徾徿忀忁忂心忄必忆忇忈忉忊忋忌忍忎忏忐忑忒忓忔忕忖志忘忙忚忛応忝忞忟忠忡忢忣忤忥忦忧忨忩忪快忬忭忮忯忰忱忲忳忴念忶忷忸忹忺忻忼忽忾忿怀态怂怃怄怅怆怇怈怉怊怋怌怍怎怏怐怑怒怓怔怕怖怗怘怙怚怛怜思怞怟怠怡怢怣怤急怦性怨怩怪怫怬怭怮怯怰怱怲怳怴怵怶怷怸怹怺总怼怽怾怿恀恁恂恃恄恅恆恇恈恉恊恋恌恍恎恏恐恑恒恓恔恕恖恗恘恙恚恛恜恝恞恟恠恡恢恣恤恥恦恧恨恩恪恫恬恭恮息恰恱恲恳恴恵恶恷恸恹恺恻恼恽恾恿悀悁悂悃悄悅悆悇悈悉悊悋悌悍悎悏悐悑悒悓悔悕悖悗悘悙悚悛悜悝悞悟悠悡悢患悤悥悦悧您悩悪悫悬悭悮悯悰悱悲悳悴悵悶悷悸悹悺悻悼悽悾悿惀惁惂惃惄情惆惇惈惉惊惋惌惍惎惏惐惑惒惓惔惕惖惗惘惙惚惛惜惝惞惟惠惡惢惣惤惥惦惧惨惩惪惫惬惭惮惯惰惱惲想惴惵惶惷惸惹惺惻惼惽惾惿愀愁愂愃愄愅愆愇愈愉愊愋愌愍愎意愐愑愒愓愔愕愖愗愘愙愚愛愜愝愞感愠愡愢愣愤愥愦愧愨愩愪愫愬愭愮愯愰愱愲愳愴愵愶愷愸愹愺愻愼愽愾愿慀慁慂慃慄慅慆慇慈慉慊態慌慍慎慏慐慑慒慓慔慕慖慗慘慙慚慛慜慝慞慟慠慡慢慣慤慥慦慧慨慩慪慫慬慭慮慯慰慱慲慳慴慵慶慷慸慹慺慻慼慽慾慿憀憁憂憃憄憅憆憇憈憉憊憋憌憍憎憏憐憑憒憓憔憕憖憗憘憙憚憛憜憝憞憟憠憡憢憣憤憥憦憧憨憩憪憫憬憭憮憯憰憱憲憳憴憵憶憷憸憹憺憻憼憽憾憿懀懁懂懃懄懅懆懇懈應懊懋懌懍懎懏懐懑懒懓懔懕懖懗懘懙懚懛懜懝懞懟懠懡懢懣懤懥懦懧懨懩懪懫懬懭懮懯懰懱懲懳懴懵懶懷懸懹懺懻懼懽懾懿戀戁戂戃戄戅戆戇戈戉戊戋戌戍戎戏成我戒戓戔戕或戗战戙戚戛戜戝戞戟戠戡戢戣戤戥戦戧戨戩截戫戬戭戮戯戰戱戲戳戴戵戶户戸戹戺戻戼戽戾房所扁扂扃扄扅扆扇扈扉扊手扌才扎扏扐扑扒打扔払扖扗托扙扚扛扜扝扞扟扠扡扢扣扤扥扦执扨扩扪扫扬扭扮扯扰扱扲扳扴扵扶扷扸批扺扻扼扽找承技抁抂抃抄抅抆抇抈抉把抋抌抍抎抏抐抑抒抓抔投抖抗折抙抚抛抜抝択抟抠抡抢抣护报抦抧抨抩抪披抬抭抮抯抰抱抲抳抴抵抶抷抸抹抺抻押抽抾抿拀拁拂拃拄担拆拇拈拉拊拋拌拍拎拏拐拑拒拓拔拕拖拗拘拙拚招拜拝拞拟拠拡拢拣拤拥拦拧拨择拪拫括拭拮拯拰拱拲拳拴拵拶拷拸拹拺拻拼拽拾拿挀持挂挃挄挅挆指挈按挊挋挌挍挎挏挐挑挒挓挔挕挖挗挘挙挚挛挜挝挞挟挠挡挢挣挤挥挦挧挨挩挪挫挬挭挮振挰挱挲挳挴挵挶挷挸挹挺挻挼挽挾挿捀捁捂捃捄捅捆捇捈捉捊捋捌捍捎捏捐捑捒捓捔捕捖捗捘捙捚捛捜捝捞损捠捡换捣捤捥捦捧捨捩捪捫捬捭据捯捰捱捲捳捴捵捶捷捸捹捺捻捼捽捾捿掀掁掂掃掄掅掆掇授掉掊掋掌掍掎掏掐掑排掓掔掕掖掗掘掙掚掛掜掝掞掟掠採探掣掤接掦控推掩措掫掬掭掮掯掰掱掲掳掴掵掶掷掸掹掺掻掼掽掾掿揀揁揂揃揄揅揆揇揈揉揊揋揌揍揎描提揑插揓揔揕揖揗揘揙揚換揜揝揞揟揠握揢揣揤揥揦揧揨揩揪揫揬揭揮揯揰揱揲揳援揵揶揷揸揹揺揻揼揽揾揿搀搁搂搃搄搅搆搇搈搉搊搋搌損搎搏搐搑搒搓搔搕搖搗搘搙搚搛搜搝搞搟搠搡搢搣搤搥搦搧搨搩搪搫搬搭搮搯搰搱搲搳搴搵搶搷搸搹携搻搼搽搾搿摀摁摂摃摄摅摆摇摈摉摊摋摌摍摎摏摐摑摒摓摔摕摖摗摘摙摚摛摜摝摞摟摠摡摢摣摤摥摦摧摨摩摪摫摬摭摮摯摰摱摲摳摴摵摶摷摸摹摺摻摼摽摾摿撀撁撂撃撄撅撆撇撈撉撊撋撌撍撎撏撐撑撒撓撔撕撖撗撘撙撚撛撜撝撞撟撠撡撢撣撤撥撦撧撨撩撪撫撬播撮撯撰撱撲撳撴撵撶撷撸撹撺撻撼撽撾撿擀擁擂擃擄擅擆擇擈擉擊擋擌操擎擏擐擑擒擓擔擕擖擗擘擙據擛擜擝擞擟擠擡擢擣擤擥擦擧擨擩擪擫擬擭擮擯擰擱擲擳擴擵擶擷擸擹擺擻擼擽擾擿攀攁攂攃攄攅攆攇攈攉攊攋攌攍攎攏攐攑攒攓攔攕攖攗攘攙攚攛攜攝攞攟攠攡攢攣攤攥攦攧攨攩攪攫攬攭攮支攰攱攲攳攴攵收攷攸改攺攻攼攽放政敀敁敂敃敄故敆敇效敉敊敋敌敍敎敏敐救敒敓敔敕敖敗敘教敚敛敜敝敞敟敠敡敢散敤敥敦敧敨敩敪敫敬敭敮敯数敱敲敳整敵敶敷數敹敺敻敼敽敾敿斀斁斂斃斄斅斆文斈斉斊斋斌斍斎斏斐斑斒斓斔斕斖斗斘料斚斛斜斝斞斟斠斡斢斣斤斥斦斧斨斩斪斫斬断斮斯新斱斲斳斴斵斶斷斸方斺斻於施斾斿旀旁旂旃旄旅旆旇旈旉旊旋旌旍旎族旐旑旒旓旔旕旖旗旘旙旚旛旜旝旞旟无旡既旣旤日旦旧旨早旪旫旬旭旮旯旰旱旲旳旴旵时旷旸旹旺旻旼旽旾旿昀昁昂昃昄昅昆昇昈昉昊昋昌昍明昏昐昑昒易昔昕昖昗昘昙昚昛昜昝昞星映昡昢昣昤春昦昧昨昩昪昫昬昭昮是昰昱昲昳昴昵昶昷昸昹昺昻昼昽显昿晀晁時晃晄晅晆晇晈晉晊晋晌晍晎晏晐晑晒晓晔晕晖晗晘晙晚晛晜晝晞晟晠晡晢晣晤晥晦晧晨晩晪晫晬晭普景晰晱晲晳晴晵晶晷晸晹智晻晼晽晾晿暀暁暂暃暄暅暆暇暈暉暊暋暌暍暎暏暐暑暒暓暔暕暖暗暘暙暚暛暜暝暞暟暠暡暢暣暤暥暦暧暨暩暪暫暬暭暮暯暰暱暲暳暴暵暶暷暸暹暺暻暼暽暾暿曀曁曂曃曄曅曆曇曈曉曊曋曌曍曎曏曐曑曒曓曔曕曖曗曘曙曚曛曜曝曞曟曠曡曢曣曤曥曦曧曨曩曪曫曬曭曮曯曰曱曲曳更曵曶曷書曹曺曻曼曽曾替最朁朂會朄朅朆朇月有朊朋朌服朎朏朐朑朒朓朔朕朖朗朘朙朚望朜朝朞期朠朡朢朣朤朥朦朧木朩未末本札朮术朰朱朲朳朴朵朶朷朸朹机朻朼朽朾朿杀杁杂权杄杅杆杇杈杉杊杋杌杍李杏材村杒杓杔杕杖杗杘杙杚杛杜杝杞束杠条杢杣杤来杦杧杨杩杪杫杬杭杮杯杰東杲杳杴杵杶杷杸杹杺杻杼杽松板枀极枂枃构枅枆枇枈枉枊枋枌枍枎枏析枑枒枓枔枕枖林枘枙枚枛果枝枞枟枠枡枢枣枤枥枦枧枨枩枪枫枬枭枮枯枰枱枲枳枴枵架枷枸枹枺枻枼枽枾枿柀柁柂柃柄柅柆柇柈柉柊柋柌柍柎柏某柑柒染柔柕柖柗柘柙柚柛柜柝柞柟柠柡柢柣柤查柦柧柨柩柪柫柬柭柮柯柰柱柲柳柴柵柶柷柸柹柺査柼柽柾柿栀栁栂栃栄栅栆标栈栉栊栋栌栍栎栏栐树栒栓栔栕栖栗栘栙栚栛栜栝栞栟栠校栢栣栤栥栦栧栨栩株栫栬栭栮栯栰栱栲栳栴栵栶样核根栺栻格栽栾栿桀桁桂桃桄桅框桇案桉桊桋桌桍桎桏桐桑桒桓桔桕桖桗桘桙桚桛桜桝桞桟桠桡桢档桤桥桦桧桨桩桪桫桬桭桮桯桰桱桲桳桴桵桶桷桸桹桺桻桼桽桾桿梀梁梂梃梄梅梆梇梈梉梊梋梌梍梎梏梐梑梒梓梔梕梖梗梘梙梚梛梜條梞梟梠梡梢梣梤梥梦梧梨梩梪梫梬梭梮梯械梱梲梳梴梵梶梷梸梹梺梻梼梽梾梿检棁棂棃棄棅棆棇棈棉棊棋棌棍棎棏棐棑棒棓棔棕棖棗棘棙棚棛棜棝棞棟棠棡棢棣棤棥棦棧棨棩棪棫棬棭森棯棰棱棲棳棴棵棶棷棸棹棺棻棼棽棾棿椀椁椂椃椄椅椆椇椈椉椊椋椌植椎椏椐椑椒椓椔椕椖椗椘椙椚椛検椝椞椟椠椡椢椣椤椥椦椧椨椩椪椫椬椭椮椯椰椱椲椳椴椵椶椷椸椹椺椻椼椽椾椿楀楁楂楃楄楅楆楇楈楉楊楋楌楍楎楏楐楑楒楓楔楕楖楗楘楙楚楛楜楝楞楟楠楡楢楣楤楥楦楧楨楩楪楫楬業楮楯楰楱楲楳楴極楶楷楸楹楺楻楼楽楾楿榀榁概榃榄榅榆榇榈榉榊榋榌榍榎榏榐榑榒榓榔榕榖榗榘榙榚榛榜榝榞榟榠榡榢榣榤榥榦榧榨榩榪榫榬榭榮榯榰榱榲榳榴榵榶榷榸榹榺榻榼榽榾榿槀槁槂槃槄槅槆槇槈槉槊構槌槍槎槏槐槑槒槓槔槕槖槗様槙槚槛槜槝槞槟槠槡槢槣槤槥槦槧槨槩槪槫槬槭槮槯槰槱槲槳槴槵槶槷槸槹槺槻槼槽槾槿樀樁樂樃樄樅樆樇樈樉樊樋樌樍樎樏樐樑樒樓樔樕樖樗樘標樚樛樜樝樞樟樠模樢樣樤樥樦樧樨権横樫樬樭樮樯樰樱樲樳樴樵樶樷樸樹樺樻樼樽樾樿橀橁橂橃橄橅橆橇橈橉橊橋橌橍橎橏橐橑橒橓橔橕橖橗橘橙橚橛橜橝橞機橠橡橢橣橤橥橦橧橨橩橪橫橬橭橮橯橰橱橲橳橴橵橶橷橸橹橺橻橼橽橾橿檀檁檂檃檄檅檆檇檈檉檊檋檌檍檎檏檐檑檒檓檔檕檖檗檘檙檚檛檜檝檞檟檠檡檢檣檤檥檦檧檨檩檪檫檬檭檮檯檰檱檲檳檴檵檶檷檸檹檺檻檼檽檾檿櫀櫁櫂櫃櫄櫅櫆櫇櫈櫉櫊櫋櫌櫍櫎櫏櫐櫑櫒櫓櫔櫕櫖櫗櫘櫙櫚櫛櫜櫝櫞櫟櫠櫡櫢櫣櫤櫥櫦櫧櫨櫩櫪櫫櫬櫭櫮櫯櫰櫱櫲櫳櫴櫵櫶櫷櫸櫹櫺櫻櫼櫽櫾櫿欀欁欂欃欄欅欆欇欈欉權欋欌欍欎欏欐欑欒欓欔欕欖欗欘欙欚欛欜欝欞欟欠次欢欣欤欥欦欧欨欩欪欫欬欭欮欯欰欱欲欳欴欵欶欷欸欹欺欻欼欽款欿歀歁歂歃歄歅歆歇歈歉歊歋歌歍歎歏歐歑歒歓歔歕歖歗歘歙歚歛歜歝歞歟歠歡止正此步武歧歨歩歪歫歬歭歮歯歰歱歲歳歴歵歶歷歸歹歺死歼歽歾歿殀殁殂殃殄殅殆殇殈殉殊残殌殍殎殏殐殑殒殓殔殕殖殗殘殙殚殛殜殝殞殟殠殡殢殣殤殥殦殧殨殩殪殫殬殭殮殯殰殱殲殳殴段殶殷殸殹殺殻殼殽殾殿毀毁毂毃毄毅毆毇毈毉毊毋毌母毎每毐毑毒毓比毕毖毗毘毙毚毛毜毝毞毟毠毡毢毣毤毥毦毧毨毩毪毫毬毭毮毯毰毱毲毳毴毵毶毷毸毹毺毻毼毽毾毿氀氁氂氃氄氅氆氇氈氉氊氋氌氍氎氏氐民氒氓气氕氖気氘氙氚氛氜氝氞氟氠氡氢氣氤氥氦氧氨氩氪氫氬氭氮氯氰氱氲氳水氵氶氷永氹氺氻氼氽氾氿汀汁求汃汄汅汆汇汈汉汊汋汌汍汎汏汐汑汒汓汔汕汖汗汘汙汚汛汜汝汞江池污汢汣汤汥汦汧汨汩汪汫汬汭汮汯汰汱汲汳汴汵汶汷汸汹決汻汼汽汾汿沀沁沂沃沄沅沆沇沈沉沊沋沌沍沎沏沐沑沒沓沔沕沖沗沘沙沚沛沜沝沞沟沠没沢沣沤沥沦沧沨沩沪沫沬沭沮沯沰沱沲河沴沵沶沷沸油沺治沼沽沾沿泀況泂泃泄泅泆泇泈泉泊泋泌泍泎泏泐泑泒泓泔法泖泗泘泙泚泛泜泝泞泟泠泡波泣泤泥泦泧注泩泪泫泬泭泮泯泰泱泲泳泴泵泶泷泸泹泺泻泼泽泾泿洀洁洂洃洄洅洆洇洈洉洊洋洌洍洎洏洐洑洒洓洔洕洖洗洘洙洚洛洜洝洞洟洠洡洢洣洤津洦洧洨洩洪洫洬洭洮洯洰洱洲洳洴洵洶洷洸洹洺活洼洽派洿浀流浂浃浄浅浆浇浈浉浊测浌浍济浏浐浑浒浓浔浕浖浗浘浙浚浛浜浝浞浟浠浡浢浣浤浥浦浧浨浩浪浫浬浭浮浯浰浱浲浳浴浵浶海浸浹浺浻浼浽浾浿涀涁涂涃涄涅涆涇消涉涊涋涌涍涎涏涐涑涒涓涔涕涖涗涘涙涚涛涜涝涞涟涠涡涢涣涤涥润涧涨涩涪涫涬涭涮涯涰涱液涳涴涵涶涷涸涹涺涻涼涽涾涿淀淁淂淃淄淅淆淇淈淉淊淋淌淍淎淏淐淑淒淓淔淕淖淗淘淙淚淛淜淝淞淟淠淡淢淣淤淥淦淧淨淩淪淫淬淭淮淯淰深淲淳淴淵淶混淸淹淺添淼淽淾淿渀渁渂渃渄清渆渇済渉渊渋渌渍渎渏渐渑渒渓渔渕渖渗渘渙渚減渜渝渞渟渠渡渢渣渤渥渦渧渨温渪渫測渭渮港渰渱渲渳渴渵渶渷游渹渺渻渼渽渾渿湀湁湂湃湄湅湆湇湈湉湊湋湌湍湎湏湐湑湒湓湔湕湖湗湘湙湚湛湜湝湞湟湠湡湢湣湤湥湦湧湨湩湪湫湬湭湮湯湰湱湲湳湴湵湶湷湸湹湺湻湼湽湾湿満溁溂溃溄溅溆溇溈溉溊溋溌溍溎溏源溑溒溓溔溕準溗溘溙溚溛溜溝溞溟溠溡溢溣溤溥溦溧溨溩溪溫溬溭溮溯溰溱溲溳溴溵溶溷溸溹溺溻溼溽溾溿滀滁滂滃滄滅滆滇滈滉滊滋滌滍滎滏滐滑滒滓滔滕滖滗滘滙滚滛滜滝滞滟滠满滢滣滤滥滦滧滨滩滪滫滬滭滮滯滰滱滲滳滴滵滶滷滸滹滺滻滼滽滾滿漀漁漂漃漄漅漆漇漈漉漊漋漌漍漎漏漐漑漒漓演漕漖漗漘漙漚漛漜漝漞漟漠漡漢漣漤漥漦漧漨漩漪漫漬漭漮漯漰漱漲漳漴漵漶漷漸漹漺漻漼漽漾漿潀潁潂潃潄潅潆潇潈潉潊潋潌潍潎潏潐潑潒潓潔潕潖潗潘潙潚潛潜潝潞潟潠潡潢潣潤潥潦潧潨潩潪潫潬潭潮潯潰潱潲潳潴潵潶潷潸潹潺潻潼潽潾潿澀澁澂澃澄澅澆澇澈澉澊澋澌澍澎澏澐澑澒澓澔澕澖澗澘澙澚澛澜澝澞澟澠澡澢澣澤澥澦澧澨澩澪澫澬澭澮澯澰澱澲澳澴澵澶澷澸澹澺澻澼澽澾澿激濁濂濃濄濅濆濇濈濉濊濋濌濍濎濏濐濑濒濓濔濕濖濗濘濙濚濛濜濝濞濟濠濡濢濣濤濥濦濧濨濩濪濫濬濭濮濯濰濱濲濳濴濵濶濷濸濹濺濻濼濽濾濿瀀瀁瀂瀃瀄瀅瀆瀇瀈瀉瀊瀋瀌瀍瀎瀏瀐瀑瀒瀓瀔瀕瀖瀗瀘瀙瀚瀛瀜瀝瀞瀟瀠瀡瀢瀣瀤瀥瀦瀧瀨瀩瀪瀫瀬瀭瀮瀯瀰瀱瀲瀳瀴瀵瀶瀷瀸瀹瀺瀻瀼瀽瀾瀿灀灁灂灃灄灅灆灇灈灉灊灋灌灍灎灏灐灑灒灓灔灕灖灗灘灙灚灛灜灝灞灟灠灡灢灣灤灥灦灧灨灩灪火灬灭灮灯灰灱灲灳灴灵灶灷灸灹灺灻灼災灾灿炀炁炂炃炄炅炆炇炈炉炊炋炌炍炎炏炐炑炒炓炔炕炖炗炘炙炚炛炜炝炞炟炠炡炢炣炤炥炦炧炨炩炪炫炬炭炮炯炰炱炲炳炴炵炶炷炸点為炻炼炽炾炿烀烁烂烃烄烅烆烇烈烉烊烋烌烍烎烏烐烑烒烓烔烕烖烗烘烙烚烛烜烝烞烟烠烡烢烣烤烥烦烧烨烩烪烫烬热烮烯烰烱烲烳烴烵烶烷烸烹烺烻烼烽烾烿焀焁焂焃焄焅焆焇焈焉焊焋焌焍焎焏焐焑焒焓焔焕焖焗焘焙焚焛焜焝焞焟焠無焢焣焤焥焦焧焨焩焪焫焬焭焮焯焰焱焲焳焴焵然焷焸焹焺焻焼焽焾焿煀煁煂煃煄煅煆煇煈煉煊煋煌煍煎煏煐煑煒煓煔煕煖煗煘煙煚煛煜煝煞煟煠煡煢煣煤煥煦照煨煩煪煫煬煭煮煯煰煱煲煳煴煵煶煷煸煹煺煻煼煽煾煿熀熁熂熃熄熅熆熇熈熉熊熋熌熍熎熏熐熑熒熓熔熕熖熗熘熙熚熛熜熝熞熟熠熡熢熣熤熥熦熧熨熩熪熫熬熭熮熯熰熱熲熳熴熵熶熷熸熹熺熻熼熽熾熿燀燁燂燃燄燅燆燇燈燉燊燋燌燍燎燏燐燑燒燓燔燕燖燗燘燙燚燛燜燝燞營燠燡燢燣燤燥燦燧燨燩燪燫燬燭燮燯燰燱燲燳燴燵燶燷燸燹燺燻燼燽燾燿爀爁爂爃爄爅爆爇爈爉爊爋爌爍爎爏爐爑爒爓爔爕爖爗爘爙爚爛爜爝爞爟爠爡爢爣爤爥爦爧爨爩爪爫爬爭爮爯爰爱爲爳爴爵父爷爸爹爺爻爼爽爾爿牀牁牂牃牄牅牆片版牉牊牋牌牍牎牏牐牑牒牓牔牕牖牗牘牙牚牛牜牝牞牟牠牡牢牣牤牥牦牧牨物牪牫牬牭牮牯牰牱牲牳牴牵牶牷牸特牺牻牼牽牾牿犀犁犂犃犄犅犆犇犈犉犊犋犌犍犎犏犐犑犒犓犔犕犖犗犘犙犚犛犜犝犞犟犠犡犢犣犤犥犦犧犨犩犪犫犬犭犮犯犰犱犲犳犴犵状犷犸犹犺犻犼犽犾犿狀狁狂狃狄狅狆狇狈狉狊狋狌狍狎狏狐狑狒狓狔狕狖狗狘狙狚狛狜狝狞狟狠狡狢狣狤狥狦狧狨狩狪狫独狭狮狯狰狱狲狳狴狵狶狷狸狹狺狻狼狽狾狿猀猁猂猃猄猅猆猇猈猉猊猋猌猍猎猏猐猑猒猓猔猕猖猗猘猙猚猛猜猝猞猟猠猡猢猣猤猥猦猧猨猩猪猫猬猭献猯猰猱猲猳猴猵猶猷猸猹猺猻猼猽猾猿獀獁獂獃獄獅獆獇獈獉獊獋獌獍獎獏獐獑獒獓獔獕獖獗獘獙獚獛獜獝獞獟獠獡獢獣獤獥獦獧獨獩獪獫獬獭獮獯獰獱獲獳獴獵獶獷獸獹獺獻獼獽獾獿玀玁玂玃玄玅玆率玈玉玊王玌玍玎玏玐玑玒玓玔玕玖玗玘玙玚玛玜玝玞玟玠玡玢玣玤玥玦玧玨玩玪玫玬玭玮环现玱玲玳玴玵玶玷玸玹玺玻玼玽玾玿珀珁珂珃珄珅珆珇珈珉珊珋珌珍珎珏珐珑珒珓珔珕珖珗珘珙珚珛珜珝珞珟珠珡珢珣珤珥珦珧珨珩珪珫珬班珮珯珰珱珲珳珴珵珶珷珸珹珺珻珼珽現珿琀琁琂球琄琅理琇琈琉琊琋琌琍琎琏琐琑琒琓琔琕琖琗琘琙琚琛琜琝琞琟琠琡琢琣琤琥琦琧琨琩琪琫琬琭琮琯琰琱琲琳琴琵琶琷琸琹琺琻琼琽琾琿瑀瑁瑂瑃瑄瑅瑆瑇瑈瑉瑊瑋瑌瑍瑎瑏瑐瑑瑒瑓瑔瑕瑖瑗瑘瑙瑚瑛瑜瑝瑞瑟瑠瑡瑢瑣瑤瑥瑦瑧瑨瑩瑪瑫瑬瑭瑮瑯瑰瑱瑲瑳瑴瑵瑶瑷瑸瑹瑺瑻瑼瑽瑾瑿璀璁璂璃璄璅璆璇璈璉璊璋璌璍璎璏璐璑璒璓璔璕璖璗璘璙璚璛璜璝璞璟璠璡璢璣璤璥璦璧璨璩璪璫璬璭璮璯環璱璲璳璴璵璶璷璸璹璺璻璼璽璾璿瓀瓁瓂瓃瓄瓅瓆瓇瓈瓉瓊瓋瓌瓍瓎瓏瓐瓑瓒瓓瓔瓕瓖瓗瓘瓙瓚瓛瓜瓝瓞瓟瓠瓡瓢瓣瓤瓥瓦瓧瓨瓩瓪瓫瓬瓭瓮瓯瓰瓱瓲瓳瓴瓵瓶瓷瓸瓹瓺瓻瓼瓽瓾瓿甀甁甂甃甄甅甆甇甈甉甊甋甌甍甎甏甐甑甒甓甔甕甖甗甘甙甚甛甜甝甞生甠甡產産甤甥甦甧用甩甪甫甬甭甮甯田由甲申甴电甶男甸甹町画甼甽甾甿畀畁畂畃畄畅畆畇畈畉畊畋界畍畎畏畐畑畒畓畔畕畖畗畘留畚畛畜畝畞畟畠畡畢畣畤略畦畧畨畩番畫畬畭畮畯異畱畲畳畴畵當畷畸畹畺畻畼畽畾畿疀疁疂疃疄疅疆疇疈疉疊疋疌疍疎疏疐疑疒疓疔疕疖疗疘疙疚疛疜疝疞疟疠疡疢疣疤疥疦疧疨疩疪疫疬疭疮疯疰疱疲疳疴疵疶疷疸疹疺疻疼疽疾疿痀痁痂痃痄病痆症痈痉痊痋痌痍痎痏痐痑痒痓痔痕痖痗痘痙痚痛痜痝痞痟痠痡痢痣痤痥痦痧痨痩痪痫痬痭痮痯痰痱痲痳痴痵痶痷痸痹痺痻痼痽痾痿瘀瘁瘂瘃瘄瘅瘆瘇瘈瘉瘊瘋瘌瘍瘎瘏瘐瘑瘒瘓瘔瘕瘖瘗瘘瘙瘚瘛瘜瘝瘞瘟瘠瘡瘢瘣瘤瘥瘦瘧瘨瘩瘪瘫瘬瘭瘮瘯瘰瘱瘲瘳瘴瘵瘶瘷瘸瘹瘺瘻瘼瘽瘾瘿癀癁療癃癄癅癆癇癈癉癊癋癌癍癎癏癐癑癒癓癔癕癖癗癘癙癚癛癜癝癞癟癠癡癢癣癤癥癦癧癨癩癪癫癬癭癮癯癰癱癲癳癴癵癶癷癸癹発登發白百癿皀皁皂皃的皅皆皇皈皉皊皋皌皍皎皏皐皑皒皓皔皕皖皗皘皙皚皛皜皝皞皟皠皡皢皣皤皥皦皧皨皩皪皫皬皭皮皯皰皱皲皳皴皵皶皷皸皹皺皻皼皽皾皿盀盁盂盃盄盅盆盇盈盉益盋盌盍盎盏盐监盒盓盔盕盖盗盘盙盚盛盜盝盞盟盠盡盢監盤盥盦盧盨盩盪盫盬盭目盯盰盱盲盳直盵盶盷相盹盺盻盼盽盾盿眀省眂眃眄眅眆眇眈眉眊看県眍眎眏眐眑眒眓眔眕眖眗眘眙眚眛眜眝眞真眠眡眢眣眤眥眦眧眨眩眪眫眬眭眮眯眰眱眲眳眴眵眶眷眸眹眺眻眼眽眾眿着睁睂睃睄睅睆睇睈睉睊睋睌睍睎睏睐睑睒睓睔睕睖睗睘睙睚睛睜睝睞睟睠睡睢督睤睥睦睧睨睩睪睫睬睭睮睯睰睱睲睳睴睵睶睷睸睹睺睻睼睽睾睿瞀瞁瞂瞃瞄瞅瞆瞇瞈瞉瞊瞋瞌瞍瞎瞏瞐瞑瞒瞓瞔瞕瞖瞗瞘瞙瞚瞛瞜瞝瞞瞟瞠瞡瞢瞣瞤瞥瞦瞧瞨瞩瞪瞫瞬瞭瞮瞯瞰瞱瞲瞳瞴瞵瞶瞷瞸瞹瞺瞻瞼瞽瞾瞿矀矁矂矃矄矅矆矇矈矉矊矋矌矍矎矏矐矑矒矓矔矕矖矗矘矙矚矛矜矝矞矟矠矡矢矣矤知矦矧矨矩矪矫矬短矮矯矰矱矲石矴矵矶矷矸矹矺矻矼矽矾矿砀码砂砃砄砅砆砇砈砉砊砋砌砍砎砏砐砑砒砓研砕砖砗砘砙砚砛砜砝砞砟砠砡砢砣砤砥砦砧砨砩砪砫砬砭砮砯砰砱砲砳破砵砶砷砸砹砺砻砼砽砾砿础硁硂硃硄硅硆硇硈硉硊硋硌硍硎硏硐硑硒硓硔硕硖硗硘硙硚硛硜硝硞硟硠硡硢硣硤硥硦硧硨硩硪硫硬硭确硯硰硱硲硳硴硵硶硷硸硹硺硻硼硽硾硿碀碁碂碃碄碅碆碇碈碉碊碋碌碍碎碏碐碑碒碓碔碕碖碗碘碙碚碛碜碝碞碟碠碡碢碣碤碥碦碧碨碩碪碫碬碭碮碯碰碱碲碳碴碵碶碷碸碹確碻碼碽碾碿磀磁磂磃磄磅磆磇磈磉磊磋磌磍磎磏磐磑磒磓磔磕磖磗磘磙磚磛磜磝磞磟磠磡磢磣磤磥磦磧磨磩磪磫磬磭磮磯磰磱磲磳磴磵磶磷磸磹磺磻磼磽磾磿礀礁礂礃礄礅礆礇礈礉礊礋礌礍礎礏礐礑礒礓礔礕礖礗礘礙礚礛礜礝礞礟礠礡礢礣礤礥礦礧礨礩礪礫礬礭礮礯礰礱礲礳礴礵礶礷礸礹示礻礼礽社礿祀祁祂祃祄祅祆祇祈祉祊祋祌祍祎祏祐祑祒祓祔祕祖祗祘祙祚祛祜祝神祟祠祡祢祣祤祥祦祧票祩祪祫祬祭祮祯祰祱祲祳祴祵祶祷祸祹祺祻祼祽祾祿禀禁禂禃禄禅禆禇禈禉禊禋禌禍禎福禐禑禒禓禔禕禖禗禘禙禚禛禜禝禞禟禠禡禢禣禤禥禦禧禨禩禪禫禬禭禮禯禰禱禲禳禴禵禶禷禸禹禺离禼禽禾禿秀私秂秃秄秅秆秇秈秉秊秋秌种秎秏秐科秒秓秔秕秖秗秘秙秚秛秜秝秞租秠秡秢秣秤秥秦秧秨秩秪秫秬秭秮积称秱秲秳秴秵秶秷秸秹秺移秼秽秾秿稀稁稂稃稄稅稆稇稈稉稊程稌稍税稏稐稑稒稓稔稕稖稗稘稙稚稛稜稝稞稟稠稡稢稣稤稥稦稧稨稩稪稫稬稭種稯稰稱稲稳稴稵稶稷稸稹稺稻稼稽稾稿穀穁穂穃穄穅穆穇穈穉穊穋穌積穎穏穐穑穒穓穔穕穖穗穘穙穚穛穜穝穞穟穠穡穢穣穤穥穦穧穨穩穪穫穬穭穮穯穰穱穲穳穴穵究穷穸穹空穻穼穽穾穿窀突窂窃窄窅窆窇窈窉窊窋窌窍窎窏窐窑窒窓窔窕窖窗窘窙窚窛窜窝窞窟窠窡窢窣窤窥窦窧窨窩窪窫窬窭窮窯窰窱窲窳窴窵窶窷窸窹窺窻窼窽窾窿竀竁竂竃竄竅竆竇竈竉竊立竌竍竎竏竐竑竒竓竔竕竖竗竘站竚竛竜竝竞竟章竡竢竣竤童竦竧竨竩竪竫竬竭竮端竰竱竲竳竴竵競竷竸竹竺竻竼竽竾竿笀笁笂笃笄笅笆笇笈笉笊笋笌笍笎笏笐笑笒笓笔笕笖笗笘笙笚笛笜笝笞笟笠笡笢笣笤笥符笧笨笩笪笫第笭笮笯笰笱笲笳笴笵笶笷笸笹笺笻笼笽笾笿筀筁筂筃筄筅筆筇筈等筊筋筌筍筎筏筐筑筒筓答筕策筗筘筙筚筛筜筝筞筟筠筡筢筣筤筥筦筧筨筩筪筫筬筭筮筯筰筱筲筳筴筵筶筷筸筹筺筻筼筽签筿简箁箂箃箄箅箆箇箈箉箊箋箌箍箎箏箐箑箒箓箔箕箖算箘箙箚箛箜箝箞箟箠管箢箣箤箥箦箧箨箩箪箫箬箭箮箯箰箱箲箳箴箵箶箷箸箹箺箻箼箽箾箿節篁篂篃範篅篆篇篈築篊篋篌篍篎篏篐篑篒篓篔篕篖篗篘篙篚篛篜篝篞篟篠篡篢篣篤篥篦篧篨篩篪篫篬篭篮篯篰篱篲篳篴篵篶篷篸篹篺篻篼篽篾篿簀簁簂簃簄簅簆簇簈簉簊簋簌簍簎簏簐簑簒簓簔簕簖簗簘簙簚簛簜簝簞簟簠簡簢簣簤簥簦簧簨簩簪簫簬簭簮簯簰簱簲簳簴簵簶簷簸簹簺簻簼簽簾簿籀籁籂籃籄籅籆籇籈籉籊籋籌籍籎籏籐籑籒籓籔籕籖籗籘籙籚籛籜籝籞籟籠籡籢籣籤籥籦籧籨籩籪籫籬籭籮籯籰籱籲米籴籵籶籷籸籹籺类籼籽籾籿粀粁粂粃粄粅粆粇粈粉粊粋粌粍粎粏粐粑粒粓粔粕粖粗粘粙粚粛粜粝粞粟粠粡粢粣粤粥粦粧粨粩粪粫粬粭粮粯粰粱粲粳粴粵粶粷粸粹粺粻粼粽精粿糀糁糂糃糄糅糆糇糈糉糊糋糌糍糎糏糐糑糒糓糔糕糖糗糘糙糚糛糜糝糞糟糠糡糢糣糤糥糦糧糨糩糪糫糬糭糮糯糰糱糲糳糴糵糶糷糸糹糺系糼糽糾糿紀紁紂紃約紅紆紇紈紉紊紋紌納紎紏紐紑紒紓純紕紖紗紘紙級紛紜紝紞紟素紡索紣紤紥紦紧紨紩紪紫紬紭紮累細紱紲紳紴紵紶紷紸紹紺紻紼紽紾紿絀絁終絃組絅絆絇絈絉絊絋経絍絎絏結絑絒絓絔絕絖絗絘絙絚絛絜絝絞絟絠絡絢絣絤絥給絧絨絩絪絫絬絭絮絯絰統絲絳絴絵絶絷絸絹絺絻絼絽絾絿綀綁綂綃綄綅綆綇綈綉綊綋綌綍綎綏綐綑綒經綔綕綖綗綘継続綛綜綝綞綟綠綡綢綣綤綥綦綧綨綩綪綫綬維綮綯綰綱網綳綴綵綶綷綸綹綺綻綼綽綾綿緀緁緂緃緄緅緆緇緈緉緊緋緌緍緎総緐緑緒緓緔緕緖緗緘緙線緛緜緝緞緟締緡緢緣緤緥緦緧編緩緪緫緬緭緮緯緰緱緲緳練緵緶緷緸緹緺緻緼緽緾緿縀縁縂縃縄縅縆縇縈縉縊縋縌縍縎縏縐縑縒縓縔縕縖縗縘縙縚縛縜縝縞縟縠縡縢縣縤縥縦縧縨縩縪縫縬縭縮縯縰縱縲縳縴縵縶縷縸縹縺縻縼總績縿繀繁繂繃繄繅繆繇繈繉繊繋繌繍繎繏繐繑繒繓織繕繖繗繘繙繚繛繜繝繞繟繠繡繢繣繤繥繦繧繨繩繪繫繬繭繮繯繰繱繲繳繴繵繶繷繸繹繺繻繼繽繾繿纀纁纂纃纄纅纆纇纈纉纊纋續纍纎纏纐纑纒纓纔纕纖纗纘纙纚纛纜纝纞纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵缶缷缸缹缺缻缼缽缾缿罀罁罂罃罄罅罆罇罈罉罊罋罌罍罎罏罐网罒罓罔罕罖罗罘罙罚罛罜罝罞罟罠罡罢罣罤罥罦罧罨罩罪罫罬罭置罯罰罱署罳罴罵罶罷罸罹罺罻罼罽罾罿羀羁羂羃羄羅羆羇羈羉羊羋羌羍美羏羐羑羒羓羔羕羖羗羘羙羚羛羜羝羞羟羠羡羢羣群羥羦羧羨義羪羫羬羭羮羯羰羱羲羳羴羵羶羷羸羹羺羻羼羽羾羿翀翁翂翃翄翅翆翇翈翉翊翋翌翍翎翏翐翑習翓翔翕翖翗翘翙翚翛翜翝翞翟翠翡翢翣翤翥翦翧翨翩翪翫翬翭翮翯翰翱翲翳翴翵翶翷翸翹翺翻翼翽翾翿耀老耂考耄者耆耇耈耉耊耋而耍耎耏耐耑耒耓耔耕耖耗耘耙耚耛耜耝耞耟耠耡耢耣耤耥耦耧耨耩耪耫耬耭耮耯耰耱耲耳耴耵耶耷耸耹耺耻耼耽耾耿聀聁聂聃聄聅聆聇聈聉聊聋职聍聎聏聐聑聒聓联聕聖聗聘聙聚聛聜聝聞聟聠聡聢聣聤聥聦聧聨聩聪聫聬聭聮聯聰聱聲聳聴聵聶職聸聹聺聻聼聽聾聿肀肁肂肃肄肅肆肇肈肉肊肋肌肍肎肏肐肑肒肓肔肕肖肗肘肙肚肛肜肝肞肟肠股肢肣肤肥肦肧肨肩肪肫肬肭肮肯肰肱育肳肴肵肶肷肸肹肺肻肼肽肾肿胀胁胂胃胄胅胆胇胈胉胊胋背胍胎胏胐胑胒胓胔胕胖胗胘胙胚胛胜胝胞胟胠胡胢胣胤胥胦胧胨胩胪胫胬胭胮胯胰胱胲胳胴胵胶胷胸胹胺胻胼能胾胿脀脁脂脃脄脅脆脇脈脉脊脋脌脍脎脏脐脑脒脓脔脕脖脗脘脙脚脛脜脝脞脟脠脡脢脣脤脥脦脧脨脩脪脫脬脭脮脯脰脱脲脳脴脵脶脷脸脹脺脻脼脽脾脿腀腁腂腃腄腅腆腇腈腉腊腋腌腍腎腏腐腑腒腓腔腕腖腗腘腙腚腛腜腝腞腟腠腡腢腣腤腥腦腧腨腩腪腫腬腭腮腯腰腱腲腳腴腵腶腷腸腹腺腻腼腽腾腿膀膁膂膃膄膅膆膇膈膉膊膋膌膍膎膏膐膑膒膓膔膕膖膗膘膙膚膛膜膝膞膟膠膡膢膣膤膥膦膧膨膩膪膫膬膭膮膯膰膱膲膳膴膵膶膷膸膹膺膻膼膽膾膿臀臁臂臃臄臅臆臇臈臉臊臋臌臍臎臏臐臑臒臓臔臕臖臗臘臙臚臛臜臝臞臟臠臡臢臣臤臥臦臧臨臩自臫臬臭臮臯臰臱臲至致臵臶臷臸臹臺臻臼臽臾臿舀舁舂舃舄舅舆與興舉舊舋舌舍舎舏舐舑舒舓舔舕舖舗舘舙舚舛舜舝舞舟舠舡舢舣舤舥舦舧舨舩航舫般舭舮舯舰舱舲舳舴舵舶舷舸船舺舻舼舽舾舿艀艁艂艃艄艅艆艇艈艉艊艋艌艍艎艏艐艑艒艓艔艕艖艗艘艙艚艛艜艝艞艟艠艡艢艣艤艥艦艧艨艩艪艫艬艭艮良艰艱色艳艴艵艶艷艸艹艺艻艼艽艾艿芀芁节芃芄芅芆芇芈芉芊芋芌芍芎芏芐芑芒芓芔芕芖芗芘芙芚芛芜芝芞芟芠芡芢芣芤芥芦芧芨芩芪芫芬芭芮芯芰花芲芳芴芵芶芷芸芹芺芻芼芽芾芿苀苁苂苃苄苅苆苇苈苉苊苋苌苍苎苏苐苑苒苓苔苕苖苗苘苙苚苛苜苝苞苟苠苡苢苣苤若苦苧苨苩苪苫苬苭苮苯苰英苲苳苴苵苶苷苸苹苺苻苼苽苾苿茀茁茂范茄茅茆茇茈茉茊茋茌茍茎茏茐茑茒茓茔茕茖茗茘茙茚茛茜茝茞茟茠茡茢茣茤茥茦茧茨茩茪茫茬茭茮茯茰茱茲茳茴茵茶茷茸茹茺茻茼茽茾茿荀荁荂荃荄荅荆荇荈草荊荋荌荍荎荏荐荑荒荓荔荕荖荗荘荙荚荛荜荝荞荟荠荡荢荣荤荥荦荧荨荩荪荫荬荭荮药荰荱荲荳荴荵荶荷荸荹荺荻荼荽荾荿莀莁莂莃莄莅莆莇莈莉莊莋莌莍莎莏莐莑莒莓莔莕莖莗莘莙莚莛莜莝莞莟莠莡莢莣莤莥莦莧莨莩莪莫莬莭莮莯莰莱莲莳莴莵莶获莸莹莺莻莼莽莾莿菀菁菂菃菄菅菆菇菈菉菊菋菌菍菎菏菐菑菒菓菔菕菖菗菘菙菚菛菜菝菞菟菠菡菢菣菤菥菦菧菨菩菪菫菬菭菮華菰菱菲菳菴菵菶菷菸菹菺菻菼菽菾菿萀萁萂萃萄萅萆萇萈萉萊萋萌萍萎萏萐萑萒萓萔萕萖萗萘萙萚萛萜萝萞萟萠萡萢萣萤营萦萧萨萩萪萫萬萭萮萯萰萱萲萳萴萵萶萷萸萹萺萻萼落萾萿葀葁葂葃葄葅葆葇葈葉葊葋葌葍葎葏葐葑葒葓葔葕葖著葘葙葚葛葜葝葞葟葠葡葢董葤葥葦葧葨葩葪葫葬葭葮葯葰葱葲葳葴葵葶葷葸葹葺葻葼葽葾葿蒀蒁蒂蒃蒄蒅蒆蒇蒈蒉蒊蒋蒌蒍蒎蒏蒐蒑蒒蒓蒔蒕蒖蒗蒘蒙蒚蒛蒜蒝蒞蒟蒠蒡蒢蒣蒤蒥蒦蒧蒨蒩蒪蒫蒬蒭蒮蒯蒰蒱蒲蒳蒴蒵蒶蒷蒸蒹蒺蒻蒼蒽蒾蒿蓀蓁蓂蓃蓄蓅蓆蓇蓈蓉蓊蓋蓌蓍蓎蓏蓐蓑蓒蓓蓔蓕蓖蓗蓘蓙蓚蓛蓜蓝蓞蓟蓠蓡蓢蓣蓤蓥蓦蓧蓨蓩蓪蓫蓬蓭蓮蓯蓰蓱蓲蓳蓴蓵蓶蓷蓸蓹蓺蓻蓼蓽蓾蓿蔀蔁蔂蔃蔄蔅蔆蔇蔈蔉蔊蔋蔌蔍蔎蔏蔐蔑蔒蔓蔔蔕蔖蔗蔘蔙蔚蔛蔜蔝蔞蔟蔠蔡蔢蔣蔤蔥蔦蔧蔨蔩蔪蔫蔬蔭蔮蔯蔰蔱蔲蔳蔴蔵蔶蔷蔸蔹蔺蔻蔼蔽蔾蔿蕀蕁蕂蕃蕄蕅蕆蕇蕈蕉蕊蕋蕌蕍蕎蕏蕐蕑蕒蕓蕔蕕蕖蕗蕘蕙蕚蕛蕜蕝蕞蕟蕠蕡蕢蕣蕤蕥蕦蕧蕨蕩蕪蕫蕬蕭蕮蕯蕰蕱蕲蕳蕴蕵蕶蕷蕸蕹蕺蕻蕼蕽蕾蕿薀薁薂薃薄薅薆薇薈薉薊薋薌薍薎薏薐薑薒薓薔薕薖薗薘薙薚薛薜薝薞薟薠薡薢薣薤薥薦薧薨薩薪薫薬薭薮薯薰薱薲薳薴薵薶薷薸薹薺薻薼薽薾薿藀藁藂藃藄藅藆藇藈藉藊藋藌藍藎藏藐藑藒藓藔藕藖藗藘藙藚藛藜藝藞藟藠藡藢藣藤藥藦藧藨藩藪藫藬藭藮藯藰藱藲藳藴藵藶藷藸藹藺藻藼藽藾藿蘀蘁蘂蘃蘄蘅蘆蘇蘈蘉蘊蘋蘌蘍蘎蘏蘐蘑蘒蘓蘔蘕蘖蘗蘘蘙蘚蘛蘜蘝蘞蘟蘠蘡蘢蘣蘤蘥蘦蘧蘨蘩蘪蘫蘬蘭蘮蘯蘰蘱蘲蘳蘴蘵蘶蘷蘸蘹蘺蘻蘼蘽蘾蘿虀虁虂虃虄虅虆虇虈虉虊虋虌虍虎虏虐虑虒虓虔處虖虗虘虙虚虛虜虝虞號虠虡虢虣虤虥虦虧虨虩虪虫虬虭虮虯虰虱虲虳虴虵虶虷虸虹虺虻虼虽虾虿蚀蚁蚂蚃蚄蚅蚆蚇蚈蚉蚊蚋蚌蚍蚎蚏蚐蚑蚒蚓蚔蚕蚖蚗蚘蚙蚚蚛蚜蚝蚞蚟蚠蚡蚢蚣蚤蚥蚦蚧蚨蚩蚪蚫蚬蚭蚮蚯蚰蚱蚲蚳蚴蚵蚶蚷蚸蚹蚺蚻蚼蚽蚾蚿蛀蛁蛂蛃蛄蛅蛆蛇蛈蛉蛊蛋蛌蛍蛎蛏蛐蛑蛒蛓蛔蛕蛖蛗蛘蛙蛚蛛蛜蛝蛞蛟蛠蛡蛢蛣蛤蛥蛦蛧蛨蛩蛪蛫蛬蛭蛮蛯蛰蛱蛲蛳蛴蛵蛶蛷蛸蛹蛺蛻蛼蛽蛾蛿蜀蜁蜂蜃蜄蜅蜆蜇蜈蜉蜊蜋蜌蜍蜎蜏蜐蜑蜒蜓蜔蜕蜖蜗蜘蜙蜚蜛蜜蜝蜞蜟蜠蜡蜢蜣蜤蜥蜦蜧蜨蜩蜪蜫蜬蜭蜮蜯蜰蜱蜲蜳蜴蜵蜶蜷蜸蜹蜺蜻蜼蜽蜾蜿蝀蝁蝂蝃蝄蝅蝆蝇蝈蝉蝊蝋蝌蝍蝎蝏蝐蝑蝒蝓蝔蝕蝖蝗蝘蝙蝚蝛蝜蝝蝞蝟蝠蝡蝢蝣蝤蝥蝦蝧蝨蝩蝪蝫蝬蝭蝮蝯蝰蝱蝲蝳蝴蝵蝶蝷蝸蝹蝺蝻蝼蝽蝾蝿螀螁螂螃螄螅螆螇螈螉螊螋螌融螎螏螐螑螒螓螔螕螖螗螘螙螚螛螜螝螞螟螠螡螢螣螤螥螦螧螨螩螪螫螬螭螮螯螰螱螲螳螴螵螶螷螸螹螺螻螼螽螾螿蟀蟁蟂蟃蟄蟅蟆蟇蟈蟉蟊蟋蟌蟍蟎蟏蟐蟑蟒蟓蟔蟕蟖蟗蟘蟙蟚蟛蟜蟝蟞蟟蟠蟡蟢蟣蟤蟥蟦蟧蟨蟩蟪蟫蟬蟭蟮蟯蟰蟱蟲蟳蟴蟵蟶蟷蟸蟹蟺蟻蟼蟽蟾蟿蠀蠁蠂蠃蠄蠅蠆蠇蠈蠉蠊蠋蠌蠍蠎蠏蠐蠑蠒蠓蠔蠕蠖蠗蠘蠙蠚蠛蠜蠝蠞蠟蠠蠡蠢蠣蠤蠥蠦蠧蠨蠩蠪蠫蠬蠭蠮蠯蠰蠱蠲蠳蠴蠵蠶蠷蠸蠹蠺蠻蠼蠽蠾蠿血衁衂衃衄衅衆衇衈衉衊衋行衍衎衏衐衑衒術衔衕衖街衘衙衚衛衜衝衞衟衠衡衢衣衤补衦衧表衩衪衫衬衭衮衯衰衱衲衳衴衵衶衷衸衹衺衻衼衽衾衿袀袁袂袃袄袅袆袇袈袉袊袋袌袍袎袏袐袑袒袓袔袕袖袗袘袙袚袛袜袝袞袟袠袡袢袣袤袥袦袧袨袩袪被袬袭袮袯袰袱袲袳袴袵袶袷袸袹袺袻袼袽袾袿裀裁裂裃裄装裆裇裈裉裊裋裌裍裎裏裐裑裒裓裔裕裖裗裘裙裚裛補裝裞裟裠裡裢裣裤裥裦裧裨裩裪裫裬裭裮裯裰裱裲裳裴裵裶裷裸裹裺裻裼製裾裿褀褁褂褃褄褅褆複褈褉褊褋褌褍褎褏褐褑褒褓褔褕褖褗褘褙褚褛褜褝褞褟褠褡褢褣褤褥褦褧褨褩褪褫褬褭褮褯褰褱褲褳褴褵褶褷褸褹褺褻褼褽褾褿襀襁襂襃襄襅襆襇襈襉襊襋襌襍襎襏襐襑襒襓襔襕襖襗襘襙襚襛襜襝襞襟襠襡襢襣襤襥襦襧襨襩襪襫襬襭襮襯襰襱襲襳襴襵襶襷襸襹襺襻襼襽襾西覀要覂覃覄覅覆覇覈覉覊見覌覍覎規覐覑覒覓覔覕視覗覘覙覚覛覜覝覞覟覠覡覢覣覤覥覦覧覨覩親覫覬覭覮覯覰覱覲観覴覵覶覷覸覹覺覻覼覽覾覿觀见观觃规觅视觇览觉觊觋觌觍觎觏觐觑角觓觔觕觖觗觘觙觚觛觜觝觞觟觠觡觢解觤觥触觧觨觩觪觫觬觭觮觯觰觱觲觳觴觵觶觷觸觹觺觻觼觽觾觿言訁訂訃訄訅訆訇計訉訊訋訌訍討訏訐訑訒訓訔訕訖託記訙訚訛訜訝訞訟訠訡訢訣訤訥訦訧訨訩訪訫訬設訮訯訰許訲訳訴訵訶訷訸訹診註証訽訾訿詀詁詂詃詄詅詆詇詈詉詊詋詌詍詎詏詐詑詒詓詔評詖詗詘詙詚詛詜詝詞詟詠詡詢詣詤詥試詧詨詩詪詫詬詭詮詯詰話該詳詴詵詶詷詸詹詺詻詼詽詾詿誀誁誂誃誄誅誆誇誈誉誊誋誌認誎誏誐誑誒誓誔誕誖誗誘誙誚誛誜誝語誟誠誡誢誣誤誥誦誧誨誩說誫説読誮誯誰誱課誳誴誵誶誷誸誹誺誻誼誽誾調諀諁諂諃諄諅諆談諈諉諊請諌諍諎諏諐諑諒諓諔諕論諗諘諙諚諛諜諝諞諟諠諡諢諣諤諥諦諧諨諩諪諫諬諭諮諯諰諱諲諳諴諵諶諷諸諹諺諻諼諽諾諿謀謁謂謃謄謅謆謇謈謉謊謋謌謍謎謏謐謑謒謓謔謕謖謗謘謙謚講謜謝謞謟謠謡謢謣謤謥謦謧謨謩謪謫謬謭謮謯謰謱謲謳謴謵謶謷謸謹謺謻謼謽謾謿譀譁譂譃譄譅譆譇譈證譊譋譌譍譎譏譐譑譒譓譔譕譖譗識譙譚譛譜譝譞譟譠譡譢譣譤譥警譧譨譩譪譫譬譭譮譯議譱譲譳譴譵譶護譸譹譺譻譼譽譾譿讀讁讂讃讄讅讆讇讈讉變讋讌讍讎讏讐讑讒讓讔讕讖讗讘讙讚讛讜讝讞讟讠计订讣认讥讦讧讨让讪讫讬训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谉谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷谸谹谺谻谼谽谾谿豀豁豂豃豄豅豆豇豈豉豊豋豌豍豎豏豐豑豒豓豔豕豖豗豘豙豚豛豜豝豞豟豠象豢豣豤豥豦豧豨豩豪豫豬豭豮豯豰豱豲豳豴豵豶豷豸豹豺豻豼豽豾豿貀貁貂貃貄貅貆貇貈貉貊貋貌貍貎貏貐貑貒貓貔貕貖貗貘貙貚貛貜貝貞貟負財貢貣貤貥貦貧貨販貪貫責貭貮貯貰貱貲貳貴貵貶買貸貹貺費貼貽貾貿賀賁賂賃賄賅賆資賈賉賊賋賌賍賎賏賐賑賒賓賔賕賖賗賘賙賚賛賜賝賞賟賠賡賢賣賤賥賦賧賨賩質賫賬賭賮賯賰賱賲賳賴賵賶賷賸賹賺賻購賽賾賿贀贁贂贃贄贅贆贇贈贉贊贋贌贍贎贏贐贑贒贓贔贕贖贗贘贙贚贛贜贝贞负贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赤赥赦赧赨赩赪赫赬赭赮赯走赱赲赳赴赵赶起赸赹赺赻赼赽赾赿趀趁趂趃趄超趆趇趈趉越趋趌趍趎趏趐趑趒趓趔趕趖趗趘趙趚趛趜趝趞趟趠趡趢趣趤趥趦趧趨趩趪趫趬趭趮趯趰趱趲足趴趵趶趷趸趹趺趻趼趽趾趿跀跁跂跃跄跅跆跇跈跉跊跋跌跍跎跏跐跑跒跓跔跕跖跗跘跙跚跛跜距跞跟跠跡跢跣跤跥跦跧跨跩跪跫跬跭跮路跰跱跲跳跴践跶跷跸跹跺跻跼跽跾跿踀踁踂踃踄踅踆踇踈踉踊踋踌踍踎踏踐踑踒踓踔踕踖踗踘踙踚踛踜踝踞踟踠踡踢踣踤踥踦踧踨踩踪踫踬踭踮踯踰踱踲踳踴踵踶踷踸踹踺踻踼踽踾踿蹀蹁蹂蹃蹄蹅蹆蹇蹈蹉蹊蹋蹌蹍蹎蹏蹐蹑蹒蹓蹔蹕蹖蹗蹘蹙蹚蹛蹜蹝蹞蹟蹠蹡蹢蹣蹤蹥蹦蹧蹨蹩蹪蹫蹬蹭蹮蹯蹰蹱蹲蹳蹴蹵蹶蹷蹸蹹蹺蹻蹼蹽蹾蹿躀躁躂躃躄躅躆躇躈躉躊躋躌躍躎躏躐躑躒躓躔躕躖躗躘躙躚躛躜躝躞躟躠躡躢躣躤躥躦躧躨躩躪身躬躭躮躯躰躱躲躳躴躵躶躷躸躹躺躻躼躽躾躿軀軁軂軃軄軅軆軇軈軉車軋軌軍軎軏軐軑軒軓軔軕軖軗軘軙軚軛軜軝軞軟軠軡転軣軤軥軦軧軨軩軪軫軬軭軮軯軰軱軲軳軴軵軶軷軸軹軺軻軼軽軾軿輀輁輂較輄輅輆輇輈載輊輋輌輍輎輏輐輑輒輓輔輕輖輗輘輙輚輛輜輝輞輟輠輡輢輣輤輥輦輧輨輩輪輫輬輭輮輯輰輱輲輳輴輵輶輷輸輹輺輻輼輽輾輿轀轁轂轃轄轅轆轇轈轉轊轋轌轍轎轏轐轑轒轓轔轕轖轗轘轙轚轛轜轝轞轟轠轡轢轣轤轥车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辛辜辝辞辟辠辡辢辣辤辥辦辧辨辩辪辫辬辭辮辯辰辱農辳辴辵辶辷辸边辺辻込辽达辿迀迁迂迃迄迅迆过迈迉迊迋迌迍迎迏运近迒迓返迕迖迗还这迚进远违连迟迠迡迢迣迤迥迦迧迨迩迪迫迬迭迮迯述迱迲迳迴迵迶迷迸迹迺迻迼追迾迿退送适逃逄逅逆逇逈选逊逋逌逍逎透逐逑递逓途逕逖逗逘這通逛逜逝逞速造逡逢連逤逥逦逧逨逩逪逫逬逭逮逯逰週進逳逴逵逶逷逸逹逺逻逼逽逾逿遀遁遂遃遄遅遆遇遈遉遊運遌遍過遏遐遑遒道達違遖遗遘遙遚遛遜遝遞遟遠遡遢遣遤遥遦遧遨適遪遫遬遭遮遯遰遱遲遳遴遵遶遷選遹遺遻遼遽遾避邀邁邂邃還邅邆邇邈邉邊邋邌邍邎邏邐邑邒邓邔邕邖邗邘邙邚邛邜邝邞邟邠邡邢那邤邥邦邧邨邩邪邫邬邭邮邯邰邱邲邳邴邵邶邷邸邹邺邻邼邽邾邿郀郁郂郃郄郅郆郇郈郉郊郋郌郍郎郏郐郑郒郓郔郕郖郗郘郙郚郛郜郝郞郟郠郡郢郣郤郥郦郧部郩郪郫郬郭郮郯郰郱郲郳郴郵郶郷郸郹郺郻郼都郾郿鄀鄁鄂鄃鄄鄅鄆鄇鄈鄉鄊鄋鄌鄍鄎鄏鄐鄑鄒鄓鄔鄕鄖鄗鄘鄙鄚鄛鄜鄝鄞鄟鄠鄡鄢鄣鄤鄥鄦鄧鄨鄩鄪鄫鄬鄭鄮鄯鄰鄱鄲鄳鄴鄵鄶鄷鄸鄹鄺鄻鄼鄽鄾鄿酀酁酂酃酄酅酆酇酈酉酊酋酌配酎酏酐酑酒酓酔酕酖酗酘酙酚酛酜酝酞酟酠酡酢酣酤酥酦酧酨酩酪酫酬酭酮酯酰酱酲酳酴酵酶酷酸酹酺酻酼酽酾酿醀醁醂醃醄醅醆醇醈醉醊醋醌醍醎醏醐醑醒醓醔醕醖醗醘醙醚醛醜醝醞醟醠醡醢醣醤醥醦醧醨醩醪醫醬醭醮醯醰醱醲醳醴醵醶醷醸醹醺醻醼醽醾醿釀釁釂釃釄釅釆采釈釉释釋里重野量釐金釒釓釔釕釖釗釘釙釚釛釜針釞釟釠釡釢釣釤釥釦釧釨釩釪釫釬釭釮釯釰釱釲釳釴釵釶釷釸釹釺釻釼釽釾釿鈀鈁鈂鈃鈄鈅鈆鈇鈈鈉鈊鈋鈌鈍鈎鈏鈐鈑鈒鈓鈔鈕鈖鈗鈘鈙鈚鈛鈜鈝鈞鈟鈠鈡鈢鈣鈤鈥鈦鈧鈨鈩鈪鈫鈬鈭鈮鈯鈰鈱鈲鈳鈴鈵鈶鈷鈸鈹鈺鈻鈼鈽鈾鈿鉀鉁鉂鉃鉄鉅鉆鉇鉈鉉鉊鉋鉌鉍鉎鉏鉐鉑鉒鉓鉔鉕鉖鉗鉘鉙鉚鉛鉜鉝鉞鉟鉠鉡鉢鉣鉤鉥鉦鉧鉨鉩鉪鉫鉬鉭鉮鉯鉰鉱鉲鉳鉴鉵鉶鉷鉸鉹鉺鉻鉼鉽鉾鉿銀銁銂銃銄銅銆銇銈銉銊銋銌銍銎銏銐銑銒銓銔銕銖銗銘銙銚銛銜銝銞銟銠銡銢銣銤銥銦銧銨銩銪銫銬銭銮銯銰銱銲銳銴銵銶銷銸銹銺銻銼銽銾銿鋀鋁鋂鋃鋄鋅鋆鋇鋈鋉鋊鋋鋌鋍鋎鋏鋐鋑鋒鋓鋔鋕鋖鋗鋘鋙鋚鋛鋜鋝鋞鋟鋠鋡鋢鋣鋤鋥鋦鋧鋨鋩鋪鋫鋬鋭鋮鋯鋰鋱鋲鋳鋴鋵鋶鋷鋸鋹鋺鋻鋼鋽鋾鋿錀錁錂錃錄錅錆錇錈錉錊錋錌錍錎錏錐錑錒錓錔錕錖錗錘錙錚錛錜錝錞錟錠錡錢錣錤錥錦錧錨錩錪錫錬錭錮錯錰錱録錳錴錵錶錷錸錹錺錻錼錽錾錿鍀鍁鍂鍃鍄鍅鍆鍇鍈鍉鍊鍋鍌鍍鍎鍏鍐鍑鍒鍓鍔鍕鍖鍗鍘鍙鍚鍛鍜鍝鍞鍟鍠鍡鍢鍣鍤鍥鍦鍧鍨鍩鍪鍫鍬鍭鍮鍯鍰鍱鍲鍳鍴鍵鍶鍷鍸鍹鍺鍻鍼鍽鍾鍿鎀鎁鎂鎃鎄鎅鎆鎇鎈鎉鎊鎋鎌鎍鎎鎏鎐鎑鎒鎓鎔鎕鎖鎗鎘鎙鎚鎛鎜鎝鎞鎟鎠鎡鎢鎣鎤鎥鎦鎧鎨鎩鎪鎫鎬鎭鎮鎯鎰鎱鎲鎳鎴鎵鎶鎷鎸鎹鎺鎻鎼鎽鎾鎿鏀鏁鏂鏃鏄鏅鏆鏇鏈鏉鏊鏋鏌鏍鏎鏏鏐鏑鏒鏓鏔鏕鏖鏗鏘鏙鏚鏛鏜鏝鏞鏟鏠鏡鏢鏣鏤鏥鏦鏧鏨鏩鏪鏫鏬鏭鏮鏯鏰鏱鏲鏳鏴鏵鏶鏷鏸鏹鏺鏻鏼鏽鏾鏿鐀鐁鐂鐃鐄鐅鐆鐇鐈鐉鐊鐋鐌鐍鐎鐏鐐鐑鐒鐓鐔鐕鐖鐗鐘鐙鐚鐛鐜鐝鐞鐟鐠鐡鐢鐣鐤鐥鐦鐧鐨鐩鐪鐫鐬鐭鐮鐯鐰鐱鐲鐳鐴鐵鐶鐷鐸鐹鐺鐻鐼鐽鐾鐿鑀鑁鑂鑃鑄鑅鑆鑇鑈鑉鑊鑋鑌鑍鑎鑏鑐鑑鑒鑓鑔鑕鑖鑗鑘鑙鑚鑛鑜鑝鑞鑟鑠鑡鑢鑣鑤鑥鑦鑧鑨鑩鑪鑫鑬鑭鑮鑯鑰鑱鑲鑳鑴鑵鑶鑷鑸鑹鑺鑻鑼鑽鑾鑿钀钁钂钃钄钅钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钜钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铇铈铉铊铋铌铍铎铏铐铑铒铓铔铕铖铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铩铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗锘错锚锛锜锝锞锟锠锡锢锣锤锥锦锧锨锩锪锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镄镅镆镇镈镉镊镋镌镍镎镏镐镑镒镓镔镕镖镗镘镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镵镶長镸镹镺镻镼镽镾长門閁閂閃閄閅閆閇閈閉閊開閌閍閎閏閐閑閒間閔閕閖閗閘閙閚閛閜閝閞閟閠閡関閣閤閥閦閧閨閩閪閫閬閭閮閯閰閱閲閳閴閵閶閷閸閹閺閻閼閽閾閿闀闁闂闃闄闅闆闇闈闉闊闋闌闍闎闏闐闑闒闓闔闕闖闗闘闙闚闛關闝闞闟闠闡闢闣闤闥闦闧门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛阜阝阞队阠阡阢阣阤阥阦阧阨阩阪阫阬阭阮阯阰阱防阳阴阵阶阷阸阹阺阻阼阽阾阿陀陁陂陃附际陆陇陈陉陊陋陌降陎陏限陑陒陓陔陕陖陗陘陙陚陛陜陝陞陟陠陡院陣除陥陦陧陨险陪陫陬陭陮陯陰陱陲陳陴陵陶陷陸陹険陻陼陽陾陿隀隁隂隃隄隅隆隇隈隉隊隋隌隍階随隐隑隒隓隔隕隖隗隘隙隚際障隝隞隟隠隡隢隣隤隥隦隧隨隩險隫隬隭隮隯隰隱隲隳隴隵隶隷隸隹隺隻隼隽难隿雀雁雂雃雄雅集雇雈雉雊雋雌雍雎雏雐雑雒雓雔雕雖雗雘雙雚雛雜雝雞雟雠雡離難雤雥雦雧雨雩雪雫雬雭雮雯雰雱雲雳雴雵零雷雸雹雺電雼雽雾雿需霁霂霃霄霅霆震霈霉霊霋霌霍霎霏霐霑霒霓霔霕霖霗霘霙霚霛霜霝霞霟霠霡霢霣霤霥霦霧霨霩霪霫霬霭霮霯霰霱露霳霴霵霶霷霸霹霺霻霼霽霾霿靀靁靂靃靄靅靆靇靈靉靊靋靌靍靎靏靐靑青靓靔靕靖靗靘静靚靛靜靝非靟靠靡面靣靤靥靦靧靨革靪靫靬靭靮靯靰靱靲靳靴靵靶靷靸靹靺靻靼靽靾靿鞀鞁鞂鞃鞄鞅鞆鞇鞈鞉鞊鞋鞌鞍鞎鞏鞐鞑鞒鞓鞔鞕鞖鞗鞘鞙鞚鞛鞜鞝鞞鞟鞠鞡鞢鞣鞤鞥鞦鞧鞨鞩鞪鞫鞬鞭鞮鞯鞰鞱鞲鞳鞴鞵鞶鞷鞸鞹鞺鞻鞼鞽鞾鞿韀韁韂韃韄韅韆韇韈韉韊韋韌韍韎韏韐韑韒韓韔韕韖韗韘韙韚韛韜韝韞韟韠韡韢韣韤韥韦韧韨韩韪韫韬韭韮韯韰韱韲音韴韵韶韷韸韹韺韻韼韽韾響頀頁頂頃頄項順頇須頉頊頋頌頍頎頏預頑頒頓頔頕頖頗領頙頚頛頜頝頞頟頠頡頢頣頤頥頦頧頨頩頪頫頬頭頮頯頰頱頲頳頴頵頶頷頸頹頺頻頼頽頾頿顀顁顂顃顄顅顆顇顈顉顊顋題額顎顏顐顑顒顓顔顕顖顗願顙顚顛顜顝類顟顠顡顢顣顤顥顦顧顨顩顪顫顬顭顮顯顰顱顲顳顴页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧風颩颪颫颬颭颮颯颰颱颲颳颴颵颶颷颸颹颺颻颼颽颾颿飀飁飂飃飄飅飆飇飈飉飊飋飌飍风飏飐飑飒飓飔飕飖飗飘飙飚飛飜飝飞食飠飡飢飣飤飥飦飧飨飩飪飫飬飭飮飯飰飱飲飳飴飵飶飷飸飹飺飻飼飽飾飿餀餁餂餃餄餅餆餇餈餉養餋餌餍餎餏餐餑餒餓餔餕餖餗餘餙餚餛餜餝餞餟餠餡餢餣餤餥餦餧館餩餪餫餬餭餮餯餰餱餲餳餴餵餶餷餸餹餺餻餼餽餾餿饀饁饂饃饄饅饆饇饈饉饊饋饌饍饎饏饐饑饒饓饔饕饖饗饘饙饚饛饜饝饞饟饠饡饢饣饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕首馗馘香馚馛馜馝馞馟馠馡馢馣馤馥馦馧馨馩馪馫馬馭馮馯馰馱馲馳馴馵馶馷馸馹馺馻馼馽馾馿駀駁駂駃駄駅駆駇駈駉駊駋駌駍駎駏駐駑駒駓駔駕駖駗駘駙駚駛駜駝駞駟駠駡駢駣駤駥駦駧駨駩駪駫駬駭駮駯駰駱駲駳駴駵駶駷駸駹駺駻駼駽駾駿騀騁騂騃騄騅騆騇騈騉騊騋騌騍騎騏騐騑騒験騔騕騖騗騘騙騚騛騜騝騞騟騠騡騢騣騤騥騦騧騨騩騪騫騬騭騮騯騰騱騲騳騴騵騶騷騸騹騺騻騼騽騾騿驀驁驂驃驄驅驆驇驈驉驊驋驌驍驎驏驐驑驒驓驔驕驖驗驘驙驚驛驜驝驞驟驠驡驢驣驤驥驦驧驨驩驪驫马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧骨骩骪骫骬骭骮骯骰骱骲骳骴骵骶骷骸骹骺骻骼骽骾骿髀髁髂髃髄髅髆髇髈髉髊髋髌髍髎髏髐髑髒髓體髕髖髗高髙髚髛髜髝髞髟髠髡髢髣髤髥髦髧髨髩髪髫髬髭髮髯髰髱髲髳髴髵髶髷髸髹髺髻髼髽髾髿鬀鬁鬂鬃鬄鬅鬆鬇鬈鬉鬊鬋鬌鬍鬎鬏鬐鬑鬒鬓鬔鬕鬖鬗鬘鬙鬚鬛鬜鬝鬞鬟鬠鬡鬢鬣鬤鬥鬦鬧鬨鬩鬪鬫鬬鬭鬮鬯鬰鬱鬲鬳鬴鬵鬶鬷鬸鬹鬺鬻鬼鬽鬾鬿魀魁魂魃魄魅魆魇魈魉魊魋魌魍魎魏魐魑魒魓魔魕魖魗魘魙魚魛魜魝魞魟魠魡魢魣魤魥魦魧魨魩魪魫魬魭魮魯魰魱魲魳魴魵魶魷魸魹魺魻魼魽魾魿鮀鮁鮂鮃鮄鮅鮆鮇鮈鮉鮊鮋鮌鮍鮎鮏鮐鮑鮒鮓鮔鮕鮖鮗鮘鮙鮚鮛鮜鮝鮞鮟鮠鮡鮢鮣鮤鮥鮦鮧鮨鮩鮪鮫鮬鮭鮮鮯鮰鮱鮲鮳鮴鮵鮶鮷鮸鮹鮺鮻鮼鮽鮾鮿鯀鯁鯂鯃鯄鯅鯆鯇鯈鯉鯊鯋鯌鯍鯎鯏鯐鯑鯒鯓鯔鯕鯖鯗鯘鯙鯚鯛鯜鯝鯞鯟鯠鯡鯢鯣鯤鯥鯦鯧鯨鯩鯪鯫鯬鯭鯮鯯鯰鯱鯲鯳鯴鯵鯶鯷鯸鯹鯺鯻鯼鯽鯾鯿鰀鰁鰂鰃鰄鰅鰆鰇鰈鰉鰊鰋鰌鰍鰎鰏鰐鰑鰒鰓鰔鰕鰖鰗鰘鰙鰚鰛鰜鰝鰞鰟鰠鰡鰢鰣鰤鰥鰦鰧鰨鰩鰪鰫鰬鰭鰮鰯鰰鰱鰲鰳鰴鰵鰶鰷鰸鰹鰺鰻鰼鰽鰾鰿鱀鱁鱂鱃鱄鱅鱆鱇鱈鱉鱊鱋鱌鱍鱎鱏鱐鱑鱒鱓鱔鱕鱖鱗鱘鱙鱚鱛鱜鱝鱞鱟鱠鱡鱢鱣鱤鱥鱦鱧鱨鱩鱪鱫鱬鱭鱮鱯鱰鱱鱲鱳鱴鱵鱶鱷鱸鱹鱺鱻鱼鱽鱾鱿鲀鲁鲂鲃鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳚鳛鳜鳝鳞鳟鳠鳡鳢鳣鳤鳥鳦鳧鳨鳩鳪鳫鳬鳭鳮鳯鳰鳱鳲鳳鳴鳵鳶鳷鳸鳹鳺鳻鳼鳽鳾鳿鴀鴁鴂鴃鴄鴅鴆鴇鴈鴉鴊鴋鴌鴍鴎鴏鴐鴑鴒鴓鴔鴕鴖鴗鴘鴙鴚鴛鴜鴝鴞鴟鴠鴡鴢鴣鴤鴥鴦鴧鴨鴩鴪鴫鴬鴭鴮鴯鴰鴱鴲鴳鴴鴵鴶鴷鴸鴹鴺鴻鴼鴽鴾鴿鵀鵁鵂鵃鵄鵅鵆鵇鵈鵉鵊鵋鵌鵍鵎鵏鵐鵑鵒鵓鵔鵕鵖鵗鵘鵙鵚鵛鵜鵝鵞鵟鵠鵡鵢鵣鵤鵥鵦鵧鵨鵩鵪鵫鵬鵭鵮鵯鵰鵱鵲鵳鵴鵵鵶鵷鵸鵹鵺鵻鵼鵽鵾鵿鶀鶁鶂鶃鶄鶅鶆鶇鶈鶉鶊鶋鶌鶍鶎鶏鶐鶑鶒鶓鶔鶕鶖鶗鶘鶙鶚鶛鶜鶝鶞鶟鶠鶡鶢鶣鶤鶥鶦鶧鶨鶩鶪鶫鶬鶭鶮鶯鶰鶱鶲鶳鶴鶵鶶鶷鶸鶹鶺鶻鶼鶽鶾鶿鷀鷁鷂鷃鷄鷅鷆鷇鷈鷉鷊鷋鷌鷍鷎鷏鷐鷑鷒鷓鷔鷕鷖鷗鷘鷙鷚鷛鷜鷝鷞鷟鷠鷡鷢鷣鷤鷥鷦鷧鷨鷩鷪鷫鷬鷭鷮鷯鷰鷱鷲鷳鷴鷵鷶鷷鷸鷹鷺鷻鷼鷽鷾鷿鸀鸁鸂鸃鸄鸅鸆鸇鸈鸉鸊鸋鸌鸍鸎鸏鸐鸑鸒鸓鸔鸕鸖鸗鸘鸙鸚鸛鸜鸝鸞鸟鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹙鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹮鹯鹰鹱鹲鹳鹴鹵鹶鹷鹸鹹鹺鹻鹼鹽鹾鹿麀麁麂麃麄麅麆麇麈麉麊麋麌麍麎麏麐麑麒麓麔麕麖麗麘麙麚麛麜麝麞麟麠麡麢麣麤麥麦麧麨麩麪麫麬麭麮麯麰麱麲麳麴麵麶麷麸麹麺麻麼麽麾麿黀黁黂黃黄黅黆黇黈黉黊黋黌黍黎黏黐黑黒黓黔黕黖黗默黙黚黛黜黝點黟黠黡黢黣黤黥黦黧黨黩黪黫黬黭黮黯黰黱黲黳黴黵黶黷黸黹黺黻黼黽黾黿鼀鼁鼂鼃鼄鼅鼆鼇鼈鼉鼊鼋鼌鼍鼎鼏鼐鼑鼒鼓鼔鼕鼖鼗鼘鼙鼚鼛鼜鼝鼞鼟鼠鼡鼢鼣鼤鼥鼦鼧鼨鼩鼪鼫鼬鼭鼮鼯鼰鼱鼲鼳鼴鼵鼶鼷鼸鼹鼺鼻鼼鼽鼾鼿齀齁齂齃齄齅齆齇齈齉齊齋齌齍齎齏齐齑齒齓齔齕齖齗齘齙齚齛齜齝齞齟齠齡齢齣齤齥齦齧齨齩齪齫齬齭齮齯齰齱齲齳齴齵齶齷齸齹齺齻齼齽齾齿龀龁龂龃龄龅龆龇龈龉龊龋龌龍龎龏龐龑龒龓龔龕龖龗龘龙龚龛龜龝龞龟龠龡龢龣龤龥龦龧龨龩龪龫龬龭龮龯龰龱龲龳龴龵龶龷龸龹龺龻龼龽龾龿鿀鿁鿂鿃鿄鿅鿆鿇鿈鿉鿊鿋鿌ꀀꀁꀂꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌꀍꀎꀏꀐꀑꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜꀝꀞꀟꀠꀡꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿꁀꁁꁂꁃꁄꁅꁆꁇꁈꁉꁊꁋꁌꁍꁎꁏꁐꁑꁒꁓꁔꁕꁖꁗꁘꁙꁚꁛꁜꁝꁞꁟꁠꁡꁢꁣꁤꁥꁦꁧꁨꁩꁪꁫꁬꁭꁮꁯꁰꁱꁲꁳꁴꁵꁶꁷꁸꁹꁺꁻꁼꁽꁾꁿꂀꂁꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌꂍꂎꂏꂐꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜꂝꂞꂟꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿꃀꃁꃂꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌꃍꃎꃏꃐꃑꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜꃝꃞꃟꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿꄀꄁꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌꄍꄎꄏꄐꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜꄝꄞꄟꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿꅀꅁꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌꅍꅎꅏꅐꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜꅝꅞꅟꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿꆀꆁꆂꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌꆍꆎꆏꆐꆑꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜꆝꆞꆟꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿꇀꇁꇂꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌꇍꇎꇏꇐꇑꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜꇝꇞꇟꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿꈀꈁꈂꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌꈍꈎꈏꈐꈑꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜꈝꈞꈟꈠꈡꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿꉀꉁꉂꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌꉍꉎꉏꉐꉑꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜꉝꉞꉟꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿꊀꊁꊂꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌꊍꊎꊏꊐꊑꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜꊝꊞꊟꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿꋀꋁꋂꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌꋍꋎꋏꋐꋑꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜꋝꋞꋟꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿꌀꌁꌂꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌꌍꌎꌏꌐꌑꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜꌝꌞꌟꌠꌡꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿꍀꍁꍂꍃꍄꍅꍆꍇꍈꍉꍊꍋꍌꍍꍎꍏꍐꍑꍒꍓꍔꍕꍖꍗꍘꍙꍚꍛꍜꍝꍞꍟꍠꍡꍢꍣꍤꍥꍦꍧꍨꍩꍪꍫꍬꍭꍮꍯꍰꍱꍲꍳꍴꍵꍶꍷꍸꍹꍺꍻꍼꍽꍾꍿꎀꎁꎂꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌꎍꎎꎏꎐꎑꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜꎝꎞꎟꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿꏀꏁꏂꏃꏄꏅꏆꏇꏈꏉꏊꏋꏌꏍꏎꏏꏐꏑꏒꏓꏔꏕꏖꏗꏘꏙꏚꏛꏜꏝꏞꏟꏠꏡꏢꏣꏤꏥꏦꏧꏨꏩꏪꏫꏬꏭꏮꏯꏰꏱꏲꏳꏴꏵꏶꏷꏸꏹꏺꏻꏼꏽꏾꏿꐀꐁꐂꐃꐄꐅꐆꐇꐈꐉꐊꐋꐌꐍꐎꐏꐐꐑꐒꐓꐔꐕꐖꐗꐘꐙꐚꐛꐜꐝꐞꐟꐠꐡꐢꐣꐤꐥꐦꐧꐨꐩꐪꐫꐬꐭꐮꐯꐰꐱꐲꐳꐴꐵꐶꐷꐸꐹꐺꐻꐼꐽꐾꐿꑀꑁꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌꑍꑎꑏꑐꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜꑝꑞꑟꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿꒀꒁꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ꒐꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜꒝꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀꓁꓂꓃꓄꓅꓆ꓐꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜꓝꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ꓾꓿ꔀꔁꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌꔍꔎꔏꔐꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜꔝꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀꕁꕂꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌꕍꕎꕏꕐꕑꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜꕝꕞꕟꕠꕡꕢꕣꕤꕥꕦꕧꕨꕩꕪꕫꕬꕭꕮꕯꕰꕱꕲꕳꕴꕵꕶꕷꕸꕹꕺꕻꕼꕽꕾꕿꖀꖁꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌꖍꖎꖏꖐꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜꖝꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀꗁꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌꗍꗎꗏꗐꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜꗝꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀꘁꘂꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ꘍꘎꘏ꘐꘑꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜꘝꘞꘟ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ꘪꘫꙀꙁꙂꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌꙍꙎꙏꙐꙑꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜꙝꙞꙟꙠꙡꙢꙣꙤꙥꙦꙧꙨꙩꙪꙫꙬꙭꙮ꙯꙰꙱꙲꙳ꙴꙵꙶꙷꙸꙹꙺꙻ꙼꙽꙾ꙿꚀꚁꚂꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌꚍꚎꚏꚐꚑꚒꚓꚔꚕꚖꚗꚟꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀꛁꛂꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌꛍꛎꛏꛐꛑꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜꛝꛞꛟꛠꛡꛢꛣꛤꛥꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ꛰꛱꛲꛳꛴꛵꛶꛷꜀꜁꜂꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌꜍꜎꜏꜐꜑꜒꜓꜔꜕꜖ꜗꜘꜙꜚꜛꜜꜝꜞꜟ꜠꜡ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿꝀꝁꝂꝃꝄꝅꝆꝇꝈꝉꝊꝋꝌꝍꝎꝏꝐꝑꝒꝓꝔꝕꝖꝗꝘꝙꝚꝛꝜꝝꝞꝟꝠꝡꝢꝣꝤꝥꝦꝧꝨꝩꝪꝫꝬꝭꝮꝯꝰꝱꝲꝳꝴꝵꝶꝷꝸꝹꝺꝻꝼꝽꝾꝿꞀꞁꞂꞃꞄꞅꞆꞇꞈ꞉꞊ꞋꞌꞍꞎꞐꞑꞒꞓꞠꞡꞢꞣꞤꞥꞦꞧꞨꞩꞪꟸꟹꟺꟻꟼꟽꟾꟿꠀꠁꠂꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌꠍꠎꠏꠐꠑꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜꠝꠞꠟꠠꠡꠢꠣꠤꠥꠦꠧ꠨꠩꠪꠫꠰꠱꠲꠳꠴꠵꠶꠷꠸꠹ꡀꡁꡂꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌꡍꡎꡏꡐꡑꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜꡝꡞꡟꡠꡡꡢꡣꡤꡥꡦꡧꡨꡩꡪꡫꡬꡭꡮꡯꡰꡱꡲꡳ꡴꡵꡶꡷ꢀꢁꢂꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌꢍꢎꢏꢐꢑꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜꢝꢞꢟꢠꢡꢢꢣꢤꢥꢦꢧꢨꢩꢪꢫꢬꢭꢮꢯꢰꢱꢲꢳꢴꢵꢶꢷꢸꢹꢺꢻꢼꢽꢾꢿꣀꣁꣂꣃ꣄꣎꣏꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ꣸꣹꣺ꣻ꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌꤍꤎꤏꤐꤑꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜꤝꤞꤟꤠꤡꤢꤣꤤꤥꤦꤧꤨꤩꤪ꤫꤬꤭꤮꤯ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀꥁꥂꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌꥍꥎꥏꥐꥑꥒ꥓꥟ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼꦀꦁꦂꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌꦍꦎꦏꦐꦑꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜꦝꦞꦟꦠꦡꦢꦣꦤꦥꦦꦧꦨꦩꦪꦫꦬꦭꦮꦯꦰꦱꦲ꦳ꦴꦵꦶꦷꦸꦹꦺꦻꦼꦽꦾꦿ꧀꧁꧂꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌꧍ꧏ꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧞꧟ꨀꨁꨂꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌꨍꨎꨏꨐꨑꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜꨝꨞꨟꨠꨡꨢꨣꨤꨥꨦꨧꨨꨩꨪꨫꨬꨭꨮꨯꨰꨱꨲꨳꨴꨵꨶꩀꩁꩂꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌꩍ꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꩜꩝꩞꩟ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ꩷꩸꩹ꩺꩻꪀꪁꪂꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌꪍꪎꪏꪐꪑꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜꪝꪞꪟꪠꪡꪢꪣꪤꪥꪦꪧꪨꪩꪪꪫꪬꪭꪮꪯꪰꪱꪴꪲꪳꪵꪶꪷꪸꪹꪺꪻꪼꪽꪾ꪿ꫀ꫁ꫂꫛꫜꫝ꫞꫟ꫠꫡꫢꫣꫤꫥꫦꫧꫨꫩꫪꫫꫬꫭꫮꫯ꫰꫱ꫲꫳꫴꫵ꫶ꬁꬂꬃꬄꬅꬆꬉꬊꬋꬌꬍꬎꬑꬒꬓꬔꬕꬖꬠꬡꬢꬣꬤꬥꬦꬨꬩꬪꬫꬬꬭꬮꯀꯁꯂꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌꯍꯎꯏꯐꯑꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜꯝꯞꯟꯠꯡꯢꯣꯤꯥꯦꯧꯨꯩꯪ꯫꯬꯭꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹가각갂갃간갅갆갇갈갉갊갋갌갍갎갏감갑값갓갔강갖갗갘같갚갛개객갞갟갠갡갢갣갤갥갦갧갨갩갪갫갬갭갮갯갰갱갲갳갴갵갶갷갸갹갺갻갼갽갾갿걀걁걂걃걄걅걆걇걈걉걊걋걌걍걎걏걐걑걒걓걔걕걖걗걘걙걚걛걜걝걞걟걠걡걢걣걤걥걦걧걨걩걪걫걬걭걮걯거걱걲걳건걵걶걷걸걹걺걻걼걽걾걿검겁겂것겄겅겆겇겈겉겊겋게겍겎겏겐겑겒겓겔겕겖겗겘겙겚겛겜겝겞겟겠겡겢겣겤겥겦겧겨격겪겫견겭겮겯결겱겲겳겴겵겶겷겸겹겺겻겼경겾겿곀곁곂곃계곅곆곇곈곉곊곋곌곍곎곏곐곑곒곓곔곕곖곗곘곙곚곛곜곝곞곟고곡곢곣곤곥곦곧골곩곪곫곬곭곮곯곰곱곲곳곴공곶곷곸곹곺곻과곽곾곿관괁괂괃괄괅괆괇괈괉괊괋괌괍괎괏괐광괒괓괔괕괖괗괘괙괚괛괜괝괞괟괠괡괢괣괤괥괦괧괨괩괪괫괬괭괮괯괰괱괲괳괴괵괶괷괸괹괺괻괼괽괾괿굀굁굂굃굄굅굆굇굈굉굊굋굌굍굎굏교굑굒굓굔굕굖굗굘굙굚굛굜굝굞굟굠굡굢굣굤굥굦굧굨굩굪굫구국굮굯군굱굲굳굴굵굶굷굸굹굺굻굼굽굾굿궀궁궂궃궄궅궆궇궈궉궊궋권궍궎궏궐궑궒궓궔궕궖궗궘궙궚궛궜궝궞궟궠궡궢궣궤궥궦궧궨궩궪궫궬궭궮궯궰궱궲궳궴궵궶궷궸궹궺궻궼궽궾궿귀귁귂귃귄귅귆귇귈귉귊귋귌귍귎귏귐귑귒귓귔귕귖귗귘귙귚귛규귝귞귟균귡귢귣귤귥귦귧귨귩귪귫귬귭귮귯귰귱귲귳귴귵귶귷그극귺귻근귽귾귿글긁긂긃긄긅긆긇금급긊긋긌긍긎긏긐긑긒긓긔긕긖긗긘긙긚긛긜긝긞긟긠긡긢긣긤긥긦긧긨긩긪긫긬긭긮긯기긱긲긳긴긵긶긷길긹긺긻긼긽긾긿김깁깂깃깄깅깆깇깈깉깊깋까깍깎깏깐깑깒깓깔깕깖깗깘깙깚깛깜깝깞깟깠깡깢깣깤깥깦깧깨깩깪깫깬깭깮깯깰깱깲깳깴깵깶깷깸깹깺깻깼깽깾깿꺀꺁꺂꺃꺄꺅꺆꺇꺈꺉꺊꺋꺌꺍꺎꺏꺐꺑꺒꺓꺔꺕꺖꺗꺘꺙꺚꺛꺜꺝꺞꺟꺠꺡꺢꺣꺤꺥꺦꺧꺨꺩꺪꺫꺬꺭꺮꺯꺰꺱꺲꺳꺴꺵꺶꺷꺸꺹꺺꺻꺼꺽꺾꺿껀껁껂껃껄껅껆껇껈껉껊껋껌껍껎껏껐껑껒껓껔껕껖껗께껙껚껛껜껝껞껟껠껡껢껣껤껥껦껧껨껩껪껫껬껭껮껯껰껱껲껳껴껵껶껷껸껹껺껻껼껽껾껿꼀꼁꼂꼃꼄꼅꼆꼇꼈꼉꼊꼋꼌꼍꼎꼏꼐꼑꼒꼓꼔꼕꼖꼗꼘꼙꼚꼛꼜꼝꼞꼟꼠꼡꼢꼣꼤꼥꼦꼧꼨꼩꼪꼫꼬꼭꼮꼯꼰꼱꼲꼳꼴꼵꼶꼷꼸꼹꼺꼻꼼꼽꼾꼿꽀꽁꽂꽃꽄꽅꽆꽇꽈꽉꽊꽋꽌꽍꽎꽏꽐꽑꽒꽓꽔꽕꽖꽗꽘꽙꽚꽛꽜꽝꽞꽟꽠꽡꽢꽣꽤꽥꽦꽧꽨꽩꽪꽫꽬꽭꽮꽯꽰꽱꽲꽳꽴꽵꽶꽷꽸꽹꽺꽻꽼꽽꽾꽿꾀꾁꾂꾃꾄꾅꾆꾇꾈꾉꾊꾋꾌꾍꾎꾏꾐꾑꾒꾓꾔꾕꾖꾗꾘꾙꾚꾛꾜꾝꾞꾟꾠꾡꾢꾣꾤꾥꾦꾧꾨꾩꾪꾫꾬꾭꾮꾯꾰꾱꾲꾳꾴꾵꾶꾷꾸꾹꾺꾻꾼꾽꾾꾿꿀꿁꿂꿃꿄꿅꿆꿇꿈꿉꿊꿋꿌꿍꿎꿏꿐꿑꿒꿓꿔꿕꿖꿗꿘꿙꿚꿛꿜꿝꿞꿟꿠꿡꿢꿣꿤꿥꿦꿧꿨꿩꿪꿫꿬꿭꿮꿯꿰꿱꿲꿳꿴꿵꿶꿷꿸꿹꿺꿻꿼꿽꿾꿿뀀뀁뀂뀃뀄뀅뀆뀇뀈뀉뀊뀋뀌뀍뀎뀏뀐뀑뀒뀓뀔뀕뀖뀗뀘뀙뀚뀛뀜뀝뀞뀟뀠뀡뀢뀣뀤뀥뀦뀧뀨뀩뀪뀫뀬뀭뀮뀯뀰뀱뀲뀳뀴뀵뀶뀷뀸뀹뀺뀻뀼뀽뀾뀿끀끁끂끃끄끅끆끇끈끉끊끋끌끍끎끏끐끑끒끓끔끕끖끗끘끙끚끛끜끝끞끟끠끡끢끣끤끥끦끧끨끩끪끫끬끭끮끯끰끱끲끳끴끵끶끷끸끹끺끻끼끽끾끿낀낁낂낃낄낅낆낇낈낉낊낋낌낍낎낏낐낑낒낓낔낕낖낗나낙낚낛난낝낞낟날낡낢낣낤낥낦낧남납낪낫났낭낮낯낰낱낲낳내낵낶낷낸낹낺낻낼낽낾낿냀냁냂냃냄냅냆냇냈냉냊냋냌냍냎냏냐냑냒냓냔냕냖냗냘냙냚냛냜냝냞냟냠냡냢냣냤냥냦냧냨냩냪냫냬냭냮냯냰냱냲냳냴냵냶냷냸냹냺냻냼냽냾냿넀넁넂넃넄넅넆넇너넉넊넋넌넍넎넏널넑넒넓넔넕넖넗넘넙넚넛넜넝넞넟넠넡넢넣네넥넦넧넨넩넪넫넬넭넮넯넰넱넲넳넴넵넶넷넸넹넺넻넼넽넾넿녀녁녂녃년녅녆녇녈녉녊녋녌녍녎녏념녑녒녓녔녕녖녗녘녙녚녛녜녝녞녟녠녡녢녣녤녥녦녧녨녩녪녫녬녭녮녯녰녱녲녳녴녵녶녷노녹녺녻논녽녾녿놀놁놂놃놄놅놆놇놈놉놊놋놌농놎놏놐놑높놓놔놕놖놗놘놙놚놛놜놝놞놟놠놡놢놣놤놥놦놧놨놩놪놫놬놭놮놯놰놱놲놳놴놵놶놷놸놹놺놻놼놽놾놿뇀뇁뇂뇃뇄뇅뇆뇇뇈뇉뇊뇋뇌뇍뇎뇏뇐뇑뇒뇓뇔뇕뇖뇗뇘뇙뇚뇛뇜뇝뇞뇟뇠뇡뇢뇣뇤뇥뇦뇧뇨뇩뇪뇫뇬뇭뇮뇯뇰뇱뇲뇳뇴뇵뇶뇷뇸뇹뇺뇻뇼뇽뇾뇿눀눁눂눃누눅눆눇눈눉눊눋눌눍눎눏눐눑눒눓눔눕눖눗눘눙눚눛눜눝눞눟눠눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿늀늁늂늃늄늅늆늇늈늉늊늋늌늍늎늏느늑늒늓는늕늖늗늘늙늚늛늜늝늞늟늠늡늢늣늤능늦늧늨늩늪늫늬늭늮늯늰늱늲늳늴늵늶늷늸늹늺늻늼늽늾늿닀닁닂닃닄닅닆닇니닉닊닋닌닍닎닏닐닑닒닓닔닕닖닗님닙닚닛닜닝닞닟닠닡닢닣다닥닦닧단닩닪닫달닭닮닯닰닱닲닳담답닶닷닸당닺닻닼닽닾닿대댁댂댃댄댅댆댇댈댉댊댋댌댍댎댏댐댑댒댓댔댕댖댗댘댙댚댛댜댝댞댟댠댡댢댣댤댥댦댧댨댩댪댫댬댭댮댯댰댱댲댳댴댵댶댷댸댹댺댻댼댽댾댿덀덁덂덃덄덅덆덇덈덉덊덋덌덍덎덏덐덑덒덓더덕덖덗던덙덚덛덜덝덞덟덠덡덢덣덤덥덦덧덨덩덪덫덬덭덮덯데덱덲덳덴덵덶덷델덹덺덻덼덽덾덿뎀뎁뎂뎃뎄뎅뎆뎇뎈뎉뎊뎋뎌뎍뎎뎏뎐뎑뎒뎓뎔뎕뎖뎗뎘뎙뎚뎛뎜뎝뎞뎟뎠뎡뎢뎣뎤뎥뎦뎧뎨뎩뎪뎫뎬뎭뎮뎯뎰뎱뎲뎳뎴뎵뎶뎷뎸뎹뎺뎻뎼뎽뎾뎿돀돁돂돃도독돆돇돈돉돊돋돌돍돎돏돐돑돒돓돔돕돖돗돘동돚돛돜돝돞돟돠돡돢돣돤돥돦돧돨돩돪돫돬돭돮돯돰돱돲돳돴돵돶돷돸돹돺돻돼돽돾돿됀됁됂됃됄됅됆됇됈됉됊됋됌됍됎됏됐됑됒됓됔됕됖됗되됙됚됛된됝됞됟될됡됢됣됤됥됦됧됨됩됪됫됬됭됮됯됰됱됲됳됴됵됶됷됸됹됺됻됼됽됾됿둀둁둂둃둄둅둆둇둈둉둊둋둌둍둎둏두둑둒둓둔둕둖둗둘둙둚둛둜둝둞둟둠둡둢둣둤둥둦둧둨둩둪둫둬둭둮둯둰둱둲둳둴둵둶둷둸둹둺둻둼둽둾둿뒀뒁뒂뒃뒄뒅뒆뒇뒈뒉뒊뒋뒌뒍뒎뒏뒐뒑뒒뒓뒔뒕뒖뒗뒘뒙뒚뒛뒜뒝뒞뒟뒠뒡뒢뒣뒤뒥뒦뒧뒨뒩뒪뒫뒬뒭뒮뒯뒰뒱뒲뒳뒴뒵뒶뒷뒸뒹뒺뒻뒼뒽뒾뒿듀듁듂듃듄듅듆듇듈듉듊듋듌듍듎듏듐듑듒듓듔듕듖듗듘듙듚듛드득듞듟든듡듢듣들듥듦듧듨듩듪듫듬듭듮듯듰등듲듳듴듵듶듷듸듹듺듻듼듽듾듿딀딁딂딃딄딅딆딇딈딉딊딋딌딍딎딏딐딑딒딓디딕딖딗딘딙딚딛딜딝딞딟딠딡딢딣딤딥딦딧딨딩딪딫딬딭딮딯따딱딲딳딴딵딶딷딸딹딺딻딼딽딾딿땀땁땂땃땄땅땆땇땈땉땊땋때땍땎땏땐땑땒땓땔땕땖땗땘땙땚땛땜땝땞땟땠땡땢땣땤땥땦땧땨땩땪땫땬땭땮땯땰땱땲땳땴땵땶땷땸땹땺땻땼땽땾땿떀떁떂떃떄떅떆떇떈떉떊떋떌떍떎떏떐떑떒떓떔떕떖떗떘떙떚떛떜떝떞떟떠떡떢떣떤떥떦떧떨떩떪떫떬떭떮떯떰떱떲떳떴떵떶떷떸떹떺떻떼떽떾떿뗀뗁뗂뗃뗄뗅뗆뗇뗈뗉뗊뗋뗌뗍뗎뗏뗐뗑뗒뗓뗔뗕뗖뗗뗘뗙뗚뗛뗜뗝뗞뗟뗠뗡뗢뗣뗤뗥뗦뗧뗨뗩뗪뗫뗬뗭뗮뗯뗰뗱뗲뗳뗴뗵뗶뗷뗸뗹뗺뗻뗼뗽뗾뗿똀똁똂똃똄똅똆똇똈똉똊똋똌똍똎똏또똑똒똓똔똕똖똗똘똙똚똛똜똝똞똟똠똡똢똣똤똥똦똧똨똩똪똫똬똭똮똯똰똱똲똳똴똵똶똷똸똹똺똻똼똽똾똿뙀뙁뙂뙃뙄뙅뙆뙇뙈뙉뙊뙋뙌뙍뙎뙏뙐뙑뙒뙓뙔뙕뙖뙗뙘뙙뙚뙛뙜뙝뙞뙟뙠뙡뙢뙣뙤뙥뙦뙧뙨뙩뙪뙫뙬뙭뙮뙯뙰뙱뙲뙳뙴뙵뙶뙷뙸뙹뙺뙻뙼뙽뙾뙿뚀뚁뚂뚃뚄뚅뚆뚇뚈뚉뚊뚋뚌뚍뚎뚏뚐뚑뚒뚓뚔뚕뚖뚗뚘뚙뚚뚛뚜뚝뚞뚟뚠뚡뚢뚣뚤뚥뚦뚧뚨뚩뚪뚫뚬뚭뚮뚯뚰뚱뚲뚳뚴뚵뚶뚷뚸뚹뚺뚻뚼뚽뚾뚿뛀뛁뛂뛃뛄뛅뛆뛇뛈뛉뛊뛋뛌뛍뛎뛏뛐뛑뛒뛓뛔뛕뛖뛗뛘뛙뛚뛛뛜뛝뛞뛟뛠뛡뛢뛣뛤뛥뛦뛧뛨뛩뛪뛫뛬뛭뛮뛯뛰뛱뛲뛳뛴뛵뛶뛷뛸뛹뛺뛻뛼뛽뛾뛿뜀뜁뜂뜃뜄뜅뜆뜇뜈뜉뜊뜋뜌뜍뜎뜏뜐뜑뜒뜓뜔뜕뜖뜗뜘뜙뜚뜛뜜뜝뜞뜟뜠뜡뜢뜣뜤뜥뜦뜧뜨뜩뜪뜫뜬뜭뜮뜯뜰뜱뜲뜳뜴뜵뜶뜷뜸뜹뜺뜻뜼뜽뜾뜿띀띁띂띃띄띅띆띇띈띉띊띋띌띍띎띏띐띑띒띓띔띕띖띗띘띙띚띛띜띝띞띟띠띡띢띣띤띥띦띧띨띩띪띫띬띭띮띯띰띱띲띳띴띵띶띷띸띹띺띻라락띾띿란랁랂랃랄랅랆랇랈랉랊랋람랍랎랏랐랑랒랓랔랕랖랗래랙랚랛랜랝랞랟랠랡랢랣랤랥랦랧램랩랪랫랬랭랮랯랰랱랲랳랴략랶랷랸랹랺랻랼랽랾랿럀럁럂럃럄럅럆럇럈량럊럋럌럍럎럏럐럑럒럓럔럕럖럗럘럙럚럛럜럝럞럟럠럡럢럣럤럥럦럧럨럩럪럫러럭럮럯런럱럲럳럴럵럶럷럸럹럺럻럼럽럾럿렀렁렂렃렄렅렆렇레렉렊렋렌렍렎렏렐렑렒렓렔렕렖렗렘렙렚렛렜렝렞렟렠렡렢렣려력렦렧련렩렪렫렬렭렮렯렰렱렲렳렴렵렶렷렸령렺렻렼렽렾렿례롁롂롃롄롅롆롇롈롉롊롋롌롍롎롏롐롑롒롓롔롕롖롗롘롙롚롛로록롞롟론롡롢롣롤롥롦롧롨롩롪롫롬롭롮롯롰롱롲롳롴롵롶롷롸롹롺롻롼롽롾롿뢀뢁뢂뢃뢄뢅뢆뢇뢈뢉뢊뢋뢌뢍뢎뢏뢐뢑뢒뢓뢔뢕뢖뢗뢘뢙뢚뢛뢜뢝뢞뢟뢠뢡뢢뢣뢤뢥뢦뢧뢨뢩뢪뢫뢬뢭뢮뢯뢰뢱뢲뢳뢴뢵뢶뢷뢸뢹뢺뢻뢼뢽뢾뢿룀룁룂룃룄룅룆룇룈룉룊룋료룍룎룏룐룑룒룓룔룕룖룗룘룙룚룛룜룝룞룟룠룡룢룣룤룥룦룧루룩룪룫룬룭룮룯룰룱룲룳룴룵룶룷룸룹룺룻룼룽룾룿뤀뤁뤂뤃뤄뤅뤆뤇뤈뤉뤊뤋뤌뤍뤎뤏뤐뤑뤒뤓뤔뤕뤖뤗뤘뤙뤚뤛뤜뤝뤞뤟뤠뤡뤢뤣뤤뤥뤦뤧뤨뤩뤪뤫뤬뤭뤮뤯뤰뤱뤲뤳뤴뤵뤶뤷뤸뤹뤺뤻뤼뤽뤾뤿륀륁륂륃륄륅륆륇륈륉륊륋륌륍륎륏륐륑륒륓륔륕륖륗류륙륚륛륜륝륞륟률륡륢륣륤륥륦륧륨륩륪륫륬륭륮륯륰륱륲륳르륵륶륷른륹륺륻를륽륾륿릀릁릂릃름릅릆릇릈릉릊릋릌릍릎릏릐릑릒릓릔릕릖릗릘릙릚릛릜릝릞릟릠릡릢릣릤릥릦릧릨릩릪릫리릭릮릯린릱릲릳릴릵릶릷릸릹릺릻림립릾릿맀링맂맃맄맅맆맇마막맊맋만맍많맏말맑맒맓맔맕맖맗맘맙맚맛맜망맞맟맠맡맢맣매맥맦맧맨맩맪맫맬맭맮맯맰맱맲맳맴맵맶맷맸맹맺맻맼맽맾맿먀먁먂먃먄먅먆먇먈먉먊먋먌먍먎먏먐먑먒먓먔먕먖먗먘먙먚먛먜먝먞먟먠먡먢먣먤먥먦먧먨먩먪먫먬먭먮먯먰먱먲먳먴먵먶먷머먹먺먻먼먽먾먿멀멁멂멃멄멅멆멇멈멉멊멋멌멍멎멏멐멑멒멓메멕멖멗멘멙멚멛멜멝멞멟멠멡멢멣멤멥멦멧멨멩멪멫멬멭멮멯며멱멲멳면멵멶멷멸멹멺멻멼멽멾멿몀몁몂몃몄명몆몇몈몉몊몋몌몍몎몏몐몑몒몓몔몕몖몗몘몙몚몛몜몝몞몟몠몡몢몣몤몥몦몧모목몪몫몬몭몮몯몰몱몲몳몴몵몶몷몸몹몺못몼몽몾몿뫀뫁뫂뫃뫄뫅뫆뫇뫈뫉뫊뫋뫌뫍뫎뫏뫐뫑뫒뫓뫔뫕뫖뫗뫘뫙뫚뫛뫜뫝뫞뫟뫠뫡뫢뫣뫤뫥뫦뫧뫨뫩뫪뫫뫬뫭뫮뫯뫰뫱뫲뫳뫴뫵뫶뫷뫸뫹뫺뫻뫼뫽뫾뫿묀묁묂묃묄묅묆묇묈묉묊묋묌묍묎묏묐묑묒묓묔묕묖묗묘묙묚묛묜묝묞묟묠묡묢묣묤묥묦묧묨묩묪묫묬묭묮묯묰묱묲묳무묵묶묷문묹묺묻물묽묾묿뭀뭁뭂뭃뭄뭅뭆뭇뭈뭉뭊뭋뭌뭍뭎뭏뭐뭑뭒뭓뭔뭕뭖뭗뭘뭙뭚뭛뭜뭝뭞뭟뭠뭡뭢뭣뭤뭥뭦뭧뭨뭩뭪뭫뭬뭭뭮뭯뭰뭱뭲뭳뭴뭵뭶뭷뭸뭹뭺뭻뭼뭽뭾뭿뮀뮁뮂뮃뮄뮅뮆뮇뮈뮉뮊뮋뮌뮍뮎뮏뮐뮑뮒뮓뮔뮕뮖뮗뮘뮙뮚뮛뮜뮝뮞뮟뮠뮡뮢뮣뮤뮥뮦뮧뮨뮩뮪뮫뮬뮭뮮뮯뮰뮱뮲뮳뮴뮵뮶뮷뮸뮹뮺뮻뮼뮽뮾뮿므믁믂믃믄믅믆믇믈믉믊믋믌믍믎믏믐믑믒믓믔믕믖믗믘믙믚믛믜믝믞믟믠믡믢믣믤믥믦믧믨믩믪믫믬믭믮믯믰믱믲믳믴믵믶믷미믹믺믻민믽믾믿밀밁밂밃밄밅밆밇밈밉밊밋밌밍밎및밐밑밒밓바박밖밗반밙밚받발밝밞밟밠밡밢밣밤밥밦밧밨방밪밫밬밭밮밯배백밲밳밴밵밶밷밸밹밺밻밼밽밾밿뱀뱁뱂뱃뱄뱅뱆뱇뱈뱉뱊뱋뱌뱍뱎뱏뱐뱑뱒뱓뱔뱕뱖뱗뱘뱙뱚뱛뱜뱝뱞뱟뱠뱡뱢뱣뱤뱥뱦뱧뱨뱩뱪뱫뱬뱭뱮뱯뱰뱱뱲뱳뱴뱵뱶뱷뱸뱹뱺뱻뱼뱽뱾뱿벀벁벂벃버벅벆벇번벉벊벋벌벍벎벏벐벑벒벓범법벖벗벘벙벚벛벜벝벞벟베벡벢벣벤벥벦벧벨벩벪벫벬벭벮벯벰벱벲벳벴벵벶벷벸벹벺벻벼벽벾벿변볁볂볃별볅볆볇볈볉볊볋볌볍볎볏볐병볒볓볔볕볖볗볘볙볚볛볜볝볞볟볠볡볢볣볤볥볦볧볨볩볪볫볬볭볮볯볰볱볲볳보복볶볷본볹볺볻볼볽볾볿봀봁봂봃봄봅봆봇봈봉봊봋봌봍봎봏봐봑봒봓봔봕봖봗봘봙봚봛봜봝봞봟봠봡봢봣봤봥봦봧봨봩봪봫봬봭봮봯봰봱봲봳봴봵봶봷봸봹봺봻봼봽봾봿뵀뵁뵂뵃뵄뵅뵆뵇뵈뵉뵊뵋뵌뵍뵎뵏뵐뵑뵒뵓뵔뵕뵖뵗뵘뵙뵚뵛뵜뵝뵞뵟뵠뵡뵢뵣뵤뵥뵦뵧뵨뵩뵪뵫뵬뵭뵮뵯뵰뵱뵲뵳뵴뵵뵶뵷뵸뵹뵺뵻뵼뵽뵾뵿부북붂붃분붅붆붇불붉붊붋붌붍붎붏붐붑붒붓붔붕붖붗붘붙붚붛붜붝붞붟붠붡붢붣붤붥붦붧붨붩붪붫붬붭붮붯붰붱붲붳붴붵붶붷붸붹붺붻붼붽붾붿뷀뷁뷂뷃뷄뷅뷆뷇뷈뷉뷊뷋뷌뷍뷎뷏뷐뷑뷒뷓뷔뷕뷖뷗뷘뷙뷚뷛뷜뷝뷞뷟뷠뷡뷢뷣뷤뷥뷦뷧뷨뷩뷪뷫뷬뷭뷮뷯뷰뷱뷲뷳뷴뷵뷶뷷뷸뷹뷺뷻뷼뷽뷾뷿븀븁븂븃븄븅븆븇븈븉븊븋브븍븎븏븐븑븒븓블븕븖븗븘븙븚븛븜븝븞븟븠븡븢븣븤븥븦븧븨븩븪븫븬븭븮븯븰븱븲븳븴븵븶븷븸븹븺븻븼븽븾븿빀빁빂빃비빅빆빇빈빉빊빋빌빍빎빏빐빑빒빓빔빕빖빗빘빙빚빛빜빝빞빟빠빡빢빣빤빥빦빧빨빩빪빫빬빭빮빯빰빱빲빳빴빵빶빷빸빹빺빻빼빽빾빿뺀뺁뺂뺃뺄뺅뺆뺇뺈뺉뺊뺋뺌뺍뺎뺏뺐뺑뺒뺓뺔뺕뺖뺗뺘뺙뺚뺛뺜뺝뺞뺟뺠뺡뺢뺣뺤뺥뺦뺧뺨뺩뺪뺫뺬뺭뺮뺯뺰뺱뺲뺳뺴뺵뺶뺷뺸뺹뺺뺻뺼뺽뺾뺿뻀뻁뻂뻃뻄뻅뻆뻇뻈뻉뻊뻋뻌뻍뻎뻏뻐뻑뻒뻓뻔뻕뻖뻗뻘뻙뻚뻛뻜뻝뻞뻟뻠뻡뻢뻣뻤뻥뻦뻧뻨뻩뻪뻫뻬뻭뻮뻯뻰뻱뻲뻳뻴뻵뻶뻷뻸뻹뻺뻻뻼뻽뻾뻿뼀뼁뼂뼃뼄뼅뼆뼇뼈뼉뼊뼋뼌뼍뼎뼏뼐뼑뼒뼓뼔뼕뼖뼗뼘뼙뼚뼛뼜뼝뼞뼟뼠뼡뼢뼣뼤뼥뼦뼧뼨뼩뼪뼫뼬뼭뼮뼯뼰뼱뼲뼳뼴뼵뼶뼷뼸뼹뼺뼻뼼뼽뼾뼿뽀뽁뽂뽃뽄뽅뽆뽇뽈뽉뽊뽋뽌뽍뽎뽏뽐뽑뽒뽓뽔뽕뽖뽗뽘뽙뽚뽛뽜뽝뽞뽟뽠뽡뽢뽣뽤뽥뽦뽧뽨뽩뽪뽫뽬뽭뽮뽯뽰뽱뽲뽳뽴뽵뽶뽷뽸뽹뽺뽻뽼뽽뽾뽿뾀뾁뾂뾃뾄뾅뾆뾇뾈뾉뾊뾋뾌뾍뾎뾏뾐뾑뾒뾓뾔뾕뾖뾗뾘뾙뾚뾛뾜뾝뾞뾟뾠뾡뾢뾣뾤뾥뾦뾧뾨뾩뾪뾫뾬뾭뾮뾯뾰뾱뾲뾳뾴뾵뾶뾷뾸뾹뾺뾻뾼뾽뾾뾿뿀뿁뿂뿃뿄뿅뿆뿇뿈뿉뿊뿋뿌뿍뿎뿏뿐뿑뿒뿓뿔뿕뿖뿗뿘뿙뿚뿛뿜뿝뿞뿟뿠뿡뿢뿣뿤뿥뿦뿧뿨뿩뿪뿫뿬뿭뿮뿯뿰뿱뿲뿳뿴뿵뿶뿷뿸뿹뿺뿻뿼뿽뿾뿿쀀쀁쀂쀃쀄쀅쀆쀇쀈쀉쀊쀋쀌쀍쀎쀏쀐쀑쀒쀓쀔쀕쀖쀗쀘쀙쀚쀛쀜쀝쀞쀟쀠쀡쀢쀣쀤쀥쀦쀧쀨쀩쀪쀫쀬쀭쀮쀯쀰쀱쀲쀳쀴쀵쀶쀷쀸쀹쀺쀻쀼쀽쀾쀿쁀쁁쁂쁃쁄쁅쁆쁇쁈쁉쁊쁋쁌쁍쁎쁏쁐쁑쁒쁓쁔쁕쁖쁗쁘쁙쁚쁛쁜쁝쁞쁟쁠쁡쁢쁣쁤쁥쁦쁧쁨쁩쁪쁫쁬쁭쁮쁯쁰쁱쁲쁳쁴쁵쁶쁷쁸쁹쁺쁻쁼쁽쁾쁿삀삁삂삃삄삅삆삇삈삉삊삋삌삍삎삏삐삑삒삓삔삕삖삗삘삙삚삛삜삝삞삟삠삡삢삣삤삥삦삧삨삩삪삫사삭삮삯산삱삲삳살삵삶삷삸삹삺삻삼삽삾삿샀상샂샃샄샅샆샇새색샊샋샌샍샎샏샐샑샒샓샔샕샖샗샘샙샚샛샜생샞샟샠샡샢샣샤샥샦샧샨샩샪샫샬샭샮샯샰샱샲샳샴샵샶샷샸샹샺샻샼샽샾샿섀섁섂섃섄섅섆섇섈섉섊섋섌섍섎섏섐섑섒섓섔섕섖섗섘섙섚섛서석섞섟선섡섢섣설섥섦섧섨섩섪섫섬섭섮섯섰성섲섳섴섵섶섷세섹섺섻센섽섾섿셀셁셂셃셄셅셆셇셈셉셊셋셌셍셎셏셐셑셒셓셔셕셖셗션셙셚셛셜셝셞셟셠셡셢셣셤셥셦셧셨셩셪셫셬셭셮셯셰셱셲셳셴셵셶셷셸셹셺셻셼셽셾셿솀솁솂솃솄솅솆솇솈솉솊솋소속솎솏손솑솒솓솔솕솖솗솘솙솚솛솜솝솞솟솠송솢솣솤솥솦솧솨솩솪솫솬솭솮솯솰솱솲솳솴솵솶솷솸솹솺솻솼솽솾솿쇀쇁쇂쇃쇄쇅쇆쇇쇈쇉쇊쇋쇌쇍쇎쇏쇐쇑쇒쇓쇔쇕쇖쇗쇘쇙쇚쇛쇜쇝쇞쇟쇠쇡쇢쇣쇤쇥쇦쇧쇨쇩쇪쇫쇬쇭쇮쇯쇰쇱쇲쇳쇴쇵쇶쇷쇸쇹쇺쇻쇼쇽쇾쇿숀숁숂숃숄숅숆숇숈숉숊숋숌숍숎숏숐숑숒숓숔숕숖숗수숙숚숛순숝숞숟술숡숢숣숤숥숦숧숨숩숪숫숬숭숮숯숰숱숲숳숴숵숶숷숸숹숺숻숼숽숾숿쉀쉁쉂쉃쉄쉅쉆쉇쉈쉉쉊쉋쉌쉍쉎쉏쉐쉑쉒쉓쉔쉕쉖쉗쉘쉙쉚쉛쉜쉝쉞쉟쉠쉡쉢쉣쉤쉥쉦쉧쉨쉩쉪쉫쉬쉭쉮쉯쉰쉱쉲쉳쉴쉵쉶쉷쉸쉹쉺쉻쉼쉽쉾쉿슀슁슂슃슄슅슆슇슈슉슊슋슌슍슎슏슐슑슒슓슔슕슖슗슘슙슚슛슜슝슞슟슠슡슢슣스슥슦슧슨슩슪슫슬슭슮슯슰슱슲슳슴습슶슷슸승슺슻슼슽슾슿싀싁싂싃싄싅싆싇싈싉싊싋싌싍싎싏싐싑싒싓싔싕싖싗싘싙싚싛시식싞싟신싡싢싣실싥싦싧싨싩싪싫심십싮싯싰싱싲싳싴싵싶싷싸싹싺싻싼싽싾싿쌀쌁쌂쌃쌄쌅쌆쌇쌈쌉쌊쌋쌌쌍쌎쌏쌐쌑쌒쌓쌔쌕쌖쌗쌘쌙쌚쌛쌜쌝쌞쌟쌠쌡쌢쌣쌤쌥쌦쌧쌨쌩쌪쌫쌬쌭쌮쌯쌰쌱쌲쌳쌴쌵쌶쌷쌸쌹쌺쌻쌼쌽쌾쌿썀썁썂썃썄썅썆썇썈썉썊썋썌썍썎썏썐썑썒썓썔썕썖썗썘썙썚썛썜썝썞썟썠썡썢썣썤썥썦썧써썩썪썫썬썭썮썯썰썱썲썳썴썵썶썷썸썹썺썻썼썽썾썿쎀쎁쎂쎃쎄쎅쎆쎇쎈쎉쎊쎋쎌쎍쎎쎏쎐쎑쎒쎓쎔쎕쎖쎗쎘쎙쎚쎛쎜쎝쎞쎟쎠쎡쎢쎣쎤쎥쎦쎧쎨쎩쎪쎫쎬쎭쎮쎯쎰쎱쎲쎳쎴쎵쎶쎷쎸쎹쎺쎻쎼쎽쎾쎿쏀쏁쏂쏃쏄쏅쏆쏇쏈쏉쏊쏋쏌쏍쏎쏏쏐쏑쏒쏓쏔쏕쏖쏗쏘쏙쏚쏛쏜쏝쏞쏟쏠쏡쏢쏣쏤쏥쏦쏧쏨쏩쏪쏫쏬쏭쏮쏯쏰쏱쏲쏳쏴쏵쏶쏷쏸쏹쏺쏻쏼쏽쏾쏿쐀쐁쐂쐃쐄쐅쐆쐇쐈쐉쐊쐋쐌쐍쐎쐏쐐쐑쐒쐓쐔쐕쐖쐗쐘쐙쐚쐛쐜쐝쐞쐟쐠쐡쐢쐣쐤쐥쐦쐧쐨쐩쐪쐫쐬쐭쐮쐯쐰쐱쐲쐳쐴쐵쐶쐷쐸쐹쐺쐻쐼쐽쐾쐿쑀쑁쑂쑃쑄쑅쑆쑇쑈쑉쑊쑋쑌쑍쑎쑏쑐쑑쑒쑓쑔쑕쑖쑗쑘쑙쑚쑛쑜쑝쑞쑟쑠쑡쑢쑣쑤쑥쑦쑧쑨쑩쑪쑫쑬쑭쑮쑯쑰쑱쑲쑳쑴쑵쑶쑷쑸쑹쑺쑻쑼쑽쑾쑿쒀쒁쒂쒃쒄쒅쒆쒇쒈쒉쒊쒋쒌쒍쒎쒏쒐쒑쒒쒓쒔쒕쒖쒗쒘쒙쒚쒛쒜쒝쒞쒟쒠쒡쒢쒣쒤쒥쒦쒧쒨쒩쒪쒫쒬쒭쒮쒯쒰쒱쒲쒳쒴쒵쒶쒷쒸쒹쒺쒻쒼쒽쒾쒿쓀쓁쓂쓃쓄쓅쓆쓇쓈쓉쓊쓋쓌쓍쓎쓏쓐쓑쓒쓓쓔쓕쓖쓗쓘쓙쓚쓛쓜쓝쓞쓟쓠쓡쓢쓣쓤쓥쓦쓧쓨쓩쓪쓫쓬쓭쓮쓯쓰쓱쓲쓳쓴쓵쓶쓷쓸쓹쓺쓻쓼쓽쓾쓿씀씁씂씃씄씅씆씇씈씉씊씋씌씍씎씏씐씑씒씓씔씕씖씗씘씙씚씛씜씝씞씟씠씡씢씣씤씥씦씧씨씩씪씫씬씭씮씯씰씱씲씳씴씵씶씷씸씹씺씻씼씽씾씿앀앁앂앃아악앆앇안앉않앋알앍앎앏앐앑앒앓암압앖앗았앙앚앛앜앝앞앟애액앢앣앤앥앦앧앨앩앪앫앬앭앮앯앰앱앲앳앴앵앶앷앸앹앺앻야약앾앿얀얁얂얃얄얅얆얇얈얉얊얋얌얍얎얏얐양얒얓얔얕얖얗얘얙얚얛얜얝얞얟얠얡얢얣얤얥얦얧얨얩얪얫얬얭얮얯얰얱얲얳어억얶얷언얹얺얻얼얽얾얿엀엁엂엃엄업없엇었엉엊엋엌엍엎엏에엑엒엓엔엕엖엗엘엙엚엛엜엝엞엟엠엡엢엣엤엥엦엧엨엩엪엫여역엮엯연엱엲엳열엵엶엷엸엹엺엻염엽엾엿였영옂옃옄옅옆옇예옉옊옋옌옍옎옏옐옑옒옓옔옕옖옗옘옙옚옛옜옝옞옟옠옡옢옣오옥옦옧온옩옪옫올옭옮옯옰옱옲옳옴옵옶옷옸옹옺옻옼옽옾옿와왁왂왃완왅왆왇왈왉왊왋왌왍왎왏왐왑왒왓왔왕왖왗왘왙왚왛왜왝왞왟왠왡왢왣왤왥왦왧왨왩왪왫왬왭왮왯왰왱왲왳왴왵왶왷외왹왺왻왼왽왾왿욀욁욂욃욄욅욆욇욈욉욊욋욌욍욎욏욐욑욒욓요욕욖욗욘욙욚욛욜욝욞욟욠욡욢욣욤욥욦욧욨용욪욫욬욭욮욯우욱욲욳운욵욶욷울욹욺욻욼욽욾욿움웁웂웃웄웅웆웇웈웉웊웋워웍웎웏원웑웒웓월웕웖웗웘웙웚웛웜웝웞웟웠웡웢웣웤웥웦웧웨웩웪웫웬웭웮웯웰웱웲웳웴웵웶웷웸웹웺웻웼웽웾웿윀윁윂윃위윅윆윇윈윉윊윋윌윍윎윏윐윑윒윓윔윕윖윗윘윙윚윛윜윝윞윟유육윢윣윤윥윦윧율윩윪윫윬윭윮윯윰윱윲윳윴융윶윷윸윹윺윻으윽윾윿은읁읂읃을읅읆읇읈읉읊읋음읍읎읏읐응읒읓읔읕읖읗의읙읚읛읜읝읞읟읠읡읢읣읤읥읦읧읨읩읪읫읬읭읮읯읰읱읲읳이익읶읷인읹읺읻일읽읾읿잀잁잂잃임입잆잇있잉잊잋잌잍잎잏자작잒잓잔잕잖잗잘잙잚잛잜잝잞잟잠잡잢잣잤장잦잧잨잩잪잫재잭잮잯잰잱잲잳잴잵잶잷잸잹잺잻잼잽잾잿쟀쟁쟂쟃쟄쟅쟆쟇쟈쟉쟊쟋쟌쟍쟎쟏쟐쟑쟒쟓쟔쟕쟖쟗쟘쟙쟚쟛쟜쟝쟞쟟쟠쟡쟢쟣쟤쟥쟦쟧쟨쟩쟪쟫쟬쟭쟮쟯쟰쟱쟲쟳쟴쟵쟶쟷쟸쟹쟺쟻쟼쟽쟾쟿저적젂젃전젅젆젇절젉젊젋젌젍젎젏점접젒젓젔정젖젗젘젙젚젛제젝젞젟젠젡젢젣젤젥젦젧젨젩젪젫젬젭젮젯젰젱젲젳젴젵젶젷져젹젺젻젼젽젾젿졀졁졂졃졄졅졆졇졈졉졊졋졌졍졎졏졐졑졒졓졔졕졖졗졘졙졚졛졜졝졞졟졠졡졢졣졤졥졦졧졨졩졪졫졬졭졮졯조족졲졳존졵졶졷졸졹졺졻졼졽졾졿좀좁좂좃좄종좆좇좈좉좊좋좌좍좎좏좐좑좒좓좔좕좖좗좘좙좚좛좜좝좞좟좠좡좢좣좤좥좦좧좨좩좪좫좬좭좮좯좰좱좲좳좴좵좶좷좸좹좺좻좼좽좾좿죀죁죂죃죄죅죆죇죈죉죊죋죌죍죎죏죐죑죒죓죔죕죖죗죘죙죚죛죜죝죞죟죠죡죢죣죤죥죦죧죨죩죪죫죬죭죮죯죰죱죲죳죴죵죶죷죸죹죺죻주죽죾죿준줁줂줃줄줅줆줇줈줉줊줋줌줍줎줏줐중줒줓줔줕줖줗줘줙줚줛줜줝줞줟줠줡줢줣줤줥줦줧줨줩줪줫줬줭줮줯줰줱줲줳줴줵줶줷줸줹줺줻줼줽줾줿쥀쥁쥂쥃쥄쥅쥆쥇쥈쥉쥊쥋쥌쥍쥎쥏쥐쥑쥒쥓쥔쥕쥖쥗쥘쥙쥚쥛쥜쥝쥞쥟쥠쥡쥢쥣쥤쥥쥦쥧쥨쥩쥪쥫쥬쥭쥮쥯쥰쥱쥲쥳쥴쥵쥶쥷쥸쥹쥺쥻쥼쥽쥾쥿즀즁즂즃즄즅즆즇즈즉즊즋즌즍즎즏즐즑즒즓즔즕즖즗즘즙즚즛즜증즞즟즠즡즢즣즤즥즦즧즨즩즪즫즬즭즮즯즰즱즲즳즴즵즶즷즸즹즺즻즼즽즾즿지직짂짃진짅짆짇질짉짊짋짌짍짎짏짐집짒짓짔징짖짗짘짙짚짛짜짝짞짟짠짡짢짣짤짥짦짧짨짩짪짫짬짭짮짯짰짱짲짳짴짵짶짷째짹짺짻짼짽짾짿쨀쨁쨂쨃쨄쨅쨆쨇쨈쨉쨊쨋쨌쨍쨎쨏쨐쨑쨒쨓쨔쨕쨖쨗쨘쨙쨚쨛쨜쨝쨞쨟쨠쨡쨢쨣쨤쨥쨦쨧쨨쨩쨪쨫쨬쨭쨮쨯쨰쨱쨲쨳쨴쨵쨶쨷쨸쨹쨺쨻쨼쨽쨾쨿쩀쩁쩂쩃쩄쩅쩆쩇쩈쩉쩊쩋쩌쩍쩎쩏쩐쩑쩒쩓쩔쩕쩖쩗쩘쩙쩚쩛쩜쩝쩞쩟쩠쩡쩢쩣쩤쩥쩦쩧쩨쩩쩪쩫쩬쩭쩮쩯쩰쩱쩲쩳쩴쩵쩶쩷쩸쩹쩺쩻쩼쩽쩾쩿쪀쪁쪂쪃쪄쪅쪆쪇쪈쪉쪊쪋쪌쪍쪎쪏쪐쪑쪒쪓쪔쪕쪖쪗쪘쪙쪚쪛쪜쪝쪞쪟쪠쪡쪢쪣쪤쪥쪦쪧쪨쪩쪪쪫쪬쪭쪮쪯쪰쪱쪲쪳쪴쪵쪶쪷쪸쪹쪺쪻쪼쪽쪾쪿쫀쫁쫂쫃쫄쫅쫆쫇쫈쫉쫊쫋쫌쫍쫎쫏쫐쫑쫒쫓쫔쫕쫖쫗쫘쫙쫚쫛쫜쫝쫞쫟쫠쫡쫢쫣쫤쫥쫦쫧쫨쫩쫪쫫쫬쫭쫮쫯쫰쫱쫲쫳쫴쫵쫶쫷쫸쫹쫺쫻쫼쫽쫾쫿쬀쬁쬂쬃쬄쬅쬆쬇쬈쬉쬊쬋쬌쬍쬎쬏쬐쬑쬒쬓쬔쬕쬖쬗쬘쬙쬚쬛쬜쬝쬞쬟쬠쬡쬢쬣쬤쬥쬦쬧쬨쬩쬪쬫쬬쬭쬮쬯쬰쬱쬲쬳쬴쬵쬶쬷쬸쬹쬺쬻쬼쬽쬾쬿쭀쭁쭂쭃쭄쭅쭆쭇쭈쭉쭊쭋쭌쭍쭎쭏쭐쭑쭒쭓쭔쭕쭖쭗쭘쭙쭚쭛쭜쭝쭞쭟쭠쭡쭢쭣쭤쭥쭦쭧쭨쭩쭪쭫쭬쭭쭮쭯쭰쭱쭲쭳쭴쭵쭶쭷쭸쭹쭺쭻쭼쭽쭾쭿쮀쮁쮂쮃쮄쮅쮆쮇쮈쮉쮊쮋쮌쮍쮎쮏쮐쮑쮒쮓쮔쮕쮖쮗쮘쮙쮚쮛쮜쮝쮞쮟쮠쮡쮢쮣쮤쮥쮦쮧쮨쮩쮪쮫쮬쮭쮮쮯쮰쮱쮲쮳쮴쮵쮶쮷쮸쮹쮺쮻쮼쮽쮾쮿쯀쯁쯂쯃쯄쯅쯆쯇쯈쯉쯊쯋쯌쯍쯎쯏쯐쯑쯒쯓쯔쯕쯖쯗쯘쯙쯚쯛쯜쯝쯞쯟쯠쯡쯢쯣쯤쯥쯦쯧쯨쯩쯪쯫쯬쯭쯮쯯쯰쯱쯲쯳쯴쯵쯶쯷쯸쯹쯺쯻쯼쯽쯾쯿찀찁찂찃찄찅찆찇찈찉찊찋찌찍찎찏찐찑찒찓찔찕찖찗찘찙찚찛찜찝찞찟찠찡찢찣찤찥찦찧차착찪찫찬찭찮찯찰찱찲찳찴찵찶찷참찹찺찻찼창찾찿챀챁챂챃채책챆챇챈챉챊챋챌챍챎챏챐챑챒챓챔챕챖챗챘챙챚챛챜챝챞챟챠챡챢챣챤챥챦챧챨챩챪챫챬챭챮챯챰챱챲챳챴챵챶챷챸챹챺챻챼챽챾챿첀첁첂첃첄첅첆첇첈첉첊첋첌첍첎첏첐첑첒첓첔첕첖첗처척첚첛천첝첞첟철첡첢첣첤첥첦첧첨첩첪첫첬청첮첯첰첱첲첳체첵첶첷첸첹첺첻첼첽첾첿쳀쳁쳂쳃쳄쳅쳆쳇쳈쳉쳊쳋쳌쳍쳎쳏쳐쳑쳒쳓쳔쳕쳖쳗쳘쳙쳚쳛쳜쳝쳞쳟쳠쳡쳢쳣쳤쳥쳦쳧쳨쳩쳪쳫쳬쳭쳮쳯쳰쳱쳲쳳쳴쳵쳶쳷쳸쳹쳺쳻쳼쳽쳾쳿촀촁촂촃촄촅촆촇초촉촊촋촌촍촎촏촐촑촒촓촔촕촖촗촘촙촚촛촜총촞촟촠촡촢촣촤촥촦촧촨촩촪촫촬촭촮촯촰촱촲촳촴촵촶촷촸촹촺촻촼촽촾촿쵀쵁쵂쵃쵄쵅쵆쵇쵈쵉쵊쵋쵌쵍쵎쵏쵐쵑쵒쵓쵔쵕쵖쵗쵘쵙쵚쵛최쵝쵞쵟쵠쵡쵢쵣쵤쵥쵦쵧쵨쵩쵪쵫쵬쵭쵮쵯쵰쵱쵲쵳쵴쵵쵶쵷쵸쵹쵺쵻쵼쵽쵾쵿춀춁춂춃춄춅춆춇춈춉춊춋춌춍춎춏춐춑춒춓추축춖춗춘춙춚춛출춝춞춟춠춡춢춣춤춥춦춧춨충춪춫춬춭춮춯춰춱춲춳춴춵춶춷춸춹춺춻춼춽춾춿췀췁췂췃췄췅췆췇췈췉췊췋췌췍췎췏췐췑췒췓췔췕췖췗췘췙췚췛췜췝췞췟췠췡췢췣췤췥췦췧취췩췪췫췬췭췮췯췰췱췲췳췴췵췶췷췸췹췺췻췼췽췾췿츀츁츂츃츄츅츆츇츈츉츊츋츌츍츎츏츐츑츒츓츔츕츖츗츘츙츚츛츜츝츞츟츠측츢츣츤츥츦츧츨츩츪츫츬츭츮츯츰츱츲츳츴층츶츷츸츹츺츻츼츽츾츿칀칁칂칃칄칅칆칇칈칉칊칋칌칍칎칏칐칑칒칓칔칕칖칗치칙칚칛친칝칞칟칠칡칢칣칤칥칦칧침칩칪칫칬칭칮칯칰칱칲칳카칵칶칷칸칹칺칻칼칽칾칿캀캁캂캃캄캅캆캇캈캉캊캋캌캍캎캏캐캑캒캓캔캕캖캗캘캙캚캛캜캝캞캟캠캡캢캣캤캥캦캧캨캩캪캫캬캭캮캯캰캱캲캳캴캵캶캷캸캹캺캻캼캽캾캿컀컁컂컃컄컅컆컇컈컉컊컋컌컍컎컏컐컑컒컓컔컕컖컗컘컙컚컛컜컝컞컟컠컡컢컣커컥컦컧컨컩컪컫컬컭컮컯컰컱컲컳컴컵컶컷컸컹컺컻컼컽컾컿케켁켂켃켄켅켆켇켈켉켊켋켌켍켎켏켐켑켒켓켔켕켖켗켘켙켚켛켜켝켞켟켠켡켢켣켤켥켦켧켨켩켪켫켬켭켮켯켰켱켲켳켴켵켶켷켸켹켺켻켼켽켾켿콀콁콂콃콄콅콆콇콈콉콊콋콌콍콎콏콐콑콒콓코콕콖콗콘콙콚콛콜콝콞콟콠콡콢콣콤콥콦콧콨콩콪콫콬콭콮콯콰콱콲콳콴콵콶콷콸콹콺콻콼콽콾콿쾀쾁쾂쾃쾄쾅쾆쾇쾈쾉쾊쾋쾌쾍쾎쾏쾐쾑쾒쾓쾔쾕쾖쾗쾘쾙쾚쾛쾜쾝쾞쾟쾠쾡쾢쾣쾤쾥쾦쾧쾨쾩쾪쾫쾬쾭쾮쾯쾰쾱쾲쾳쾴쾵쾶쾷쾸쾹쾺쾻쾼쾽쾾쾿쿀쿁쿂쿃쿄쿅쿆쿇쿈쿉쿊쿋쿌쿍쿎쿏쿐쿑쿒쿓쿔쿕쿖쿗쿘쿙쿚쿛쿜쿝쿞쿟쿠쿡쿢쿣쿤쿥쿦쿧쿨쿩쿪쿫쿬쿭쿮쿯쿰쿱쿲쿳쿴쿵쿶쿷쿸쿹쿺쿻쿼쿽쿾쿿퀀퀁퀂퀃퀄퀅퀆퀇퀈퀉퀊퀋퀌퀍퀎퀏퀐퀑퀒퀓퀔퀕퀖퀗퀘퀙퀚퀛퀜퀝퀞퀟퀠퀡퀢퀣퀤퀥퀦퀧퀨퀩퀪퀫퀬퀭퀮퀯퀰퀱퀲퀳퀴퀵퀶퀷퀸퀹퀺퀻퀼퀽퀾퀿큀큁큂큃큄큅큆큇큈큉큊큋큌큍큎큏큐큑큒큓큔큕큖큗큘큙큚큛큜큝큞큟큠큡큢큣큤큥큦큧큨큩큪큫크큭큮큯큰큱큲큳클큵큶큷큸큹큺큻큼큽큾큿킀킁킂킃킄킅킆킇킈킉킊킋킌킍킎킏킐킑킒킓킔킕킖킗킘킙킚킛킜킝킞킟킠킡킢킣키킥킦킧킨킩킪킫킬킭킮킯킰킱킲킳킴킵킶킷킸킹킺킻킼킽킾킿타탁탂탃탄탅탆탇탈탉탊탋탌탍탎탏탐탑탒탓탔탕탖탗탘탙탚탛태택탞탟탠탡탢탣탤탥탦탧탨탩탪탫탬탭탮탯탰탱탲탳탴탵탶탷탸탹탺탻탼탽탾탿턀턁턂턃턄턅턆턇턈턉턊턋턌턍턎턏턐턑턒턓턔턕턖턗턘턙턚턛턜턝턞턟턠턡턢턣턤턥턦턧턨턩턪턫턬턭턮턯터턱턲턳턴턵턶턷털턹턺턻턼턽턾턿텀텁텂텃텄텅텆텇텈텉텊텋테텍텎텏텐텑텒텓텔텕텖텗텘텙텚텛템텝텞텟텠텡텢텣텤텥텦텧텨텩텪텫텬텭텮텯텰텱텲텳텴텵텶텷텸텹텺텻텼텽텾텿톀톁톂톃톄톅톆톇톈톉톊톋톌톍톎톏톐톑톒톓톔톕톖톗톘톙톚톛톜톝톞톟토톡톢톣톤톥톦톧톨톩톪톫톬톭톮톯톰톱톲톳톴통톶톷톸톹톺톻톼톽톾톿퇀퇁퇂퇃퇄퇅퇆퇇퇈퇉퇊퇋퇌퇍퇎퇏퇐퇑퇒퇓퇔퇕퇖퇗퇘퇙퇚퇛퇜퇝퇞퇟퇠퇡퇢퇣퇤퇥퇦퇧퇨퇩퇪퇫퇬퇭퇮퇯퇰퇱퇲퇳퇴퇵퇶퇷퇸퇹퇺퇻퇼퇽퇾퇿툀툁툂툃툄툅툆툇툈툉툊툋툌툍툎툏툐툑툒툓툔툕툖툗툘툙툚툛툜툝툞툟툠툡툢툣툤툥툦툧툨툩툪툫투툭툮툯툰툱툲툳툴툵툶툷툸툹툺툻툼툽툾툿퉀퉁퉂퉃퉄퉅퉆퉇퉈퉉퉊퉋퉌퉍퉎퉏퉐퉑퉒퉓퉔퉕퉖퉗퉘퉙퉚퉛퉜퉝퉞퉟퉠퉡퉢퉣퉤퉥퉦퉧퉨퉩퉪퉫퉬퉭퉮퉯퉰퉱퉲퉳퉴퉵퉶퉷퉸퉹퉺퉻퉼퉽퉾퉿튀튁튂튃튄튅튆튇튈튉튊튋튌튍튎튏튐튑튒튓튔튕튖튗튘튙튚튛튜튝튞튟튠튡튢튣튤튥튦튧튨튩튪튫튬튭튮튯튰튱튲튳튴튵튶튷트특튺튻튼튽튾튿틀틁틂틃틄틅틆틇틈틉틊틋틌틍틎틏틐틑틒틓틔틕틖틗틘틙틚틛틜틝틞틟틠틡틢틣틤틥틦틧틨틩틪틫틬틭틮틯티틱틲틳틴틵틶틷틸틹틺틻틼틽틾틿팀팁팂팃팄팅팆팇팈팉팊팋파팍팎팏판팑팒팓팔팕팖팗팘팙팚팛팜팝팞팟팠팡팢팣팤팥팦팧패팩팪팫팬팭팮팯팰팱팲팳팴팵팶팷팸팹팺팻팼팽팾팿퍀퍁퍂퍃퍄퍅퍆퍇퍈퍉퍊퍋퍌퍍퍎퍏퍐퍑퍒퍓퍔퍕퍖퍗퍘퍙퍚퍛퍜퍝퍞퍟퍠퍡퍢퍣퍤퍥퍦퍧퍨퍩퍪퍫퍬퍭퍮퍯퍰퍱퍲퍳퍴퍵퍶퍷퍸퍹퍺퍻퍼퍽퍾퍿펀펁펂펃펄펅펆펇펈펉펊펋펌펍펎펏펐펑펒펓펔펕펖펗페펙펚펛펜펝펞펟펠펡펢펣펤펥펦펧펨펩펪펫펬펭펮펯펰펱펲펳펴펵펶펷편펹펺펻펼펽펾펿폀폁폂폃폄폅폆폇폈평폊폋폌폍폎폏폐폑폒폓폔폕폖폗폘폙폚폛폜폝폞폟폠폡폢폣폤폥폦폧폨폩폪폫포폭폮폯폰폱폲폳폴폵폶폷폸폹폺폻폼폽폾폿퐀퐁퐂퐃퐄퐅퐆퐇퐈퐉퐊퐋퐌퐍퐎퐏퐐퐑퐒퐓퐔퐕퐖퐗퐘퐙퐚퐛퐜퐝퐞퐟퐠퐡퐢퐣퐤퐥퐦퐧퐨퐩퐪퐫퐬퐭퐮퐯퐰퐱퐲퐳퐴퐵퐶퐷퐸퐹퐺퐻퐼퐽퐾퐿푀푁푂푃푄푅푆푇푈푉푊푋푌푍푎푏푐푑푒푓푔푕푖푗푘푙푚푛표푝푞푟푠푡푢푣푤푥푦푧푨푩푪푫푬푭푮푯푰푱푲푳푴푵푶푷푸푹푺푻푼푽푾푿풀풁풂풃풄풅풆풇품풉풊풋풌풍풎풏풐풑풒풓풔풕풖풗풘풙풚풛풜풝풞풟풠풡풢풣풤풥풦풧풨풩풪풫풬풭풮풯풰풱풲풳풴풵풶풷풸풹풺풻풼풽풾풿퓀퓁퓂퓃퓄퓅퓆퓇퓈퓉퓊퓋퓌퓍퓎퓏퓐퓑퓒퓓퓔퓕퓖퓗퓘퓙퓚퓛퓜퓝퓞퓟퓠퓡퓢퓣퓤퓥퓦퓧퓨퓩퓪퓫퓬퓭퓮퓯퓰퓱퓲퓳퓴퓵퓶퓷퓸퓹퓺퓻퓼퓽퓾퓿픀픁픂픃프픅픆픇픈픉픊픋플픍픎픏픐픑픒픓픔픕픖픗픘픙픚픛픜픝픞픟픠픡픢픣픤픥픦픧픨픩픪픫픬픭픮픯픰픱픲픳픴픵픶픷픸픹픺픻피픽픾픿핀핁핂핃필핅핆핇핈핉핊핋핌핍핎핏핐핑핒핓핔핕핖핗하학핚핛한핝핞핟할핡핢핣핤핥핦핧함합핪핫핬항핮핯핰핱핲핳해핵핶핷핸핹핺핻핼핽핾핿햀햁햂햃햄햅햆햇했행햊햋햌햍햎햏햐햑햒햓햔햕햖햗햘햙햚햛햜햝햞햟햠햡햢햣햤향햦햧햨햩햪햫햬햭햮햯햰햱햲햳햴햵햶햷햸햹햺햻햼햽햾햿헀헁헂헃헄헅헆헇허헉헊헋헌헍헎헏헐헑헒헓헔헕헖헗험헙헚헛헜헝헞헟헠헡헢헣헤헥헦헧헨헩헪헫헬헭헮헯헰헱헲헳헴헵헶헷헸헹헺헻헼헽헾헿혀혁혂혃현혅혆혇혈혉혊혋혌혍혎혏혐협혒혓혔형혖혗혘혙혚혛혜혝혞혟혠혡혢혣혤혥혦혧혨혩혪혫혬혭혮혯혰혱혲혳혴혵혶혷호혹혺혻혼혽혾혿홀홁홂홃홄홅홆홇홈홉홊홋홌홍홎홏홐홑홒홓화확홖홗환홙홚홛활홝홞홟홠홡홢홣홤홥홦홧홨황홪홫홬홭홮홯홰홱홲홳홴홵홶홷홸홹홺홻홼홽홾홿횀횁횂횃횄횅횆횇횈횉횊횋회획횎횏횐횑횒횓횔횕횖횗횘횙횚횛횜횝횞횟횠횡횢횣횤횥횦횧효횩횪횫횬횭횮횯횰횱횲횳횴횵횶횷횸횹횺횻횼횽횾횿훀훁훂훃후훅훆훇훈훉훊훋훌훍훎훏훐훑훒훓훔훕훖훗훘훙훚훛훜훝훞훟훠훡훢훣훤훥훦훧훨훩훪훫훬훭훮훯훰훱훲훳훴훵훶훷훸훹훺훻훼훽훾훿휀휁휂휃휄휅휆휇휈휉휊휋휌휍휎휏휐휑휒휓휔휕휖휗휘휙휚휛휜휝휞휟휠휡휢휣휤휥휦휧휨휩휪휫휬휭휮휯휰휱휲휳휴휵휶휷휸휹휺휻휼휽휾휿흀흁흂흃흄흅흆흇흈흉흊흋흌흍흎흏흐흑흒흓흔흕흖흗흘흙흚흛흜흝흞흟흠흡흢흣흤흥흦흧흨흩흪흫희흭흮흯흰흱흲흳흴흵흶흷흸흹흺흻흼흽흾흿힀힁힂힃힄힅힆힇히힉힊힋힌힍힎힏힐힑힒힓힔힕힖힗힘힙힚힛힜힝힞힟힠힡힢힣ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀퟁퟂퟃퟄퟅퟆퟋퟌퟍퟎퟏퟐퟑퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜퟝퟞퟟퟠퟡퟢퟣퟤퟥퟦퟧퟨퟩퟪퟫퟬퟭퟮퟯퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????􏰀???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????豈更車賈滑串句龜龜契金喇奈懶癩羅蘿螺裸邏樂洛烙珞落酪駱亂卵欄爛蘭鸞嵐濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄鹿論壟弄籠聾牢磊賂雷壘屢樓淚漏累縷陋勒肋凜凌稜綾菱陵讀拏樂諾丹寧怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵呂女廬旅濾礪閭驪麗黎力曆歷轢年憐戀撚漣煉璉秊練聯輦蓮連鍊列劣咽烈裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂燎療蓼遼龍暈阮劉杻柳流溜琉留硫紐類六戮陸倫崙淪輪律慄栗率隆利吏履易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺切度拓糖宅洞暴輻行降見廓兀嗀﨎﨏塚﨑晴﨓﨔凞猪益礼神祥福靖精羽﨟蘒﨡諸﨣﨤逸都﨧﨨﨩飯飼館鶴郞隷侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎懲敏既暑梅海渚漢煮爫琢碑社祉祈祐祖祝禍禎穀突節練縉繁署者臭艹艹著褐視謁謹賓贈辶逸難響頻恵𤋮舘並况全侀充冀勇勺喝啕喙嗢塚墳奄奔婢嬨廒廙彩徭惘慎愈憎慠懲戴揄搜摒敖晴朗望杖歹殺流滛滋漢瀞煮瞧爵犯猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹變贈輸遲醙鉶陼難靖韛響頋頻鬒龜𢡊𢡄𣏕㮝䀘䀹𥉉𥳐𧻓齃龎fffiflffifflſtstﬓﬔﬕﬖﬗיִﬞײַﬠﬡﬢﬣﬤﬥﬦﬧﬨ﬩שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּטּיּךּכּלּמּנּסּףּפּצּקּרּשּתּוֹבֿכֿפֿﭏﭐﭑﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜﭝﭞﭟﭠﭡﭢﭣﭤﭥﭦﭧﭨﭩﭪﭫﭬﭭﭮﭯﭰﭱﭲﭳﭴﭵﭶﭷﭸﭹﭺﭻﭼﭽﭾﭿﮀﮁﮂﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌﮍﮎﮏﮐﮑﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜﮝﮞﮟﮠﮡﮢﮣﮤﮥﮦﮧﮨﮩﮪﮫﮬﮭﮮﮯﮰﮱ﮲﮳﮴﮵﮶﮷﮸﮹﮺﮻﮼﮽﮾﮿﯀﯁ﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜﯝﯞﯟﯠﯡﯢﯣﯤﯥﯦﯧﯨﯩﯪﯫﯬﯭﯮﯯﯰﯱﯲﯳﯴﯵﯶﯷﯸﯹﯺﯻﯼﯽﯾﯿﰀﰁﰂﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌﰍﰎﰏﰐﰑﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜﰝﰞﰟﰠﰡﰢﰣﰤﰥﰦﰧﰨﰩﰪﰫﰬﰭﰮﰯﰰﰱﰲﰳﰴﰵﰶﰷﰸﰹﰺﰻﰼﰽﰾﰿﱀﱁﱂﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌﱍﱎﱏﱐﱑﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜﱝﱞﱟﱠﱡﱢﱣﱤﱥﱦﱧﱨﱩﱪﱫﱬﱭﱮﱯﱰﱱﱲﱳﱴﱵﱶﱷﱸﱹﱺﱻﱼﱽﱾﱿﲀﲁﲂﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌﲍﲎﲏﲐﲑﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜﲝﲞﲟﲠﲡﲢﲣﲤﲥﲦﲧﲨﲩﲪﲫﲬﲭﲮﲯﲰﲱﲲﲳﲴﲵﲶﲷﲸﲹﲺﲻﲼﲽﲾﲿﳀﳁﳂﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌﳍﳎﳏﳐﳑﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜﳝﳞﳟﳠﳡﳢﳣﳤﳥﳦﳧﳨﳩﳪﳫﳬﳭﳮﳯﳰﳱﳲﳳﳴﳵﳶﳷﳸﳹﳺﳻﳼﳽﳾﳿﴀﴁﴂﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌﴍﴎﴏﴐﴑﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜﴝﴞﴟﴠﴡﴢﴣﴤﴥﴦﴧﴨﴩﴪﴫﴬﴭﴮﴯﴰﴱﴲﴳﴴﴵﴶﴷﴸﴹﴺﴻﴼﴽ﴾﴿ﵐﵑﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜﵝﵞﵟﵠﵡﵢﵣﵤﵥﵦﵧﵨﵩﵪﵫﵬﵭﵮﵯﵰﵱﵲﵳﵴﵵﵶﵷﵸﵹﵺﵻﵼﵽﵾﵿﶀﶁﶂﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌﶍﶎﶏﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜﶝﶞﶟﶠﶡﶢﶣﶤﶥﶦﶧﶨﶩﶪﶫﶬﶭﶮﶯﶰﶱﶲﶳﶴﶵﶶﶷﶸﶹﶺﶻﶼﶽﶾﶿﷀﷁﷂﷃﷄﷅﷆﷇﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ﷼﷽︀︁︂︃︄︅︆︇︈︉︊︋︌︍︎️︐︑︒︓︔︕︖︗︘︙︠︡︢︣︤︥︦︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌﹍﹎﹏﹐﹑﹒﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹨﹩﹪﹫ﹰﹱﹲﹳﹴﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⦅⦆。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑ하ᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵ¢£¬ ̄¦¥₩│←↑→↓■○�‬‬‬