diff --git a/.github/workflows/jdbc_unit_tests.yml b/.github/workflows/jdbc_unit_tests.yml new file mode 100644 index 00000000000..a89d63169f2 --- /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_5 + pull_request: + branches: + - BABEL_1_X_DEV__PG_13_5 + +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: | + 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 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 mssql-tools unixodbc-dev + export PATH=/opt/mssql-tools/bin:$PATH + - 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 ~ + sudo chown -R runner: ~/postgres + ~/postgres/bin/initdb -D ~/postgres/data/ -E "UTF8" + ~/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 \ No newline at end of file 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..b1681806624 --- /dev/null +++ b/contrib/babelfishpg_common/Makefile @@ -0,0 +1,97 @@ +include Version.config + +EXTENSION = babelfishpg_common +EXTVERSION = $(BBFPGCMN_MAJOR_VERSION).$(BBFPGCMN_MINOR_VERSION).$(BBFPGCMN_MICRO_VERSION) + +# Note: +# Set PREV_EXTVERSION after release, i.e after release of 2.0.0, set PREV_EXTVERSION to 1.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)-$(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) + +UPGRADES = $(patsubst sql/upgrades/%.sql,sql/%.sql,$(wildcard sql/upgrades/*.sql)) + +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) + +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) + +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 $< $@ + + +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..a412d68e871 --- /dev/null +++ b/contrib/babelfishpg_common/Version.config @@ -0,0 +1,4 @@ +BBFPGCMN_MAJOR_VERSION=1 +BBFPGCMN_MINOR_VERSION=1 +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..7ae1a3eb720 --- /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.1.0' +module_pathname = '$libdir/babelfishpg_common' +relocatable = true +superuser = true +requires = '' diff --git a/contrib/babelfishpg_common/sql/babelfishpg_common--1.0.0.sql b/contrib/babelfishpg_common/sql/babelfishpg_common--1.0.0.sql new file mode 100644 index 00000000000..5273f6dd72b --- /dev/null +++ b/contrib/babelfishpg_common/sql/babelfishpg_common--1.0.0.sql @@ -0,0 +1,5794 @@ +-- 1 "sql/babelfishpg_common.in" +-- 1 "" +-- 1 "" +-- 1 "sql/babelfishpg_common.in" + + + + + +CREATE SCHEMA sys; +GRANT USAGE ON SCHEMA sys TO PUBLIC; + + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +-- 1 "sql/money/fixeddecimal--1.1.0_base_parallel.sql" 1 +------------------ +-- 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); +-- 13 "sql/babelfishpg_common.in" 2 +-- 1 "sql/money/fixeddecimal--parallelaggs.sql" 1 + +-- 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 +); +-- 14 "sql/babelfishpg_common.in" 2 +-- 1 "sql/money/fixeddecimal--brin.sql" 1 +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); +-- 15 "sql/babelfishpg_common.in" 2 +-- 1 "sql/bpchar.sql" 1 +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'; +-- 16 "sql/babelfishpg_common.in" 2 +-- 1 "sql/varchar.sql" 1 +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'; +-- 17 "sql/babelfishpg_common.in" 2 +-- 1 "sql/numerics.sql" 1 +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 = ^ +); +-- 18 "sql/babelfishpg_common.in" 2 +-- 1 "sql/strings.sql" 1 +CREATE DOMAIN sys.NTEXT AS TEXT; +CREATE DOMAIN sys.SYSNAME AS sys.VARCHAR(128); +-- 19 "sql/babelfishpg_common.in" 2 +-- 1 "sql/bit.sql" 1 +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 + - * / of 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); + + +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 +); + + +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; +-- 20 "sql/babelfishpg_common.in" 2 +-- 1 "sql/varbinary.sql" 1 +-- 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); +-- 21 "sql/babelfishpg_common.in" 2 +-- 1 "sql/binary.sql" 1 +-- 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); +-- 22 "sql/babelfishpg_common.in" 2 +-- 1 "sql/uniqueidentifier.sql" 1 +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; +-- 23 "sql/babelfishpg_common.in" 2 +-- 1 "sql/datetime.sql" 1 +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; +-- 24 "sql/babelfishpg_common.in" 2 +-- 1 "sql/datetime2.sql" 1 +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; +-- 25 "sql/babelfishpg_common.in" 2 +-- 1 "sql/smalldatetime.sql" 1 +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; +-- 26 "sql/babelfishpg_common.in" 2 +-- 1 "sql/datetimeoffset.sql" 1 +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; +-- 27 "sql/babelfishpg_common.in" 2 +-- 1 "sql/sqlvariant.sql" 1 +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); +-- 28 "sql/babelfishpg_common.in" 2 +-- 1 "sql/string_operators.sql" 1 +-- 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 + + + + 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; +-- 29 "sql/babelfishpg_common.in" 2 +-- 1 "sql/coerce.sql" 1 +-- 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; +-- 30 "sql/babelfishpg_common.in" 2 + +-- 1 "sql/utils.sql" 1 +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; +-- 32 "sql/babelfishpg_common.in" 2 + + + + + + +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/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..9e0802ee396 --- /dev/null +++ b/contrib/babelfishpg_common/sql/binary.sql @@ -0,0 +1,275 @@ +-- 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 OR REPLACE FUNCTION sys.binaryfloat8(sys.BBF_BINARY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_common', 'binaryfloat8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +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..876065da79f --- /dev/null +++ b/contrib/babelfishpg_common/sql/bpchar.sql @@ -0,0 +1,298 @@ +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; + +CREATE OR REPLACE FUNCTION sys.bpchar2int2(sys.BPCHAR) +RETURNS INT2 +AS 'babelfishpg_common', 'bpchar2int2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS INT2) +WITH FUNCTION sys.bpchar2int2(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2int4(sys.BPCHAR) +RETURNS INT4 +AS 'babelfishpg_common', 'bpchar2int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS INT4) +WITH FUNCTION sys.bpchar2int4(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2int8(sys.BPCHAR) +RETURNS INT8 +AS 'babelfishpg_common', 'bpchar2int8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS INT8) +WITH FUNCTION sys.bpchar2int8(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2float4(sys.BPCHAR) +RETURNS FLOAT4 +AS 'babelfishpg_common', 'bpchar2float4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS FLOAT4) +WITH FUNCTION sys.bpchar2float4(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2float8(sys.BPCHAR) +RETURNS FLOAT8 +AS 'babelfishpg_common', 'bpchar2float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS FLOAT8) +WITH FUNCTION sys.bpchar2float8(sys.BPCHAR) 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..c9d8bbf963e --- /dev/null +++ b/contrib/babelfishpg_common/sql/money/fixeddecimal--1.1.0_base_parallel.sql @@ -0,0 +1,1602 @@ +------------------ +-- 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. +-- + +CREATE OPERATOR FAMILY fixeddecimal_ops USING btree; +CREATE OPERATOR FAMILY fixeddecimal_ops USING hash; + +-- 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 FAMILY fixeddecimal_ops 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 FAMILY fixeddecimal_ops 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (FIXEDDECIMAL, NUMERIC); + +-- 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (NUMERIC, 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (FIXEDDECIMAL, INT8); + +-- 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (INT8, 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (FIXEDDECIMAL, INT4); + +-- 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (INT4, 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (FIXEDDECIMAL, INT2); + +-- 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 +); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + 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); + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (INT2, FIXEDDECIMAL); + +-- add combination of (int8/int4/int2) to fixeddecimal_ops to make it complete +ALTER OPERATOR FAMILY fixeddecimal_ops USING btree ADD + -- INT8 + OPERATOR 1 < (INT8, INT8), + OPERATOR 2 <= (INT8, INT8), + OPERATOR 3 = (INT8, INT8), + OPERATOR 4 >= (INT8, INT8), + OPERATOR 5 > (INT8, INT8), + FUNCTION 1 btint8cmp(INT8, INT8), + + OPERATOR 1 < (INT8, INT4), + OPERATOR 2 <= (INT8, INT4), + OPERATOR 3 = (INT8, INT4), + OPERATOR 4 >= (INT8, INT4), + OPERATOR 5 > (INT8, INT4), + FUNCTION 1 btint84cmp(INT8, INT4), + + OPERATOR 1 < (INT8, INT2), + OPERATOR 2 <= (INT8, INT2), + OPERATOR 3 = (INT8, INT2), + OPERATOR 4 >= (INT8, INT2), + OPERATOR 5 > (INT8, INT2), + FUNCTION 1 btint82cmp(INT8, INT2), + + -- INT4 + OPERATOR 1 < (INT4, INT8), + OPERATOR 2 <= (INT4, INT8), + OPERATOR 3 = (INT4, INT8), + OPERATOR 4 >= (INT4, INT8), + OPERATOR 5 > (INT4, INT8), + FUNCTION 1 btint48cmp(INT4, INT8), + + OPERATOR 1 < (INT4, INT4), + OPERATOR 2 <= (INT4, INT4), + OPERATOR 3 = (INT4, INT4), + OPERATOR 4 >= (INT4, INT4), + OPERATOR 5 > (INT4, INT4), + FUNCTION 1 btint4cmp(INT4, INT4), + + OPERATOR 1 < (INT4, INT2), + OPERATOR 2 <= (INT4, INT2), + OPERATOR 3 = (INT4, INT2), + OPERATOR 4 >= (INT4, INT2), + OPERATOR 5 > (INT4, INT2), + FUNCTION 1 btint42cmp(INT4, INT2), + + -- INT2 + OPERATOR 1 < (INT2, INT8), + OPERATOR 2 <= (INT2, INT8), + OPERATOR 3 = (INT2, INT8), + OPERATOR 4 >= (INT2, INT8), + OPERATOR 5 > (INT2, INT8), + FUNCTION 1 btint28cmp(INT2, INT8), + + OPERATOR 1 < (INT2, INT4), + OPERATOR 2 <= (INT2, INT4), + OPERATOR 3 = (INT2, INT4), + OPERATOR 4 >= (INT2, INT4), + OPERATOR 5 > (INT2, INT4), + FUNCTION 1 btint24cmp(INT2, INT4), + + OPERATOR 1 < (INT2, INT2), + OPERATOR 2 <= (INT2, INT2), + OPERATOR 3 = (INT2, INT2), + OPERATOR 4 >= (INT2, INT2), + OPERATOR 5 > (INT2, INT2), + FUNCTION 1 btint2cmp(INT2, INT2), + + -- numeric + OPERATOR 1 < (NUMERIC, NUMERIC), + OPERATOR 2 <= (NUMERIC, NUMERIC), + OPERATOR 3 = (NUMERIC, NUMERIC), + OPERATOR 4 >= (NUMERIC, NUMERIC), + OPERATOR 5 > (NUMERIC, NUMERIC), + FUNCTION 1 numeric_cmp(NUMERIC, NUMERIC); + + +ALTER OPERATOR FAMILY fixeddecimal_ops USING hash ADD + OPERATOR 1 = (INT8, INT8), + OPERATOR 1 = (INT8, INT4), + OPERATOR 1 = (INT8, INT2), + OPERATOR 1 = (INT4, INT8), + OPERATOR 1 = (INT4, INT4), + OPERATOR 1 = (INT4, INT2), + OPERATOR 1 = (INT2, INT8), + OPERATOR 1 = (INT2, INT4), + OPERATOR 1 = (INT2, INT2), + OPERATOR 1 = (NUMERIC, NUMERIC), + FUNCTION 1 hashint8(INT8), + FUNCTION 1 hashint4(INT4), + FUNCTION 1 hashint2(INT2), + FUNCTION 1 hash_numeric(NUMERIC); + +-- +-- 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/upgrades/Release.md b/contrib/babelfishpg_common/sql/upgrades/Release.md new file mode 100644 index 00000000000..13710c9d2b4 --- /dev/null +++ b/contrib/babelfishpg_common/sql/upgrades/Release.md @@ -0,0 +1 @@ +Upgrade script files, i.e babelfishpg_common--1.0.0--1.1.0.sql, etc diff --git a/contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.0.0--1.1.0.sql b/contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.0.0--1.1.0.sql new file mode 100644 index 00000000000..0e23b3124cd --- /dev/null +++ b/contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.0.0--1.1.0.sql @@ -0,0 +1,338 @@ +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION ""babelfishpg_common"" UPDATE TO '1.1.0'" to load this file. \quit + +DROP OPERATOR FAMILY IF EXISTS sys.fixeddecimal_ops USING btree; +DROP OPERATOR FAMILY IF EXISTS sys.fixeddecimal_ops USING hash; + +CREATE OPERATOR FAMILY sys.fixeddecimal_ops USING btree; +CREATE OPERATOR FAMILY sys.fixeddecimal_ops USING hash; + +-- drop fixeddecimal_ops and re-create it in operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_ops USING hash; + +CREATE OPERATOR CLASS sys.fixeddecimal_ops +DEFAULT FOR TYPE sys.FIXEDDECIMAL USING btree FAMILY sys.fixeddecimal_ops AS + OPERATOR 1 sys.< (sys.FIXEDDECIMAL, sys.FIXEDDECIMAL), + OPERATOR 2 sys.<= (sys.FIXEDDECIMAL, sys.FIXEDDECIMAL), + OPERATOR 3 sys.= (sys.FIXEDDECIMAL, sys.FIXEDDECIMAL), + OPERATOR 4 sys.>= (sys.FIXEDDECIMAL, sys.FIXEDDECIMAL), + OPERATOR 5 sys.> (sys.FIXEDDECIMAL, sys.FIXEDDECIMAL), + FUNCTION 1 sys.fixeddecimal_cmp(sys.FIXEDDECIMAL, sys.FIXEDDECIMAL); + +CREATE OPERATOR CLASS sys.fixeddecimal_ops +DEFAULT FOR TYPE sys.FIXEDDECIMAL USING hash FAMILY sys.fixeddecimal_ops AS + OPERATOR 1 sys.= (sys.FIXEDDECIMAL, sys.FIXEDDECIMAL), + FUNCTION 1 sys.fixeddecimal_hash(sys.FIXEDDECIMAL); + + +-- drop fixeddecimal_numeric_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_numeric_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_numeric_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (sys.FIXEDDECIMAL, NUMERIC), + OPERATOR 2 sys.<= (sys.FIXEDDECIMAL, NUMERIC), + OPERATOR 3 sys.= (sys.FIXEDDECIMAL, NUMERIC), + OPERATOR 4 sys.>= (sys.FIXEDDECIMAL, NUMERIC), + OPERATOR 5 sys.> (sys.FIXEDDECIMAL, NUMERIC), + FUNCTION 1 sys.fixeddecimal_numeric_cmp(sys.FIXEDDECIMAL, NUMERIC); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (sys.FIXEDDECIMAL, NUMERIC); + + +-- drop numeric_fixeddecimal_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.numeric_fixeddecimal_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.numeric_fixeddecimal_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (NUMERIC, sys.FIXEDDECIMAL) FOR SEARCH, + OPERATOR 2 sys.<= (NUMERIC, sys.FIXEDDECIMAL) FOR SEARCH, + OPERATOR 3 sys.= (NUMERIC, sys.FIXEDDECIMAL) FOR SEARCH, + OPERATOR 4 sys.>= (NUMERIC, sys.FIXEDDECIMAL) FOR SEARCH, + OPERATOR 5 sys.> (NUMERIC, sys.FIXEDDECIMAL) FOR SEARCH, + FUNCTION 1 sys.numeric_fixeddecimal_cmp(NUMERIC, sys.FIXEDDECIMAL); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (NUMERIC, sys.FIXEDDECIMAL); + + +-- drop fixeddecimal_int8_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_int8_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_int8_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (sys.FIXEDDECIMAL, INT8), + OPERATOR 2 sys.<= (sys.FIXEDDECIMAL, INT8), + OPERATOR 3 sys.= (sys.FIXEDDECIMAL, INT8), + OPERATOR 4 sys.>= (sys.FIXEDDECIMAL, INT8), + OPERATOR 5 sys.> (sys.FIXEDDECIMAL, INT8), + FUNCTION 1 sys.fixeddecimal_int8_cmp(sys.FIXEDDECIMAL, INT8); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (sys.FIXEDDECIMAL, INT8); + + +-- drop int8_fixeddecimal_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.int8_fixeddecimal_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.int8_fixeddecimal_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (INT8, sys.FIXEDDECIMAL), + OPERATOR 2 sys.<= (INT8, sys.FIXEDDECIMAL), + OPERATOR 3 sys.= (INT8, sys.FIXEDDECIMAL), + OPERATOR 4 sys.>= (INT8, sys.FIXEDDECIMAL), + OPERATOR 5 sys.> (INT8, sys.FIXEDDECIMAL), + FUNCTION 1 sys.int8_fixeddecimal_cmp(INT8, sys.FIXEDDECIMAL); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (INT8, sys.FIXEDDECIMAL); + + +-- drop fixeddecimal_int4_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_int4_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_int4_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (sys.FIXEDDECIMAL, INT4), + OPERATOR 2 sys.<= (sys.FIXEDDECIMAL, INT4), + OPERATOR 3 sys.= (sys.FIXEDDECIMAL, INT4), + OPERATOR 4 sys.>= (sys.FIXEDDECIMAL, INT4), + OPERATOR 5 sys.> (sys.FIXEDDECIMAL, INT4), + FUNCTION 1 sys.fixeddecimal_int4_cmp(sys.FIXEDDECIMAL, INT4); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (sys.FIXEDDECIMAL, INT4); + + +-- drop int4_fixeddecimal_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.int4_fixeddecimal_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.int4_fixeddecimal_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (INT4, sys.FIXEDDECIMAL), + OPERATOR 2 sys.<= (INT4, sys.FIXEDDECIMAL), + OPERATOR 3 sys.= (INT4, sys.FIXEDDECIMAL), + OPERATOR 4 sys.>= (INT4, sys.FIXEDDECIMAL), + OPERATOR 5 sys.> (INT4, sys.FIXEDDECIMAL), + FUNCTION 1 sys.int4_fixeddecimal_cmp(INT4, sys.FIXEDDECIMAL); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (INT4, sys.FIXEDDECIMAL); + + +-- drop fixeddecimal_int2_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_int2_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.fixeddecimal_int2_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (sys.FIXEDDECIMAL, INT2), + OPERATOR 2 sys.<= (sys.FIXEDDECIMAL, INT2), + OPERATOR 3 sys.= (sys.FIXEDDECIMAL, INT2), + OPERATOR 4 sys.>= (sys.FIXEDDECIMAL, INT2), + OPERATOR 5 sys.> (sys.FIXEDDECIMAL, INT2), + FUNCTION 1 sys.fixeddecimal_int2_cmp(sys.FIXEDDECIMAL, INT2); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (sys.FIXEDDECIMAL, INT2); + + +-- drop int2_fixeddecimal_ops and add corresponding operators to operator family fixeddecimal_ops +DROP OPERATOR CLASS IF EXISTS sys.int2_fixeddecimal_ops USING btree; +DROP OPERATOR CLASS IF EXISTS sys.int2_fixeddecimal_ops USING hash; + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + OPERATOR 1 sys.< (INT2, sys.FIXEDDECIMAL), + OPERATOR 2 sys.<= (INT2, sys.FIXEDDECIMAL), + OPERATOR 3 sys.= (INT2, sys.FIXEDDECIMAL), + OPERATOR 4 sys.>= (INT2, sys.FIXEDDECIMAL), + OPERATOR 5 sys.> (INT2, sys.FIXEDDECIMAL), + FUNCTION 1 sys.int2_fixeddecimal_cmp(INT2, sys.FIXEDDECIMAL); + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 sys.= (INT2, sys.FIXEDDECIMAL); + + +-- add combination of (int8/int4/int2/numeric) to fixeddecimal_ops to make it complete +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING btree ADD + -- INT8 + OPERATOR 1 < (INT8, INT8), + OPERATOR 2 <= (INT8, INT8), + OPERATOR 3 = (INT8, INT8), + OPERATOR 4 >= (INT8, INT8), + OPERATOR 5 > (INT8, INT8), + FUNCTION 1 btint8cmp(INT8, INT8), + + OPERATOR 1 < (INT8, INT4), + OPERATOR 2 <= (INT8, INT4), + OPERATOR 3 = (INT8, INT4), + OPERATOR 4 >= (INT8, INT4), + OPERATOR 5 > (INT8, INT4), + FUNCTION 1 btint84cmp(INT8, INT4), + + OPERATOR 1 < (INT8, INT2), + OPERATOR 2 <= (INT8, INT2), + OPERATOR 3 = (INT8, INT2), + OPERATOR 4 >= (INT8, INT2), + OPERATOR 5 > (INT8, INT2), + FUNCTION 1 btint82cmp(INT8, INT2), + + -- INT4 + OPERATOR 1 < (INT4, INT8), + OPERATOR 2 <= (INT4, INT8), + OPERATOR 3 = (INT4, INT8), + OPERATOR 4 >= (INT4, INT8), + OPERATOR 5 > (INT4, INT8), + FUNCTION 1 btint48cmp(INT4, INT8), + + OPERATOR 1 < (INT4, INT4), + OPERATOR 2 <= (INT4, INT4), + OPERATOR 3 = (INT4, INT4), + OPERATOR 4 >= (INT4, INT4), + OPERATOR 5 > (INT4, INT4), + FUNCTION 1 btint4cmp(INT4, INT4), + + OPERATOR 1 < (INT4, INT2), + OPERATOR 2 <= (INT4, INT2), + OPERATOR 3 = (INT4, INT2), + OPERATOR 4 >= (INT4, INT2), + OPERATOR 5 > (INT4, INT2), + FUNCTION 1 btint42cmp(INT4, INT2), + + -- INT2 + OPERATOR 1 < (INT2, INT8), + OPERATOR 2 <= (INT2, INT8), + OPERATOR 3 = (INT2, INT8), + OPERATOR 4 >= (INT2, INT8), + OPERATOR 5 > (INT2, INT8), + FUNCTION 1 btint28cmp(INT2, INT8), + + OPERATOR 1 < (INT2, INT4), + OPERATOR 2 <= (INT2, INT4), + OPERATOR 3 = (INT2, INT4), + OPERATOR 4 >= (INT2, INT4), + OPERATOR 5 > (INT2, INT4), + FUNCTION 1 btint24cmp(INT2, INT4), + + OPERATOR 1 < (INT2, INT2), + OPERATOR 2 <= (INT2, INT2), + OPERATOR 3 = (INT2, INT2), + OPERATOR 4 >= (INT2, INT2), + OPERATOR 5 > (INT2, INT2), + FUNCTION 1 btint2cmp(INT2, INT2), + + -- numeric + OPERATOR 1 < (NUMERIC, NUMERIC), + OPERATOR 2 <= (NUMERIC, NUMERIC), + OPERATOR 3 = (NUMERIC, NUMERIC), + OPERATOR 4 >= (NUMERIC, NUMERIC), + OPERATOR 5 > (NUMERIC, NUMERIC), + FUNCTION 1 numeric_cmp(NUMERIC, NUMERIC); + + +ALTER OPERATOR FAMILY sys.fixeddecimal_ops USING hash ADD + OPERATOR 1 = (INT8, INT8), + OPERATOR 1 = (INT8, INT4), + OPERATOR 1 = (INT8, INT2), + OPERATOR 1 = (INT4, INT8), + OPERATOR 1 = (INT4, INT4), + OPERATOR 1 = (INT4, INT2), + OPERATOR 1 = (INT2, INT8), + OPERATOR 1 = (INT2, INT4), + OPERATOR 1 = (INT2, INT2), + OPERATOR 1 = (NUMERIC, NUMERIC), + FUNCTION 1 hashint8(INT8), + FUNCTION 1 hashint4(INT4), + FUNCTION 1 hashint2(INT2), + FUNCTION 1 hash_numeric(NUMERIC); + + +-- Any casting from (var)binary to float4 and float8 is not allowed. drop CAST +DROP CAST IF EXISTS (sys.BBF_BINARY as REAL); +DROP CAST IF EXISTS (sys.BBF_BINARY as DOUBLE PRECISION); +DROP CAST IF EXISTS (sys.BBF_VARBINARY as REAL); +DROP CAST IF EXISTS (sys.BBF_VARBINARY as DOUBLE PRECISION); + +CREATE CAST (sys.VARCHAR as sys.BPCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2int2(sys.BPCHAR) +RETURNS INT2 +AS 'babelfishpg_common', 'bpchar2int2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS INT2) +WITH FUNCTION sys.bpchar2int2(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2int4(sys.BPCHAR) +RETURNS INT4 +AS 'babelfishpg_common', 'bpchar2int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS INT4) +WITH FUNCTION sys.bpchar2int4(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2int8(sys.BPCHAR) +RETURNS INT8 +AS 'babelfishpg_common', 'bpchar2int8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS INT8) +WITH FUNCTION sys.bpchar2int8(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2float4(sys.BPCHAR) +RETURNS FLOAT4 +AS 'babelfishpg_common', 'bpchar2float4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS FLOAT4) +WITH FUNCTION sys.bpchar2float4(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.bpchar2float8(sys.BPCHAR) +RETURNS FLOAT8 +AS 'babelfishpg_common', 'bpchar2float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS FLOAT8) +WITH FUNCTION sys.bpchar2float8(sys.BPCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2int2(sys.VARCHAR) +RETURNS INT2 +AS 'babelfishpg_common', 'varchar2int2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS INT2) +WITH FUNCTION sys.varchar2int2(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2int4(sys.VARCHAR) +RETURNS INT4 +AS 'babelfishpg_common', 'varchar2int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS INT4) +WITH FUNCTION sys.varchar2int4(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2int8(sys.VARCHAR) +RETURNS INT8 +AS 'babelfishpg_common', 'varchar2int8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS INT8) +WITH FUNCTION sys.varchar2int8(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2float4(sys.VARCHAR) +RETURNS FLOAT4 +AS 'babelfishpg_common', 'varchar2float4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS FLOAT4) +WITH FUNCTION sys.varchar2float4(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2float8(sys.VARCHAR) +RETURNS FLOAT8 +AS 'babelfishpg_common', 'varchar2float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS FLOAT8) +WITH FUNCTION sys.varchar2float8(sys.VARCHAR) AS IMPLICIT; \ No newline at end of file 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..bdf6862ba44 --- /dev/null +++ b/contrib/babelfishpg_common/sql/varbinary.sql @@ -0,0 +1,278 @@ +-- 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 OR REPLACE FUNCTION sys.varbinaryfloat8(sys.BBF_VARBINARY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_common', 'varbinaryfloat8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +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..3f767379e42 --- /dev/null +++ b/contrib/babelfishpg_common/sql/varchar.sql @@ -0,0 +1,243 @@ +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; + +CREATE CAST (sys.VARCHAR as sys.BPCHAR) +WITHOUT FUNCTION AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2int2(sys.VARCHAR) +RETURNS INT2 +AS 'babelfishpg_common', 'varchar2int2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS INT2) +WITH FUNCTION sys.varchar2int2(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2int4(sys.VARCHAR) +RETURNS INT4 +AS 'babelfishpg_common', 'varchar2int4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS INT4) +WITH FUNCTION sys.varchar2int4(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2int8(sys.VARCHAR) +RETURNS INT8 +AS 'babelfishpg_common', 'varchar2int8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS INT8) +WITH FUNCTION sys.varchar2int8(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2float4(sys.VARCHAR) +RETURNS FLOAT4 +AS 'babelfishpg_common', 'varchar2float4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS FLOAT4) +WITH FUNCTION sys.varchar2float4(sys.VARCHAR) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.varchar2float8(sys.VARCHAR) +RETURNS FLOAT8 +AS 'babelfishpg_common', 'varchar2float8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS FLOAT8) +WITH FUNCTION sys.varchar2float8(sys.VARCHAR) 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..b1859e8a5ff --- /dev/null +++ b/contrib/babelfishpg_common/src/typecode.c @@ -0,0 +1,285 @@ +#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; +} + +Oid tsql_bpchar_oid = InvalidOid; +Oid tsql_nchar_oid = InvalidOid; +Oid tsql_varchar_oid = InvalidOid; +Oid tsql_nvarchar_oid = InvalidOid; +Oid tsql_ntext_oid = InvalidOid; +Oid tsql_image_oid = InvalidOid; +Oid tsql_binary_oid = InvalidOid; +Oid tsql_varbinary_oid = InvalidOid; + +Oid +lookup_tsql_datatype_oid(const char *typename) +{ + Oid nspoid; + Oid typoid; + + nspoid = get_namespace_oid("sys", true); + if (nspoid == InvalidOid) + return InvalidOid; + + typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid, CStringGetDatum(typename), ObjectIdGetDatum(nspoid)); + return typoid; +} + +bool +is_tsql_bpchar_datatype(Oid oid) +{ + if (tsql_bpchar_oid == InvalidOid) + tsql_bpchar_oid = lookup_tsql_datatype_oid("bpchar"); + return tsql_bpchar_oid == oid; +} + +bool +is_tsql_nchar_datatype(Oid oid) +{ + if (tsql_nchar_oid == InvalidOid) + tsql_nchar_oid = lookup_tsql_datatype_oid("nchar"); + return tsql_nchar_oid == oid; +} + +bool +is_tsql_varchar_datatype(Oid oid) +{ + if (tsql_varchar_oid == InvalidOid) + tsql_varchar_oid = lookup_tsql_datatype_oid("varchar"); + return tsql_varchar_oid == oid; +} + +bool +is_tsql_nvarchar_datatype(Oid oid) +{ + if (tsql_nvarchar_oid == InvalidOid) + tsql_nvarchar_oid = lookup_tsql_datatype_oid("nvarchar"); + return tsql_nvarchar_oid == oid; +} + +bool +is_tsql_text_datatype(Oid oid) +{ + return TEXTOID == oid; +} + +bool +is_tsql_ntext_datatype(Oid oid) +{ + if (tsql_ntext_oid == InvalidOid) + tsql_ntext_oid = lookup_tsql_datatype_oid("ntext"); + return tsql_ntext_oid == oid; +} + +bool +is_tsql_image_datatype(Oid oid) +{ + if (tsql_image_oid == InvalidOid) + tsql_image_oid = lookup_tsql_datatype_oid("image"); + return tsql_image_oid == oid; +} + +bool +is_tsql_binary_datatype(Oid oid) +{ + if (tsql_binary_oid == InvalidOid) + tsql_binary_oid = lookup_tsql_datatype_oid("bbf_binary"); + return tsql_binary_oid == oid; +} + +bool +is_tsql_varbinary_datatype(Oid oid) +{ + if (tsql_varbinary_oid == InvalidOid) + tsql_varbinary_oid = lookup_tsql_datatype_oid("bbf_varbinary"); + return tsql_varbinary_oid == oid; +} diff --git a/contrib/babelfishpg_common/src/typecode.h b/contrib/babelfishpg_common/src/typecode.h new file mode 100644 index 00000000000..01dc727f976 --- /dev/null +++ b/contrib/babelfishpg_common/src/typecode.h @@ -0,0 +1,84 @@ +#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); + +extern Oid tsql_bpchar_oid; +extern Oid tsql_nchar_oid; +extern Oid tsql_varchar_oid; +extern Oid tsql_nvarchar_oid; +extern Oid tsql_ntext_oid; +extern Oid tsql_image_oid; +extern Oid tsql_binary_oid; +extern Oid tsql_varbinary_oid; + +extern Oid lookup_tsql_datatype_oid(const char *typename); +extern bool is_tsql_bpchar_datatype(Oid oid); +extern bool is_tsql_nchar_datatype(Oid oid); +extern bool is_tsql_varchar_datatype(Oid oid); +extern bool is_tsql_nvarchar_datatype(Oid oid); +extern bool is_tsql_text_datatype(Oid oid); +extern bool is_tsql_ntext_datatype(Oid oid); +extern bool is_tsql_image_datatype(Oid oid); +extern bool is_tsql_binary_datatype(Oid oid); +extern bool is_tsql_varbinary_datatype(Oid oid); + +#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..ad2c42ac77b --- /dev/null +++ b/contrib/babelfishpg_common/src/varchar.c @@ -0,0 +1,1258 @@ +/*------------------------------------------------------------------------- + * + * 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/float.h" +#include "utils/int8.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); +static inline int varcharTruelen(VarChar *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; +} + +/* + * 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(bpchar2int2); +PG_FUNCTION_INFO_V1(bpchar2int4); +PG_FUNCTION_INFO_V1(bpchar2int8); +PG_FUNCTION_INFO_V1(bpchar2float4); +PG_FUNCTION_INFO_V1(bpchar2float8); + +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); + +PG_FUNCTION_INFO_V1(varchar2int2); +PG_FUNCTION_INFO_V1(varchar2int4); +PG_FUNCTION_INFO_V1(varchar2int8); +PG_FUNCTION_INFO_V1(varchar2float4); +PG_FUNCTION_INFO_V1(varchar2float8); + +/***************************************************************************** + * 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)); +} + +static char * +varchar2cstring(const VarChar *source) +{ + const char *s_data = VARDATA_ANY(source); + int len = VARSIZE_ANY_EXHDR(source); + + char *result = (char *) palloc(len + 1); + memcpy(result, s_data, len); + result[len] = '\0'; + + return result; +} + +Datum +varchar2int2(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + if (varcharTruelen(source) == 0) + PG_RETURN_INT16(0); + + PG_RETURN_INT16(pg_strtoint16(varchar2cstring(source))); +} + +Datum +varchar2int4(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + if (varcharTruelen(source) == 0) + PG_RETURN_INT32(0); + + PG_RETURN_INT32(pg_strtoint32(varchar2cstring(source))); +} + +Datum +varchar2int8(PG_FUNCTION_ARGS) +{ + int64 result; + VarChar *source = PG_GETARG_VARCHAR_PP(0); + if (varcharTruelen(source) == 0) + PG_RETURN_INT64(0); + + (void) scanint8(varchar2cstring(source), false, &result); + PG_RETURN_INT64(result); +} + +static Datum +cstring2float4(char *num) +{ + /* This came from float4in() in backend/utils/adt/float.c */ + char *orig_num; + float val; + char *endptr; + /* + * endptr points to the first character _after_ the sequence we recognized + * as a valid floating point number. orig_num points to the original input + * string. + */ + orig_num = num; + + /* skip leading whitespace */ + while (*num != '\0' && isspace((unsigned char) *num)) + num++; + + /* + * Check for an empty-string input to begin with, to avoid the vagaries of + * strtod() on different platforms. + */ + if (*num == '\0') + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "real", orig_num))); + + errno = 0; + val = strtof(num, &endptr); + + /* did we not see anything that looks like a double? */ + if (endptr == num || errno != 0) + { + int save_errno = errno; + + /* + * C99 requires that strtof() accept NaN, [+-]Infinity, and [+-]Inf, + * but not all platforms support all of these (and some accept them + * but set ERANGE anyway...) Therefore, we check for these inputs + * ourselves if strtof() fails. + * + * Note: C99 also requires hexadecimal input as well as some extended + * forms of NaN, but we consider these forms unportable and don't try + * to support them. You can use 'em if your strtof() takes 'em. + */ + if (pg_strncasecmp(num, "NaN", 3) == 0) + { + val = get_float4_nan(); + endptr = num + 3; + } + else if (pg_strncasecmp(num, "Infinity", 8) == 0) + { + val = get_float4_infinity(); + endptr = num + 8; + } + else if (pg_strncasecmp(num, "+Infinity", 9) == 0) + { + val = get_float4_infinity(); + endptr = num + 9; + } + else if (pg_strncasecmp(num, "-Infinity", 9) == 0) + { + val = -get_float4_infinity(); + endptr = num + 9; + } + else if (pg_strncasecmp(num, "inf", 3) == 0) + { + val = get_float4_infinity(); + endptr = num + 3; + } + else if (pg_strncasecmp(num, "+inf", 4) == 0) + { + val = get_float4_infinity(); + endptr = num + 4; + } + else if (pg_strncasecmp(num, "-inf", 4) == 0) + { + val = -get_float4_infinity(); + endptr = num + 4; + } + else if (save_errno == ERANGE) + { + /* + * Some platforms return ERANGE for denormalized numbers (those + * that are not zero, but are too close to zero to have full + * precision). We'd prefer not to throw error for that, so try to + * detect whether it's a "real" out-of-range condition by checking + * to see if the result is zero or huge. + * + * Use isinf() rather than HUGE_VALF on VS2013 because it + * generates a spurious overflow warning for -HUGE_VALF. Also use + * isinf() if HUGE_VALF is missing. + */ + if (val == 0.0 || +#if !defined(HUGE_VALF) || (defined(_MSC_VER) && (_MSC_VER < 1900)) + isinf(val) +#else + (val >= HUGE_VALF || val <= -HUGE_VALF) +#endif + ) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"%s\" is out of range for type real", + orig_num))); + } + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "real", orig_num))); + } +#ifdef HAVE_BUGGY_SOLARIS_STRTOD + else + { + /* + * Many versions of Solaris have a bug wherein strtod sets endptr to + * point one byte beyond the end of the string when given "inf" or + * "infinity". + */ + if (endptr != num && endptr[-1] == '\0') + endptr--; + } +#endif /* HAVE_BUGGY_SOLARIS_STRTOD */ + + /* skip trailing whitespace */ + while (*endptr != '\0' && isspace((unsigned char) *endptr)) + endptr++; + + /* if there is any junk left at the end of the string, bail out */ + if (*endptr != '\0') + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type %s: \"%s\"", + "real", orig_num))); + + PG_RETURN_FLOAT4(val); +} + +Datum +varchar2float4(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + + if (varcharTruelen(source) == 0) + PG_RETURN_FLOAT4(0); + + return cstring2float4(varchar2cstring(source)); +} + +Datum +varchar2float8(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *num; + + if (varcharTruelen(source) == 0) + PG_RETURN_FLOAT8(0); + + num = varchar2cstring(source); + PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num)); +} + +/***************************************************************************** + * 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 char * +bpchar2cstring(const BpChar *source) +{ + const char *s_data = VARDATA_ANY(source); + int len = VARSIZE_ANY_EXHDR(source); + + char *result = (char *) palloc(len + 1); + memcpy(result, s_data, len); + result[len] = '\0'; + + return result; +} + +Datum +bpchar2int2(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + if (bpchartruelen(VARDATA_ANY(source), VARSIZE_ANY_EXHDR(source)) == 0) + PG_RETURN_INT16(0); + + PG_RETURN_INT16(pg_strtoint16(bpchar2cstring(source))); +} + +Datum +bpchar2int4(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + if (bpchartruelen(VARDATA_ANY(source), VARSIZE_ANY_EXHDR(source)) == 0) + PG_RETURN_INT32(0); + + PG_RETURN_INT32(pg_strtoint32(bpchar2cstring(source))); +} + +Datum +bpchar2int8(PG_FUNCTION_ARGS) +{ + int64 result; + BpChar *source = PG_GETARG_BPCHAR_PP(0); + if (bpchartruelen(VARDATA_ANY(source), VARSIZE_ANY_EXHDR(source)) == 0) + PG_RETURN_INT64(0); + + (void) scanint8(bpchar2cstring(source), false, &result); + PG_RETURN_INT64(result); +} + +Datum +bpchar2float4(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + + if (bpchartruelen(VARDATA_ANY(source), VARSIZE_ANY_EXHDR(source)) == 0) + PG_RETURN_FLOAT4(0); + + return cstring2float4(bpchar2cstring(source)); +} + +Datum +bpchar2float8(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + char *num; + + if (bpchartruelen(VARDATA_ANY(source), VARSIZE_ANY_EXHDR(source)) == 0) + PG_RETURN_FLOAT8(0); + + num = bpchar2cstring(source); + PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num)); +} + +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..07c719d3c48 --- /dev/null +++ b/contrib/babelfishpg_money/Makefile @@ -0,0 +1,47 @@ +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 + +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..9ce8ba73f95 --- /dev/null +++ b/contrib/babelfishpg_tds/Makefile @@ -0,0 +1,43 @@ +# 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 + +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..6a546a60603 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/err_handler.c @@ -0,0 +1,324 @@ +#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, (edata->message_id != NULL) ? strlen(edata->message_id) : 0); + 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) + { + 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); + + *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..77649655f1d --- /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..123af09c0c4 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds_data_map.c @@ -0,0 +1,221 @@ +#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", "decimal", TDS_TYPE_DECIMALN, 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}, + {"sys", "fixeddecimal", TDS_TYPE_MONEYN, 8, 1, TDS_SEND_MONEY, TDS_RECV_INVALID}, + + /* 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/tdsbulkload.c b/contrib/babelfishpg_tds/src/backend/tds/tdsbulkload.c new file mode 100644 index 00000000000..e5b308b96ce --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsbulkload.c @@ -0,0 +1,691 @@ +/*------------------------------------------------------------------------- + * + * tdsbulkload.c + * TDS Listener functions for handling Bulk Load 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/tdsbulkload.c + * + *------------------------------------------------------------------------- + */ + + +#include "postgres.h" + +#include "utils/guc.h" +#include "lib/stringinfo.h" +#include "pgstat.h" + +#include "src/include/tds_instr.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" +#include "src/include/tds_typeio.h" + +static void SetBulkLoadRowData(TDSRequestBulkLoad request, const StringInfo message, uint64_t offset); +void ProcessBCPRequest(TDSRequest request); + +/* Check if retStatus Not OK. */ +#define CheckPLPStatusNotOK(temp, retStatus, colNum) \ +do \ +{ \ + if (retStatus != STATUS_OK) \ + { \ + ereport(ERROR, \ + (errcode(ERRCODE_PROTOCOL_VIOLATION), \ + errmsg("The incoming tabular data stream (TDS) Bulk Load Request (BulkLoadBCP) protocol stream is incorrect. " \ + "Row %d, column %d: The chunking format is incorrect for a " \ + "large object parameter of data type 0x%02X.", \ + temp->rowCount, colNum + 1, temp->colMetaData[i].columnTdsType))); \ + } \ +} while(0) + + +/* For checking the invalid length in the request. */ +#define CheckForInvalidLength(rowData, temp, colNum) \ +do \ +{ \ + if ((uint32_t)rowData->columnValues[i].len > (uint32_t)temp->colMetaData[i].maxLen) \ + ereport(ERROR, \ + (errcode(ERRCODE_PROTOCOL_VIOLATION), \ + errmsg("The incoming tabular data stream (TDS) Bulk Load Request (BulkLoadBCP) protocol stream is incorrect. " \ + "Row %d, column %d: Data type 0x%02X has an invalid data length or metadata length.", \ + temp->rowCount, colNum + 1, temp->colMetaData[i].columnTdsType))); \ +} while(0) + +/* + * GetBulkLoadRequest - Builds the request structure associated + * with Bulk Load. + * TODO: Reuse for TVP. + */ +TDSRequest +GetBulkLoadRequest(StringInfo message) +{ + TDSRequestBulkLoad request; + uint16_t colCount; + BulkLoadColMetaData *colmetadata; + uint64_t offset = 0; + + TdsErrorContext->err_text = "Fetching Bulk Load Request"; + + TDSInstrumentation(INSTR_TDS_BULK_LOAD_REQUEST); + + request = palloc0(sizeof(TDSRequestBulkLoadData)); + request->rowData = NIL; + request->reqType = TDS_REQUEST_BULK_LOAD; + + if(unlikely((uint8_t)message->data[offset] != TDS_TOKEN_COLMETADATA)) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) Bulk Load Request (BulkLoadBCP) protocol stream is incorrect. " + "unexpected token encountered processing the request."))); + + offset++; + + memcpy(&colCount, &message->data[offset], sizeof(uint16)); + colmetadata = palloc0(colCount * sizeof(BulkLoadColMetaData)); + request->colCount = colCount; + request->colMetaData = colmetadata; + offset += sizeof(uint16); + + for (int currentColumn = 0; currentColumn < colCount; currentColumn++) + { + /* UserType */ + memcpy(&colmetadata[currentColumn].userType, &message->data[offset], sizeof(uint32_t)); + offset += sizeof(uint32_t); + + /* Flags */ + memcpy(&colmetadata[currentColumn].flags, &message->data[offset], sizeof(uint16)); + offset += sizeof(uint16); + + /* TYPE_INFO */ + colmetadata[currentColumn].columnTdsType = message->data[offset++]; + + /* Datatype specific Column Metadata. */ + switch(colmetadata[currentColumn].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[currentColumn].maxLen = message->data[offset++]; + break; + case TDS_TYPE_DECIMALN: + case TDS_TYPE_NUMERICN: + colmetadata[currentColumn].maxLen = message->data[offset++]; + colmetadata[currentColumn].precision = message->data[offset++]; + colmetadata[currentColumn].scale = message->data[offset++]; + break; + case TDS_TYPE_CHAR: + case TDS_TYPE_VARCHAR: + case TDS_TYPE_NCHAR: + case TDS_TYPE_NVARCHAR: + { + memcpy(&colmetadata[currentColumn].maxLen, &message->data[offset], sizeof(uint16)); + offset += sizeof(uint16); + + memcpy(&colmetadata[currentColumn].collation, &message->data[offset], sizeof(uint32_t)); + offset += sizeof(uint32_t); + colmetadata[currentColumn].sortId = message->data[offset++]; + } + break; + case TDS_TYPE_TEXT: + case TDS_TYPE_NTEXT: + case TDS_TYPE_IMAGE: + { + uint16_t tableLen = 0; + memcpy(&colmetadata[currentColumn].maxLen, &message->data[offset], sizeof(uint32_t)); + offset += sizeof(uint32_t); + + /* Read collation(LICD) and sort-id for TEXT and NTEXT. */ + if (colmetadata[currentColumn].columnTdsType == TDS_TYPE_TEXT || + colmetadata[currentColumn].columnTdsType == TDS_TYPE_NTEXT) + { + memcpy(&colmetadata[currentColumn].collation, &message->data[offset], sizeof(uint32_t)); + offset += sizeof(uint32_t); + colmetadata[currentColumn].sortId = message->data[offset++]; + } + + memcpy(&tableLen, &message->data[offset], sizeof(uint16_t)); + offset += sizeof(uint16_t); + + /* Skip table name for now. */ + offset += tableLen * 2; + } + break; + case TDS_TYPE_XML: + { + colmetadata[currentColumn].maxLen = message->data[offset++]; + } + break; + case TDS_TYPE_DATETIME2: + { + colmetadata[currentColumn].scale = message->data[offset++]; + colmetadata[currentColumn].maxLen = 8; + } + break; + case TDS_TYPE_TIME: + { + colmetadata[currentColumn].scale = message->data[offset++]; + colmetadata[currentColumn].maxLen = 5; + } + break; + case TDS_TYPE_DATETIMEOFFSET: + { + colmetadata[currentColumn].scale = message->data[offset++]; + colmetadata[currentColumn].maxLen = 10; + } + break; + case TDS_TYPE_BINARY: + case TDS_TYPE_VARBINARY: + { + uint16 plp; + memcpy(&plp, &message->data[offset], sizeof(uint16)); + offset += sizeof(uint16); + colmetadata[currentColumn].maxLen = plp; + } + break; + case TDS_TYPE_DATE: + colmetadata[currentColumn].maxLen = 3; + break; + case TDS_TYPE_SQLVARIANT: + memcpy(&colmetadata[currentColumn].maxLen, &message->data[offset], sizeof(uint32_t)); + offset += sizeof(uint32_t); + break; + /* + * Below cases are for variant types; in case of fixed length datatype columns, with + * a Not NUll constraint, makes use of this type as an optimisation for not receiving + * the the lengths for the column metadata and row data. + */ + case VARIANT_TYPE_INT: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_INTEGER; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_INT; + } + break; + case VARIANT_TYPE_BIT: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_BIT; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_BIT; + } + break; + case VARIANT_TYPE_BIGINT: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_INTEGER; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_BIGINT; + } + break; + case VARIANT_TYPE_SMALLINT: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_INTEGER; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_SMALLINT; + } + break; + case VARIANT_TYPE_TINYINT: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_INTEGER; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_TINYINT; + } + break; + case VARIANT_TYPE_REAL: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_FLOAT; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_FLOAT4; + } + break; + case VARIANT_TYPE_FLOAT: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_FLOAT; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_FLOAT8; + } + break; + case VARIANT_TYPE_DATETIME: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_DATETIMEN; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_DATETIME; + } + break; + case VARIANT_TYPE_SMALLDATETIME: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_DATETIMEN; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_SMALLDATETIME; + } + break; + case VARIANT_TYPE_MONEY: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_MONEYN; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_MONEY; + } + break; + case VARIANT_TYPE_SMALLMONEY: + { + colmetadata[currentColumn].columnTdsType = TDS_TYPE_MONEYN; + colmetadata[currentColumn].variantType = true; + colmetadata[currentColumn].maxLen = TDS_MAXLEN_SMALLMONEY; + } + break; + default: + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) is incorrect. " + "Data type 0x%02X is unknown.", colmetadata[currentColumn].columnTdsType))); + } + + /* Column Name */ + memcpy(&colmetadata[currentColumn].colNameLen, &message->data[offset++], sizeof(uint8_t)); + colmetadata[currentColumn].colName = (char *)palloc0(colmetadata[currentColumn].colNameLen * sizeof(char) * 2 + 1); + memcpy(colmetadata[currentColumn].colName, &message->data[offset], + colmetadata[currentColumn].colNameLen * 2); + colmetadata[currentColumn].colName[colmetadata[currentColumn].colNameLen * 2] = '\0'; + + offset += colmetadata[currentColumn].colNameLen * 2; + } + + SetBulkLoadRowData(request, message, offset); + + return (TDSRequest)request; +} + +/* + * SetBulkLoadRowData - Builds the row data structure associated + * with Bulk Load. + * TODO: Reuse for TVP. + */ +static void +SetBulkLoadRowData(TDSRequestBulkLoad request, const StringInfo message, uint64_t offset) +{ + BulkLoadColMetaData *colmetadata = request->colMetaData; + char *messageData = message->data; + int retStatus = 0; + request->rowCount = 0; + request->rowData = NIL; + while((uint8_t)messageData[offset] == TDS_TOKEN_ROW) /* Loop over each row. */ + { + int i = 0; /* Current Column Number. */ + BulkLoadRowData *rowData = palloc0(sizeof(BulkLoadRowData)); + request->rowCount++; + retStatus += 1; + rowData->columnValues = palloc0(request->colCount * sizeof(StringInfoData)); + rowData->isNull = palloc0(request->colCount); + offset++; + while(i != request->colCount) /* Loop over each column. */ + { + initStringInfo(&rowData->columnValues[i]); + rowData->isNull[i] = 'f'; + 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: + { + if (colmetadata[i].variantType) + { + rowData->columnValues[i].len = colmetadata[i].maxLen; + } + else + { + rowData->columnValues[i].len = messageData[offset++]; + if (rowData->columnValues[i].len == 0) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + } + CheckForInvalidLength(rowData, request, i); + + if (rowData->columnValues[i].len > rowData->columnValues[i].maxlen) + enlargeStringInfo(&rowData->columnValues[i], rowData->columnValues[i].len); + + 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) Bulk Load Request (BulkLoadBCP) protocol stream is incorrect. " + "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.", + request->rowCount, i + 1))); + rowData->columnValues[i].len = messageData[offset++]; + if (rowData->columnValues[i].len == 0) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + + CheckForInvalidLength(rowData, request, i); + + if (rowData->columnValues[i].len > rowData->columnValues[i].maxlen) + enlargeStringInfo(&rowData->columnValues[i], rowData->columnValues[i].len); + + 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); + if (rowData->columnValues[i].len != 0xffff) + { + CheckForInvalidLength(rowData, request, i); + + if (rowData->columnValues[i].len > rowData->columnValues[i].maxlen) + enlargeStringInfo(&rowData->columnValues[i], rowData->columnValues[i].len); + + memcpy(rowData->columnValues[i].data, &messageData[offset], rowData->columnValues[i].len); + offset += rowData->columnValues[i].len; + } + else /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + } + else + { + StringInfo plpStr; + ParameterToken temp = palloc0(sizeof(ParameterTokenData)); + retStatus = ReadPlp(temp, message, &offset); + CheckPLPStatusNotOK(request, retStatus, i); + if (temp->isNull) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + temp->isNull = false; + continue; + } + + plpStr = TdsGetPlpStringInfoBufferFromToken(messageData, temp); + rowData->columnValues[i] = *plpStr; + pfree(plpStr); + pfree(temp); + } + } + break; + case TDS_TYPE_TEXT: + case TDS_TYPE_NTEXT: + case TDS_TYPE_IMAGE: + { + /* Ignore the Data Text Ptr since its currently of no use. */ + uint8 dataTextPtrLen = messageData[offset++]; + if (dataTextPtrLen == 0) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + offset += dataTextPtrLen; + offset += 8; /* TODO: Ignored the Data Text TimeStamp for now. */ + + memcpy(&rowData->columnValues[i].len, &messageData[offset], sizeof(uint32_t)); + offset += sizeof(uint32_t); + if (rowData->columnValues[i].len == 0) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + + CheckForInvalidLength(rowData, request, i); + + if (rowData->columnValues[i].len > rowData->columnValues[i].maxlen) + enlargeStringInfo(&rowData->columnValues[i], rowData->columnValues[i].len); + + memcpy(rowData->columnValues[i].data, &messageData[offset], rowData->columnValues[i].len); + offset += rowData->columnValues[i].len; + } + break; + case TDS_TYPE_XML: + { + StringInfo plpStr; + ParameterToken temp = palloc0(sizeof(ParameterTokenData)); + retStatus = ReadPlp(temp, message, &offset); + CheckPLPStatusNotOK(request, retStatus, i); + if (temp->isNull) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + temp->isNull = false; + continue; + } + + plpStr = TdsGetPlpStringInfoBufferFromToken(messageData, temp); + rowData->columnValues[i] = *plpStr; + pfree(plpStr); + pfree(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) /* null */ + { + rowData->isNull[i] = 'n'; + i++; + continue; + } + + CheckForInvalidLength(rowData, request, i); + + if (rowData->columnValues[i].len > rowData->columnValues[i].maxlen) + enlargeStringInfo(&rowData->columnValues[i], rowData->columnValues[i].len); + + memcpy(rowData->columnValues[i].data, &messageData[offset], rowData->columnValues[i].len); + offset += rowData->columnValues[i].len; + } + break; + } + i++; + } + request->rowData = lappend(request->rowData, rowData); + } + if ((uint8_t)messageData[offset] != TDS_TOKEN_DONE) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("The incoming tabular data stream (TDS) Bulk Load Request (BulkLoadBCP) protocol stream is incorrect. " + "Row %d, column %d, unexpected token encountered processing the request. %d", + request->rowCount, request->colCount, (uint8_t)messageData[offset]))); + offset++; +} + +/* + * ProcessBCPRequest - Processes the request and calls the bulk_load_callback + * for futher execution. + * TODO: Reuse for TVP. + */ +void +ProcessBCPRequest(TDSRequest request) +{ + int retValue = 0; + StringInfo temp = makeStringInfo(); + TDSRequestBulkLoad req = (TDSRequestBulkLoad) request; + BulkLoadColMetaData *colMetaData = req->colMetaData; + + int nargs = req->colCount * req->rowCount; + Datum *values = palloc0(nargs * sizeof(Datum)); + char *nulls = palloc0(nargs * sizeof(char)); + Oid *argtypes= palloc0(nargs * sizeof(Oid)); + + int count = 0; + ListCell *lc; + + TdsErrorContext->err_text = "Processing Bulk Load Request"; + pgstat_report_activity(STATE_RUNNING, "Processing Bulk Load Request"); + + foreach (lc, req->rowData) /* build an array of Value Datums */ + { + BulkLoadRowData *row = (BulkLoadRowData *) lfirst(lc); + TdsIoFunctionInfo tempFuncInfo; + int currentColumn = 0; + + while(currentColumn != req->colCount) + { + temp = &(row->columnValues[currentColumn]); + tempFuncInfo = TdsLookupTypeFunctionsByTdsId(colMetaData[currentColumn].columnTdsType, colMetaData[currentColumn].maxLen); + GetPgOid(argtypes[count], tempFuncInfo); + if (row->isNull[currentColumn] == 'n') /* null */ + nulls[count] = row->isNull[currentColumn]; + else + switch(colMetaData[currentColumn].columnTdsType) + { + case TDS_TYPE_CHAR: + case TDS_TYPE_VARCHAR: + case TDS_TYPE_TEXT: + values[count] = TdsTypeVarcharToDatum(temp, argtypes[count], colMetaData[currentColumn].collation); + break; + case TDS_TYPE_NCHAR: + case TDS_TYPE_NVARCHAR: + case TDS_TYPE_NTEXT: + values[count] = TdsTypeNCharToDatum(temp); + break; + case TDS_TYPE_INTEGER: + case TDS_TYPE_BIT: + values[count] = TdsTypeIntegerToDatum(temp, colMetaData[currentColumn].maxLen); + break; + case TDS_TYPE_FLOAT: + values[count] = TdsTypeFloatToDatum(temp, colMetaData[currentColumn].maxLen); + break; + case TDS_TYPE_NUMERICN: + case TDS_TYPE_DECIMALN: + values[count] = TdsTypeNumericToDatum(temp, colMetaData[currentColumn].scale); + break; + case TDS_TYPE_VARBINARY: + case TDS_TYPE_BINARY: + case TDS_TYPE_IMAGE: + values[count] = TdsTypeVarbinaryToDatum(temp); + argtypes[count] = tempFuncInfo->ttmtypeid; + break; + case TDS_TYPE_DATE: + values[count] = TdsTypeDateToDatum(temp); + break; + case TDS_TYPE_TIME: + values[count] = TdsTypeTimeToDatum(temp, colMetaData[currentColumn].scale, temp->len); + break; + case TDS_TYPE_DATETIME2: + values[count] = TdsTypeDatetime2ToDatum(temp, colMetaData[currentColumn].scale, temp->len); + break; + case TDS_TYPE_DATETIMEN: + if (colMetaData[currentColumn].maxLen == TDS_MAXLEN_SMALLDATETIME) + values[count] = TdsTypeSmallDatetimeToDatum(temp); + else + values[count] = TdsTypeDatetimeToDatum(temp); + break; + case TDS_TYPE_DATETIMEOFFSET: + values[count] = TdsTypeDatetimeoffsetToDatum(temp, colMetaData[currentColumn].scale, temp->len); + break; + case TDS_TYPE_MONEYN: + if (colMetaData[currentColumn].maxLen == TDS_MAXLEN_SMALLMONEY) + values[count] = TdsTypeSmallMoneyToDatum(temp); + else + values[count] = TdsTypeMoneyToDatum(temp); + break; + case TDS_TYPE_XML: + values[count] = TdsTypeXMLToDatum(temp); + break; + case TDS_TYPE_UNIQUEIDENTIFIER: + values[count] = TdsTypeUIDToDatum(temp); + break; + case TDS_TYPE_SQLVARIANT: + values[count] = TdsTypeSqlVariantToDatum(temp); + break; + } + count++; + currentColumn++; + } + } + + if (req->rowData) /* If any row exists then do an insert. */ + { + PG_TRY(); + { + retValue = pltsql_plugin_handler_ptr->bulk_load_callback(req->colCount, + req->rowCount, argtypes, + values, nulls); + } + PG_CATCH(); + { + if (TDS_DEBUG_ENABLED(TDS_DEBUG2)) + ereport(LOG, + (errmsg("Bulk Load Request. Number of Rows: %d and Number of columns: %d.", + req->rowCount, req->colCount), + errhidestmt(true))); + + PG_RE_THROW(); + } + PG_END_TRY(); + + } + + /* Send Done Token if rows processed is a positive number. Command type - execute (0xf0). */ + if (retValue >= 0) + TdsSendDone(TDS_TOKEN_DONE, TDS_DONE_COUNT, 0xf0, retValue); + else /* Send Unknown Error. */ + ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("Unknown error occurred during Insert Bulk"))); + + /* + * 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("Bulk Load Request. Number of Rows: %d and Number of columns: %d.", + req->rowCount, req->colCount), + errhidestmt(true))); + pltsql_plugin_handler_ptr->stmt_needs_logging = false; + error_context_stack = plerrcontext; + } + + /* Free the List of Rows. */ + list_free_deep(req->rowData); + if (values) + pfree(values); + if (nulls) + pfree(nulls); + if (argtypes) + pfree(argtypes); +} 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..92027c28311 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdslogin.c @@ -0,0 +1,2366 @@ +/*------------------------------------------------------------------------- + * + * 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 "parser/scansup.h" +#include "utils/guc.h" +#include "utils/acl.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); + + /* + * Downcase and copy the username and database name in port structure so that no one + * messes up with the local copy. + */ + if (request->username != NULL) + { + request->username = downcase_identifier(request->username, + strlen(request->username), + false, + false); + port->user_name = pstrdup(request->username); + } + if (request->database != NULL) + { + request->database = downcase_identifier(request->database, + strlen(request->database), + false, + false); + 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); + } + + /* Check if the user is a valid babelfish login. + * We will only allow following users to login: + * 1. An existing PG user that we have initialised with sys.babelfish_initialize() + * 2. A Postgres SUPERUSER. + * 3. New users created using CREATE LOGIN command through TDS endpoint. */ + if (port->user_name != NULL && port->user_name[0] != '\0') + { + bool login_exist; + Oid roleid; + + StartTransactionCommand(); + roleid = get_role_oid(port->user_name, false); + login_exist = pltsql_plugin_handler_ptr->pltsql_is_login(roleid); + CommitTransactionCommand(); + + /* Throw error if this user is not one of the type mentioned above */ + if(!login_exist && !superuser_arg(roleid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("\"%s\" is not a Babelfish user", port->user_name))); + } + + 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..bcfd5b17aa9 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsprotocol.c @@ -0,0 +1,707 @@ +/*------------------------------------------------------------------------- + * + * 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_BULK_LOAD: /* Bulk Load request */ + { + request = GetBulkLoadRequest(&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; + case TDS_REQUEST_BULK_LOAD: + { + ProcessBCPRequest(request); + } + 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..6cdc663b115 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c @@ -0,0 +1,2905 @@ +/*------------------------------------------------------------------------- + * + * 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 "utils/syscache.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 bool get_attnotnull(Oid relid, AttrNumber attnum); + +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 from 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; + } + + /* + * Mitigate precision overflow if integral precision <= 38 + * Otherwise it simply won't fit in 38 precision and let an + * overflow error be thrown in PrepareRowDescription. + */ + if (precision > TDS_MAX_NUM_PRECISION && + precision - scale <= 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; + + /* Be smart about length-coercion functions... */ + if (exprIsLengthCoercion(expr, &rettypmod)) + return rettypmod; + + /* + * 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 == NIL ? 0 : 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 integral_precision and scale (fractional precision) in a CoalesceExpr */ + CoalesceExpr *coale = (CoalesceExpr *) expr; + ListCell *lc; + Node *arg; + int32 arg_typmod; + uint8_t precision, max_integral_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_integral_precision = Max(precision - scale, max_integral_precision); + } + return (((max_integral_precision + max_scale) << 16) | max_scale) + VARHDRSZ; + } + case T_CaseExpr: + { + /* Find max possible integral_precision and scale (fractional precision) in a CoalesceExpr */ + CaseExpr *case_expr = (CaseExpr *) expr; + ListCell *lc; + CaseWhen *casewhen; + Node *casewhen_result; + int32 typmod; + uint8_t precision, max_integral_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_integral_precision = Max(precision - scale, max_integral_precision); + } + return (((max_integral_precision + max_scale) << 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); + } + case T_RelabelType: + { + RelabelType *rlt = (RelabelType *) expr; + + if (rlt->resulttypmod != -1) + return rlt->resulttypmod; + else + return resolve_numeric_typmod_from_exp((Node *) rlt->arg); + } + /* 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, TDS_MAXLEN_BIT); + temp->maxLen = 1; + break; + case TDS_SEND_TINYINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_TINYINT); + temp->maxLen = 1; + break; + case TDS_SEND_SMALLINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_SMALLINT); + temp->maxLen = 2; + break; + case TDS_SEND_INTEGER: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_INT); + temp->maxLen = 4; + break; + case TDS_SEND_BIGINT: + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_BIGINT); + temp->maxLen = 8; + break; + case TDS_SEND_FLOAT4: + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, TDS_MAXLEN_FLOAT4); + temp->maxLen = 4; + break; + case TDS_SEND_FLOAT8: + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, TDS_MAXLEN_FLOAT8); + 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, TDS_MAXLEN_MONEY); + temp->maxLen = 8; + break; + case TDS_SEND_SMALLMONEY: + SetColMetadataForFixedType(col, TDS_TYPE_MONEYN, TDS_MAXLEN_SMALLMONEY); + 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, TDS_MAXLEN_DATETIME); + 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, TDS_MAXLEN_SMALLDATETIME); + 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, TDS_MAXLEN_UNIQUEIDENTIFIER); + 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; + } + + col->attNotNull = get_attnotnull(col->relOid, col->attrNum); + switch (finfo->sendFuncId) + { + /* + * In case of Not NULL constraint on the column, send the variant type. + * This is only done for the Fixed length datat types except uniqueidentifier. + * + * TODO PG boolean is equivalent to TSQL BIT type + */ + case TDS_SEND_BIT: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_BIT, TDS_MAXLEN_BIT); + else + SetColMetadataForFixedType(col, TDS_TYPE_BIT, TDS_MAXLEN_BIT); + break; + case TDS_SEND_TINYINT: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_TINYINT, TDS_MAXLEN_TINYINT); + else + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_TINYINT); + break; + case TDS_SEND_SMALLINT: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_SMALLINT, TDS_MAXLEN_SMALLINT); + else + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_SMALLINT); + break; + case TDS_SEND_INTEGER: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_INT, TDS_MAXLEN_INT); + else + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_INT); + break; + case TDS_SEND_BIGINT: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_BIGINT, TDS_MAXLEN_BIGINT); + else + SetColMetadataForFixedType(col, TDS_TYPE_INTEGER, TDS_MAXLEN_BIGINT); + break; + case TDS_SEND_FLOAT4: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_REAL, TDS_MAXLEN_FLOAT4); + else + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, TDS_MAXLEN_FLOAT4); + break; + case TDS_SEND_FLOAT8: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_FLOAT, TDS_MAXLEN_FLOAT8); + else + SetColMetadataForFixedType(col, TDS_TYPE_FLOAT, TDS_MAXLEN_FLOAT8); + 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: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_MONEY, TDS_MAXLEN_MONEY); + else + SetColMetadataForFixedType(col, TDS_TYPE_MONEYN, TDS_MAXLEN_MONEY); + break; + case TDS_SEND_SMALLMONEY: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_SMALLMONEY, TDS_MAXLEN_SMALLMONEY); + else + SetColMetadataForFixedType(col, TDS_TYPE_MONEYN, TDS_MAXLEN_SMALLMONEY); + 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: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_DATETIME, TDS_MAXLEN_DATETIME); + else + SetColMetadataForFixedType(col, TDS_TYPE_DATETIMEN, TDS_MAXLEN_DATETIME); + 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; + if (precision > TDS_MAX_NUM_PRECISION) + { + ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("Arithmetic overflow error for data type numeric."))); + } + } + else + { + precision = tds_default_numeric_precision; + scale = tds_default_numeric_scale; + } + SetColMetadataForNumericType(col, TDS_TYPE_NUMERICN, 17, precision, scale); + } + break; + case TDS_SEND_SMALLDATETIME: + if (col->attNotNull) + SetColMetadataForFixedType(col, VARIANT_TYPE_SMALLDATETIME, TDS_MAXLEN_SMALLDATETIME); + else + SetColMetadataForFixedType(col, TDS_TYPE_DATETIMEN, TDS_MAXLEN_SMALLDATETIME); + break; + case TDS_SEND_IMAGE: + SetColMetadataForImageType(col, TDS_TYPE_IMAGE); + sendTableName |= col->sendTableName; + break; + case TDS_SEND_BINARY: + /* The default binary data length is 1 when maxLen isn't specified */ + SetColMetadataForBinaryType(col, TDS_TYPE_BINARY, (atttypmod == -1) ? + 1 : atttypmod - VARHDRSZ); + 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, TDS_MAXLEN_UNIQUEIDENTIFIER); + 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; + + /* + * Return if we are inside a function. Continue if it's a trigger. + */ + if (estate && estate->func && estate->func->fn_oid != InvalidOid && + estate->func->fn_prokind == PROKIND_FUNCTION && estate->func->fn_is_trigger == PLTSQL_NOT_TRIGGER) + return; + + 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); +} + +/* + * get_attnotnull + * Given the relation id and the attribute number, + * return the "attnotnull" field from the attribute relation. + */ +static bool +get_attnotnull(Oid relid, AttrNumber attnum) +{ + HeapTuple tp; + Form_pg_attribute att_tup; + + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); + + if (HeapTupleIsValid(tp)) + { + bool result; + + att_tup = (Form_pg_attribute) GETSTRUCT(tp); + result = att_tup->attnotnull; + ReleaseSysCache(tp); + + return result; + } + /* Assume att is nullable if no valid heap tuple is found */ + return false; +} \ No newline at end of file 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..fe85a1a410a --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsrpc.c @@ -0,0 +1,3905 @@ +#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 "pgstat.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; + char *activity; + + 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); + + activity = psprintf("SP_EXECUTESQL: %s", s.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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; + char *activity; + + 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); + + activity = psprintf("SP_PREPARE: %s", s.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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; + + char *activity = psprintf("SP_EXECUTE Handle: %d", req->handle); + TdsErrorContext->err_text = "Processing SP_EXECUTE Request"; + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + 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; + char *activity; + + 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); + + activity = psprintf("SP_PREPEXEC: %s", s.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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; + char *activity; + + 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); + + activity = psprintf("SP_CUSTOMTYPE: %s", s.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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); + + char *activity = psprintf("SP_UNPREPARE Handle: %d", req->handle); + TdsErrorContext->err_text = "Processing SP_UNPREPARE Request"; + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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); + /* If this is an OUT parameter, mark it */ + if(token->flags & SP_FLAGS_BYREFVALUE) + appendStringInfo(inBuf, " OUT"); + 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); + /* If this is an OUT parameter, mark it */ + if(token->flags & SP_FLAGS_BYREFVALUE) + appendStringInfo(inBuf, " OUT"); + 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) + { + char *activity = psprintf("SP_CURSOREXEC Handle: %d", (int)req->cursorPreparedHandle); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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 + { + char *activity; + initStringInfo(&buf); + /* fetch the query */ + FillQueryFromParameterToken(req, &buf); + + switch (req->spType) + { + case SP_CURSOROPEN: + activity = psprintf("SP_CURSOROPEN: %s", buf.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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: + activity = psprintf("SP_CURSORPREPARE: %s", buf.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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: + activity = psprintf("SP_CURSORPREPEXEC: %s", buf.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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; + char *activity = psprintf("SP_CURSORFETCH Handle: %d", (int)req->cursorHandle); + + TdsErrorContext->err_text = "Processing SP_CURSORFETCH Request"; + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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; + char *activity = psprintf("SP_CURSORCLOSE Handle: %d", (int)req->cursorHandle); + + TdsErrorContext->err_text = "Processing SP_CURSORCLOSE Request"; + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + 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; + char *activity = psprintf("SP_CURSORUNPREPARE Handle: %d", (int)req->cursorPreparedHandle); + + TdsErrorContext->err_text = "Processing SP_CURSORUNPREPARE Request"; + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + 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; + + char *activity = psprintf("SP_CURSOROPTION Handle: %d", (int)req->cursorHandle); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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; + + pgstat_report_activity(STATE_RUNNING, "SP_CURSOR"); + 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_cursor 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..49f61da9a07 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdssqlbatch.c @@ -0,0 +1,139 @@ +/*------------------------------------------------------------------------- + * + * 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 "pgstat.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); + char *activity = psprintf("SQL_BATCH: %s", query); + + TdsErrorContext->err_text = "Processing SQL Batch Request"; + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + /* 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..9e62027c137 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c @@ -0,0 +1,3561 @@ +/*------------------------------------------------------------------------- + * + * 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 VARCHAR_MAX 2147483647 + +#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 TdsTypeSmallDatetimeToDatum(StringInfo buf); +Datum TdsTypeDateToDatum(StringInfo buf); +Datum TdsTypeTimeToDatum(StringInfo buf, int scale, int len); +Datum TdsTypeDatetimeoffsetToDatum(StringInfo buf, int scale, int len); +Datum TdsTypeMoneyToDatum(StringInfo buf); +Datum TdsTypeSmallMoneyToDatum(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 scale is more than length then we need to append zeros at the start; + * Since there is a '-' at the start of decString, we should ignore it before + * appending and then add it later. + */ + if (num != 0 && scale >= len) + { + int diff = scale - len + 1; + char *zeros = palloc0(sizeof(char) * diff + 1); + char *tempString = decString; + while(diff) + { + zeros[--diff] = '0'; + } + /* + * Add extra '.' character in psprintf; Later we make use of + * this index during shifting the scale part of the string. + */ + decString = psprintf("-%s%s.", zeros, tempString + 1); + len = strlen(decString) - 1; + pfree(tempString); + } + 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 Small Datetime value into Datum. */ +Datum +TdsTypeSmallDatetimeToDatum(StringInfo buf) +{ + uint16 numDays, numMins; + uint32 val; + Timestamp timestamp; + + val = (uint32)GetMsgInt(buf, 4); + numMins = val >> 16; + numDays = val & 0x0000ffff; + + TdsTimeGetDatumFromSmalldatetime(numDays, numMins, ×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 Datetimeoffset value into Datum. */ +Datum +TdsTypeDatetimeoffsetToDatum(StringInfo buf, int scale, int len) +{ + uint64_t numMicro = 0; + uint32_t numDays = 0; + int16_t timezone = 0; + tsql_datetimeoffset *tdt = (tsql_datetimeoffset *) palloc0(DATETIMEOFFSET_LEN); + TimestampTz timestamp; + /* + * 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; + + 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; + + PG_RETURN_DATETIMEOFFSET(tdt); +} + +/* 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 Small Money value into Datum. */ +Datum +TdsTypeSmallMoneyToDatum(StringInfo buf) +{ + uint64 val = 0; + uint32 low = GetMsgInt(buf, 4); + + val = (uint64)low; + + 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) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + result = TdsTypeSmallDatetimeToDatum(buf); + + pfree(buf); + return result; +} + +/* ------------------------------- + * 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'; + + + len = strlen(decString); + temp1 = '.'; + + /* + * If scale is more than length then we need to append zeros at the start; + * Since there is a '-' at the start of decString, we should ignore it before + * appending and then add it later. + */ + if (num != 0 && scale >= len) + { + int diff = scale - len + 1; + char *zeros = palloc0(sizeof(char) * diff + 1); + char *tempString = decString; + while(diff) + { + zeros[--diff] = '0'; + } + /* + * Add extra '.' character in psprintf; Later we make use of + * this index during shifting the scale part of the string. + */ + decString = psprintf("-%s%s.", zeros, tempString + 1); + len = strlen(decString) - 1; + pfree(tempString); + } + 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--; + } + } + /* + * We use wholeString just to free the address at decString later, + * since it gets updated later. + */ + wholeString = decString; + + if (sign == 1 && num != 0) + decString++; + + res = TdsSetVarFromStrWrapper(decString); + + if (wholeString) + pfree(wholeString); + if (buf) + 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_DATETIMEOFFSET: + values[i] = TdsTypeDatetimeoffsetToDatum(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) +{ + Datum result; + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + + TDSInstrumentation(INSTR_TDS_DATATYPE_SQLVARIANT); + + result = TdsTypeSqlVariantToDatum(buf); + + pfree(buf); + return result; +} + +int +TdsSendTypeBit(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0; + int8_t out = DatumGetBool(value); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(sizeof(out)); + + if (rc == 0) + rc = TdsPutInt8(out); + return rc; +} + +int +TdsSendTypeTinyint(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0; + int8_t out = DatumGetUInt8(value); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(sizeof(out)); + + if (rc == 0) + rc = TdsPutInt8(out); + return rc; +} +int +TdsSendTypeSmallint(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0; + int16_t out = DatumGetInt16(value); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(sizeof(out)); + + if (rc == 0) + rc = TdsPutInt16LE(out); + return rc; +} + +int +TdsSendTypeInteger(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0; + int32_t out = DatumGetInt32(value); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(sizeof(out)); + + if (rc == 0) + rc = TdsPutInt32LE(out); + return rc; +} + +int +TdsSendTypeBigint(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0; + int64_t out = DatumGetInt64(value); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(sizeof(out)); + + if (rc == 0) + rc = TdsPutInt64LE(out); + return rc; +} + +int +TdsSendTypeFloat4(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0; + float4 out = DatumGetFloat4(value); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(sizeof(out)); + + if (rc == 0) + rc = TdsPutFloat4LE(out); + return rc; +} + +int +TdsSendTypeFloat8(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0; + float8 out = DatumGetFloat8(value); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(sizeof(out)); + + if (rc == 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, /* number of bytes used to store the string. */ + actualLen, /* Number of bytes that would be needed to store given string in given encoding. */ + maxLen; /* max size of given column in bytes */ + char *destBuf, *buf = OutputFunctionCall(finfo, value); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + len = strlen(buf); + destBuf = server_to_any(buf, len, col->encoding); + + maxLen = col->metaEntry.type2.maxSize; + actualLen = (buf != destBuf) ? strlen(destBuf) : len; + + if (maxLen != 0xffff) + { + if (unlikely(actualLen > maxLen)) + elog(ERROR, "Number of bytes for the field of varchar(n) exeeds max specified for the field."); + + if ((rc = TdsPutInt16LE(actualLen)) == 0) + rc = TdsPutbytes(destBuf, actualLen); + } + else + { + /* We can store upto 2GB (2^31 - 1 bytes) for the varchar(max). */ + if (unlikely(actualLen > VARCHAR_MAX)) + elog(ERROR, "Number of bytes required for the field of varchar(max) exeeds 2GB"); + TDSInstrumentation(INSTR_TDS_DATATYPE_VARCHAR_MAX); + + rc = TdsSendPlpDataHelper(destBuf, actualLen); + } + + pfree(buf); + return rc; +} + +int +TdsSendTypeChar(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = EOF, + maxLen, /* max size of given column in bytes */ + actualLen, /* Number of bytes that would be needed to store given string in given encoding. */ + len; /* number of bytes used to store the string. */ + char *destBuf, *buf = OutputFunctionCall(finfo, value); + TdsColumnMetaData *col = (TdsColumnMetaData *)vMetaData; + + len = strlen(buf); + destBuf = server_to_any(buf, len, col->encoding); + + maxLen = col->metaEntry.type2.maxSize; + actualLen = (buf != destBuf) ? strlen(destBuf) : len; + if (unlikely(maxLen != actualLen)) + elog(ERROR, "Number of bytes required for the field of char(n) does not match with max bytes specified of the field"); + + if ((rc = TdsPutUInt16LE(actualLen)) == 0) + rc = TdsPutbytes(destBuf, actualLen); + + 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; + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(length); + + if (rc == 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; + } + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(length); + + if (rc == 0) + rc = TdsPutUInt32LE(low); + return rc; +} + +int +TdsSendTypeSmalldatetime(FmgrInfo *finfo, Datum value, void *vMetaData) +{ + int rc = 0, length = 4; + uint16 numDays = 0, numMins = 0; + + TdsTimeDifferenceSmalldatetime(value, &numDays, &numMins); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(length); + + if (rc == 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); + + /* Don't send the length if the column type is not null. */ + if (!((TdsColumnMetaData *)vMetaData)->attNotNull) + rc = TdsPutInt8(length); + + if (rc == 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, resLen = 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); + } + + resLen = dataLen + variantHeaderLen; + /* We need an extra varlena header for varlena datatypes */ + if (variantBaseType == VARIANT_TYPE_CHAR || variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_VARCHAR || variantBaseType == VARIANT_TYPE_NVARCHAR || + variantBaseType == VARIANT_TYPE_BINARY || variantBaseType == VARIANT_TYPE_VARBINARY || + variantBaseType == VARIANT_TYPE_NUMERIC) + { + resLen += VARHDRSZ; + } + + if (resLen + VARHDRSZ_SHORT <= VARATT_SHORT_MAX) + { + resLen += VARHDRSZ_SHORT; + result = (bytea *) palloc0(resLen); + SET_VARSIZE_SHORT(result, resLen); + } + else + { + resLen += VARHDRSZ; + result = (bytea *) palloc0(resLen); + SET_VARSIZE(result, resLen); + } + + if (variantBaseType == VARIANT_TYPE_CHAR || variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_VARCHAR || variantBaseType == VARIANT_TYPE_NVARCHAR) + { + SET_VARSIZE(READ_DATA(result, variantHeaderLen), VARHDRSZ + dataLen); + memcpy(&maxLen, &buf->data[7], 2); + if (variantBaseType == VARIANT_TYPE_NCHAR || variantBaseType == VARIANT_TYPE_NVARCHAR) + { + memcpy(VARDATA(READ_DATA(result, variantHeaderLen)), strbuf.data, dataLen); + } + else + { + memcpy(VARDATA(READ_DATA(result, variantHeaderLen)), &buf->data[9], dataLen); + } + } + else if (variantBaseType == VARIANT_TYPE_BINARY || variantBaseType == VARIANT_TYPE_VARBINARY) + { + SET_VARSIZE(READ_DATA(result, variantHeaderLen), VARHDRSZ + dataLen); + memcpy(&maxLen, &buf->data[2], 2); + memcpy(VARDATA(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) + { + SET_VARSIZE(READ_DATA(result, variantHeaderLen), VARHDRSZ + dataLen); + 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(VARDATA(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; + 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) +{ + StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); + Datum result; + TdsColumnMetaData col = token->paramMeta; + int scale = col.metaEntry.type6.scale; + + TDSInstrumentation(INSTR_TDS_DATATYPE_DATETIME_OFFSET); + + result = TdsTypeDatetimeoffsetToDatum(buf, scale, token->len); + + pfree(buf); + return result; +} + +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..2ec3e8e17a8 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsxact.c @@ -0,0 +1,442 @@ +/*------------------------------------------------------------------------- + * + * 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 "pgstat.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; + char *activity; + 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: + { + activity = psprintf("TDS_TM_BEGIN_XACT: %s", req->query.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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: + { + activity = psprintf("TDS_TM_COMMIT_XACT: %s", req->query.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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: + { + activity = psprintf("TDS_TM_ROLLBACK_XACT: %s", req->query.data); + pgstat_report_activity(STATE_RUNNING, activity); + pfree(activity); + + 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..b6c389b6dcf --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_instr.h @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------- + * 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_BULK_LOAD_REQUEST, + + 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..9835ac1fa3a --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_int.h @@ -0,0 +1,345 @@ +/*------------------------------------------------------------------------- + * + * 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_BULK_LOAD 0x07 +#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..a60395a42dc --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_iofuncmap.h @@ -0,0 +1,159 @@ +/*------------------------------------------------------------------------- + * + * 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 +#define TDS_MAXLEN_SMALLDATETIME 4 +#define TDS_MAXLEN_DATETIME 8 +#define TDS_MAXLEN_SMALLMONEY 4 +#define TDS_MAXLEN_MONEY 8 +#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..e0237088b27 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_request.h @@ -0,0 +1,896 @@ +/*------------------------------------------------------------------------- + * + * 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_BULK_LOAD = 4, /* bulk load 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; + +typedef struct TDSRequestBulkLoadData +{ + TDSRequestType reqType; + int colCount; + int rowCount; + + BulkLoadColMetaData *colMetaData; /* Array of each column's metadata. */ + List *rowData; /* List holding each row. */ +} TDSRequestBulkLoadData; +typedef TDSRequestBulkLoadData *TDSRequestBulkLoad; + +/* 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 +#define TDS_COL_METADATA_NOT_NULL_FLAGS 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 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; + + /* + * If column is Not NULL constrained then we don't want to send + * maxSize except for uniqueidentifier and xml. + * TODO: We should send TDS_COL_METADATA_NOT_NULL_FLAGS + * This needs to be done for identity contraints + */ + if (col->attNotNull && tdsType != TDS_TYPE_UNIQUEIDENTIFIER && tdsType != TDS_TYPE_XML) + { + col->metaLen = sizeof(col->metaEntry.type1) - 1; + col->metaEntry.type1.flags = TDS_COL_METADATA_NOT_NULL_FLAGS; + } + else + { + 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); + +/* Functions in tdsbulkload.c */ +extern TDSRequest GetBulkLoadRequest(StringInfo message); +extern void ProcessBCPRequest(TDSRequest request); + +#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..2a50d8690e4 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_typeio.h @@ -0,0 +1,474 @@ +/*------------------------------------------------------------------------- + * + * 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; + bool attNotNull; /* true if the column has not null constraint */ +} 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; + +typedef struct BulkLoadColMetaData +{ + int userType; + uint16 flags; + uint8_t columnTdsType; + + /* For numeric and decimal. */ + uint8_t scale; + uint8_t precision; + + /* For String Datatpes. */ + uint32_t collation; + uint8_t sortId; + + uint32_t maxLen; + + uint32_t colNameLen; + char *colName; + + bool variantType; +} BulkLoadColMetaData; + +typedef struct BulkLoadRowData +{ + /* Array of length col count, holds value of each column in that row. */ + StringInfo columnValues; + + char *isNull; +} BulkLoadRowData; + +/* 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); + +extern Datum TdsTypeBitToDatum(StringInfo buf); +extern Datum TdsTypeIntegerToDatum(StringInfo buf, int maxLen); +extern Datum TdsTypeFloatToDatum(StringInfo buf, int maxLen); +extern Datum TdsTypeVarcharToDatum(StringInfo buf, Oid pgTypeOid, uint32_t collation); +extern Datum TdsTypeNCharToDatum(StringInfo buf); +extern Datum TdsTypeNumericToDatum(StringInfo buf, int scale); +extern Datum TdsTypeVarbinaryToDatum(StringInfo buf); +extern Datum TdsTypeDatetime2ToDatum(StringInfo buf, int scale, int len); +extern Datum TdsTypeDatetimeToDatum(StringInfo buf); +extern Datum TdsTypeSmallDatetimeToDatum(StringInfo buf); +extern Datum TdsTypeDateToDatum(StringInfo buf); +extern Datum TdsTypeTimeToDatum(StringInfo buf, int scale, int len); +extern Datum TdsTypeDatetimeoffsetToDatum(StringInfo buf, int scale, int len); +extern Datum TdsTypeMoneyToDatum(StringInfo buf); +extern Datum TdsTypeSmallMoneyToDatum(StringInfo buf); +extern Datum TdsTypeXMLToDatum(StringInfo buf); +extern Datum TdsTypeUIDToDatum(StringInfo buf); +extern Datum TdsTypeSqlVariantToDatum(StringInfo buf); + +#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..e99f5ca3e11 --- /dev/null +++ b/contrib/babelfishpg_tsql/Makefile @@ -0,0 +1,235 @@ +# 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..8ed9b3dd140 --- /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=1 +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..61fb8789ff8 --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/TSqlParser.g4 @@ -0,0 +1,5031 @@ +// 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? + | INSERT BULK ddl_object (LR_BRACKET (insert_bulk_column_definition (COMMA insert_bulk_column_definition)*)? column_constraint* COMMA? RR_BRACKET) + ; + +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 + : simple_column_name (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? + ; + +// Temporary workaround for COLLATE default in INSERT BULK +insert_bulk_column_definition + : simple_column_name (data_type system_versioning_column? | AS expression PERSISTED? ) ( special_column_option | (COLLATE (id | DEFAULT)) | 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_clause + ; + +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_DEFAULTS + | ANSI_NULLS + | ANSI_NULL_DEFAULT + | ANSI_NULL_DFLT_OFF + | ANSI_NULL_DFLT_ON + | 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 + | AUTOCOMMIT + | 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 + | FIPS_FLAGGER + | FIRST + | FIRST_ROW + | FIRST_VALUE + | FMTONLY + | 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 + | NOEXEC + | 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 + | PARSEONLY + | 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_PROC_TRANSACTIONS + | 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 + | SHOWPLAN_ALL + | SHOWPLAN_TEXT + | SHOWPLAN_XML + | 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 + | XACT_ABORT + | 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 + : simple_column_name (ASC | DESC)? (COMMA simple_column_name (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+=simple_column_name (COMMA col+=simple_column_name)* + ; + +cursor_name + : id + | LOCAL_ID + ; + +simple_column_name + : 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..6ccc2c39ee8 --- /dev/null +++ b/contrib/babelfishpg_tsql/runtime/functions.c @@ -0,0 +1,542 @@ +#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 "utils/float.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); +PG_FUNCTION_INFO_V1(tsql_exp); + +/* 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) +{ + PG_RETURN_OID(procid_var); +} + +/* + * 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(); +} + +/* + * tsql_exp - returns the exponential function of arg1 + */ +Datum +tsql_exp(PG_FUNCTION_ARGS) +{ + float8 arg1 = PG_GETARG_FLOAT8(0); + float8 result; + + errno = 0; + result = exp(arg1); + if (errno == ERANGE && result != 0 && !isinf(result)) + result = get_float8_infinity(); + + if (unlikely(isinf(result)) && !isinf(arg1)) + float_overflow_error(); + PG_RETURN_FLOAT8(result); +} diff --git a/contrib/babelfishpg_tsql/sql/babelfishpg_tsql--1.0.0.sql b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql--1.0.0.sql new file mode 100644 index 00000000000..801002a81a0 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql--1.0.0.sql @@ -0,0 +1,16610 @@ +-- 1 "sql/babelfishpg_tsql.in" +-- 1 "" +-- 1 "" +-- 1 "sql/babelfishpg_tsql.in" + + + + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +-- 1 "sql/datatype.sql" 1 +-- Types with different default typmod behavior +SET enable_domain_typmod = TRUE; + +CREATE DOMAIN sys.CURSOR AS REFCURSOR; + +RESET enable_domain_typmod; +-- 8 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/datatype_string_operators.sql" 1 +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; +-- 9 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/sys.sql" 1 + +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; + + +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; +-- 10 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/sys_languages.sql" 1 + +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'))); +-- 11 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/sys_function_helpers.sql" 1 + + + +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; + + + + + +create or replace function sys.babelfish_ROUND3(x in numeric, y in int, z in int)returns numeric +AS +$body$ +BEGIN + + + + 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 + + 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; + 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; + + + 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_job_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 + , par_category_id + , var_notify_email_operator_id + , var_notify_netsend_operator_id + , var_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) + THEN + returncode := 1; + RETURN; + END IF; + + var_notify_email_operator_name := par_notify_email_operator_name; + + + 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); + + + SELECT LASTVAL() INTO par_job_id; + + + + + 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 + returncode := 1; + RETURN; + END IF; + + + 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 + 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 + 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 + returncode := 1; + RETURN; + END IF; + + + 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); + + + 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 + returncode := 1; + RETURN; + END IF; + + + + UPDATE sys.sysjobs + SET version_number = version_number + 1 + --, date_modified = GETDATE() + WHERE (job_id = par_job_id); + + + + 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); + + + 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 + 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 + WHERE (on_fail_step_id = var_step_id) + AND (job_id = par_job_id); + END IF; + + + SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring) INTO par_step_uid; + + + 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 + + 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; + + + 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 + , par_schedule_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_start_time + , par_active_end_date + , par_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 ; + + IF (var_retval <> 0) THEN + returncode := 1; + RETURN; + END IF; + + IF (par_schedule_uid IS NULL) + THEN + + 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 + ); + + + SELECT 0 , LASTVAL() + INTO var_retval, par_schedule_id; + + + 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 + + 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; + + + 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 + , '@schedule_id'::character varying + , par_schedule_name + , par_schedule_id + , var_sched_owner_sid + , NULL::integer + , NULL::integer) t + INTO par_schedule_name + , par_schedule_id + , var_sched_owner_sid + , var_retval ; + + IF (var_retval <> 0) THEN + returncode := 1; + RETURN; + END IF + + ; + 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; + END IF; + + + PERFORM sys.babelfish_sp_set_next_run (par_job_id, par_schedule_id); + + + 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 + returncode := (1); + RETURN; + END IF; + END IF; + + + + + SELECT category_id + INTO var_category_id + FROM sys.sysjobs + WHERE job_id = par_job_id; + + + 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 INTO var_err; + + + IF (par_delete_unused_schedule = 1) + THEN + + 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; + + + IF (par_delete_history = 1) + THEN + DELETE FROM sys.sysjobhistory + WHERE job_id = par_job_id; + END IF; + + + + --DROP TABLE "#temp_schedules_to_delete"; + END IF; + + + 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 + + SELECT LTRIM(RTRIM(par_name)) INTO par_name; + + + 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; + + IF (LOWER(UPPER(par_name)) = LOWER('ALL')) + THEN + SELECT - 1 INTO var_schedule_id; + + + + CREATE TEMPORARY TABLE "#temp_schedules_to_delete" (schedule_id INT NOT NULL) + + ; + + IF (par_keep_schedule = 0) + THEN + + 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))); + + 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 + 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; + + + DELETE FROM sys.sysjobschedules + WHERE (job_id = par_job_id); + + + 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) + 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 + 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 FROM sys.sysjobschedules + WHERE (job_id = par_job_id) + AND (schedule_id = var_schedule_id); + + + IF (par_keep_schedule = 0) + THEN + + 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 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"; + + + + 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 + returncode := 1; + RETURN; + END IF; + + + SELECT COALESCE(MAX(step_id), 0) + INTO var_max_step_id + FROM sys.sysjobsteps + WHERE (job_id = par_job_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; + + END IF; + + + + 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 + + UPDATE sys.sysjobsteps + SET step_id = step_id - 1 + WHERE (step_id > par_step_id) + AND (job_id = par_job_id); + + + 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); + + + 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); + + + 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 sys.sysjobs + SET version_number = version_number + 1 + --, date_modified = GETDATE() / + WHERE (job_id = par_job_id); + + + + + 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 + + SELECT COUNT(*) + INTO var_job_count + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id); + + + IF ((par_force_delete = 0) AND (var_job_count > 0)) + THEN + RAISE 'The schedule was not deleted because it is being used by one or more jobs.' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + DELETE FROM sys.sysjobschedules + WHERE schedule_id = par_schedule_id; + + + DELETE FROM sys.sysschedules + WHERE schedule_id = par_schedule_id; + + + 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 + + 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; + + + 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' + , '@schedule_id' + , par_schedule_name + , par_schedule_id + , var_sched_owner_sid + , NULL + , 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 + returncode := 1; + RETURN; + END IF; + + + IF (NOT EXISTS ( + SELECT * + FROM sys.sysjobschedules + WHERE (schedule_id = par_schedule_id) + AND (job_id = par_job_id))) + THEN + 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 0 -- ZZZ + INTO var_retval; + + + 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; +-- 7268 "sql/sys_function_helpers.sql" + -- PERFORM sys.babelfish_sp_delete_job (par_job_id := par_job_id); + + + 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; + + + CASE var_freq_type + WHEN 1 THEN + NULL; + + WHEN 4 THEN + BEGIN + cron_expression := + CASE + + + WHEN var_freq_subday_type = 4 THEN format('cron(*/%s * * * ? *)', var_freq_subday_interval::character varying) + WHEN var_freq_subday_type = 8 THEN format('cron(0 */%s * * ? *)', var_freq_subday_interval::character varying) + 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; + + + CASE var_freq_type + WHEN 1 THEN + NULL; + + WHEN 4 THEN + BEGIN + + + 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 = 2 THEN var_freq_subday_interval::character varying || ' second' + WHEN var_freq_subday_type = 4 THEN var_freq_subday_interval::character varying || ' minute' + WHEN var_freq_subday_type = 8 THEN var_freq_subday_interval::character varying || ' 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 + + + 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 + ; + + 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 + ; + + 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 + ; + + 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 + ; + + 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 + ; + 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 + + + ; + + 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 + + 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 + , 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 + , notify_netsend_operator_id = var_notify_netsend_operator_id + , notify_page_operator_id = var_notify_page_operator_id + , delete_level = par_delete_level, version_number = version_number + 1 + + + WHERE (job_id = par_job_id); + SELECT + 0 + INTO var_retval + + ; + ReturnCode := (var_retval); + RETURN + ; +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 + + SELECT + LTRIM(RTRIM(par_name)) + INTO par_name; + SELECT + LTRIM(RTRIM(par_new_name)) + INTO par_new_name + ; + + IF (par_new_name = '') THEN + SELECT + NULL + INTO par_new_name; + END IF + ; + 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 + + ; + + 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 + ; + 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 + , par_new_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_start_time + , par_active_end_date + , par_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 ; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + + ; + 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 + , version_number = version_number + 1 + WHERE (schedule_id = var_schedule_id); + SELECT + 0 + INTO var_retval + + ; + UPDATE sys.sysjobs + SET version_number = version_number + 1 + + WHERE (job_id = par_job_id); + ReturnCode := (var_retval); + RETURN + ; +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; + + 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; + + + + + + 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) + INTO par_job_name, par_job_id, var_job_owner_sid, var_retval + ; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF; + + + + 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; + + END IF; + + 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; + + + 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 par_proxy_name = '' THEN + SELECT NULL INTO var_new_proxy_id; + END IF; + + + 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 + ; + 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 + + ; + UPDATE sys.sysjobs + SET version_number = version_number + 1 + + WHERE (job_id = par_job_id) + ; + 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 + ; +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 + + 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 + ; + + IF (par_new_name = '') THEN + SELECT + NULL + INTO par_new_name; + END IF + ; + 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' + , '@schedule_id' + , par_name + , par_schedule_id + , var_cur_owner_sid + , NULL + , NULL) t + INTO par_name, par_schedule_id, var_cur_owner_sid, var_retval + ; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + + ; + + 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 (var_owner_sid IS NULL) THEN + SELECT + var_cur_owner_sid + INTO var_owner_sid; + END IF + ; + 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) + ; + + 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 + ; + 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 + , par_new_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_start_time + , par_active_end_date + , par_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 ; + + IF (var_retval <> 0) THEN + ReturnCode := (1); + RETURN; + END IF + + ; + 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 + , version_number = version_number + 1 + WHERE (schedule_id = par_schedule_id); + SELECT + 0 + INTO var_retval; + + ReturnCode := (var_retval); + RETURN + ; +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 + + 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) + + ) + ) + THEN + RAISE 'The specified % ("%") already exists.', 'par_name', par_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + IF (par_enabled <> 0) AND (par_enabled <> 1) THEN + RAISE 'The specified "%" is invalid (valid values are: %).', 'par_enabled', '0, 1' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + + IF (par_job_id IS NULL) THEN + IF (par_start_step_id <> 1) THEN + RAISE 'The specified "%" is invalid (valid values are: %).', 'par_start_step_id', '1' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + ELSE + + 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 + 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; + + + SELECT NULL INTO par_category_id; + + IF (par_category_name = '[DEFAULT]') + THEN + SELECT + CASE COALESCE(var_job_type, 1) + WHEN 1 THEN 0 + WHEN 2 THEN 2 + END + INTO par_category_id; + ELSE + SELECT 0 INTO par_category_id; + END IF; + + returncode := (0); + 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 + + SELECT LTRIM(RTRIM(par_date_name)) INTO par_date_name; + + + 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 + + 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 + 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; + + + 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); + + + IF (par_job_name IS NULL) + THEN + 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 + + IF (par_job_name IS NOT NULL) + THEN + + IF (SELECT COUNT(*) FROM sys.sysjobs WHERE name = par_job_name) > 1 + THEN + 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; + + + SELECT job_id + , owner_sid + INTO par_job_id + , par_owner_sid + FROM sys.sysjobs + WHERE (name = par_job_name); + + + IF (par_job_id IS NULL) + THEN + RAISE 'The specified % ("%") does not exist.', 'job_name', par_job_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + END IF; + + + 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 + + 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; + + + IF (var_hour > 23) THEN + RAISE 'The "%" supplied has an invalid %.', par_time_name, 'hour' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + IF (var_minute > 59) THEN + RAISE 'The "%" supplied has an invalid %.', par_time_name, 'minute' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + 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 + + 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; + + + SELECT COALESCE(MAX(step_id), 0) + INTO var_max_step_id + FROM sys.sysjobsteps + WHERE (job_id = par_job_id); + + + IF (par_step_id < 1) OR (par_step_id > var_max_step_id + 1) + 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; + + + IF ( + EXISTS ( + SELECT * + FROM sys.sysjobsteps + WHERE (job_id = par_job_id) AND (step_name = par_step_name) + ) + ) + THEN + RAISE 'The specified % ("%") already exists.', 'step_name', par_step_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + IF (par_on_success_action <> 1) + AND (par_on_success_action <> 2) + AND (par_on_success_action <> 3) + AND (par_on_success_action <> 4) + THEN + 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 + 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; + + + IF (par_on_fail_action <> 1) + AND (par_on_fail_action <> 2) + AND (par_on_fail_action <> 3) + AND (par_on_fail_action <> 4) + THEN + 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 + 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; + + + 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; + + + + IF (par_os_run_priority NOT IN (- 15, - 1, 0, 1, 15)) + THEN + RAISE 'The specified "%" is invalid (valid values are: %).', '@os_run_priority', '-15, -1, 0, 1, 15' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + IF ((par_flags < 0) OR (par_flags > 114)) THEN + 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 + RAISE 'The specified "%" is invalid (valid values are: %).', '@subsystem', 'TSQL' USING ERRCODE := '50000'; + returncode := (1); + RETURN; + END IF; + + + 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 + + SELECT LTRIM(RTRIM(par_name)) INTO par_name; + + + 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; + + + SELECT 0 INTO var_isAdmin; + + IF ( + EXISTS ( + SELECT * + FROM sys.sysschedules + WHERE (name = par_name) + ) + ) + THEN + RAISE 'The specified % ("%") already exists.', 'par_name', par_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + IF (UPPER(par_name) = 'ALL') + THEN + RAISE 'The specified "%" is invalid.', 'name' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + IF (par_enabled <> 0) AND (par_enabled <> 1) + THEN + RAISE 'The specified "%" is invalid (valid values are: %).', '@enabled', '0, 1' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + IF (par_freq_type = 2) + THEN + 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 + 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; + + + IF (par_freq_subday_type <> 0) AND (par_freq_subday_type NOT IN (1, 2, 4, 8)) + THEN + RAISE 'The specified "%" is invalid (valid values are: %).', 'freq_subday_type', '1, 2, 4, 8' USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + + + 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; + + + IF (par_active_end_date = 0) + THEN + + SELECT 99991231 INTO par_active_end_date; + END IF; + + IF (par_active_start_time = 0) + THEN + + SELECT 000000 INTO par_active_start_time; + END IF; + + IF (par_active_end_time = 0) + THEN + + SELECT 235959 INTO par_active_end_time; + END IF; + + + 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 + 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 + returncode := 1; + RETURN; + END IF; + + IF (par_active_end_date < par_active_start_date) + THEN + 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 + 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 + returncode := 1; + RETURN; + END IF; + + IF (par_active_start_time = par_active_end_time AND (par_freq_subday_type IN (2, 4, 8))) + THEN + 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) + OR (par_freq_type = 64) + OR (par_freq_type = 128)) + THEN + 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; + + returncode := 0; + RETURN; + END IF; + + IF (par_freq_subday_type = 0) + THEN + SELECT 1 INTO par_freq_subday_type; + END IF; + + IF ((par_freq_subday_type <> 1) + AND (par_freq_subday_type <> 2) + AND (par_freq_subday_type <> 4) + AND (par_freq_subday_type <> 8)) + THEN + 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)) + OR ((par_freq_subday_type = 2) AND (par_freq_subday_interval < 10)) + THEN + 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) + THEN + SELECT 0 INTO par_freq_recurrence_factor; + + IF (par_freq_interval < 1) THEN + 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) + THEN + IF (par_freq_interval < 1) OR (par_freq_interval > 127) + THEN + 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) + THEN + IF (par_freq_interval < 1) OR (par_freq_interval > 31) + THEN + 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) + THEN + IF (par_freq_relative_interval <> 1) + AND (par_freq_relative_interval <> 2) + AND (par_freq_relative_interval <> 4) + AND (par_freq_relative_interval <> 8) + AND (par_freq_relative_interval <> 16) + THEN + 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) + THEN + IF (par_freq_interval <> 1) + AND (par_freq_interval <> 2) + AND (par_freq_interval <> 3) + AND (par_freq_interval <> 4) + AND (par_freq_interval <> 5) + AND (par_freq_interval <> 6) + AND (par_freq_interval <> 7) + AND (par_freq_interval <> 8) + AND (par_freq_interval <> 9) + AND (par_freq_interval <> 10) + THEN + 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) + OR (par_freq_type = 16) + OR (par_freq_type = 32)) + AND (par_freq_recurrence_factor < 1) + THEN + 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; + + 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 + + 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 + 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; + + + IF (par_schedule_id IS NOT NULL) + THEN + + 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 + 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 + + IF (SELECT COUNT(*) FROM sys.sysschedules WHERE name = par_schedule_name) > 1 + THEN + 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; + + + SELECT schedule_id + , owner_sid + INTO par_schedule_id, par_owner_sid + FROM sys.sysschedules + WHERE (name = par_schedule_name); + + + IF (par_schedule_id IS NULL) + THEN + RAISE 'The specified % ("%") does not exist.', 'par_schedule_name', par_schedule_name USING ERRCODE := '50000'; + returncode := 1; + RETURN; + END IF; + END IF; + END IF; + + + 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 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; + + + + + +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 + + + + 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 +-- 9994 "sql/sys_function_helpers.sql" + 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'; +-- 12 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/sys_functions.sql" 1 +-- 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 $$ + + + +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; +-- 13 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/sys_cast.sql" 1 +-- 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; +-- 14 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/coerce.sql" 1 +-- 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(); +-- 15 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/sys_views.sql" 1 + +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; +-- 16 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/collation.sql" 1 +-- 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; +-- 17 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/sys_procedures.sql" 1 +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; +-- 18 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/ownership.sql" 1 +-- 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; + +-- Babelfish Specific Metadata Consistency Check +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; +-- 19 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/import_export_compatibility.sql" 1 +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; +-- 20 "sql/babelfishpg_tsql.in" 2 +-- 1 "sql/babelfishpg_tsql.sql" 1 +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; +-- 21 "sql/babelfishpg_tsql.in" 2 + + + + + + +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.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..e26107124ca --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql @@ -0,0 +1,1146 @@ +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, + CASE WHEN p.attoptions[1] LIKE 'bbf_original_name=%' THEN split_part(p.attoptions[1], '=', 2) + ELSE c.name END 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) JOIN + pg_attribute p ON (c.name = p.attname) + WHERE + c.is_sparse = 0 AND p.attnum >= 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, + s_tcv.collation_100 AS collation + from + sys.spt_tablecollations_view s_tcv + where + s_tcv.object_id = sys.object_id(@object) + 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; + +-- BABEL-2259: Support sp_databases System Stored Procedure +-- Lists databases that either reside in an instance of the SQL Server or +-- are accessible through a database gateway +DROP VIEW IF EXISTS sys.sp_databases_view CASCADE; + +CREATE OR REPLACE VIEW sys.sp_databases_view AS + SELECT CAST(database_name AS sys.SYSNAME), + -- DATABASE_SIZE returns a NULL value for databases larger than 2.15 TB + CASE WHEN (sum(table_size)/1024.0) > 2.15 * 1024.0 * 1024.0 * 1024.0 THEN NULL + ELSE CAST((sum(table_size)/1024.0) AS int) END as database_size, + CAST(NULL AS sys.VARCHAR(254)) as remarks + FROM ( + SELECT pg_catalog.pg_namespace.oid as schema_oid, + pg_catalog.pg_namespace.nspname as schema_name, + INT.name AS database_name, + coalesce(pg_relation_size(pg_catalog.pg_class.oid), 0) as table_size + FROM + sys.babelfish_namespace_ext EXT + JOIN sys.babelfish_sysdatabases INT ON EXT.dbid = INT.dbid + JOIN pg_catalog.pg_namespace ON pg_catalog.pg_namespace.nspname = EXT.nspname + LEFT JOIN pg_catalog.pg_class ON relnamespace = pg_catalog.pg_namespace.oid + ) t + GROUP BY database_name + ORDER BY database_name; +GRANT SELECT on sys.sp_databases_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_databases () +AS $$ +BEGIN + SELECT database_name as "DATABASE_NAME", + database_size as "DATABASE_SIZE", + remarks as "REMARKS" from sys.sp_databases_view; +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_databases TO PUBLIC; + +CREATE VIEW sys.sp_pkeys_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(seq AS smallint) AS KEY_SEQ, +CAST(t5.conname AS sys.sysname) AS PK_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 + JOIN pg_constraint t5 ON t1.oid = t5.conrelid + , generate_series(1,16) seq -- SQL server has max 16 columns per primary key +WHERE t5.contype = 'p' + AND CAST(t4.dtd_identifier AS smallint) = ANY (t5.conkey) + AND CAST(t4.dtd_identifier AS smallint) = t5.conkey[seq]; + +GRANT SELECT on sys.sp_pkeys_view TO PUBLIC; + +-- internal function in order to workaround BABEL-1597 +create function sys.sp_pkeys_internal( + in_table_name sys.nvarchar(384), + in_table_owner sys.nvarchar(384) = '', + in_table_qualifier sys.nvarchar(384) = '' +) +returns table( + out_table_qualifier sys.sysname, + out_table_owner sys.sysname, + out_table_name sys.sysname, + out_column_name sys.sysname, + out_key_seq smallint, + out_pk_name sys.sysname +) +as $$ +begin + return query + select * from sys.sp_pkeys_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) + order by table_qualifier, table_owner, table_name, key_seq; +end; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE PROCEDURE sys.sp_pkeys( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = '', + "@table_qualifier" sys.nvarchar(384) = '' +) +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_key_seq as key_seq, + out_pk_name as pk_name + from sys.sp_pkeys_internal(@table_name, @table_owner, @table_qualifier); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_pkeys TO PUBLIC; + +CREATE VIEW sys.sp_statistics_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, +CASE +WHEN t5.indisunique = 't' THEN CAST(0 AS smallint) +ELSE CAST(1 AS smallint) +END AS NON_UNIQUE, +CAST(t1.relname AS sys.sysname) AS INDEX_QUALIFIER, +-- the index name created by CREATE INDEX is re-mapped, find it (by checking +-- the ones not in pg_constraint) and restoring it back before display +CASE +WHEN t8.oid > 0 THEN CAST(t6.relname AS sys.sysname) +ELSE CAST(SUBSTRING(t6.relname,1,LENGTH(t6.relname)-32-LENGTH(t1.relname)) AS sys.sysname) +END AS INDEX_NAME, +CASE +WHEN t7.starelid > 0 THEN CAST(0 AS smallint) +ELSE + CASE + WHEN t5.indisclustered = 't' THEN CAST(1 AS smallint) + ELSE CAST(3 AS smallint) + END +END AS TYPE, +CAST(seq + 1 AS smallint) AS SEQ_IN_INDEX, +CAST(t4.column_name AS sys.sysname) AS COLUMN_NAME, +CAST('A' AS sys.varchar(1)) AS COLLATION, +CAST(t7.stadistinct AS int) AS CARDINALITY, +CAST(0 AS int) AS PAGES, --not supported +CAST(NULL AS sys.varchar(128)) AS FILTER_CONDITION +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 + JOIN (pg_catalog.pg_index t5 JOIN + pg_catalog.pg_class t6 ON t5.indexrelid = t6.oid) ON t1.oid = t5.indrelid + LEFT JOIN pg_catalog.pg_statistic t7 ON t1.oid = t7.starelid + LEFT JOIN pg_catalog.pg_constraint t8 ON t5.indexrelid = t8.conindid + , generate_series(0,31) seq -- SQL server has max 32 columns per index +WHERE CAST(t4.dtd_identifier AS smallint) = ANY (t5.indkey) + AND CAST(t4.dtd_identifier AS smallint) = t5.indkey[seq]; +GRANT SELECT on sys.sp_statistics_view TO PUBLIC; + +create function sys.sp_statistics_internal( + in_table_name sys.sysname, + in_table_owner sys.sysname = '', + in_table_qualifier sys.sysname = '', + in_index_name sys.sysname = '', + in_is_unique char = 'N', + in_accuracy char = 'Q' +) +returns table( + out_table_qualifier sys.sysname, + out_table_owner sys.sysname, + out_table_name sys.sysname, + out_non_unique smallint, + out_index_qualifier sys.sysname, + out_index_name sys.sysname, + out_type smallint, + out_seq_in_index smallint, + out_column_name sys.sysname, + out_collation sys.varchar(1), + out_cardinality int, + out_pages int, + out_filter_condition sys.varchar(128) +) +as $$ +begin + return query + select * from sys.sp_statistics_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_index_name,'')) = '' or index_name like in_index_name) + and ((in_is_unique = 'N') or (in_is_unique = 'Y' and non_unique = 0)) + order by non_unique, type, index_name, seq_in_index; +end; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE PROCEDURE sys.sp_statistics( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@table_qualifier" sys.sysname = '', + "@index_name" sys.sysname = '', + "@is_unique" char = 'N', + "@accuracy" char = 'Q' +) +AS $$ +BEGIN + select out_table_qualifier as table_qualifier, + out_table_owner as table_owner, + out_table_name as table_name, + out_non_unique as non_unique, + out_index_qualifier as index_qualifier, + out_index_name as index_name, + out_type as type, + out_seq_in_index as seq_in_index, + out_column_name as column_name, + out_collation as collation, + out_cardinality as cardinality, + out_pages as pages, + out_filter_condition as filter_condition + from sys.sp_statistics_internal(@table_name, @table_owner, @table_qualifier, @index_name, @is_unique, @accuracy); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_statistics TO PUBLIC; + +-- same as sp_statistics +CREATE OR REPLACE PROCEDURE sys.sp_statistics_100( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@table_qualifier" sys.sysname = '', + "@index_name" sys.sysname = '', + "@is_unique" char = 'N', + "@accuracy" char = 'Q' +) +AS $$ +BEGIN + select out_table_qualifier as table_qualifier, + out_table_owner as table_owner, + out_table_name as table_name, + out_non_unique as non_unique, + out_index_qualifier as index_qualifier, + out_index_name as index_name, + out_type as type, + out_seq_in_index as seq_in_index, + out_column_name as column_name, + out_collation as collation, + out_cardinality as cardinality, + out_pages as pages, + out_filter_condition as filter_condition + from sys.sp_statistics_internal(@table_name, @table_owner, @table_qualifier, @index_name, @is_unique, @accuracy); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_statistics_100 TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.printarg(IN "@message" TEXT) +AS $$ +BEGIN + PRINT @message; +END; +$$ LANGUAGE pltsql; +GRANT EXECUTE ON PROCEDURE sys.printarg(IN "@message" TEXT) TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_updatestats(IN "@resample" VARCHAR(8) DEFAULT 'NO') +AS $$ +BEGIN + IF sys.user_name() != 'dbo' THEN + RAISE EXCEPTION 'user does not have permission'; + END IF; + + IF lower("@resample") = 'resample' THEN + RAISE NOTICE 'ignoring resample option'; + ELSIF lower("@resample") != 'no' THEN + RAISE EXCEPTION 'Invalid option name %', "@resample"; + END IF; + + ANALYZE VERBOSE; + + CALL printarg('Statistics for all tables have been updated. Refer logs for details.'); +END; +$$ LANGUAGE plpgsql; +GRANT EXECUTE on PROCEDURE sys.sp_updatestats(IN "@resample" VARCHAR(8)) 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..7f7bf45e42b --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_functions.sql @@ -0,0 +1,1879 @@ +-- 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.square(in x double precision) returns double precision +AS +$BODY$ +DECLARE + res double precision; +BEGIN + res = pow(x, 2::float); + return res; +END; +$BODY$ +LANGUAGE plpgsql PARALLEL SAFE IMMUTABLE RETURNS NULL ON NULL INPUT; + +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; + +-- Duplicate function with arg TEXT since ANYELEMENT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.datepart(IN datepart PG_CATALOG.TEXT, IN arg TEXT) 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; + + -- Duplicate functions with arg TEXT since ANYELEMENT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.dateadd(IN datepart PG_CATALOG.TEXT, IN num INTEGER, IN startdate TEXT) RETURNS DATETIME +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.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; + +-- Duplicate functions with arg TEXT since ANYELEMENT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.datename(IN dp PG_CATALOG.TEXT, IN arg TEXT) 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 201332885::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; + +CREATE OR REPLACE FUNCTION sys.exp(IN arg DOUBLE PRECISION) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_tsql', 'tsql_exp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.exp(DOUBLE PRECISION) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.exp(IN arg NUMERIC) +RETURNS DOUBLE PRECISION +AS +$BODY$ +SELECT sys.exp(arg::DOUBLE PRECISION); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.exp(NUMERIC) TO PUBLIC; + +-- For numeric/decimal and float/double precision there is already inbuilt functions, +-- Following sign functions are for remaining datatypes +CREATE OR REPLACE FUNCTION sys.sign(IN arg INT) RETURNS INT AS +$BODY$ +SELECT + CASE + WHEN arg > 0 THEN 1::INT + WHEN arg < 0 THEN -1::INT + ELSE 0::INT + END; +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(INT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SMALLINT) RETURNS INT AS +$BODY$ +SELECT sys.sign(arg::INT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SMALLINT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SYS.TINYINT) RETURNS INT AS +$BODY$ +SELECT sys.sign(arg::INT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SYS.TINYINT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg BIGINT) RETURNS BIGINT AS +$BODY$ +SELECT + CASE + WHEN arg > 0::BIGINT THEN 1::BIGINT + WHEN arg < 0::BIGINT THEN -1::BIGINT + ELSE 0::BIGINT + END; +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(BIGINT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SYS.MONEY) RETURNS SYS.MONEY AS +$BODY$ +SELECT + CASE + WHEN arg > 0::SYS.MONEY THEN 1::SYS.MONEY + WHEN arg < 0::SYS.MONEY THEN -1::SYS.MONEY + ELSE 0::SYS.MONEY + END; +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SYS.MONEY) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SYS.SMALLMONEY) RETURNS SYS.MONEY AS +$BODY$ +SELECT sys.sign(arg::SYS.MONEY); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SYS.SMALLMONEY) TO PUBLIC; + +-- To handle remaining input datatypes +CREATE OR REPLACE FUNCTION sys.sign(IN arg ANYELEMENT) RETURNS SYS.FLOAT AS +$BODY$ +SELECT + sign(arg::SYS.FLOAT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(ANYELEMENT) TO PUBLIC; + +-- Duplicate functions with arg TEXT since ANYELEMNT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.sign(IN arg TEXT) RETURNS SYS.FLOAT AS +$BODY$ +SELECT + sign(arg::SYS.FLOAT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(TEXT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.lock_timeout() +RETURNS integer +LANGUAGE plpgsql +STRICT +AS $$ +declare return_value integer; +begin + return_value := (select s.setting FROM pg_catalog.pg_settings s where name = 'lock_timeout'); + RETURN return_value; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.lock_timeout() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.max_connections() +RETURNS integer +LANGUAGE plpgsql +STRICT +AS $$ +declare return_value integer; +begin + return_value := (select s.setting FROM pg_catalog.pg_settings s where name = 'max_connections'); + RETURN return_value; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.max_connections() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.trigger_nestlevel() +RETURNS integer +LANGUAGE plpgsql +STRICT +AS $$ +declare return_value integer; +begin + return_value := (select pg_trigger_depth()); + RETURN return_value; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.trigger_nestlevel() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.schema_name() +RETURNS sys.sysname +LANGUAGE plpgsql +STRICT +AS $function$ +begin + RETURN (select orig_name from sys.babelfish_namespace_ext ext + where ext.nspname = (select current_schema()) and ext.dbid::oid = sys.db_id()::oid)::sys.sysname; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$function$ +; +GRANT EXECUTE ON FUNCTION sys.schema_name() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.original_login() +RETURNS sys.sysname +LANGUAGE plpgsql +STRICT +AS $$ +declare return_value text; +begin + RETURN (select session_user)::sys.sysname; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.original_login() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.columnproperty(object_id oid, property name, property_name text) +RETURNS integer +LANGUAGE plpgsql +STRICT +AS $$ + +declare extra_bytes CONSTANT integer := 4; +declare return_value integer; +begin + return_value := ( + select + case LOWER(property_name) + when 'charmaxlen' then + (select CASE WHEN a.atttypmod > 0 THEN a.atttypmod - extra_bytes ELSE NULL END from pg_catalog.pg_attribute a where a.attrelid = object_id and a.attname = property) + when 'allowsnull' then + (select CASE WHEN a.attnotnull THEN 0 ELSE 1 END from pg_catalog.pg_attribute a where a.attrelid = object_id and a.attname = property) + else + null + end + ); + + RETURN return_value::integer; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.columnproperty(object_id oid, property name, property_name text) TO PUBLIC; + +COMMENT ON FUNCTION sys.columnproperty +IS 'This function returns column or parameter information. Currently only works with "charmaxlen", and "allowsnull" otherwise returns 0.'; + +-- substring -- +CREATE OR REPLACE FUNCTION sys.substring(string TEXT, i INTEGER, j INTEGER) +RETURNS sys.VARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.VARCHAR, i INTEGER, j INTEGER) +RETURNS sys.VARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.VARCHAR, i INTEGER, j INTEGER) +RETURNS sys.VARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.NVARCHAR, i INTEGER, j INTEGER) +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.NCHAR, i INTEGER, j INTEGER) +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; 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..953cbbd8c80 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_views.sql @@ -0,0 +1,1100 @@ +/* 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_schema_privilege(s.oid, 'USAGE') +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 = a.attcollation +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') +and has_schema_privilege(s.oid, 'USAGE') +and a.attnum > 0; +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_schema_privilege(s.oid, 'USAGE') +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; + +-- internal function in order to workaround BABEL-1597 +CREATE FUNCTION sys.columns_internal() +RETURNS TABLE ( + out_object_id int, + out_name sys.sysname, + out_column_id int, + out_system_type_id int, + out_user_type_id int, + out_max_length smallint, + out_precision sys.tinyint, + out_scale sys.tinyint, + out_collation_name sys.sysname, + out_collation_id int, + out_offset smallint, + out_is_nullable sys.bit, + out_is_ansi_padded sys.bit, + out_is_rowguidcol sys.bit, + out_is_identity sys.bit, + out_is_computed sys.bit, + out_is_filestream sys.bit, + out_is_replicated sys.bit, + out_is_non_sql_subscribed sys.bit, + out_is_merge_published sys.bit, + out_is_dts_replicated sys.bit, + out_is_xml_document sys.bit, + out_xml_collection_id int, + out_default_object_id int, + out_rule_object_id int, + out_is_sparse sys.bit, + out_is_column_set sys.bit, + out_generated_always_type sys.tinyint, + out_generated_always_type_desc sys.nvarchar(60), + out_encryption_type int, + out_encryption_type_desc sys.nvarchar(64), + out_encryption_algorithm_name sys.sysname, + out_column_encryption_key_id int, + out_column_encryption_key_database_name sys.sysname, + out_is_hidden sys.bit, + out_is_masked sys.bit, + out_graph_type int, + out_graph_type_desc sys.nvarchar(60) +) +AS +$$ +BEGIN + RETURN QUERY + SELECT CAST(c.oid AS int), + CAST(a.attname AS sys.sysname), + CAST(a.attnum AS int), + CAST(t.oid AS int), + CAST(t.oid AS int), + CAST(a.attlen AS smallint), + CAST(case when isc.datetime_precision is null then coalesce(isc.numeric_precision, 0) else isc.datetime_precision end AS sys.tinyint), + CAST(coalesce(isc.numeric_scale, 0) AS sys.tinyint), + CAST(coll.collname AS sys.sysname), + CAST(a.attcollation AS int), + CAST(a.attnum AS smallint), + CAST(case when a.attnotnull then 0 else 1 end AS sys.bit), + CAST(case when t.typname in ('bpchar', 'nchar', 'binary') then 1 else 0 end AS sys.bit), + CAST(0 AS sys.bit), + CAST(case when a.attidentity <> ''::"char" then 1 else 0 end AS sys.bit), + CAST(case when a.attgenerated <> ''::"char" then 1 else 0 end AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS int), + CAST(coalesce(d.oid, 0) AS int), + CAST(coalesce((select oid from pg_constraint where conrelid = t.oid + and contype = 'c' and a.attnum = any(conkey) limit 1), 0) AS int), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.tinyint), + CAST('NOT_APPLICABLE' AS sys.nvarchar(60)), + CAST(null AS int), + CAST(null AS sys.nvarchar(64)), + CAST(null AS sys.sysname), + CAST(null AS int), + CAST(null AS sys.sysname), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(null AS int), + CAST(null AS sys.nvarchar(60)) + 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 + INNER JOIN information_schema.columns isc ON c.relname = isc.table_name AND s.nspname = isc.table_schema AND a.attname = isc.column_name + LEFT JOIN pg_attrdef d ON c.oid = d.adrelid AND a.attnum = d.adnum + LEFT JOIN pg_collation coll ON coll.oid = a.attcollation + WHERE NOT a.attisdropped + AND a.attnum > 0 + -- 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', 'sys') + AND has_schema_privilege(s.oid, 'USAGE') + AND has_column_privilege(quote_ident(s.nspname) ||'.'||quote_ident(c.relname), a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +END; +$$ +language plpgsql; + +create or replace view sys.columns AS +select out_object_id::oid as object_id + , out_name::name as name + , out_column_id::smallint as column_id + , out_system_type_id::oid as system_type_id + , out_user_type_id::oid as user_type_id + , out_max_length::smallint as max_length + , out_precision::integer as precision + , out_scale::integer as scale + , out_collation_name::name as collation_name + , out_is_nullable::integer as is_nullable + , out_is_ansi_padded::integer as is_ansi_padded + , out_is_rowguidcol::integer as is_rowguidcol + , out_is_identity::integer as is_identity + , out_is_computed::integer as is_computed + , out_is_filestream::integer as is_filestream + , out_is_replicated::integer as is_replicated + , out_is_non_sql_subscribed::integer as is_non_sql_subscribed + , out_is_merge_published::integer as is_merge_published + , out_is_dts_replicated::integer as is_dts_replicated + , out_is_xml_document::integer as is_xml_document + , out_xml_collection_id::integer as xml_collection_id + , out_default_object_id::oid as default_object_id + , out_rule_object_id::oid as rule_object_id + , out_is_sparse::integer as is_sparse + , out_is_column_set::integer as is_column_set + , out_generated_always_type::integer as generated_always_type + , out_generated_always_type_desc::varchar(60) as generated_always_type_desc + , out_encryption_type::integer as encryption_type + , out_encryption_type_desc::varchar(64) as encryption_type_desc + , out_encryption_algorithm_name::varchar as encryption_algorithm_name + , out_column_encryption_key_id::integer as column_encryption_key_id + , out_column_encryption_key_database_name::varchar as column_encryption_key_database_name + , out_is_hidden::integer as is_hidden + , out_is_masked::integer as is_masked + , out_graph_type as graph_type + , out_graph_type_desc as graph_type_desc +from sys.columns_internal(); +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_schema_privilege(s.oid, 'USAGE') +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 + , c.oid as index_id +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::integer as id + , null::integer as status + , null::binary(6) as first + , i.type::smallint as indid + , null::binary(6) as root + , 0::smallint as minlen + , 1::smallint as keycnt + , null::smallint as groupid + , 0 as dpages + , 0 as reserved + , 0 as used + , 0::bigint as rowcnt + , 0 as rowmodctr + , 0 as reserved3 + , 0 as reserved4 + , 0::smallint as xmaxlen + , null::smallint as maxirow + , 90::sys.tinyint as "OrigFillFactor" + , 0::sys.tinyint as "StatVersion" + , 0 as reserved2 + , null::binary(6) as "FirstIAM" + , 0::smallint as impid + , 0::smallint as lockflags + , 0 as pgmodctr + , null::sys.varbinary(816) as keys + , i.name::sys.sysname as name + , null::sys.image as statblob + , 0 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 +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.all_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 p.relkind = 'S' +and has_schema_privilege(s.oid, 'USAGE'); +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; + +create or replace view sys.default_constraints +AS +select CAST(('DF_' || o.relname || '_' || d.oid) as sys.sysname) as name + , d.oid as object_id + , null::int as principal_id + , o.relnamespace as schema_id + , d.adrelid as parent_object_id + , 'D'::char(2) as type + , 'DEFAULT_CONSTRAINT'::sys.nvarchar(60) AS type_desc + , null::timestamp as create_date + , null::timestamp as modified_date + , 0::sys.bit as is_ms_shipped + , 0::sys.bit as is_published + , 0::sys.bit as is_schema_published + , d.adnum::int as parent_column_id + , pg_get_expr(d.adbin, d.adrelid) as definition + , 1::sys.bit as is_system_named +from pg_catalog.pg_attrdef as d +inner join pg_catalog.pg_class as o on (d.adrelid = o.oid); +GRANT SELECT ON sys.default_constraints TO PUBLIC; + +CREATE OR REPLACE VIEW sys.computed_columns +AS +SELECT out_object_id as object_id + , out_name as name + , out_column_id as column_id + , out_system_type_id as system_type_id + , out_user_type_id as user_type_id + , out_max_length as max_length + , out_precision as precision + , out_scale as scale + , out_collation_name as collation_name + , out_is_nullable as is_nullable + , out_is_ansi_padded as is_ansi_padded + , out_is_rowguidcol as is_rowguidcol + , out_is_identity as is_identity + , out_is_computed as is_computed + , out_is_filestream as is_filestream + , out_is_replicated as is_replicated + , out_is_non_sql_subscribed as is_non_sql_subscribed + , out_is_merge_published as is_merge_published + , out_is_dts_replicated as is_dts_replicated + , out_is_xml_document as is_xml_document + , out_xml_collection_id as xml_collection_id + , out_default_object_id as default_object_id + , out_rule_object_id as rule_object_id + , out_is_sparse as is_sparse + , out_is_column_set as is_column_set + , out_generated_always_type as generated_always_type + , out_generated_always_type_desc as generated_always_type_desc + , out_encryption_type as encryption_type + , out_encryption_type_desc as encryption_type_desc + , out_encryption_algorithm_name as encryption_algorithm_name + , out_column_encryption_key_id as column_encryption_key_id + , out_column_encryption_key_database_name as column_encryption_key_database_name + , out_is_hidden as is_hidden + , out_is_masked as is_masked + , out_graph_type as graph_type + , out_graph_type_desc as graph_type_desc + , substring(pg_get_expr(d.adbin, d.adrelid), 1, 4000)::sys.nvarchar(4000) AS definition + , 1::sys.bit AS uses_database_collation + , 0::sys.bit AS is_persisted +FROM sys.columns_internal() sc +INNER JOIN pg_attribute a ON sc.out_name = a.attname AND sc.out_column_id = a.attnum +INNER JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum +WHERE a.attgenerated = 's' AND sc.out_is_computed::integer = 1; +GRANT SELECT ON sys.computed_columns TO PUBLIC; + +create or replace view sys.index_columns +as +select i.indrelid::integer as object_id + , i.indexrelid::integer as index_id + , a.attrelid::integer as index_column_id + , a.attnum::integer as column_id + , a.attnum::sys.tinyint as key_ordinal + , 0::sys.tinyint as partition_ordinal + , 0::sys.bit as is_descending_key + , 1::sys.bit as is_included_column +from pg_index as i +inner join pg_catalog.pg_attribute a on i.indexrelid = a.attrelid; +GRANT SELECT ON sys.index_columns TO PUBLIC; + +CREATE or replace VIEW sys.check_constraints AS +SELECT CAST(c.conname as sys.sysname) as name + , oid::integer as object_id + , c.connamespace::integer as principal_id + , c.connamespace::integer as schema_id + , conrelid::integer as parent_object_id + , 'C'::char(2) as type + , 'CHECK_CONSTRAINT'::sys.nvarchar(60) as type_desc + , null::sys.datetime as create_date + , null::sys.datetime as modify_date + , 0::sys.bit as is_ms_shipped + , 0::sys.bit as is_published + , 0::sys.bit as is_schema_published + , 0::sys.bit as is_disabled + , 0::sys.bit as is_not_for_replication + , 0::sys.bit as is_not_trusted + , c.conkey[1]::integer AS parent_column_id + , substring(pg_get_constraintdef(c.oid) from 7) AS definition + , 1::sys.bit as uses_database_collation + , 0::sys.bit as is_system_named +FROM pg_catalog.pg_constraint as c +WHERE c.contype = 'c' and c.conrelid != 0; +GRANT SELECT ON sys.check_constraints TO PUBLIC; + +-- internal function that returns relevant info needed +-- by sys.syscolumns view for all procedure parameters. +-- This separate function was needed to workaround BABEL-1597 +CREATE FUNCTION sys.proc_param_helper() +RETURNS TABLE ( + name sys.sysname, + id int, + xtype int, + colid smallint, + collationid int, + prec smallint, + scale int, + isoutparam int, + collation sys.sysname +) +AS +$$ +BEGIN +RETURN QUERY +select params.parameter_name::sys.sysname + , pgproc.oid::int + , CAST(case when pgproc.proallargtypes is null then split_part(pgproc.proargtypes::varchar, ' ', params.ordinal_position) + else split_part(btrim(pgproc.proallargtypes::text,'{}'), ',', params.ordinal_position) end AS int) + , params.ordinal_position::smallint + , coll.oid::int + , params.numeric_precision::smallint + , params.numeric_scale::int + , case params.parameter_mode when 'OUT' then 1 when 'INOUT' then 1 else 0 end + , params.collation_name::sys.sysname +from information_schema.routines routine +left join information_schema.parameters params + on routine.specific_schema = params.specific_schema + and routine.specific_name = params.specific_name +left join pg_collation coll on coll.collname = params.collation_name +/* assuming routine.specific_name is constructed by concatenating procedure name and oid */ +left join pg_proc pgproc on routine.specific_name = nameconcatoid(pgproc.proname, pgproc.oid) +where routine.routine_schema not in ('pg_catalog', 'information_schema') + and routine.routine_type = 'PROCEDURE'; +END; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE VIEW sys.syscolumns AS +SELECT out_name as name + , out_object_id as id + , out_system_type_id as xtype + , 0::sys.tinyint as typestat + , (case when out_user_type_id < 32767 then out_user_type_id else null end)::smallint as xusertype + , out_max_length as length + , 0::sys.tinyint as xprec + , 0::sys.tinyint as xscale + , out_column_id::smallint as colid + , 0::smallint as xoffset + , 0::sys.tinyint as bitpos + , 0::sys.tinyint as reserved + , 0::smallint as colstat + , out_default_object_id::int as cdefault + , out_rule_object_id::int as domain + , 0::smallint as number + , 0::smallint as colorder + , null::sys.varbinary(8000) as autoval + , out_offset as offset + , out_collation_id as collationid + , (case out_is_nullable::int when 1 then 8 else 0 end + + case out_is_identity::int when 1 then 128 else 0 end)::sys.tinyint as status + , out_system_type_id as type + , (case when out_user_type_id < 32767 then out_user_type_id else null end)::smallint as usertype + , null::varchar(255) as printfmt + , out_precision::smallint as prec + , out_scale::int as scale + , out_is_computed::int as iscomputed + , 0::int as isoutparam + , out_is_nullable::int as isnullable + , out_collation_name::sys.sysname as collation +FROM sys.columns_internal() +union all +SELECT p.name + , p.id + , p.xtype + , 0::sys.tinyint as typestat + , (case when p.xtype < 32767 then p.xtype else null end)::smallint as xusertype + , null as length + , 0::sys.tinyint as xprec + , 0::sys.tinyint as xscale + , p.colid + , 0::smallint as xoffset + , 0::sys.tinyint as bitpos + , 0::sys.tinyint as reserved + , 0::smallint as colstat + , null::int as cdefault + , null::int as domain + , 0::smallint as number + , 0::smallint as colorder + , null::sys.varbinary(8000) as autoval + , 0::smallint as offset + , collationid + , (case p.isoutparam when 1 then 64 else 0 end)::sys.tinyint as status + , p.xtype as type + , (case when p.xtype < 32767 then p.xtype else null end)::smallint as usertype + , null::varchar(255) as printfmt + , p.prec + , p.scale + , 0::int as iscomputed + , p.isoutparam + , 1::int as isnullable + , p.collation +FROM sys.proc_param_helper() as p; +GRANT SELECT ON sys.syscolumns 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/sql/upgrades/babelfishpg_tsql--1.0.0--1.1.0.sql b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.0.0--1.1.0.sql new file mode 100644 index 00000000000..c64e9eb27e7 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.0.0--1.1.0.sql @@ -0,0 +1,1282 @@ +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION ""babelfishpg_tsql"" UPDATE TO '1.1.0'" to load this file. \quit + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +/* Caution: Be careful while dropping an object in a minor version upgrade + * script as the object might be getting used in some user defined + * objects and dropping it here might result in upgrade failure or + * even user defined objects getting dropped. + * The following sys.sysindexes view was not working previously, so dropping + * it here is ok. + */ +DROP VIEW IF EXISTS sys.sysindexes; + +CREATE FUNCTION sys.columns_internal() +RETURNS TABLE ( + out_object_id int, + out_name sys.sysname, + out_column_id int, + out_system_type_id int, + out_user_type_id int, + out_max_length smallint, + out_precision sys.tinyint, + out_scale sys.tinyint, + out_collation_name sys.sysname, + out_collation_id int, + out_offset smallint, + out_is_nullable sys.bit, + out_is_ansi_padded sys.bit, + out_is_rowguidcol sys.bit, + out_is_identity sys.bit, + out_is_computed sys.bit, + out_is_filestream sys.bit, + out_is_replicated sys.bit, + out_is_non_sql_subscribed sys.bit, + out_is_merge_published sys.bit, + out_is_dts_replicated sys.bit, + out_is_xml_document sys.bit, + out_xml_collection_id int, + out_default_object_id int, + out_rule_object_id int, + out_is_sparse sys.bit, + out_is_column_set sys.bit, + out_generated_always_type sys.tinyint, + out_generated_always_type_desc sys.nvarchar(60), + out_encryption_type int, + out_encryption_type_desc sys.nvarchar(64), + out_encryption_algorithm_name sys.sysname, + out_column_encryption_key_id int, + out_column_encryption_key_database_name sys.sysname, + out_is_hidden sys.bit, + out_is_masked sys.bit, + out_graph_type int, + out_graph_type_desc sys.nvarchar(60) +) +AS +$$ +BEGIN + RETURN QUERY + SELECT CAST(c.oid AS int), + CAST(a.attname AS sys.sysname), + CAST(a.attnum AS int), + CAST(t.oid AS int), + CAST(t.oid AS int), + CAST(a.attlen AS smallint), + CAST(case when isc.datetime_precision is null then coalesce(isc.numeric_precision, 0) else isc.datetime_precision end AS sys.tinyint), + CAST(coalesce(isc.numeric_scale, 0) AS sys.tinyint), + CAST(coll.collname AS sys.sysname), + CAST(a.attcollation AS int), + CAST(a.attnum AS smallint), + CAST(case when a.attnotnull then 0 else 1 end AS sys.bit), + CAST(case when t.typname in ('bpchar', 'nchar', 'binary') then 1 else 0 end AS sys.bit), + CAST(0 AS sys.bit), + CAST(case when a.attidentity <> ''::"char" then 1 else 0 end AS sys.bit), + CAST(case when a.attgenerated <> ''::"char" then 1 else 0 end AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS int), + CAST(coalesce(d.oid, 0) AS int), + CAST(coalesce((select oid from pg_constraint where conrelid = t.oid + and contype = 'c' and a.attnum = any(conkey) limit 1), 0) AS int), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(0 AS sys.tinyint), + CAST('NOT_APPLICABLE' AS sys.nvarchar(60)), + CAST(null AS int), + CAST(null AS sys.nvarchar(64)), + CAST(null AS sys.sysname), + CAST(null AS int), + CAST(null AS sys.sysname), + CAST(0 AS sys.bit), + CAST(0 AS sys.bit), + CAST(null AS int), + CAST(null AS sys.nvarchar(60)) + 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 + INNER JOIN information_schema.columns isc ON c.relname = isc.table_name AND s.nspname = isc.table_schema AND a.attname = isc.column_name + LEFT JOIN pg_attrdef d ON c.oid = d.adrelid AND a.attnum = d.adnum + LEFT JOIN pg_collation coll ON coll.oid = a.attcollation + WHERE NOT a.attisdropped + AND a.attnum > 0 + -- 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', 'sys') + AND has_schema_privilege(s.oid, 'USAGE') + AND has_column_privilege(quote_ident(s.nspname) ||'.'||quote_ident(c.relname), a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +END; +$$ +language plpgsql; + +create or replace view sys.columns AS +select out_object_id::oid as object_id + , out_name::name as name + , out_column_id::smallint as column_id + , out_system_type_id::oid as system_type_id + , out_user_type_id::oid as user_type_id + , out_max_length::smallint as max_length + , out_precision::integer as precision + , out_scale::integer as scale + , out_collation_name::name as collation_name + , out_is_nullable::integer as is_nullable + , out_is_ansi_padded::integer as is_ansi_padded + , out_is_rowguidcol::integer as is_rowguidcol + , out_is_identity::integer as is_identity + , out_is_computed::integer as is_computed + , out_is_filestream::integer as is_filestream + , out_is_replicated::integer as is_replicated + , out_is_non_sql_subscribed::integer as is_non_sql_subscribed + , out_is_merge_published::integer as is_merge_published + , out_is_dts_replicated::integer as is_dts_replicated + , out_is_xml_document::integer as is_xml_document + , out_xml_collection_id::integer as xml_collection_id + , out_default_object_id::oid as default_object_id + , out_rule_object_id::oid as rule_object_id + , out_is_sparse::integer as is_sparse + , out_is_column_set::integer as is_column_set + , out_generated_always_type::integer as generated_always_type + , out_generated_always_type_desc::varchar(60) as generated_always_type_desc + , out_encryption_type::integer as encryption_type + , out_encryption_type_desc::varchar(64) as encryption_type_desc + , out_encryption_algorithm_name::varchar as encryption_algorithm_name + , out_column_encryption_key_id::integer as column_encryption_key_id + , out_column_encryption_key_database_name::varchar as column_encryption_key_database_name + , out_is_hidden::integer as is_hidden + , out_is_masked::integer as is_masked + , out_graph_type as graph_type + , out_graph_type_desc as graph_type_desc +from sys.columns_internal(); +GRANT SELECT ON sys.columns 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_schema_privilege(s.oid, 'USAGE') +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 = a.attcollation +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') +and has_schema_privilege(s.oid, 'USAGE') +and a.attnum > 0; +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_schema_privilege(s.oid, 'USAGE') +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.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_schema_privilege(s.oid, 'USAGE') +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.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 +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.all_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 p.relkind = 'S' +and has_schema_privilege(s.oid, 'USAGE'); +GRANT SELECT ON sys.all_objects TO PUBLIC; + +create or replace function sys.square(in x double precision) returns double precision +AS +$BODY$ +DECLARE + res double precision; +BEGIN + res = pow(x, 2::float); + return res; +END; +$BODY$ +LANGUAGE plpgsql PARALLEL SAFE IMMUTABLE RETURNS NULL ON NULL INPUT; + +create or replace view sys.sysindexes as +select + i.object_id::integer as id + , null::integer as status + , null::binary(6) as first + , i.type::smallint as indid + , null::binary(6) as root + , 0::smallint as minlen + , 1::smallint as keycnt + , null::smallint as groupid + , 0 as dpages + , 0 as reserved + , 0 as used + , 0::bigint as rowcnt + , 0 as rowmodctr + , 0 as reserved3 + , 0 as reserved4 + , 0::smallint as xmaxlen + , null::smallint as maxirow + , 90::sys.tinyint as "OrigFillFactor" + , 0::sys.tinyint as "StatVersion" + , 0 as reserved2 + , null::binary(6) as "FirstIAM" + , 0::smallint as impid + , 0::smallint as lockflags + , 0 as pgmodctr + , null::sys.varbinary(816) as keys + , i.name::sys.sysname as name + , null::sys.image as statblob + , 0 as maxlen + , 0 as rows +from sys.indexes i; +GRANT SELECT ON sys.sysindexes TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.exp(IN arg DOUBLE PRECISION) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_tsql', 'tsql_exp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.exp(DOUBLE PRECISION) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.exp(IN arg NUMERIC) +RETURNS DOUBLE PRECISION +AS +$BODY$ +SELECT sys.exp(arg::DOUBLE PRECISION); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.exp(NUMERIC) TO PUBLIC; + +-- BABEL-2259: Support sp_databases System Stored Procedure +-- Lists databases that either reside in an instance of the SQL Server or +-- are accessible through a database gateway + +CREATE OR REPLACE VIEW sys.sp_databases_view AS + SELECT CAST(database_name AS sys.SYSNAME), + -- DATABASE_SIZE returns a NULL value for databases larger than 2.15 TB + CASE WHEN (sum(table_size)/1024.0) > 2.15 * 1024.0 * 1024.0 * 1024.0 THEN NULL + ELSE CAST((sum(table_size)/1024.0) AS int) END as database_size, + CAST(NULL AS sys.VARCHAR(254)) as remarks + FROM ( + SELECT pg_catalog.pg_namespace.oid as schema_oid, + pg_catalog.pg_namespace.nspname as schema_name, + INT.name AS database_name, + coalesce(pg_relation_size(pg_catalog.pg_class.oid), 0) as table_size + FROM + sys.babelfish_namespace_ext EXT + JOIN sys.babelfish_sysdatabases INT ON EXT.dbid = INT.dbid + JOIN pg_catalog.pg_namespace ON pg_catalog.pg_namespace.nspname = EXT.nspname + LEFT JOIN pg_catalog.pg_class ON relnamespace = pg_catalog.pg_namespace.oid + ) t + GROUP BY database_name + ORDER BY database_name; +GRANT SELECT on sys.sp_databases_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_databases () +AS $$ +BEGIN + SELECT database_name as "DATABASE_NAME", + database_size as "DATABASE_SIZE", + remarks as "REMARKS" from sys.sp_databases_view; +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_databases TO PUBLIC; + +-- For numeric/decimal and float/double precision there is already inbuilt functions, +-- Following sign functions are for remaining datatypes +CREATE OR REPLACE FUNCTION sys.sign(IN arg INT) RETURNS INT AS +$BODY$ +SELECT + CASE + WHEN arg > 0 THEN 1::INT + WHEN arg < 0 THEN -1::INT + ELSE 0::INT + END; +$BODY$ +STRICT +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(INT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SMALLINT) RETURNS INT AS +$BODY$ +SELECT sys.sign(arg::INT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SMALLINT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SYS.TINYINT) RETURNS INT AS +$BODY$ +SELECT sys.sign(arg::INT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SYS.TINYINT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg BIGINT) RETURNS BIGINT AS +$BODY$ +SELECT + CASE + WHEN arg > 0::BIGINT THEN 1::BIGINT + WHEN arg < 0::BIGINT THEN -1::BIGINT + ELSE 0::BIGINT + END; +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(BIGINT) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SYS.MONEY) RETURNS SYS.MONEY AS +$BODY$ +SELECT + CASE + WHEN arg > 0::SYS.MONEY THEN 1::SYS.MONEY + WHEN arg < 0::SYS.MONEY THEN -1::SYS.MONEY + ELSE 0::SYS.MONEY + END; +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SYS.MONEY) TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sign(IN arg SYS.SMALLMONEY) RETURNS SYS.MONEY AS +$BODY$ +SELECT sys.sign(arg::SYS.MONEY); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(SYS.SMALLMONEY) TO PUBLIC; + +-- To handle remaining input datatypes +CREATE OR REPLACE FUNCTION sys.sign(IN arg ANYELEMENT) RETURNS SYS.FLOAT AS +$BODY$ +SELECT + sign(arg::SYS.FLOAT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(ANYELEMENT) TO PUBLIC; + +-- Duplicate functions with arg TEXT since ANYELEMNT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.sign(IN arg TEXT) RETURNS SYS.FLOAT AS +$BODY$ +SELECT + sign(arg::SYS.FLOAT); +$BODY$ +LANGUAGE SQL IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.sign(TEXT) TO PUBLIC; +CREATE OR REPLACE FUNCTION sys.lock_timeout() + RETURNS integer + LANGUAGE plpgsql + STRICT + AS $$ + declare return_value integer; + begin + return_value := (select s.setting FROM pg_catalog.pg_settings s where name = 'lock_timeout'); + RETURN return_value; + EXCEPTION + WHEN others THEN + RETURN NULL; + END; + $$; + GRANT EXECUTE ON FUNCTION sys.lock_timeout() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.max_connections() +RETURNS integer +LANGUAGE plpgsql +STRICT +AS $$ +declare return_value integer; +begin + return_value := (select s.setting FROM pg_catalog.pg_settings s where name = 'max_connections'); + RETURN return_value; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.max_connections() TO PUBLIC; + + CREATE OR REPLACE FUNCTION sys.trigger_nestlevel() + RETURNS integer + LANGUAGE plpgsql + STRICT + AS $$ + declare return_value integer; + begin + return_value := (select pg_trigger_depth()); + RETURN return_value; + EXCEPTION + WHEN others THEN + RETURN NULL; + END; + $$; + GRANT EXECUTE ON FUNCTION sys.trigger_nestlevel() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.schema_name() +RETURNS sys.sysname +LANGUAGE plpgsql +STRICT +AS $function$ +begin + RETURN (select orig_name from sys.babelfish_namespace_ext ext + where ext.nspname = (select current_schema()) and ext.dbid::oid = sys.db_id()::oid)::sys.sysname; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$function$ +; +GRANT EXECUTE ON FUNCTION sys.schema_name() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.original_login() +RETURNS sys.sysname +LANGUAGE plpgsql +STRICT +AS $$ +declare return_value text; +begin + RETURN (select session_user)::sys.sysname; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.original_login() TO PUBLIC; + + CREATE OR REPLACE FUNCTION sys.columnproperty(object_id oid, property name, property_name text) + RETURNS integer + LANGUAGE plpgsql + STRICT + AS $$ + declare extra_bytes CONSTANT integer := 4; + declare return_value integer; + begin + return_value := ( + select + case LOWER(property_name) + when 'charmaxlen' then + (select CASE WHEN a.atttypmod > 0 THEN a.atttypmod - extra_bytes ELSE NULL END from pg_catalog.pg_attribute a where a.attrelid = object_id and a.attname = property) + when 'allowsnull' then + (select CASE WHEN a.attnotnull THEN 0 ELSE 1 END from pg_catalog.pg_attribute a where a.attrelid = object_id and a.attname = property) + else + null + end + ); + + RETURN return_value::integer; + EXCEPTION + WHEN others THEN + RETURN NULL; + END; + $$; + GRANT EXECUTE ON FUNCTION sys.columnproperty(object_id oid, property name, property_name text) TO PUBLIC; + + COMMENT ON FUNCTION sys.columnproperty + IS 'This function returns column or parameter information. Currently only works with "charmaxlen", and "allowsnull" otherwise returns 0.'; + +create or replace view sys.default_constraints +AS +select CAST(('DF_' || o.relname || '_' || d.oid) as sys.sysname) as name + , d.oid as object_id + , null::int as principal_id + , o.relnamespace as schema_id + , d.adrelid as parent_object_id + , 'D'::char(2) as type + , 'DEFAULT_CONSTRAINT'::sys.nvarchar(60) AS type_desc + , null::timestamp as create_date + , null::timestamp as modified_date + , 0::sys.bit as is_ms_shipped + , 0::sys.bit as is_published + , 0::sys.bit as is_schema_published + , d.adnum::int as parent_column_id + , pg_get_expr(d.adbin, d.adrelid) as definition + , 1::sys.bit as is_system_named +from pg_catalog.pg_attrdef as d +inner join pg_catalog.pg_class as o on (d.adrelid = o.oid); +GRANT SELECT ON sys.default_constraints TO PUBLIC; + +CREATE OR REPLACE VIEW sys.computed_columns +AS +SELECT out_object_id as object_id + , out_name as name + , out_column_id as column_id + , out_system_type_id as system_type_id + , out_user_type_id as user_type_id + , out_max_length as max_length + , out_precision as precision + , out_scale as scale + , out_collation_name as collation_name + , out_is_nullable as is_nullable + , out_is_ansi_padded as is_ansi_padded + , out_is_rowguidcol as is_rowguidcol + , out_is_identity as is_identity + , out_is_computed as is_computed + , out_is_filestream as is_filestream + , out_is_replicated as is_replicated + , out_is_non_sql_subscribed as is_non_sql_subscribed + , out_is_merge_published as is_merge_published + , out_is_dts_replicated as is_dts_replicated + , out_is_xml_document as is_xml_document + , out_xml_collection_id as xml_collection_id + , out_default_object_id as default_object_id + , out_rule_object_id as rule_object_id + , out_is_sparse as is_sparse + , out_is_column_set as is_column_set + , out_generated_always_type as generated_always_type + , out_generated_always_type_desc as generated_always_type_desc + , out_encryption_type as encryption_type + , out_encryption_type_desc as encryption_type_desc + , out_encryption_algorithm_name as encryption_algorithm_name + , out_column_encryption_key_id as column_encryption_key_id + , out_column_encryption_key_database_name as column_encryption_key_database_name + , out_is_hidden as is_hidden + , out_is_masked as is_masked + , out_graph_type as graph_type + , out_graph_type_desc as graph_type_desc + , substring(pg_get_expr(d.adbin, d.adrelid), 1, 4000)::sys.nvarchar(4000) AS definition + , 1::sys.bit AS uses_database_collation + , 0::sys.bit AS is_persisted +FROM sys.columns_internal() sc +INNER JOIN pg_attribute a ON sc.out_name = a.attname AND sc.out_column_id = a.attnum +INNER JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum +WHERE a.attgenerated = 's' AND sc.out_is_computed::integer = 1; +GRANT SELECT ON sys.computed_columns TO PUBLIC; + +create or replace view sys.index_columns +as +select i.indrelid::integer as object_id + , i.indexrelid::integer as index_id + , a.attrelid::integer as index_column_id + , a.attnum::integer as column_id + , a.attnum::sys.tinyint as key_ordinal + , 0::sys.tinyint as partition_ordinal + , 0::sys.bit as is_descending_key + , 1::sys.bit as is_included_column +from pg_index as i +inner join pg_catalog.pg_attribute a on i.indexrelid = a.attrelid; +GRANT SELECT ON sys.index_columns TO PUBLIC; + +CREATE or replace VIEW sys.check_constraints AS +SELECT CAST(c.conname as sys.sysname) as name + , oid::integer as object_id + , c.connamespace::integer as principal_id + , c.connamespace::integer as schema_id + , conrelid::integer as parent_object_id + , 'C'::char(2) as type + , 'CHECK_CONSTRAINT'::sys.nvarchar(60) as type_desc + , null::sys.datetime as create_date + , null::sys.datetime as modify_date + , 0::sys.bit as is_ms_shipped + , 0::sys.bit as is_published + , 0::sys.bit as is_schema_published + , 0::sys.bit as is_disabled + , 0::sys.bit as is_not_for_replication + , 0::sys.bit as is_not_trusted + , c.conkey[1]::integer AS parent_column_id + , substring(pg_get_constraintdef(c.oid) from 7) AS definition + , 1::sys.bit as uses_database_collation + , 0::sys.bit as is_system_named +FROM pg_catalog.pg_constraint as c +WHERE c.contype = 'c' and c.conrelid != 0; +GRANT SELECT ON sys.check_constraints 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 + , c.oid as index_id +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; + +-- substring -- +CREATE OR REPLACE FUNCTION sys.substring(string TEXT, i INTEGER, j INTEGER) +RETURNS sys.VARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.VARCHAR, i INTEGER, j INTEGER) +RETURNS sys.VARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.VARCHAR, i INTEGER, j INTEGER) +RETURNS sys.VARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.NVARCHAR, i INTEGER, j INTEGER) +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.substring(string sys.NCHAR, i INTEGER, j INTEGER) +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'tsql_varchar_substr' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.microsoftversion() +RETURNS INTEGER AS +$BODY$ + SELECT 201332885::INTEGER; +$BODY$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; +CREATE VIEW sys.sp_pkeys_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(seq AS smallint) AS KEY_SEQ, +CAST(t5.conname AS sys.sysname) AS PK_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 + JOIN pg_constraint t5 ON t1.oid = t5.conrelid + , generate_series(1,16) seq -- SQL server has max 16 columns per primary key +WHERE t5.contype = 'p' + AND CAST(t4.dtd_identifier AS smallint) = ANY (t5.conkey) + AND CAST(t4.dtd_identifier AS smallint) = t5.conkey[seq]; + +GRANT SELECT on sys.sp_pkeys_view TO PUBLIC; + +-- internal function in order to workaround BABEL-1597 +create function sys.sp_pkeys_internal( + in_table_name sys.nvarchar(384), + in_table_owner sys.nvarchar(384) = '', + in_table_qualifier sys.nvarchar(384) = '' +) +returns table( + out_table_qualifier sys.sysname, + out_table_owner sys.sysname, + out_table_name sys.sysname, + out_column_name sys.sysname, + out_key_seq smallint, + out_pk_name sys.sysname +) +as $$ +begin + return query + select * from sys.sp_pkeys_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) + order by table_qualifier, table_owner, table_name, key_seq; +end; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE PROCEDURE sys.sp_pkeys( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = '', + "@table_qualifier" sys.nvarchar(384) = '' +) +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_key_seq as key_seq, + out_pk_name as pk_name + from sys.sp_pkeys_internal(@table_name, @table_owner, @table_qualifier); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_pkeys TO PUBLIC; + +CREATE VIEW sys.sp_statistics_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, +CASE +WHEN t5.indisunique = 't' THEN CAST(0 AS smallint) +ELSE CAST(1 AS smallint) +END AS NON_UNIQUE, +CAST(t1.relname AS sys.sysname) AS INDEX_QUALIFIER, +-- the index name created by CREATE INDEX is re-mapped, find it (by checking +-- the ones not in pg_constraint) and restoring it back before display +CASE +WHEN t8.oid > 0 THEN CAST(t6.relname AS sys.sysname) +ELSE CAST(SUBSTRING(t6.relname,1,LENGTH(t6.relname)-32-LENGTH(t1.relname)) AS sys.sysname) +END AS INDEX_NAME, +CASE +WHEN t7.starelid > 0 THEN CAST(0 AS smallint) +ELSE + CASE + WHEN t5.indisclustered = 't' THEN CAST(1 AS smallint) + ELSE CAST(3 AS smallint) + END +END AS TYPE, +CAST(seq + 1 AS smallint) AS SEQ_IN_INDEX, +CAST(t4.column_name AS sys.sysname) AS COLUMN_NAME, +CAST('A' AS sys.varchar(1)) AS COLLATION, +CAST(t7.stadistinct AS int) AS CARDINALITY, +CAST(0 AS int) AS PAGES, --not supported +CAST(NULL AS sys.varchar(128)) AS FILTER_CONDITION +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 + JOIN (pg_catalog.pg_index t5 JOIN + pg_catalog.pg_class t6 ON t5.indexrelid = t6.oid) ON t1.oid = t5.indrelid + LEFT JOIN pg_catalog.pg_statistic t7 ON t1.oid = t7.starelid + LEFT JOIN pg_catalog.pg_constraint t8 ON t5.indexrelid = t8.conindid + , generate_series(0,31) seq -- SQL server has max 32 columns per index +WHERE CAST(t4.dtd_identifier AS smallint) = ANY (t5.indkey) + AND CAST(t4.dtd_identifier AS smallint) = t5.indkey[seq]; +GRANT SELECT on sys.sp_statistics_view TO PUBLIC; + +create function sys.sp_statistics_internal( + in_table_name sys.sysname, + in_table_owner sys.sysname = '', + in_table_qualifier sys.sysname = '', + in_index_name sys.sysname = '', + in_is_unique char = 'N', + in_accuracy char = 'Q' +) +returns table( + out_table_qualifier sys.sysname, + out_table_owner sys.sysname, + out_table_name sys.sysname, + out_non_unique smallint, + out_index_qualifier sys.sysname, + out_index_name sys.sysname, + out_type smallint, + out_seq_in_index smallint, + out_column_name sys.sysname, + out_collation sys.varchar(1), + out_cardinality int, + out_pages int, + out_filter_condition sys.varchar(128) +) +as $$ +begin + return query + select * from sys.sp_statistics_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_index_name,'')) = '' or index_name like in_index_name) + and ((in_is_unique = 'N') or (in_is_unique = 'Y' and non_unique = 0)) + order by non_unique, type, index_name, seq_in_index; +end; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE PROCEDURE sys.sp_statistics( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@table_qualifier" sys.sysname = '', + "@index_name" sys.sysname = '', + "@is_unique" char = 'N', + "@accuracy" char = 'Q' +) +AS $$ +BEGIN + select out_table_qualifier as table_qualifier, + out_table_owner as table_owner, + out_table_name as table_name, + out_non_unique as non_unique, + out_index_qualifier as index_qualifier, + out_index_name as index_name, + out_type as type, + out_seq_in_index as seq_in_index, + out_column_name as column_name, + out_collation as collation, + out_cardinality as cardinality, + out_pages as pages, + out_filter_condition as filter_condition + from sys.sp_statistics_internal(@table_name, @table_owner, @table_qualifier, @index_name, @is_unique, @accuracy); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_statistics TO PUBLIC; + +-- same as sp_statistics +CREATE OR REPLACE PROCEDURE sys.sp_statistics_100( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@table_qualifier" sys.sysname = '', + "@index_name" sys.sysname = '', + "@is_unique" char = 'N', + "@accuracy" char = 'Q' +) +AS $$ +BEGIN + select out_table_qualifier as table_qualifier, + out_table_owner as table_owner, + out_table_name as table_name, + out_non_unique as non_unique, + out_index_qualifier as index_qualifier, + out_index_name as index_name, + out_type as type, + out_seq_in_index as seq_in_index, + out_column_name as column_name, + out_collation as collation, + out_cardinality as cardinality, + out_pages as pages, + out_filter_condition as filter_condition + from sys.sp_statistics_internal(@table_name, @table_owner, @table_qualifier, @index_name, @is_unique, @accuracy); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_statistics_100 TO PUBLIC; + +-- Duplicate function with arg TEXT since ANYELEMENT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.datepart(IN datepart PG_CATALOG.TEXT, IN arg TEXT) 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; + + -- Duplicate functions with arg TEXT since ANYELEMENT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.dateadd(IN datepart PG_CATALOG.TEXT, IN num INTEGER, IN startdate TEXT) RETURNS DATETIME +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; + +-- Duplicate functions with arg TEXT since ANYELEMENT cannot handle type unknown. +CREATE OR REPLACE FUNCTION sys.datename(IN dp PG_CATALOG.TEXT, IN arg TEXT) 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 VIEW sys.spt_tablecollations_view AS + SELECT + o.object_id AS object_id, + o.schema_id AS schema_id, + c.column_id AS colid, + CASE WHEN p.attoptions[1] LIKE 'bbf_original_name=%' THEN split_part(p.attoptions[1], '=', 2) + ELSE c.name END 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) JOIN + pg_attribute p ON (c.name = p.attname) + WHERE + c.is_sparse = 0 AND p.attnum >= 0; +GRANT SELECT ON sys.spt_tablecollations_view TO PUBLIC; + +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, + s_tcv.collation_100 AS collation + from + sys.spt_tablecollations_view s_tcv + where + s_tcv.object_id = sys.object_id(@object) + order by colid; +END; +$$ +LANGUAGE 'pltsql'; + +CREATE OR REPLACE PROCEDURE sys.printarg(IN "@message" TEXT) +AS $$ +BEGIN + PRINT @message; +END; +$$ LANGUAGE pltsql; +GRANT EXECUTE ON PROCEDURE sys.printarg(IN "@message" TEXT) TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_updatestats(IN "@resample" VARCHAR(8) DEFAULT 'NO') +AS $$ +BEGIN + IF sys.user_name() != 'dbo' THEN + RAISE EXCEPTION 'user does not have permission'; + END IF; + + IF lower("@resample") = 'resample' THEN + RAISE NOTICE 'ignoring resample option'; + ELSIF lower("@resample") != 'no' THEN + RAISE EXCEPTION 'Invalid option name %', "@resample"; + END IF; + + ANALYZE VERBOSE; + + CALL printarg('Statistics for all tables have been updated. Refer logs for details.'); +END; +$$ LANGUAGE plpgsql; +GRANT EXECUTE on PROCEDURE sys.sp_updatestats(IN "@resample" VARCHAR(8)) TO PUBLIC; + +-- internal function that returns relevant info needed +-- by sys.syscolumns view for all procedure parameters. +-- This separate function was needed to workaround BABEL-1597 +CREATE FUNCTION sys.proc_param_helper() +RETURNS TABLE ( + name sys.sysname, + id int, + xtype int, + colid smallint, + collationid int, + prec smallint, + scale int, + isoutparam int, + collation sys.sysname +) +AS +$$ +BEGIN +RETURN QUERY +select params.parameter_name::sys.sysname + , pgproc.oid::int + , CAST(case when pgproc.proallargtypes is null then split_part(pgproc.proargtypes::varchar, ' ', params.ordinal_position) + else split_part(btrim(pgproc.proallargtypes::text,'{}'), ',', params.ordinal_position) end AS int) + , params.ordinal_position::smallint + , coll.oid::int + , params.numeric_precision::smallint + , params.numeric_scale::int + , case params.parameter_mode when 'OUT' then 1 when 'INOUT' then 1 else 0 end + , params.collation_name::sys.sysname +from information_schema.routines routine +left join information_schema.parameters params + on routine.specific_schema = params.specific_schema + and routine.specific_name = params.specific_name +left join pg_collation coll on coll.collname = params.collation_name +/* assuming routine.specific_name is constructed by concatenating procedure name and oid */ +left join pg_proc pgproc on routine.specific_name = nameconcatoid(pgproc.proname, pgproc.oid) +where routine.routine_schema not in ('pg_catalog', 'information_schema') + and routine.routine_type = 'PROCEDURE'; +END; +$$ +LANGUAGE plpgsql; + +CREATE OR REPLACE VIEW sys.syscolumns AS +SELECT out_name as name + , out_object_id as id + , out_system_type_id as xtype + , 0::sys.tinyint as typestat + , (case when out_user_type_id < 32767 then out_user_type_id else null end)::smallint as xusertype + , out_max_length as length + , 0::sys.tinyint as xprec + , 0::sys.tinyint as xscale + , out_column_id::smallint as colid + , 0::smallint as xoffset + , 0::sys.tinyint as bitpos + , 0::sys.tinyint as reserved + , 0::smallint as colstat + , out_default_object_id::int as cdefault + , out_rule_object_id::int as domain + , 0::smallint as number + , 0::smallint as colorder + , null::sys.varbinary(8000) as autoval + , out_offset as offset + , out_collation_id as collationid + , (case out_is_nullable::int when 1 then 8 else 0 end + + case out_is_identity::int when 1 then 128 else 0 end)::sys.tinyint as status + , out_system_type_id as type + , (case when out_user_type_id < 32767 then out_user_type_id else null end)::smallint as usertype + , null::varchar(255) as printfmt + , out_precision::smallint as prec + , out_scale::int as scale + , out_is_computed::int as iscomputed + , 0::int as isoutparam + , out_is_nullable::int as isnullable + , out_collation_name::sys.sysname as collation +FROM sys.columns_internal() +union all +SELECT p.name + , p.id + , p.xtype + , 0::sys.tinyint as typestat + , (case when p.xtype < 32767 then p.xtype else null end)::smallint as xusertype + , null as length + , 0::sys.tinyint as xprec + , 0::sys.tinyint as xscale + , p.colid + , 0::smallint as xoffset + , 0::sys.tinyint as bitpos + , 0::sys.tinyint as reserved + , 0::smallint as colstat + , null::int as cdefault + , null::int as domain + , 0::smallint as number + , 0::smallint as colorder + , null::sys.varbinary(8000) as autoval + , 0::smallint as offset + , collationid + , (case p.isoutparam when 1 then 64 else 0 end)::sys.tinyint as status + , p.xtype as type + , (case when p.xtype < 32767 then p.xtype else null end)::smallint as usertype + , null::varchar(255) as printfmt + , p.prec + , p.scale + , 0::int as iscomputed + , p.isoutparam + , 1::int as isnullable + , p.collation +FROM sys.proc_param_helper() as p; +GRANT SELECT ON sys.syscolumns TO PUBLIC; + +-- Reset search_path to not affect any subsequent scripts +SELECT set_config('search_path', trim(leading 'sys, ' from current_setting('search_path')), false); 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..df09465706d --- /dev/null +++ b/contrib/babelfishpg_tsql/src/applock.c @@ -0,0 +1,889 @@ +/*------------------------------------------------------------------------- + * + * 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_INTENTEXCLUSIVE, + APPLOCKMODE_INTENTSHARED, + APPLOCKMODE_SHARED, + APPLOCKMODE_UPDATE, + 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", + "IntentExclusive", + "IntentShared", + "Shared", + "Update", + "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); + + if (pg_strcasecmp(lockowner, "Transaction") == 0 && !IsTransactionBlockActive()) + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("The statement or function must be executed in the context of a user transaction."))); + + /* + * 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..5c1561828a5 --- /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.1.0" +#define BABELFISH_INTERNAL_VERSION_STR "Babelfish 13.5.0.9" +#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..f427085ae86 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-decl.y @@ -0,0 +1,110 @@ +%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_triggername + +%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_SUBSTRING 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 TSQL_CERTIFICATE TSQL_DEFAULT_DATABASE TSQL_DEFAULT_LANGUAGE TSQL_HASHED + TSQL_MUST_CHANGE TSQL_CHECK_EXPIRATION TSQL_CHECK_POLICY TSQL_CREDENTIAL TSQL_SID TSQL_OLD_PASSWORD + TSQL_UNLOCK TSQL_VALUES + TSQL_NVARCHAR + +/* + * 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..e6c7743dc98 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-epilogue.y.c @@ -0,0 +1,1307 @@ +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; + List *ins_colnames = NIL, *del_colnames = 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, is_duplicate = 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)) + { + is_duplicate = returning_list_has_column_name(del_colnames, strVal(llast(cref->fields))); + if (!is_duplicate) + { + 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; + del_colnames = lappend(del_colnames, strVal(llast(cref->fields))); + } + } + 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)) + { + is_duplicate = returning_list_has_column_name(ins_colnames, strVal(llast(cref->fields))); + if (!is_duplicate) + { + 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; + ins_colnames = lappend(ins_colnames, strVal(llast(cref->fields))); + } + } + 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; +} + +/* +* returning_list_has_column_name() checks whether a particular column name already +* exists in the transformed returning list for OUTPUT clause. Such a scenario is +* possible because get_transformed_output_list() removes functions and expressions +* and only retains the column names. +*/ +static bool +returning_list_has_column_name(List *existing_colnames, char *current_colname) +{ + ListCell *name; + bool is_duplicate = false; + + if (existing_colnames == NIL) + return false; + + foreach(name, existing_colnames) + { + char *colname = (char*) lfirst(name); + if (!strcmp(colname, current_colname)) + { + is_duplicate = true; + break; + } + } + return is_duplicate; +} \ No newline at end of file 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..d40ee683fb5 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-prologue.y.h @@ -0,0 +1,70 @@ +/***************************************************************************** + * + * 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); +static bool returning_list_has_column_name(List *existing_colnames, char *current_colname); 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..c238d2f5321 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-rule.y @@ -0,0 +1,3763 @@ +/* + * 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 TSQL_HASHED opt_must_change + { + $$ = list_make1(makeDefElem("password", NULL, @1)); + } + | WITH PASSWORD '=' TSQL_XCONST TSQL_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: + TSQL_SID '=' TSQL_XCONST + { + $$ = NULL; + } + | TSQL_DEFAULT_DATABASE '=' NonReservedWord + { + $$ = makeDefElem("default_database", + (Node *)makeString($3), + @1); + } + | TSQL_DEFAULT_LANGUAGE '=' NonReservedWord + { + $$ = NULL; + } + | TSQL_CHECK_EXPIRATION '=' opt_boolean_or_string + { + $$ = NULL; + } + | TSQL_CHECK_POLICY '=' opt_boolean_or_string + { + $$ = NULL; + } + | TSQL_CREDENTIAL '=' NonReservedWord + { + $$ = NULL; + } + ; + +opt_must_change: + TSQL_MUST_CHANGE + | /*EMPTY*/ + ; + +tsql_login_sources: + TSQL_WINDOWS + | TSQL_WINDOWS WITH tsql_windows_options_list + | TSQL_CERTIFICATE NonReservedWord + | ASYMMETRIC KEY NonReservedWord + ; + +tsql_windows_options_list: + tsql_windows_options + | tsql_windows_options_list ',' tsql_windows_options + ; + +tsql_windows_options: + TSQL_DEFAULT_DATABASE '=' NonReservedWord + | TSQL_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 TSQL_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 TSQL_HASHED tsql_alter_login_password_option1 + { + $$ = makeDefElem("password", NULL, @1); + } + | TSQL_DEFAULT_DATABASE '=' NonReservedWord + { + $$ = makeDefElem("default_database", + (Node *)makeString($3), + @1);; + } + | TSQL_DEFAULT_LANGUAGE '=' NonReservedWord + { + $$ = NULL; + } + | NAME_P '=' RoleSpec + { + $$ = NULL; + } + | TSQL_CHECK_EXPIRATION '=' opt_boolean_or_string + { + $$ = NULL; + } + | TSQL_CHECK_POLICY '=' opt_boolean_or_string + { + $$ = NULL; + } + | TSQL_CREDENTIAL '=' NonReservedWord + { + $$ = NULL; + } + | NO TSQL_CREDENTIAL + { + $$ = NULL; + } + ; + +tsql_alter_login_password_option1: + TSQL_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: + TSQL_MUST_CHANGE + | TSQL_UNLOCK + ; + +tsql_DropLoginStmt: + DROP TSQL_LOGIN role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = false; + n->roles = $3; + $$ = (Node *)n; + } + ; + +tsql_nchar: + TSQL_NVARCHAR 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 tsql_triggername + { + 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($3); + n->behavior = DROP_CASCADE; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP drop_type_name_on_any_name IF_P EXISTS tsql_triggername + { + 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($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 TSQL_NVARCHAR 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); + } + | TSQL_NVARCHAR Sconst + { + /* This is to support N'str' in various locations */ + TypeName *t = makeTypeNameFromNameList(list_make2(makeString("sys"), makeString("nvarchar"))); + t->location = @1; + t->typmods = list_make1(makeIntConst(TSQLMaxTypmod, -1)); + $$ = makeStringConstCast($2, @2, t); + } + ; + + +/* 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 tsql_triggername 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 = ((Value *)list_nth($3,0))->val.str; + 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)); + if (list_length($3) > 1){ + n1->trigname = ((Value *)list_nth($3,1))->val.str; + /* + * Used a hack way to pass the schema name from args, in CR-58614287 + * Args will be set back to NIL in pl_handler pltsql_pre_parse_analyze() + * before calling backend functios + */ + n1->args = list_make1(makeString(((Value *)list_nth($3,0))->val.str)); + }else{ + 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; + + 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_triggername: + ColId { $$ = list_make1(makeString($1)); }; + | ColId '.' ColId { $$ = list_make2(makeString($1),makeString($3)); }; + +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 =