From a837325a4b132561e4804d3a74b6f1a5bd65066e Mon Sep 17 00:00:00 2001 From: 3manuek <3manuek@gmail.com> Date: Mon, 20 Jun 2022 17:24:14 +0200 Subject: [PATCH] Raw copy of Extensions in Engine Signed-off-by: 3manuek <3manuek@gmail.com> --- .github/workflows/main.yml | 174 +- contrib/babelfishpg_common/Makefile | 89 + contrib/babelfishpg_common/Version.config | 4 + .../babelfishpg_common.control.in | 7 + .../sql/babelfishpg_common--1.0.0.sql | 5794 ++++++ .../sql/babelfishpg_common.in | 40 + contrib/babelfishpg_common/sql/binary.sql | 275 + contrib/babelfishpg_common/sql/bit.sql | 490 + contrib/babelfishpg_common/sql/bpchar.sql | 350 + contrib/babelfishpg_common/sql/coerce.sql | 145 + contrib/babelfishpg_common/sql/datetime.sql | 418 + contrib/babelfishpg_common/sql/datetime2.sql | 347 + .../babelfishpg_common/sql/datetimeoffset.sql | 330 + .../fixeddecimal--1.1.0_base_parallel.sql | 1982 ++ .../sql/money/fixeddecimal--brin.sql | 12 + .../sql/money/fixeddecimal--parallelaggs.sql | 82 + contrib/babelfishpg_common/sql/numerics.sql | 309 + contrib/babelfishpg_common/sql/rowversion.sql | 263 + .../babelfishpg_common/sql/smalldatetime.sql | 614 + contrib/babelfishpg_common/sql/sqlvariant.sql | 548 + .../sql/string_operators.sql | 152 + contrib/babelfishpg_common/sql/strings.sql | 2 + .../sql/uniqueidentifier.sql | 214 + .../sql/upgrades/Release.md | 1 + .../babelfishpg_common--1.0.0--1.1.0.sql | 338 + .../babelfishpg_common--1.1.0--1.2.0.sql | 1319 ++ .../babelfishpg_common--1.2.0--1.2.1.sql | 3 + contrib/babelfishpg_common/sql/utils.sql | 20 + contrib/babelfishpg_common/sql/varbinary.sql | 303 + contrib/babelfishpg_common/sql/varchar.sql | 295 + .../src/babelfishpg_common.c | 26 + contrib/babelfishpg_common/src/bit.c | 548 + contrib/babelfishpg_common/src/coerce.c | 339 + contrib/babelfishpg_common/src/datetime.c | 600 + contrib/babelfishpg_common/src/datetime.h | 31 + contrib/babelfishpg_common/src/datetime2.c | 416 + contrib/babelfishpg_common/src/datetime2.h | 25 + .../babelfishpg_common/src/datetimeoffset.c | 803 + .../babelfishpg_common/src/datetimeoffset.h | 38 + contrib/babelfishpg_common/src/instr.c | 18 + contrib/babelfishpg_common/src/instr.h | 473 + contrib/babelfishpg_common/src/numeric.c | 1031 + contrib/babelfishpg_common/src/numeric.h | 9 + .../babelfishpg_common/src/smalldatetime.c | 594 + contrib/babelfishpg_common/src/sqlvariant.c | 2382 +++ contrib/babelfishpg_common/src/typecode.c | 337 + contrib/babelfishpg_common/src/typecode.h | 89 + .../babelfishpg_common/src/uniqueidentifier.c | 238 + contrib/babelfishpg_common/src/varbinary.c | 1421 ++ contrib/babelfishpg_common/src/varchar.c | 1258 ++ contrib/babelfishpg_money/Makefile | 41 + contrib/babelfishpg_money/README.md | 162 + .../babelfishpg_money.control | 4 + .../fixeddecimal--1.0.0--1.1.0.sql | 800 + .../fixeddecimal--1.0.0_base.sql | 527 + .../fixeddecimal--1.1.0_base.sql | 1193 ++ .../fixeddecimal--1.1.0_base_parallel.sql | 1419 ++ .../babelfishpg_money/fixeddecimal--aggs.sql | 44 + .../babelfishpg_money/fixeddecimal--brin.sql | 12 + .../fixeddecimal--parallelaggs.sql | 70 + .../fixeddecimal--xlaggs.sql | 57 + contrib/babelfishpg_money/fixeddecimal.c | 3039 +++ .../fixeddecimalaggstate.sql | 41 + .../test/expected/aggregate.out | 33 + .../test/expected/brin-xl.out | 23 + .../babelfishpg_money/test/expected/brin.out | 22 + .../babelfishpg_money/test/expected/cast.out | 48 + .../test/expected/comparison.out | 310 + .../test/expected/index-xl.out | 95 + .../babelfishpg_money/test/expected/index.out | 92 + .../test/expected/overflow.out | 123 + .../babelfishpg_money/test/sql/aggregate.sql | 21 + .../babelfishpg_money/test/sql/brin-xl.sql | 14 + contrib/babelfishpg_money/test/sql/brin.sql | 14 + contrib/babelfishpg_money/test/sql/cast.sql | 23 + .../babelfishpg_money/test/sql/comparison.sql | 118 + .../babelfishpg_money/test/sql/index-xl.sql | 47 + contrib/babelfishpg_money/test/sql/index.sql | 50 + .../babelfishpg_money/test/sql/overflow.sql | 79 + contrib/babelfishpg_tds/Makefile | 39 + contrib/babelfishpg_tds/README | 8 + contrib/babelfishpg_tds/README.err | 4 + .../babelfishpg_tds--1.0.0.sql | 50 + .../babelfishpg_tds/babelfishpg_tds.control | 7 + contrib/babelfishpg_tds/error_mapping.txt | 175 + .../babelfishpg_tds/generate_error_mapping.pl | 35 + .../src/backend/encoding/encoding_utils.c | 121 + .../backend/fault_injection/fault_injection.c | 403 + .../fault_injection/fault_injection_tests.c | 239 + .../src/backend/tds/err_handler.c | 337 + contrib/babelfishpg_tds/src/backend/tds/guc.c | 292 + .../src/backend/tds/support_funcs.c | 632 + .../src/backend/tds/tds-secure-openssl.c | 1442 ++ contrib/babelfishpg_tds/src/backend/tds/tds.c | 800 + .../src/backend/tds/tds_data_map.c | 225 + .../babelfishpg_tds/src/backend/tds/tds_srv.c | 492 + .../src/backend/tds/tdsbulkload.c | 691 + .../babelfishpg_tds/src/backend/tds/tdscomm.c | 953 + .../src/backend/tds/tdslogin.c | 2345 +++ .../src/backend/tds/tdsprinttup.c | 116 + .../src/backend/tds/tdsprotocol.c | 713 + .../src/backend/tds/tdsresponse.c | 2955 +++ .../babelfishpg_tds/src/backend/tds/tdsrpc.c | 3905 ++++ .../src/backend/tds/tdssecure.c | 411 + .../src/backend/tds/tdssqlbatch.c | 139 + .../src/backend/tds/tdstimestamp.c | 473 + .../src/backend/tds/tdstypeio.c | 3696 ++++ .../src/backend/tds/tdsutils.c | 543 + .../babelfishpg_tds/src/backend/tds/tdsxact.c | 442 + .../src/backend/utils/adt/README | 62 + .../src/backend/utils/adt/numeric.c | 786 + .../src/backend/utils/adt/varchar.c | 109 + .../src/backend/utils/adt/xml.c | 738 + .../src/backend/utils/mb/README | 0 .../src/backend/utils/mb/conv.c | 361 + .../utf8_and_big5/utf8_and_big5.c | 42 + .../utf8_and_gbk/utf8_and_gbk.c | 42 + .../utf8_and_sjis/utf8_and_sjis.c | 42 + .../utf8_and_uhc/utf8_and_uhc.c | 42 + .../utf8_and_win/utf8_and_win.c | 98 + .../babelfishpg_tds/src/include/.gitignore | 1 + .../babelfishpg_tds/src/include/err_handler.h | 51 + .../src/include/faultinjection.h | 81 + contrib/babelfishpg_tds/src/include/guc.h | 27 + contrib/babelfishpg_tds/src/include/tds.h | 34 + .../babelfishpg_tds/src/include/tds_debug.h | 113 + .../babelfishpg_tds/src/include/tds_instr.h | 96 + contrib/babelfishpg_tds/src/include/tds_int.h | 360 + .../src/include/tds_iofuncmap.h | 160 + .../src/include/tds_protocol.h | 81 + .../babelfishpg_tds/src/include/tds_request.h | 907 + .../src/include/tds_response.h | 102 + .../babelfishpg_tds/src/include/tds_secure.h | 61 + .../src/include/tds_timestamp.h | 48 + .../src/include/tds_typecode.h | 57 + .../babelfishpg_tds/src/include/tds_typeio.h | 476 + .../babelfishpg_tds/src/include/tdsprinttup.h | 15 + contrib/babelfishpg_tsql/Makefile | 246 + contrib/babelfishpg_tsql/Release.md | 49 + contrib/babelfishpg_tsql/Version.config | 7 + contrib/babelfishpg_tsql/antlr/CMakeLists.txt | 33 + contrib/babelfishpg_tsql/antlr/TSqlLexer.g4 | 1310 ++ contrib/babelfishpg_tsql/antlr/TSqlParser.g4 | 5098 +++++ .../antlr/cmake-dir/FindANTLR.cmake | 128 + .../antlr/cmake-dir/README.md | 157 + .../thirdparty/antlr/antlr-4.9.3-complete.jar | Bin 0 -> 3508089 bytes .../babelfishpg_tsql.control.in | 7 + .../expected/test/babel_219.out | 84 + .../expected/test/babel_collation.out | 826 + .../expected/test/babel_datatype.out | 1439 ++ .../expected/test/babel_ddl.out | 323 + .../expected/test/babel_delete.out | 294 + .../expected/test/babel_function.out | 3044 +++ .../expected/test/babel_init.out | 14 + .../expected/test/babel_like.out | 98 + .../expected/test/babel_procedures.out | 79 + .../expected/test/babel_set_command.out | 91 + .../expected/test/babel_table_type.out | 729 + .../expected/test/babel_transaction.out | 509 + .../expected/test/babel_typecode.out | 40 + .../expected/test/babel_uniqueidentifier.out | 175 + .../results/test/babel_219.out | 84 + .../results/test/babel_init.out | 14 + .../results/test/babel_like.out | 98 + contrib/babelfishpg_tsql/runtime/basic.sql | 16 + contrib/babelfishpg_tsql/runtime/functions.c | 1007 + .../sql/babelfishpg_tsql--1.0.0.sql | 16610 ++++++++++++++++ .../babelfishpg_tsql/sql/babelfishpg_tsql.in | 30 + .../babelfishpg_tsql/sql/babelfishpg_tsql.sql | 2276 +++ contrib/babelfishpg_tsql/sql/coerce.sql | 11 + contrib/babelfishpg_tsql/sql/collation.sql | 347 + contrib/babelfishpg_tsql/sql/datatype.sql | 10 + .../sql/datatype_string_operators.sql | 53 + .../sql/import_export_compatibility.sql | 37 + .../sql/information_schema_tsql.sql | 404 + contrib/babelfishpg_tsql/sql/ownership.sql | 391 + contrib/babelfishpg_tsql/sql/sys.sql | 143 + .../sql/sys_babelfish_configurations.sql | 14 + contrib/babelfishpg_tsql/sql/sys_cast.sql | 131 + .../sql/sys_function_helpers.sql | 10128 ++++++++++ .../babelfishpg_tsql/sql/sys_functions.sql | 2002 ++ .../babelfishpg_tsql/sql/sys_languages.sql | 2025 ++ .../babelfishpg_tsql/sql/sys_procedures.sql | 166 + contrib/babelfishpg_tsql/sql/sys_views.sql | 1820 ++ .../babelfishpg_tsql/sql/test/babel_219.sql | 35 + .../sql/test/babel_collation.sql | 175 + .../sql/test/babel_datatype.sql | 1128 ++ .../babelfishpg_tsql/sql/test/babel_ddl.sql | 207 + .../sql/test/babel_delete.sql | 170 + .../sql/test/babel_function.sql | 901 + .../babelfishpg_tsql/sql/test/babel_init.sql | 12 + .../babelfishpg_tsql/sql/test/babel_like.sql | 29 + .../sql/test/babel_procedures.sql | 80 + .../sql/test/babel_set_command.sql | 57 + .../sql/test/babel_table_type.sql | 589 + .../sql/test/babel_transaction.sql | 224 + .../sql/test/babel_typecode.sql | 4 + .../sql/test/babel_uniqueidentifier.sql | 82 + .../babelfishpg_tsql/sql/upgrades/.gitignore | 0 .../babelfishpg_tsql--1.0.0--1.1.0.sql | 1282 ++ .../babelfishpg_tsql--1.1.0--1.2.0.sql | 4273 ++++ .../babelfishpg_tsql--1.2.0--1.2.1.sql | 7 + contrib/babelfishpg_tsql/src/analyzer.c | 318 + contrib/babelfishpg_tsql/src/analyzer.h | 8 + .../babelfishpg_tsql/src/antlrTests/decl.sql | 14 + .../src/antlrTests/ifelse.sql | 17 + .../babelfishpg_tsql/src/antlrTests/inval.sql | 23 + .../src/antlrTests/nestedBlocks.sql | 15 + .../src/antlrTests/oneBlock.sql | 6 + .../babelfishpg_tsql/src/antlrTests/print.sql | 14 + .../src/antlrTests/print2.sql | 13 + .../src/antlrTests/proc_simple.sql | 6 + .../src/antlrTests/returnsTable.sql | 5 + .../src/antlrTests/series.sql | 11 + .../src/antlrTests/simple.sql | 6 + .../src/antlrTests/simple2.sql | 10 + .../src/antlrTests/single.sql | 3 + .../babelfishpg_tsql/src/antlrTests/throw.sql | 3 + .../src/antlrTests/tryCatch.sql | 26 + .../babelfishpg_tsql/src/antlrTests/while.sql | 23 + contrib/babelfishpg_tsql/src/applock.c | 891 + .../babelfishpg_tsql/src/babelfish_version.h | 15 + .../src/backend_parser/gram-tsql-decl.y | 111 + .../src/backend_parser/gram-tsql-epilogue.y.c | 1365 ++ .../gram-tsql-nonassoc-ident-tokens | 1 + .../src/backend_parser/gram-tsql-prologue.y.h | 73 + .../src/backend_parser/gram-tsql-rule.y | 4042 ++++ .../src/backend_parser/gram_hook.c | 254 + .../src/backend_parser/gramparse.h | 49 + .../src/backend_parser/include.pl | 168 + .../src/backend_parser/keywords.c | 18 + .../src/backend_parser/kwlist.h | 547 + .../src/backend_parser/parser.c | 276 + .../src/backend_parser/scan-tsql-decl.l | 64 + .../src/backend_parser/scan-tsql-epilogue.l.c | 88 + .../backend_parser/scan-tsql-prologue-top.l.h | 8 + .../src/backend_parser/scan-tsql-prologue.l.h | 39 + .../src/backend_parser/scan-tsql-rule.l | 361 + .../src/backend_parser/scanner.h | 19 + contrib/babelfishpg_tsql/src/catalog.c | 1452 ++ contrib/babelfishpg_tsql/src/catalog.h | 222 + contrib/babelfishpg_tsql/src/codegen.c | 669 + contrib/babelfishpg_tsql/src/codegen.h | 8 + contrib/babelfishpg_tsql/src/collation.c | 1523 ++ contrib/babelfishpg_tsql/src/collation.h | 102 + .../babelfishpg_tsql/src/collationproperty.c | 45 + .../babelfishpg_tsql/src/compile_context.c | 46 + .../babelfishpg_tsql/src/compile_context.h | 50 + contrib/babelfishpg_tsql/src/cursor.c | 1719 ++ .../babelfishpg_tsql/src/databasepropertyex.c | 226 + contrib/babelfishpg_tsql/src/datatype_info.h | 492 + contrib/babelfishpg_tsql/src/datatypes.h | 18 + contrib/babelfishpg_tsql/src/dbcmds.c | 779 + contrib/babelfishpg_tsql/src/dbcmds.h | 8 + contrib/babelfishpg_tsql/src/dynastack.c | 43 + contrib/babelfishpg_tsql/src/dynastack.h | 25 + contrib/babelfishpg_tsql/src/dynavec.c | 102 + contrib/babelfishpg_tsql/src/dynavec.h | 41 + contrib/babelfishpg_tsql/src/err_handler.c | 599 + contrib/babelfishpg_tsql/src/err_handler.h | 151 + contrib/babelfishpg_tsql/src/forxml.c | 260 + .../src/generate-plerrcodes.pl | 40 + contrib/babelfishpg_tsql/src/guc.c | 1274 ++ contrib/babelfishpg_tsql/src/guc.h | 18 + contrib/babelfishpg_tsql/src/hooks.c | 1327 ++ contrib/babelfishpg_tsql/src/hooks.h | 17 + contrib/babelfishpg_tsql/src/iterative_exec.c | 1619 ++ contrib/babelfishpg_tsql/src/iterative_exec.h | 37 + contrib/babelfishpg_tsql/src/json_funcs.c | 261 + contrib/babelfishpg_tsql/src/multidb.c | 1083 + contrib/babelfishpg_tsql/src/multidb.h | 26 + contrib/babelfishpg_tsql/src/pl_comp-2.c | 124 + contrib/babelfishpg_tsql/src/pl_comp.c | 3243 +++ contrib/babelfishpg_tsql/src/pl_exec-2.c | 2685 +++ contrib/babelfishpg_tsql/src/pl_exec.c | 9932 +++++++++ contrib/babelfishpg_tsql/src/pl_funcs-2.c | 731 + contrib/babelfishpg_tsql/src/pl_funcs-2.h | 7 + contrib/babelfishpg_tsql/src/pl_funcs.c | 1800 ++ contrib/babelfishpg_tsql/src/pl_gram.y | 8338 ++++++++ contrib/babelfishpg_tsql/src/pl_handler.c | 4442 +++++ .../babelfishpg_tsql/src/pl_reserved_kwlist.h | 59 + contrib/babelfishpg_tsql/src/pl_scanner.c | 829 + .../src/pl_unreserved_kwlist.h | 127 + contrib/babelfishpg_tsql/src/plan_inval.c | 143 + contrib/babelfishpg_tsql/src/plerrcodes.h | 1002 + contrib/babelfishpg_tsql/src/pltsql-2.h | 310 + contrib/babelfishpg_tsql/src/pltsql.h | 1979 ++ contrib/babelfishpg_tsql/src/pltsql_coerce.c | 1332 ++ .../src/pltsql_function_probin_handler.c | 495 + .../babelfishpg_tsql/src/pltsql_identity.c | 310 + contrib/babelfishpg_tsql/src/pltsql_instr.h | 640 + contrib/babelfishpg_tsql/src/pltsql_utils.c | 762 + contrib/babelfishpg_tsql/src/prepare.c | 696 + contrib/babelfishpg_tsql/src/procedures.c | 677 + contrib/babelfishpg_tsql/src/properties.c | 504 + contrib/babelfishpg_tsql/src/rolecmds.c | 1441 ++ contrib/babelfishpg_tsql/src/rolecmds.h | 57 + contrib/babelfishpg_tsql/src/schemacmds.c | 168 + contrib/babelfishpg_tsql/src/schemacmds.h | 10 + contrib/babelfishpg_tsql/src/session.c | 198 + contrib/babelfishpg_tsql/src/session.h | 13 + .../babelfishpg_tsql/src/special_keywords.c | 101 + contrib/babelfishpg_tsql/src/stmt_walker.c | 209 + contrib/babelfishpg_tsql/src/stmt_walker.h | 164 + contrib/babelfishpg_tsql/src/string.c | 493 + contrib/babelfishpg_tsql/src/tablecmds.c | 544 + contrib/babelfishpg_tsql/src/tsqlHandler.c | 30 + contrib/babelfishpg_tsql/src/tsqlIface.cpp | 5539 ++++++ contrib/babelfishpg_tsql/src/tsqlIface.hpp | 92 + contrib/babelfishpg_tsql/src/tsqlNodes.c | 41 + contrib/babelfishpg_tsql/src/tsqlNodes.h | 103 + .../src/tsqlUnsupportedFeatureHandler.cpp | 1656 ++ .../src/tsqlUnsupportedFeatureHandler.h | 0 .../src/utils/update_pg_files | 35 + .../old_expected/pltsql_at_prefixed_vars.out | 240 + .../test/old_expected/pltsql_case.out | 23 + .../test/old_expected/pltsql_if.out | 81 + .../old_expected/pltsql_localtempobjs.out | 113 + .../old_expected/pltsql_optsemicolterm.out | 155 + .../test/old_expected/pltsql_print.out | 94 + .../test/old_expected/pltsql_print_0.out | 91 + .../test/old_expected/pltsql_sanitychecks.out | 22 + .../old_expected/top_percent_keywords.out | 20 + .../test/old_sql/pltsql_at_prefixed_vars.sql | 177 + .../test/old_sql/pltsql_case.sql | 18 + .../test/old_sql/pltsql_if.sql | 65 + .../test/old_sql/pltsql_localtempobjs.sql | 86 + .../test/old_sql/pltsql_optsemicolterm.sql | 119 + .../test/old_sql/pltsql_print.sql | 63 + .../test/old_sql/pltsql_sanitychecks.sql | 12 + .../test/old_sql/top_percent_keywords.sql | 12 + contrib/test/JDBC/BABEL-2267.sql | 17 + contrib/test/JDBC/BABEL-2455.sql | 170 + contrib/test/JDBC/Info/.gitignore | 4 + contrib/test/JDBC/README.md | 384 + contrib/test/JDBC/cleanup.sh | 11 + contrib/test/JDBC/expected/102_1.out | 487 + contrib/test/JDBC/expected/102_2.out | 497 + contrib/test/JDBC/expected/102_3.out | 497 + contrib/test/JDBC/expected/102_6.out | 487 + contrib/test/JDBC/expected/113_1.out | 294 + contrib/test/JDBC/expected/11704_1.out | 245 + contrib/test/JDBC/expected/11704_2.out | 263 + contrib/test/JDBC/expected/129_1.out | 432 + contrib/test/JDBC/expected/1770_1750_1.out | 236 + contrib/test/JDBC/expected/1774_1750_1.out | 284 + contrib/test/JDBC/expected/208_1.out | 224 + contrib/test/JDBC/expected/218_1.out | 325 + contrib/test/JDBC/expected/222_1.out | 230 + contrib/test/JDBC/expected/257_2.out | 306 + contrib/test/JDBC/expected/2760_1.out | 221 + contrib/test/JDBC/expected/2812_1.out | 264 + contrib/test/JDBC/expected/2812_2.out | 212 + contrib/test/JDBC/expected/2812_3.out | 264 + contrib/test/JDBC/expected/4924_1.out | 260 + contrib/test/JDBC/expected/4924_2.out | 260 + contrib/test/JDBC/expected/4936_1.out | 230 + contrib/test/JDBC/expected/4936_2.out | 248 + contrib/test/JDBC/expected/BABEL-1000.out | 296 + contrib/test/JDBC/expected/BABEL-101.out | 86 + contrib/test/JDBC/expected/BABEL-1044.out | 286 + contrib/test/JDBC/expected/BABEL-1056.out | 61 + contrib/test/JDBC/expected/BABEL-1066.out | 47 + contrib/test/JDBC/expected/BABEL-1073.out | 39 + contrib/test/JDBC/expected/BABEL-1091.out | 355 + contrib/test/JDBC/expected/BABEL-1094.out | 28 + contrib/test/JDBC/expected/BABEL-1096.out | 69 + contrib/test/JDBC/expected/BABEL-1098.out | 725 + contrib/test/JDBC/expected/BABEL-1100.out | 110 + contrib/test/JDBC/expected/BABEL-1111.out | 34 + contrib/test/JDBC/expected/BABEL-1113.out | 66 + contrib/test/JDBC/expected/BABEL-1114.out | 145 + contrib/test/JDBC/expected/BABEL-1149.out | 83 + contrib/test/JDBC/expected/BABEL-1161.out | 176 + contrib/test/JDBC/expected/BABEL-1164.out | 98 + contrib/test/JDBC/expected/BABEL-1167.out | 34 + contrib/test/JDBC/expected/BABEL-1173.out | 13 + contrib/test/JDBC/expected/BABEL-1179.out | 127 + contrib/test/JDBC/expected/BABEL-1184.out | 96 + contrib/test/JDBC/expected/BABEL-1185.out | 14 + contrib/test/JDBC/expected/BABEL-1189.out | 43 + contrib/test/JDBC/expected/BABEL-1193.out | 291 + contrib/test/JDBC/expected/BABEL-1206.out | 110 + contrib/test/JDBC/expected/BABEL-1208.out | 110 + contrib/test/JDBC/expected/BABEL-1210.out | 149 + contrib/test/JDBC/expected/BABEL-1212.out | 25 + contrib/test/JDBC/expected/BABEL-1231.out | 49 + contrib/test/JDBC/expected/BABEL-1234.out | 7 + contrib/test/JDBC/expected/BABEL-1239.out | 23 + contrib/test/JDBC/expected/BABEL-1241.out | 133 + contrib/test/JDBC/expected/BABEL-1249.out | 98 + contrib/test/JDBC/expected/BABEL-1251.out | 157 + contrib/test/JDBC/expected/BABEL-1252.out | 168 + contrib/test/JDBC/expected/BABEL-1261.out | 159 + contrib/test/JDBC/expected/BABEL-1270.out | 37 + contrib/test/JDBC/expected/BABEL-1287.out | 174 + contrib/test/JDBC/expected/BABEL-1291.out | 38 + contrib/test/JDBC/expected/BABEL-1299.out | 44 + contrib/test/JDBC/expected/BABEL-1309.out | 97 + contrib/test/JDBC/expected/BABEL-1311.out | 128 + contrib/test/JDBC/expected/BABEL-1319.out | 34 + contrib/test/JDBC/expected/BABEL-1320.out | 66 + contrib/test/JDBC/expected/BABEL-1329.out | 71 + contrib/test/JDBC/expected/BABEL-1331.out | 56 + contrib/test/JDBC/expected/BABEL-1363.out | 62 + contrib/test/JDBC/expected/BABEL-1365.out | 48 + contrib/test/JDBC/expected/BABEL-1381.out | 161 + contrib/test/JDBC/expected/BABEL-1389.out | 76 + contrib/test/JDBC/expected/BABEL-1390.out | 35 + contrib/test/JDBC/expected/BABEL-1400.out | 73 + contrib/test/JDBC/expected/BABEL-1435.out | 258 + contrib/test/JDBC/expected/BABEL-1437.out | 116 + contrib/test/JDBC/expected/BABEL-1438.out | 9 + contrib/test/JDBC/expected/BABEL-1442.out | 434 + contrib/test/JDBC/expected/BABEL-1444.out | 263 + contrib/test/JDBC/expected/BABEL-1446.out | 163 + contrib/test/JDBC/expected/BABEL-1454.out | 411 + contrib/test/JDBC/expected/BABEL-1465.out | 598 + contrib/test/JDBC/expected/BABEL-1466.out | 133 + contrib/test/JDBC/expected/BABEL-1475.out | 144 + contrib/test/JDBC/expected/BABEL-1481.out | 167 + contrib/test/JDBC/expected/BABEL-1499.out | 61 + contrib/test/JDBC/expected/BABEL-1502.out | 26 + contrib/test/JDBC/expected/BABEL-1510.out | 229 + contrib/test/JDBC/expected/BABEL-1513.out | 111 + contrib/test/JDBC/expected/BABEL-1515.out | 63 + contrib/test/JDBC/expected/BABEL-1524.out | 337 + contrib/test/JDBC/expected/BABEL-1531.out | 58 + contrib/test/JDBC/expected/BABEL-1544.out | 81 + contrib/test/JDBC/expected/BABEL-1566.out | 393 + contrib/test/JDBC/expected/BABEL-1569.out | 84 + contrib/test/JDBC/expected/BABEL-1577.out | 32 + contrib/test/JDBC/expected/BABEL-1603.out | 78 + contrib/test/JDBC/expected/BABEL-1621.out | 15 + contrib/test/JDBC/expected/BABEL-1631.out | 56 + contrib/test/JDBC/expected/BABEL-1636.out | 91 + contrib/test/JDBC/expected/BABEL-1661.out | 33 + contrib/test/JDBC/expected/BABEL-1666.out | 39 + contrib/test/JDBC/expected/BABEL-1671.out | 38 + contrib/test/JDBC/expected/BABEL-1682.out | 26 + contrib/test/JDBC/expected/BABEL-1683.out | 170 + contrib/test/JDBC/expected/BABEL-1708.out | 174 + contrib/test/JDBC/expected/BABEL-1712.out | 77 + contrib/test/JDBC/expected/BABEL-1715.out | 67 + contrib/test/JDBC/expected/BABEL-1719.out | 31 + contrib/test/JDBC/expected/BABEL-1746.out | 13 + contrib/test/JDBC/expected/BABEL-1756.out | 29 + contrib/test/JDBC/expected/BABEL-1757.out | 13 + contrib/test/JDBC/expected/BABEL-1771.out | 116 + contrib/test/JDBC/expected/BABEL-1792.out | 77 + contrib/test/JDBC/expected/BABEL-1797.out | 20 + contrib/test/JDBC/expected/BABEL-1808.out | 65 + contrib/test/JDBC/expected/BABEL-1813.out | 215 + contrib/test/JDBC/expected/BABEL-1839.out | 67 + contrib/test/JDBC/expected/BABEL-1845.out | 50 + contrib/test/JDBC/expected/BABEL-1858.out | 70 + contrib/test/JDBC/expected/BABEL-1887.out | 84 + contrib/test/JDBC/expected/BABEL-1963.out | 40 + contrib/test/JDBC/expected/BABEL-1975.out | 73 + contrib/test/JDBC/expected/BABEL-1978.out | 48 + .../test/JDBC/expected/BABEL-1994-CHAR.out | 215 + .../test/JDBC/expected/BABEL-1994-VARCHAR.out | 213 + contrib/test/JDBC/expected/BABEL-1997.out | 38 + contrib/test/JDBC/expected/BABEL-2010.out | 48 + contrib/test/JDBC/expected/BABEL-2011.out | 45 + .../test/JDBC/expected/BABEL-2020-DELETE.out | 292 + .../test/JDBC/expected/BABEL-2020-UPDATE.out | 292 + contrib/test/JDBC/expected/BABEL-2034.out | 98 + contrib/test/JDBC/expected/BABEL-2049.out | 119 + contrib/test/JDBC/expected/BABEL-2051.out | 12 + contrib/test/JDBC/expected/BABEL-2060.out | 203 + contrib/test/JDBC/expected/BABEL-2086.out | 573 + contrib/test/JDBC/expected/BABEL-2089.out | 388 + contrib/test/JDBC/expected/BABEL-2117.out | 36 + contrib/test/JDBC/expected/BABEL-213.out | 457 + contrib/test/JDBC/expected/BABEL-2145.out | 56 + contrib/test/JDBC/expected/BABEL-2203.out | 174 + contrib/test/JDBC/expected/BABEL-2208.out | 76 + contrib/test/JDBC/expected/BABEL-2218.out | 51 + contrib/test/JDBC/expected/BABEL-2225.out | 40 + contrib/test/JDBC/expected/BABEL-2234.out | 38 + contrib/test/JDBC/expected/BABEL-2257.out | 92 + contrib/test/JDBC/expected/BABEL-2258.out | 31 + contrib/test/JDBC/expected/BABEL-2262.out | 54 + contrib/test/JDBC/expected/BABEL-2266.out | 85 + contrib/test/JDBC/expected/BABEL-2267.out | 29 + contrib/test/JDBC/expected/BABEL-2276.out | 18 + contrib/test/JDBC/expected/BABEL-2288.out | 44 + contrib/test/JDBC/expected/BABEL-2296.out | 32 + contrib/test/JDBC/expected/BABEL-2303.out | 84 + .../test/JDBC/expected/BABEL-2303_upgrade.out | 84 + contrib/test/JDBC/expected/BABEL-2317.out | 47 + contrib/test/JDBC/expected/BABEL-2325.out | 74 + contrib/test/JDBC/expected/BABEL-2328.out | 78 + contrib/test/JDBC/expected/BABEL-2337.out | 11 + contrib/test/JDBC/expected/BABEL-2347.out | 282 + contrib/test/JDBC/expected/BABEL-2349.out | 49 + contrib/test/JDBC/expected/BABEL-235.out | 123 + contrib/test/JDBC/expected/BABEL-2350.out | 70 + contrib/test/JDBC/expected/BABEL-2355.out | 31 + contrib/test/JDBC/expected/BABEL-2357.out | 14 + contrib/test/JDBC/expected/BABEL-2371.out | 133 + contrib/test/JDBC/expected/BABEL-2372.out | 22 + contrib/test/JDBC/expected/BABEL-2381.out | 1022 + contrib/test/JDBC/expected/BABEL-2390.out | 36 + contrib/test/JDBC/expected/BABEL-2392.out | 89 + contrib/test/JDBC/expected/BABEL-2403.out | 154 + contrib/test/JDBC/expected/BABEL-2409.out | 45 + contrib/test/JDBC/expected/BABEL-2410.out | 17 + contrib/test/JDBC/expected/BABEL-2412.out | 5 + contrib/test/JDBC/expected/BABEL-2416.out | 25 + contrib/test/JDBC/expected/BABEL-2417.out | 10 + contrib/test/JDBC/expected/BABEL-2418.out | 41 + contrib/test/JDBC/expected/BABEL-2433.out | 70 + contrib/test/JDBC/expected/BABEL-244.out | 58 + contrib/test/JDBC/expected/BABEL-2440.out | 80 + contrib/test/JDBC/expected/BABEL-2445.out | 31 + contrib/test/JDBC/expected/BABEL-2451.out | 13 + contrib/test/JDBC/expected/BABEL-2455.out | 345 + contrib/test/JDBC/expected/BABEL-2458.out | 137 + contrib/test/JDBC/expected/BABEL-2470.out | 34 + contrib/test/JDBC/expected/BABEL-2482.out | 43 + contrib/test/JDBC/expected/BABEL-2485.out | 158 + contrib/test/JDBC/expected/BABEL-2496.out | 34 + contrib/test/JDBC/expected/BABEL-2513.out | 1548 ++ contrib/test/JDBC/expected/BABEL-2514.out | 199 + contrib/test/JDBC/expected/BABEL-2515.out | 36 + contrib/test/JDBC/expected/BABEL-2535.out | 307 + contrib/test/JDBC/expected/BABEL-2555.out | 158 + contrib/test/JDBC/expected/BABEL-2557.out | 15 + contrib/test/JDBC/expected/BABEL-2565.out | 200 + contrib/test/JDBC/expected/BABEL-2569.out | 25 + contrib/test/JDBC/expected/BABEL-2576.out | 127 + contrib/test/JDBC/expected/BABEL-2578.out | 29 + contrib/test/JDBC/expected/BABEL-2585.out | 183 + contrib/test/JDBC/expected/BABEL-2593.out | 74 + contrib/test/JDBC/expected/BABEL-2596.out | 80 + contrib/test/JDBC/expected/BABEL-2602.out | 24 + contrib/test/JDBC/expected/BABEL-2604.out | 152 + contrib/test/JDBC/expected/BABEL-2607.out | 57 + contrib/test/JDBC/expected/BABEL-2622.out | 47 + contrib/test/JDBC/expected/BABEL-2647.out | 18 + contrib/test/JDBC/expected/BABEL-2649.out | 44 + contrib/test/JDBC/expected/BABEL-265.out | 109 + contrib/test/JDBC/expected/BABEL-2659.out | 72 + contrib/test/JDBC/expected/BABEL-2674.out | 133 + contrib/test/JDBC/expected/BABEL-2676.out | 34 + contrib/test/JDBC/expected/BABEL-2687.out | 867 + .../test/JDBC/expected/BABEL-2687_upgrade.out | 867 + contrib/test/JDBC/expected/BABEL-2701.out | 13 + contrib/test/JDBC/expected/BABEL-2706.out | 20 + contrib/test/JDBC/expected/BABEL-2716.out | 103 + contrib/test/JDBC/expected/BABEL-2724.out | 90 + contrib/test/JDBC/expected/BABEL-2725.out | 103 + contrib/test/JDBC/expected/BABEL-2730.out | 2490 +++ contrib/test/JDBC/expected/BABEL-2732.out | 78 + contrib/test/JDBC/expected/BABEL-2747.out | 63 + contrib/test/JDBC/expected/BABEL-2748.out | 258 + contrib/test/JDBC/expected/BABEL-2765.out | 143 + contrib/test/JDBC/expected/BABEL-2785.out | 7 + contrib/test/JDBC/expected/BABEL-2824.out | 85 + contrib/test/JDBC/expected/BABEL-2829.out | 27 + contrib/test/JDBC/expected/BABEL-2833.out | 93 + contrib/test/JDBC/expected/BABEL-2835.out | 36 + contrib/test/JDBC/expected/BABEL-2845.out | 91 + contrib/test/JDBC/expected/BABEL-2857.out | 10 + contrib/test/JDBC/expected/BABEL-2869.out | 81 + contrib/test/JDBC/expected/BABEL-2875.out | 48 + contrib/test/JDBC/expected/BABEL-2884.out | 82 + contrib/test/JDBC/expected/BABEL-2917.out | 55 + contrib/test/JDBC/expected/BABEL-2924.out | 78 + contrib/test/JDBC/expected/BABEL-2936.out | 13 + contrib/test/JDBC/expected/BABEL-2955.out | 66 + contrib/test/JDBC/expected/BABEL-2964.out | 107 + contrib/test/JDBC/expected/BABEL-2968.out | 26 + contrib/test/JDBC/expected/BABEL-2977.out | 26 + contrib/test/JDBC/expected/BABEL-2979.out | 6 + contrib/test/JDBC/expected/BABEL-2983.out | 308 + .../test/JDBC/expected/BABEL-2983_upgrade.out | 308 + contrib/test/JDBC/expected/BABEL-2984.out | 13 + contrib/test/JDBC/expected/BABEL-2988.out | 5 + contrib/test/JDBC/expected/BABEL-3004.out | 111 + contrib/test/JDBC/expected/BABEL-3005.out | 164 + contrib/test/JDBC/expected/BABEL-3006.out | 233 + contrib/test/JDBC/expected/BABEL-3019.out | 120 + contrib/test/JDBC/expected/BABEL-3036.out | 19 + contrib/test/JDBC/expected/BABEL-3101.out | 109 + contrib/test/JDBC/expected/BABEL-3108.out | 37 + contrib/test/JDBC/expected/BABEL-338.out | 249 + contrib/test/JDBC/expected/BABEL-381.out | 25 + contrib/test/JDBC/expected/BABEL-388.out | 115 + contrib/test/JDBC/expected/BABEL-405.out | 340 + contrib/test/JDBC/expected/BABEL-407.out | 25 + contrib/test/JDBC/expected/BABEL-453.out | 215 + contrib/test/JDBC/expected/BABEL-479.out | 81 + contrib/test/JDBC/expected/BABEL-480.out | 17 + contrib/test/JDBC/expected/BABEL-492.out | 139 + contrib/test/JDBC/expected/BABEL-493.out | 157 + contrib/test/JDBC/expected/BABEL-559.out | 43 + contrib/test/JDBC/expected/BABEL-565.out | 37 + contrib/test/JDBC/expected/BABEL-586.out | 99 + contrib/test/JDBC/expected/BABEL-588.out | 2022 ++ contrib/test/JDBC/expected/BABEL-597.out | 10 + contrib/test/JDBC/expected/BABEL-604.out | 46 + contrib/test/JDBC/expected/BABEL-625.out | 9 + contrib/test/JDBC/expected/BABEL-627.out | 25 + contrib/test/JDBC/expected/BABEL-632.out | 50 + contrib/test/JDBC/expected/BABEL-637.out | 1772 ++ contrib/test/JDBC/expected/BABEL-646.out | 96 + contrib/test/JDBC/expected/BABEL-662.out | 128 + contrib/test/JDBC/expected/BABEL-672.out | 5 + contrib/test/JDBC/expected/BABEL-690.out | 68 + contrib/test/JDBC/expected/BABEL-694.out | 44 + contrib/test/JDBC/expected/BABEL-701.out | 17 + contrib/test/JDBC/expected/BABEL-708.out | 285 + contrib/test/JDBC/expected/BABEL-719.out | 129 + contrib/test/JDBC/expected/BABEL-728.out | 651 + contrib/test/JDBC/expected/BABEL-735.out | 480 + contrib/test/JDBC/expected/BABEL-741.out | 127 + contrib/test/JDBC/expected/BABEL-758.out | 186 + contrib/test/JDBC/expected/BABEL-768.out | 116 + contrib/test/JDBC/expected/BABEL-775.out | 42 + contrib/test/JDBC/expected/BABEL-776.out | 189 + contrib/test/JDBC/expected/BABEL-785.out | 224 + contrib/test/JDBC/expected/BABEL-788.out | 258 + contrib/test/JDBC/expected/BABEL-798.out | 19 + contrib/test/JDBC/expected/BABEL-807.out | 24 + contrib/test/JDBC/expected/BABEL-820.out | 36 + contrib/test/JDBC/expected/BABEL-872.out | 36 + contrib/test/JDBC/expected/BABEL-889.out | 333 + contrib/test/JDBC/expected/BABEL-909.out | 34 + contrib/test/JDBC/expected/BABEL-911.out | 19 + contrib/test/JDBC/expected/BABEL-931.out | 66 + contrib/test/JDBC/expected/BABEL-941.out | 49 + contrib/test/JDBC/expected/BABEL-942.out | 401 + contrib/test/JDBC/expected/BABEL-955.out | 70 + contrib/test/JDBC/expected/BABEL-APPLOCK.out | 182 + .../test/JDBC/expected/BABEL-COLUMN-ALIAS.out | 226 + .../JDBC/expected/BABEL-COLUMN-CONSTRAINT.out | 86 + .../expected/BABEL-DATABASEPROPERTYEX.out | 71 + contrib/test/JDBC/expected/BABEL-GRANT.out | 208 + contrib/test/JDBC/expected/BABEL-GUC-PLAN.out | 362 + .../JDBC/expected/BABEL-IDENTITY-BIFS.out | 877 + .../JDBC/expected/BABEL-IDENTITY-COLUMN.out | 219 + .../JDBC/expected/BABEL-INSERT-EXECUTE.out | 732 + contrib/test/JDBC/expected/BABEL-JOIN.out | 26 + .../test/JDBC/expected/BABEL-JSON-FUNCS.out | 464 + .../test/JDBC/expected/BABEL-LIKE2ILIKE.out | 273 + .../JDBC/expected/BABEL-LOGIN-USER-EXT.out | 821 + .../JDBC/expected/BABEL-OBJECT-FUNCTIONS.out | 50 + contrib/test/JDBC/expected/BABEL-PROCID.out | 303 + contrib/test/JDBC/expected/BABEL-RAND.out | 89 + .../JDBC/expected/BABEL-RECURSIVE-CTE.out | 135 + contrib/test/JDBC/expected/BABEL-ROWCOUNT.out | 145 + .../test/JDBC/expected/BABEL-ROWVERSION.out | 313 + .../JDBC/expected/BABEL-SCHEMABINDING.out | 106 + contrib/test/JDBC/expected/BABEL-SEQUENCE.out | 608 + .../JDBC/expected/BABEL-SERVERPROPERTY.out | 47 + .../test/JDBC/expected/BABEL-SERVICENAME.out | 7 + contrib/test/JDBC/expected/BABEL-SESSION.out | 129 + .../test/JDBC/expected/BABEL-SET-COMMAND.out | 100 + .../test/JDBC/expected/BABEL-SPCOLUMNS.out | 149 + contrib/test/JDBC/expected/BABEL-SPCURSOR.out | 474 + .../expected/BABEL-SP_COLUMN_PRIVILEGES.out | 349 + .../test/JDBC/expected/BABEL-SP_DATABASES.out | 37 + .../JDBC/expected/BABEL-SP_DATATYPE_INFO.out | 49 + .../BABEL-SP_DESCRIBE_FRIST_RESULT_SET.out | 30 + contrib/test/JDBC/expected/BABEL-SP_FKEYS.out | 239 + contrib/test/JDBC/expected/BABEL-SP_PKEYS.out | 166 + .../expected/BABEL-SP_SPECIAL_COLUMNS.out | 711 + .../JDBC/expected/BABEL-SP_STATISTICS.out | 147 + .../expected/BABEL-SP_STORED_PROCEDURES.out | 211 + .../test/JDBC/expected/BABEL-SP_TABLES.out | 307 + .../expected/BABEL-SP_TABLE_PRIVILEGES.out | 314 + .../expected/BABEL-SQL-CONFIG-FUNCTIONS.out | 7 + .../test/JDBC/expected/BABEL-SQLvariant.out | 1063 + contrib/test/JDBC/expected/BABEL-SQUARE.out | 114 + .../JDBC/expected/BABEL-SYS-DATABASES.out | 52 + .../test/JDBC/expected/BABEL-SYSCHARSETS.out | 10 + .../test/JDBC/expected/BABEL-TABLEHINT.out | 217 + .../test/JDBC/expected/BABEL-TARGETLIST.out | 32 + .../test/JDBC/expected/BABEL-UNSUPPORTED.out | 2731 +++ contrib/test/JDBC/expected/BABEL-USER.out | 355 + contrib/test/JDBC/expected/BABEL_2429.out | 35 + contrib/test/JDBC/expected/Babel-2655.out | 33 + contrib/test/JDBC/expected/HAS_DBACCESS.out | 59 + contrib/test/JDBC/expected/ISC-Domains.out | 140 + .../JDBC/expected/ISC-Table_Constraints.out | 278 + contrib/test/JDBC/expected/ISC-Views.out | 144 + contrib/test/JDBC/expected/Test-xp_qv.out | 51 + contrib/test/JDBC/expected/TestBIT.out | 45 + .../test/JDBC/expected/TestBasicInterop.out | 188 + contrib/test/JDBC/expected/TestBigInt.out | 102 + contrib/test/JDBC/expected/TestBinary.out | 69 + contrib/test/JDBC/expected/TestChar.out | 60 + .../TestCompileTimeErrorWithDefaultBehave.out | 268 + .../JDBC/expected/TestCursorFetchNext.out | 269 + .../expected/TestCursorPrepExecFetchNext.out | 288 + contrib/test/JDBC/expected/TestDate.out | 57 + contrib/test/JDBC/expected/TestDatetime.out | 156 + contrib/test/JDBC/expected/TestDatetime2.out | 43 + contrib/test/JDBC/expected/TestDecimal.out | 546 + .../test/JDBC/expected/TestErrorFunctions.out | 399 + .../expected/TestErrorHelperFunctions.out | 177 + contrib/test/JDBC/expected/TestFloat.out | 102 + contrib/test/JDBC/expected/TestForXML.out | 486 + .../JDBC/expected/TestInsteadofTriggers.out | 1054 + contrib/test/JDBC/expected/TestInt.out | 111 + .../JDBC/expected/TestInteropProcedures.out | 220 + .../JDBC/expected/TestIsolationLevels.out | 104 + contrib/test/JDBC/expected/TestMoney.out | 111 + contrib/test/JDBC/expected/TestNotNull.out | 832 + contrib/test/JDBC/expected/TestNumeric.out | 734 + .../test/JDBC/expected/TestPrepareExec.out | 55 + .../TestProcedureWithErrorHandling.out | 499 + .../TestProcedureWithTransactions.out | 748 + .../expected/TestProcedureWithTriggers.out | 1402 ++ contrib/test/JDBC/expected/TestRaiserror.out | 754 + .../JDBC/expected/TestRaiserrorFormat.out | 113 + contrib/test/JDBC/expected/TestReal.out | 102 + .../TestRunTimeErrorWithDefaultBehave.out | 295 + .../test/JDBC/expected/TestSPExecutesql.out | 285 + contrib/test/JDBC/expected/TestSPPrepare.out | 750 + contrib/test/JDBC/expected/TestSQLQueries.out | 839 + .../test/JDBC/expected/TestSmallDatetime.out | 148 + contrib/test/JDBC/expected/TestSmallInt.out | 102 + .../JDBC/expected/TestStoredProcedures.out | 241 + contrib/test/JDBC/expected/TestText.out | 32 + contrib/test/JDBC/expected/TestThrow.out | 659 + contrib/test/JDBC/expected/TestTime.out | 300 + contrib/test/JDBC/expected/TestTinyInt.out | 103 + .../JDBC/expected/TestTransactionName.out | 135 + .../TestTransactionSupportForProcedure.out | 976 + .../expected/TestTransactionsSQLBatch.out | 494 + contrib/test/JDBC/expected/TestUDD.out | 139 + .../JDBC/expected/TestUniqueIdentifier.out | 151 + contrib/test/JDBC/expected/TestVarChar.out | 159 + contrib/test/JDBC/expected/TestXML.out | 29 + contrib/test/JDBC/expected/babel_1127.out | 21 + contrib/test/JDBC/expected/babel_404.out | 87 + contrib/test/JDBC/expected/babel_417.out | 166 + contrib/test/JDBC/expected/babel_613.out | 199 + contrib/test/JDBC/expected/babel_621.out | 197 + contrib/test/JDBC/expected/babel_bit_comp.out | 264 + .../JDBC/expected/babel_ceiling_floor.out | 116 + contrib/test/JDBC/expected/babel_char.out | 73 + contrib/test/JDBC/expected/babel_choose.out | 147 + .../test/JDBC/expected/babel_collation.out | 201 + contrib/test/JDBC/expected/babel_cursor.out | 4339 ++++ .../expected/babel_databasepropertyex.out | 103 + .../expected/babel_datatype_sqlvariant.out | 1443 ++ contrib/test/JDBC/expected/babel_datetime.out | 462 + .../test/JDBC/expected/babel_datetime2.out | 702 + .../JDBC/expected/babel_datetimeoffset.out | 943 + contrib/test/JDBC/expected/babel_declare.out | 49 + .../JDBC/expected/babel_error_handling.out | 72 + .../test/JDBC/expected/babel_exec_batch.out | 128 + .../JDBC/expected/babel_function_string.out | 836 + .../JDBC/expected/babel_functions_cast.out | 974 + .../test/JDBC/expected/babel_hashbytes.out | 225 + contrib/test/JDBC/expected/babel_iif.out | 109 + contrib/test/JDBC/expected/babel_isnull.out | 224 + .../test/JDBC/expected/babel_isnumeric.out | 310 + .../JDBC/expected/babel_iterative_exec.out | 242 + .../babel_iterative_exec_goto_label.out | 233 + .../expected/babel_iterative_exec_loop.out | 305 + .../expected/babel_iterative_exec_return.out | 174 + .../babel_iterative_exec_try_catch.out | 69 + contrib/test/JDBC/expected/babel_money.out | 544 + .../JDBC/expected/babel_money_upgrade.out | 544 + contrib/test/JDBC/expected/babel_numeric.out | 123 + .../test/JDBC/expected/babel_operators.out | 208 + contrib/test/JDBC/expected/babel_print.out | 39 + .../JDBC/expected/babel_smalldatetime.out | 388 + .../babel_sqlvariant_cast_compare.out | 576 + .../test/JDBC/expected/babel_temp_table.out | 359 + contrib/test/JDBC/expected/babel_top.out | 91 + contrib/test/JDBC/expected/babel_trigger.out | 407 + contrib/test/JDBC/expected/babel_update.out | 385 + .../test/JDBC/expected/babel_varbinary.out | 206 + .../JDBC/expected/fulltextserviceproperty.out | 55 + contrib/test/JDBC/expected/insertbulk.out | 724 + .../test/JDBC/expected/is_srvrolemember.out | 167 + .../test/JDBC/expected/pg_stat_activity.out | 65 + contrib/test/JDBC/expected/sp_columns_100.out | 222 + .../test/JDBC/expected/sp_statistics_100.out | 114 + .../JDBC/expected/sys-column-property.out | 110 + .../test/JDBC/expected/sys-configurations.out | 49 + contrib/test/JDBC/expected/sys-datefirst.out | 26 + contrib/test/JDBC/expected/sys-endpoints.out | 7 + .../JDBC/expected/sys-foreign_key_columns.out | 105 + .../test/JDBC/expected/sys-foreign_keys.out | 169 + .../JDBC/expected/sys-key_constraints.out | 61 + .../test/JDBC/expected/sys-lock_timeout.out | 80 + .../JDBC/expected/sys-max_connections.out | 30 + .../test/JDBC/expected/sys-original_login.out | 7 + contrib/test/JDBC/expected/sys-procedures.out | 200 + .../test/JDBC/expected/sys-schema-name.out | 15 + .../test/JDBC/expected/sys-sp_tables_view.out | 91 + contrib/test/JDBC/expected/sys-suser_sid.out | 27 + .../test/JDBC/expected/sys-suser_sname.out | 32 + contrib/test/JDBC/expected/sys-syscolumns.out | 451 + .../test/JDBC/expected/sys-sysforeignkeys.out | 105 + .../test/JDBC/expected/sys-table_types.out | 93 + contrib/test/JDBC/expected/sys-tables.out | 291 + .../JDBC/expected/sys-trigger_nestlevel.out | 58 + contrib/test/JDBC/expected/sys-types.out | 170 + contrib/test/JDBC/expected/sys-views.out | 127 + .../test/JDBC/expected/tds_faultinjection.out | 79 + contrib/test/JDBC/init.sh | 19 + contrib/test/JDBC/input/BABEL-1000.sql | 177 + contrib/test/JDBC/input/BABEL-101.sql | 82 + contrib/test/JDBC/input/BABEL-1044.sql | 199 + contrib/test/JDBC/input/BABEL-1056.sql | 48 + contrib/test/JDBC/input/BABEL-1066.sql | 17 + contrib/test/JDBC/input/BABEL-1073.sql | 27 + contrib/test/JDBC/input/BABEL-1091.sql | 173 + contrib/test/JDBC/input/BABEL-1094.sql | 28 + contrib/test/JDBC/input/BABEL-1096.sql | 60 + contrib/test/JDBC/input/BABEL-1098.sql | 403 + contrib/test/JDBC/input/BABEL-1100.sql | 75 + contrib/test/JDBC/input/BABEL-1111.sql | 14 + contrib/test/JDBC/input/BABEL-1113.sql | 34 + contrib/test/JDBC/input/BABEL-1114.sql | 50 + contrib/test/JDBC/input/BABEL-1149.sql | 62 + contrib/test/JDBC/input/BABEL-1161.sql | 134 + contrib/test/JDBC/input/BABEL-1164.sql | 52 + contrib/test/JDBC/input/BABEL-1167.sql | 24 + contrib/test/JDBC/input/BABEL-1173.sql | 13 + contrib/test/JDBC/input/BABEL-1179.sql | 67 + contrib/test/JDBC/input/BABEL-1184.sql | 66 + contrib/test/JDBC/input/BABEL-1185.sql | 14 + contrib/test/JDBC/input/BABEL-1189.sql | 27 + contrib/test/JDBC/input/BABEL-1193.sql | 158 + contrib/test/JDBC/input/BABEL-1206.sql | 73 + contrib/test/JDBC/input/BABEL-1208.sql | 59 + contrib/test/JDBC/input/BABEL-1210.sql | 98 + contrib/test/JDBC/input/BABEL-1212.sql | 10 + contrib/test/JDBC/input/BABEL-1231.sql | 29 + contrib/test/JDBC/input/BABEL-1234.sql | 2 + contrib/test/JDBC/input/BABEL-1239.sql | 15 + contrib/test/JDBC/input/BABEL-1241.sql | 73 + contrib/test/JDBC/input/BABEL-1243.sql | 19 + contrib/test/JDBC/input/BABEL-1249.sql | 43 + contrib/test/JDBC/input/BABEL-1251.sql | 76 + contrib/test/JDBC/input/BABEL-1252.sql | 65 + contrib/test/JDBC/input/BABEL-1261.sql | 79 + contrib/test/JDBC/input/BABEL-1270.txt | 15 + contrib/test/JDBC/input/BABEL-1287.sql | 90 + contrib/test/JDBC/input/BABEL-1291.sql | 16 + contrib/test/JDBC/input/BABEL-1299.sql | 29 + contrib/test/JDBC/input/BABEL-1309.mix | 69 + contrib/test/JDBC/input/BABEL-1311.sql | 118 + contrib/test/JDBC/input/BABEL-1319.sql | 30 + contrib/test/JDBC/input/BABEL-1320.sql | 46 + contrib/test/JDBC/input/BABEL-1329.sql | 26 + contrib/test/JDBC/input/BABEL-1331.sql | 19 + contrib/test/JDBC/input/BABEL-1363.mix | 35 + contrib/test/JDBC/input/BABEL-1381.sql | 111 + contrib/test/JDBC/input/BABEL-1389.sql | 36 + contrib/test/JDBC/input/BABEL-1400.sql | 53 + contrib/test/JDBC/input/BABEL-1435.sql | 112 + contrib/test/JDBC/input/BABEL-1437.sql | 46 + contrib/test/JDBC/input/BABEL-1438.sql | 3 + contrib/test/JDBC/input/BABEL-1442.sql | 253 + contrib/test/JDBC/input/BABEL-1444.sql | 110 + contrib/test/JDBC/input/BABEL-1446.sql | 93 + contrib/test/JDBC/input/BABEL-1454.mix | 273 + contrib/test/JDBC/input/BABEL-1465.sql | 258 + contrib/test/JDBC/input/BABEL-1466.sql | 73 + contrib/test/JDBC/input/BABEL-1475.sql | 109 + contrib/test/JDBC/input/BABEL-1481.sql | 83 + contrib/test/JDBC/input/BABEL-1499.sql | 25 + contrib/test/JDBC/input/BABEL-1502.sql | 22 + contrib/test/JDBC/input/BABEL-1510.sql | 77 + contrib/test/JDBC/input/BABEL-1513.sql | 41 + contrib/test/JDBC/input/BABEL-1515.sql | 23 + contrib/test/JDBC/input/BABEL-1524.sql | 222 + contrib/test/JDBC/input/BABEL-1531.sql | 18 + contrib/test/JDBC/input/BABEL-1544.sql | 41 + contrib/test/JDBC/input/BABEL-1566.sql | 181 + contrib/test/JDBC/input/BABEL-1569.sql | 57 + contrib/test/JDBC/input/BABEL-1577.sql | 25 + contrib/test/JDBC/input/BABEL-1603.sql | 35 + contrib/test/JDBC/input/BABEL-1621.sql | 5 + contrib/test/JDBC/input/BABEL-1631.sql | 36 + contrib/test/JDBC/input/BABEL-1636.sql | 50 + contrib/test/JDBC/input/BABEL-1654.sql | 50 + contrib/test/JDBC/input/BABEL-1661.sql | 13 + contrib/test/JDBC/input/BABEL-1666.sql | 19 + contrib/test/JDBC/input/BABEL-1671.sql | 18 + contrib/test/JDBC/input/BABEL-1682.sql | 26 + contrib/test/JDBC/input/BABEL-1683.sql | 106 + contrib/test/JDBC/input/BABEL-1708.sql | 76 + contrib/test/JDBC/input/BABEL-1712.sql | 41 + contrib/test/JDBC/input/BABEL-1715.sql | 39 + contrib/test/JDBC/input/BABEL-1719.sql | 26 + contrib/test/JDBC/input/BABEL-1746.sql | 5 + contrib/test/JDBC/input/BABEL-1756.sql | 14 + contrib/test/JDBC/input/BABEL-1757.sql | 9 + contrib/test/JDBC/input/BABEL-1771.sql | 63 + contrib/test/JDBC/input/BABEL-1792.sql | 51 + contrib/test/JDBC/input/BABEL-1797.sql | 13 + contrib/test/JDBC/input/BABEL-1808.sql | 40 + contrib/test/JDBC/input/BABEL-1813.sql | 132 + contrib/test/JDBC/input/BABEL-1839.sql | 39 + contrib/test/JDBC/input/BABEL-1845.sql | 35 + contrib/test/JDBC/input/BABEL-1858.sql | 40 + contrib/test/JDBC/input/BABEL-1887.mix | 40 + contrib/test/JDBC/input/BABEL-1944.sql | 140 + contrib/test/JDBC/input/BABEL-1963.sql | 32 + contrib/test/JDBC/input/BABEL-1975.sql | 34 + contrib/test/JDBC/input/BABEL-1978.sql | 24 + contrib/test/JDBC/input/BABEL-1994-CHAR.sql | 146 + .../test/JDBC/input/BABEL-1994-VARCHAR.sql | 130 + contrib/test/JDBC/input/BABEL-1997.sql | 25 + contrib/test/JDBC/input/BABEL-2010.sql | 31 + contrib/test/JDBC/input/BABEL-2011.sql | 29 + contrib/test/JDBC/input/BABEL-2020-DELETE.sql | 138 + contrib/test/JDBC/input/BABEL-2020-UPDATE.sql | 138 + contrib/test/JDBC/input/BABEL-2034.sql | 79 + contrib/test/JDBC/input/BABEL-2049.sql | 62 + contrib/test/JDBC/input/BABEL-2051.sql | 12 + contrib/test/JDBC/input/BABEL-2060.sql | 104 + contrib/test/JDBC/input/BABEL-2079.sql | 44 + contrib/test/JDBC/input/BABEL-2086.sql | 286 + contrib/test/JDBC/input/BABEL-2089.sql | 384 + contrib/test/JDBC/input/BABEL-2117.sql | 32 + contrib/test/JDBC/input/BABEL-213.mix | 353 + contrib/test/JDBC/input/BABEL-2145.sql | 32 + contrib/test/JDBC/input/BABEL-2203.sql | 122 + contrib/test/JDBC/input/BABEL-2208.sql | 46 + contrib/test/JDBC/input/BABEL-2218.sql | 40 + contrib/test/JDBC/input/BABEL-2225.sql | 24 + contrib/test/JDBC/input/BABEL-2234.sql | 21 + contrib/test/JDBC/input/BABEL-2257.sql | 75 + contrib/test/JDBC/input/BABEL-2258.sql | 11 + contrib/test/JDBC/input/BABEL-2262.sql | 41 + contrib/test/JDBC/input/BABEL-2266.sql | 61 + contrib/test/JDBC/input/BABEL-2276.sql | 6 + contrib/test/JDBC/input/BABEL-2288.sql | 29 + contrib/test/JDBC/input/BABEL-2296.mix | 21 + contrib/test/JDBC/input/BABEL-2303.sql | 67 + .../test/JDBC/input/BABEL-2303_upgrade.txt | 1 + contrib/test/JDBC/input/BABEL-2317.sql | 22 + contrib/test/JDBC/input/BABEL-2325.sql | 39 + contrib/test/JDBC/input/BABEL-2328.sql | 58 + contrib/test/JDBC/input/BABEL-2337.sql | 5 + contrib/test/JDBC/input/BABEL-2347.sql | 162 + contrib/test/JDBC/input/BABEL-2349.sql | 34 + contrib/test/JDBC/input/BABEL-235.sql | 59 + contrib/test/JDBC/input/BABEL-2350.sql | 44 + contrib/test/JDBC/input/BABEL-2354.sql | 15 + contrib/test/JDBC/input/BABEL-2355.sql | 15 + contrib/test/JDBC/input/BABEL-2357.sql | 9 + contrib/test/JDBC/input/BABEL-2371.sql | 65 + contrib/test/JDBC/input/BABEL-2372.sql | 18 + contrib/test/JDBC/input/BABEL-2381.sql | 331 + contrib/test/JDBC/input/BABEL-2390.sql | 24 + contrib/test/JDBC/input/BABEL-2392.sql | 49 + contrib/test/JDBC/input/BABEL-2403.mix | 44 + contrib/test/JDBC/input/BABEL-2409.mix | 31 + contrib/test/JDBC/input/BABEL-2410.sql | 9 + contrib/test/JDBC/input/BABEL-2412.sql | 5 + contrib/test/JDBC/input/BABEL-2416.sql | 15 + contrib/test/JDBC/input/BABEL-2417.sql | 5 + contrib/test/JDBC/input/BABEL-2418.sql | 26 + contrib/test/JDBC/input/BABEL-2419.sql | 20 + contrib/test/JDBC/input/BABEL-2432.sql | 27 + contrib/test/JDBC/input/BABEL-2433.sql | 34 + contrib/test/JDBC/input/BABEL-2437.sql | 16 + contrib/test/JDBC/input/BABEL-244.sql | 43 + contrib/test/JDBC/input/BABEL-2440.mix | 38 + contrib/test/JDBC/input/BABEL-2445.sql | 11 + contrib/test/JDBC/input/BABEL-2451.sql | 11 + contrib/test/JDBC/input/BABEL-2458.mix | 82 + contrib/test/JDBC/input/BABEL-2470.sql | 22 + contrib/test/JDBC/input/BABEL-2482.sql | 28 + contrib/test/JDBC/input/BABEL-2485.sql | 74 + contrib/test/JDBC/input/BABEL-2496.sql | 22 + contrib/test/JDBC/input/BABEL-2513.sql | 354 + contrib/test/JDBC/input/BABEL-2514.sql | 128 + contrib/test/JDBC/input/BABEL-2515.sql | 32 + contrib/test/JDBC/input/BABEL-2535.sql | 177 + contrib/test/JDBC/input/BABEL-2555.sql | 83 + contrib/test/JDBC/input/BABEL-2557.sql | 5 + contrib/test/JDBC/input/BABEL-2565.sql | 91 + contrib/test/JDBC/input/BABEL-2569.sql | 17 + contrib/test/JDBC/input/BABEL-2576.sql | 67 + contrib/test/JDBC/input/BABEL-2578.mix | 29 + contrib/test/JDBC/input/BABEL-2585.sql | 178 + contrib/test/JDBC/input/BABEL-2593.sql | 48 + contrib/test/JDBC/input/BABEL-2596.sql | 40 + contrib/test/JDBC/input/BABEL-2602.mix | 20 + contrib/test/JDBC/input/BABEL-2604.sql | 86 + contrib/test/JDBC/input/BABEL-2607.sql | 35 + contrib/test/JDBC/input/BABEL-2622.mix | 27 + contrib/test/JDBC/input/BABEL-2647.sql | 13 + contrib/test/JDBC/input/BABEL-2649.sql | 24 + contrib/test/JDBC/input/BABEL-265.sql | 56 + contrib/test/JDBC/input/BABEL-2659.sql | 50 + contrib/test/JDBC/input/BABEL-2674.sql | 55 + contrib/test/JDBC/input/BABEL-2676.sql | 23 + contrib/test/JDBC/input/BABEL-2681.sql | 9 + contrib/test/JDBC/input/BABEL-2687.sql | 327 + .../test/JDBC/input/BABEL-2687_upgrade.txt | 1 + contrib/test/JDBC/input/BABEL-2701.sql | 8 + contrib/test/JDBC/input/BABEL-2706.sql | 20 + contrib/test/JDBC/input/BABEL-2716.sql | 33 + contrib/test/JDBC/input/BABEL-2724.sql | 41 + contrib/test/JDBC/input/BABEL-2725.sql | 42 + contrib/test/JDBC/input/BABEL-2730.sql | 2486 +++ contrib/test/JDBC/input/BABEL-2732.sql | 78 + contrib/test/JDBC/input/BABEL-2747.sql | 47 + contrib/test/JDBC/input/BABEL-2748.sql | 126 + contrib/test/JDBC/input/BABEL-2765.sql | 95 + contrib/test/JDBC/input/BABEL-2785.sql | 2 + contrib/test/JDBC/input/BABEL-2787-2.sql | 59 + contrib/test/JDBC/input/BABEL-2787.sql | 66 + contrib/test/JDBC/input/BABEL-2824.sql | 51 + contrib/test/JDBC/input/BABEL-2829.sql | 17 + contrib/test/JDBC/input/BABEL-2833.sql | 53 + contrib/test/JDBC/input/BABEL-2835.sql | 16 + contrib/test/JDBC/input/BABEL-2845.sql | 63 + contrib/test/JDBC/input/BABEL-2857.sql | 5 + contrib/test/JDBC/input/BABEL-2869.sql | 35 + contrib/test/JDBC/input/BABEL-2875.sql | 37 + contrib/test/JDBC/input/BABEL-2884.sql | 78 + contrib/test/JDBC/input/BABEL-2917.sql | 47 + contrib/test/JDBC/input/BABEL-2924.sql | 48 + contrib/test/JDBC/input/BABEL-2936.sql | 8 + contrib/test/JDBC/input/BABEL-2944.sql | 38 + contrib/test/JDBC/input/BABEL-2955.sql | 44 + contrib/test/JDBC/input/BABEL-2964.sql | 45 + contrib/test/JDBC/input/BABEL-2968.sql | 19 + contrib/test/JDBC/input/BABEL-2977.sql | 14 + contrib/test/JDBC/input/BABEL-2979.sql | 2 + contrib/test/JDBC/input/BABEL-2983.sql | 111 + .../test/JDBC/input/BABEL-2983_upgrade.txt | 1 + contrib/test/JDBC/input/BABEL-2984.sql | 5 + contrib/test/JDBC/input/BABEL-2988.sql | 5 + contrib/test/JDBC/input/BABEL-2993.txt | 10 + contrib/test/JDBC/input/BABEL-3004.sql | 35 + contrib/test/JDBC/input/BABEL-3005.sql | 74 + contrib/test/JDBC/input/BABEL-3006.sql | 81 + contrib/test/JDBC/input/BABEL-3019.mix | 95 + contrib/test/JDBC/input/BABEL-3036.sql | 14 + contrib/test/JDBC/input/BABEL-3101.sql | 90 + contrib/test/JDBC/input/BABEL-3108.sql | 32 + contrib/test/JDBC/input/BABEL-3119.sql | 37 + contrib/test/JDBC/input/BABEL-338.sql | 126 + contrib/test/JDBC/input/BABEL-381.sql | 10 + contrib/test/JDBC/input/BABEL-383.sql | 29 + contrib/test/JDBC/input/BABEL-388.sql | 65 + contrib/test/JDBC/input/BABEL-405.sql | 200 + contrib/test/JDBC/input/BABEL-407.sql | 20 + contrib/test/JDBC/input/BABEL-453.sql | 117 + contrib/test/JDBC/input/BABEL-479.sql | 61 + contrib/test/JDBC/input/BABEL-480.sql | 17 + contrib/test/JDBC/input/BABEL-492.sql | 90 + contrib/test/JDBC/input/BABEL-493.sql | 70 + contrib/test/JDBC/input/BABEL-559.sql | 43 + contrib/test/JDBC/input/BABEL-565.sql | 37 + contrib/test/JDBC/input/BABEL-586.sql | 99 + contrib/test/JDBC/input/BABEL-588.sql | 1205 ++ contrib/test/JDBC/input/BABEL-597.sql | 6 + contrib/test/JDBC/input/BABEL-604.sql | 20 + contrib/test/JDBC/input/BABEL-625.sql | 9 + contrib/test/JDBC/input/BABEL-627.sql | 10 + contrib/test/JDBC/input/BABEL-632.sql | 24 + contrib/test/JDBC/input/BABEL-637.sql | 734 + contrib/test/JDBC/input/BABEL-646.sql | 77 + contrib/test/JDBC/input/BABEL-662.sql | 71 + contrib/test/JDBC/input/BABEL-672.sql | 5 + contrib/test/JDBC/input/BABEL-690.sql | 40 + contrib/test/JDBC/input/BABEL-694.sql | 24 + contrib/test/JDBC/input/BABEL-701.sql | 12 + contrib/test/JDBC/input/BABEL-708.sql | 190 + contrib/test/JDBC/input/BABEL-719.sql | 54 + contrib/test/JDBC/input/BABEL-728.sql | 248 + contrib/test/JDBC/input/BABEL-735.sql | 185 + contrib/test/JDBC/input/BABEL-741.sql | 49 + contrib/test/JDBC/input/BABEL-758.sql | 102 + contrib/test/JDBC/input/BABEL-768.sql | 88 + contrib/test/JDBC/input/BABEL-775.sql | 42 + contrib/test/JDBC/input/BABEL-776.sql | 74 + contrib/test/JDBC/input/BABEL-785.sql | 115 + contrib/test/JDBC/input/BABEL-788.sql | 91 + contrib/test/JDBC/input/BABEL-798.sql | 9 + contrib/test/JDBC/input/BABEL-807.sql | 9 + contrib/test/JDBC/input/BABEL-820.sql | 21 + contrib/test/JDBC/input/BABEL-872.sql | 21 + contrib/test/JDBC/input/BABEL-889.sql | 113 + contrib/test/JDBC/input/BABEL-909.sql | 30 + contrib/test/JDBC/input/BABEL-911.sql | 11 + contrib/test/JDBC/input/BABEL-931.sql | 51 + contrib/test/JDBC/input/BABEL-941.sql | 43 + contrib/test/JDBC/input/BABEL-942.sql | 201 + contrib/test/JDBC/input/BABEL-955.sql | 50 + contrib/test/JDBC/input/BABEL-APPLOCK.sql | 136 + .../test/JDBC/input/BABEL-COLUMN-ALIAS.sql | 114 + .../JDBC/input/BABEL-COLUMN-CONSTRAINT.sql | 57 + .../JDBC/input/BABEL-DATABASEPROPERTYEX.sql | 21 + .../JDBC/input/BABEL-EXTENDEDPROPERTY.sql | 17 + contrib/test/JDBC/input/BABEL-GRANT.sql | 174 + contrib/test/JDBC/input/BABEL-GUC-PLAN.sql | 213 + .../test/JDBC/input/BABEL-IDENTITY-BIFS.sql | 346 + .../test/JDBC/input/BABEL-IDENTITY-COLUMN.sql | 123 + contrib/test/JDBC/input/BABEL-IDENTITY.sql | 270 + .../input/BABEL-IMPLICIT_TRAN-PREPEXEC.txt | 40 + .../test/JDBC/input/BABEL-IMPLICIT_TRAN.sql | 232 + .../test/JDBC/input/BABEL-INSERT-EXECUTE.sql | 272 + contrib/test/JDBC/input/BABEL-JOIN.sql | 19 + contrib/test/JDBC/input/BABEL-JSON-FUNCS.sql | 183 + contrib/test/JDBC/input/BABEL-LIKE2ILIKE.sql | 96 + .../test/JDBC/input/BABEL-LOGIN-USER-EXT.mix | 409 + .../JDBC/input/BABEL-OBJECT-FUNCTIONS.sql | 25 + contrib/test/JDBC/input/BABEL-PROCID.sql | 198 + contrib/test/JDBC/input/BABEL-RAND.sql | 29 + .../test/JDBC/input/BABEL-RECURSIVE-CTE.sql | 85 + contrib/test/JDBC/input/BABEL-ROWCOUNT.sql | 112 + contrib/test/JDBC/input/BABEL-ROWVERSION.sql | 198 + .../test/JDBC/input/BABEL-SCHEMABINDING.sql | 65 + contrib/test/JDBC/input/BABEL-SEQUENCE.sql | 300 + .../test/JDBC/input/BABEL-SERVERPROPERTY.sql | 17 + contrib/test/JDBC/input/BABEL-SERVICENAME.sql | 2 + contrib/test/JDBC/input/BABEL-SESSION.mix | 90 + contrib/test/JDBC/input/BABEL-SET-COMMAND.sql | 55 + contrib/test/JDBC/input/BABEL-SPCOLUMNS.sql | 75 + contrib/test/JDBC/input/BABEL-SPCURSOR.sql | 231 + .../JDBC/input/BABEL-SP_COLUMN_PRIVILEGES.mix | 169 + .../test/JDBC/input/BABEL-SP_DATABASES.sql | 24 + .../JDBC/input/BABEL-SP_DATATYPE_INFO.sql | 17 + .../BABEL-SP_DESCRIBE_FRIST_RESULT_SET.sql | 17 + contrib/test/JDBC/input/BABEL-SP_FKEYS.sql | 122 + contrib/test/JDBC/input/BABEL-SP_PKEYS.sql | 99 + .../JDBC/input/BABEL-SP_SPECIAL_COLUMNS.sql | 392 + .../test/JDBC/input/BABEL-SP_STATISTICS.sql | 83 + .../JDBC/input/BABEL-SP_STORED_PROCEDURES.sql | 129 + contrib/test/JDBC/input/BABEL-SP_TABLES.sql | 149 + .../JDBC/input/BABEL-SP_TABLE_PRIVILEGES.sql | 138 + .../JDBC/input/BABEL-SQL-CONFIG-FUNCTIONS.sql | 2 + contrib/test/JDBC/input/BABEL-SQLvariant.sql | 523 + contrib/test/JDBC/input/BABEL-SQUARE.sql | 40 + .../test/JDBC/input/BABEL-SYS-DATABASES.mix | 26 + contrib/test/JDBC/input/BABEL-SYSCHARSETS.sql | 5 + contrib/test/JDBC/input/BABEL-TABLEHINT.sql | 86 + contrib/test/JDBC/input/BABEL-TARGETLIST.sql | 16 + contrib/test/JDBC/input/BABEL-UNSUPPORTED.sql | 1553 ++ contrib/test/JDBC/input/BABEL-USER.sql | 208 + contrib/test/JDBC/input/BABEL_2429.sql | 30 + contrib/test/JDBC/input/Babel-2655.sql | 26 + .../test/JDBC/input/ErrorMapping/102_1.sql | 316 + .../test/JDBC/input/ErrorMapping/102_2.sql | 326 + .../test/JDBC/input/ErrorMapping/102_3.sql | 326 + .../test/JDBC/input/ErrorMapping/102_6.sql | 316 + .../test/JDBC/input/ErrorMapping/1034_1.sql | 54 + .../test/JDBC/input/ErrorMapping/1049_1.sql | 323 + .../test/JDBC/input/ErrorMapping/1051_1.sql | 58 + .../test/JDBC/input/ErrorMapping/10610_1.sql | 267 + .../test/JDBC/input/ErrorMapping/113_1.sql | 207 + .../test/JDBC/input/ErrorMapping/11700_1.sql | 189 + .../test/JDBC/input/ErrorMapping/11701_1.sql | 201 + .../test/JDBC/input/ErrorMapping/11702_1.sql | 260 + .../test/JDBC/input/ErrorMapping/11703_1.sql | 300 + .../test/JDBC/input/ErrorMapping/11703_2.sql | 300 + .../test/JDBC/input/ErrorMapping/11704_1.sql | 201 + .../test/JDBC/input/ErrorMapping/11704_2.sql | 219 + .../test/JDBC/input/ErrorMapping/11705_1.sql | 195 + .../test/JDBC/input/ErrorMapping/11706_1.sql | 213 + .../test/JDBC/input/ErrorMapping/11708_1.sql | 201 + .../test/JDBC/input/ErrorMapping/129_1.sql | 305 + .../test/JDBC/input/ErrorMapping/132_1.sql | 376 + .../test/JDBC/input/ErrorMapping/133_1.sql | 378 + .../test/JDBC/input/ErrorMapping/134_1.sql | 365 + .../test/JDBC/input/ErrorMapping/135_1.sql | 336 + .../test/JDBC/input/ErrorMapping/136_1.sql | 336 + .../test/JDBC/input/ErrorMapping/141_1.sql | 345 + .../test/JDBC/input/ErrorMapping/142_1.sql | 284 + .../test/JDBC/input/ErrorMapping/1505_1.sql | 211 + .../test/JDBC/input/ErrorMapping/16948_1.sql | 502 + .../test/JDBC/input/ErrorMapping/16950_1.sql | 322 + .../JDBC/input/ErrorMapping/1752_1750_2.sql | 201 + .../JDBC/input/ErrorMapping/1765_1750_1.sql | 303 + .../JDBC/input/ErrorMapping/1768_1750_1.sql | 291 + .../JDBC/input/ErrorMapping/1770_1750_1.sql | 201 + .../JDBC/input/ErrorMapping/1774_1750_1.sql | 249 + .../test/JDBC/input/ErrorMapping/180_1.sql | 4246 ++++ .../test/JDBC/input/ErrorMapping/180_2.sql | 4242 ++++ .../test/JDBC/input/ErrorMapping/1946_1.sql | 523 + .../test/JDBC/input/ErrorMapping/201_1.sql | 423 + .../test/JDBC/input/ErrorMapping/206_1.sql | 334 + .../test/JDBC/input/ErrorMapping/208_1.sql | 189 + .../test/JDBC/input/ErrorMapping/217_1.sql | 285 + .../test/JDBC/input/ErrorMapping/218_1.sql | 263 + .../test/JDBC/input/ErrorMapping/219_1.sql | 183 + .../test/JDBC/input/ErrorMapping/220_1.sql | 219 + .../test/JDBC/input/ErrorMapping/220_2.sql | 213 + .../test/JDBC/input/ErrorMapping/220_3.sql | 213 + .../test/JDBC/input/ErrorMapping/220_4.sql | 213 + .../test/JDBC/input/ErrorMapping/222_1.sql | 195 + .../test/JDBC/input/ErrorMapping/257_2.sql | 195 + .../test/JDBC/input/ErrorMapping/2732_1.sql | 282 + .../test/JDBC/input/ErrorMapping/2733_1.sql | 71 + .../test/JDBC/input/ErrorMapping/2747_1.sql | 300 + .../test/JDBC/input/ErrorMapping/2760_1.sql | 186 + .../test/JDBC/input/ErrorMapping/2787_1.sql | 282 + .../test/JDBC/input/ErrorMapping/2812_1.sql | 177 + .../test/JDBC/input/ErrorMapping/2812_2.sql | 177 + .../test/JDBC/input/ErrorMapping/2812_3.sql | 177 + .../test/JDBC/input/ErrorMapping/293_1.sql | 378 + .../test/JDBC/input/ErrorMapping/3609_1.sql | 403 + .../test/JDBC/input/ErrorMapping/3701_3.sql | 189 + .../test/JDBC/input/ErrorMapping/3701_4.sql | 189 + .../test/JDBC/input/ErrorMapping/3701_5.sql | 189 + .../JDBC/input/ErrorMapping/3728_3727_1.sql | 225 + .../test/JDBC/input/ErrorMapping/3729_1.sql | 282 + .../test/JDBC/input/ErrorMapping/3902_1.sql | 189 + .../test/JDBC/input/ErrorMapping/3903_1.sql | 195 + .../test/JDBC/input/ErrorMapping/3930_1.sql | 271 + .../test/JDBC/input/ErrorMapping/4708_1.sql | 249 + .../test/JDBC/input/ErrorMapping/4712_1.sql | 243 + .../test/JDBC/input/ErrorMapping/4901_1.sql | 326 + .../test/JDBC/input/ErrorMapping/4920_1.sql | 243 + .../test/JDBC/input/ErrorMapping/4924_1.sql | 225 + .../test/JDBC/input/ErrorMapping/4924_2.sql | 225 + .../test/JDBC/input/ErrorMapping/4936_1.sql | 195 + .../test/JDBC/input/ErrorMapping/4936_2.sql | 213 + .../test/JDBC/input/ErrorMapping/512_1.sql | 207 + .../test/JDBC/input/ErrorMapping/515_1.sql | 201 + .../test/JDBC/input/ErrorMapping/517_1.sql | 282 + .../test/JDBC/input/ErrorMapping/545_1.sql | 261 + .../test/JDBC/input/ErrorMapping/547_1.sql | 153 + .../test/JDBC/input/ErrorMapping/547_2.sql | 156 + .../test/JDBC/input/ErrorMapping/550_1.sql | 279 + .../test/JDBC/input/ErrorMapping/556_1.sql | 468 + .../test/JDBC/input/ErrorMapping/6401_1.sql | 213 + .../test/JDBC/input/ErrorMapping/8106_1.sql | 314 + .../test/JDBC/input/ErrorMapping/8107_1.sql | 291 + .../test/JDBC/input/ErrorMapping/8143_1.sql | 201 + .../test/JDBC/input/ErrorMapping/8143_2.sql | 201 + .../test/JDBC/input/ErrorMapping/8144_1.sql | 286 + .../test/JDBC/input/ErrorMapping/8145_1.sql | 213 + .../test/JDBC/input/ErrorMapping/8146_1.sql | 336 + .../test/JDBC/input/ErrorMapping/8152_1.sql | 207 + .../test/JDBC/input/ErrorMapping/8152_2.sql | 195 + .../test/JDBC/input/ErrorMapping/8159_1.sql | 40 + .../test/JDBC/input/ErrorMapping/8179_1.sql | 302 + .../test/JDBC/input/ErrorMapping/9809_1.sql | 292 + .../TestCompileTimeErrorWithDefaultBehave.sql | 190 + .../ErrorMapping/TestErrorHelperFunctions.sql | 26 + .../TestRunTimeErrorWithDefaultBehave.sql | 207 + contrib/test/JDBC/input/HAS_DBACCESS.sql | 24 + contrib/test/JDBC/input/ISC-Domains.sql | 105 + .../test/JDBC/input/ISC-Table_Constraints.mix | 176 + contrib/test/JDBC/input/ISC-Views.sql | 64 + contrib/test/JDBC/input/TestNotNull.txt | 260 + .../JDBC/input/authentication/TestAuth.txt | 18 + contrib/test/JDBC/input/babel_1127.sql | 13 + contrib/test/JDBC/input/babel_404.sql | 59 + contrib/test/JDBC/input/babel_417.sql | 56 + contrib/test/JDBC/input/babel_613.sql | 81 + contrib/test/JDBC/input/babel_621.sql | 153 + contrib/test/JDBC/input/babel_bit_comp.sql | 94 + .../test/JDBC/input/babel_ceiling_floor.sql | 94 + contrib/test/JDBC/input/babel_char.sql | 18 + contrib/test/JDBC/input/babel_choose.sql | 58 + contrib/test/JDBC/input/babel_collation.sql | 31 + contrib/test/JDBC/input/babel_cursor.sql | 1991 ++ .../JDBC/input/babel_databasepropertyex.sql | 34 + .../JDBC/input/babel_datatype_sqlvariant.sql | 698 + contrib/test/JDBC/input/babel_datetime.sql | 173 + contrib/test/JDBC/input/babel_datetime2.sql | 233 + .../test/JDBC/input/babel_datetimeoffset.sql | 348 + contrib/test/JDBC/input/babel_declare.sql | 42 + .../test/JDBC/input/babel_error_handling.sql | 50 + contrib/test/JDBC/input/babel_exec_batch.sql | 90 + .../test/JDBC/input/babel_function_string.sql | 327 + .../test/JDBC/input/babel_functions_cast.sql | 308 + contrib/test/JDBC/input/babel_hashbytes.sql | 87 + contrib/test/JDBC/input/babel_iif.sql | 35 + contrib/test/JDBC/input/babel_isnull.mix | 128 + contrib/test/JDBC/input/babel_isnumeric.sql | 148 + .../test/JDBC/input/babel_iterative_exec.sql | 97 + .../input/babel_iterative_exec_goto_label.sql | 162 + .../JDBC/input/babel_iterative_exec_loop.sql | 115 + .../input/babel_iterative_exec_return.sql | 164 + .../input/babel_iterative_exec_try_catch.sql | 39 + contrib/test/JDBC/input/babel_money.sql | 199 + .../test/JDBC/input/babel_money_upgrade.txt | 1 + contrib/test/JDBC/input/babel_numeric.sql | 63 + contrib/test/JDBC/input/babel_operators.sql | 58 + contrib/test/JDBC/input/babel_print.sql | 39 + .../test/JDBC/input/babel_smalldatetime.sql | 102 + .../input/babel_sqlvariant_cast_compare.sql | 316 + contrib/test/JDBC/input/babel_temp_table.sql | 248 + contrib/test/JDBC/input/babel_top.sql | 40 + contrib/test/JDBC/input/babel_trigger.sql | 329 + contrib/test/JDBC/input/babel_update.sql | 180 + contrib/test/JDBC/input/babel_varbinary.sql | 86 + .../test/JDBC/input/cursors/BABEL-1390.txt | 19 + .../input/cursors/TestCursorFetchNext.txt | 89 + .../cursors/TestCursorPrepExecFetchNext.txt | 97 + contrib/test/JDBC/input/datatypes/TestBIT.txt | 16 + .../test/JDBC/input/datatypes/TestBigInt.txt | 24 + .../test/JDBC/input/datatypes/TestBinary.txt | 29 + .../test/JDBC/input/datatypes/TestChar.txt | 16 + .../test/JDBC/input/datatypes/TestDate.txt | 14 + .../JDBC/input/datatypes/TestDatetime.txt | 36 + .../JDBC/input/datatypes/TestDatetime2.txt | 15 + .../test/JDBC/input/datatypes/TestDecimal.txt | 180 + .../test/JDBC/input/datatypes/TestFloat.txt | 24 + contrib/test/JDBC/input/datatypes/TestInt.txt | 29 + .../test/JDBC/input/datatypes/TestMoney.txt | 30 + .../test/JDBC/input/datatypes/TestNumeric.txt | 228 + .../test/JDBC/input/datatypes/TestReal.txt | 24 + .../input/datatypes/TestSmallDatetime.txt | 35 + .../JDBC/input/datatypes/TestSmallInt.txt | 24 + .../test/JDBC/input/datatypes/TestText.txt | 10 + .../test/JDBC/input/datatypes/TestTime.txt | 104 + .../test/JDBC/input/datatypes/TestTinyInt.txt | 24 + contrib/test/JDBC/input/datatypes/TestUDD.txt | 79 + .../input/datatypes/TestUniqueIdentifier.txt | 34 + .../test/JDBC/input/datatypes/TestVarChar.txt | 48 + contrib/test/JDBC/input/datatypes/TestXML.txt | 11 + contrib/test/JDBC/input/ddl/temp-tables.sql | 305 + .../errorHandling/TestErrorFunctions.sql | 287 + .../errorHandling/TestErrorsWithTryCatch.sql | 979 + .../input/errorHandling/TestRaiserror.sql | 410 + .../errorHandling/TestRaiserrorFormat.sql | 53 + .../input/errorHandling/TestSimpleErrors.sql | 2532 +++ .../TestSimpleErrorsWithImplicitTran.txt | 9 + .../TestSimpleErrorsWithXactAbort.sql | 2379 +++ .../JDBC/input/errorHandling/TestThrow.sql | 382 + contrib/test/JDBC/input/forxml/TestForXML.sql | 250 + .../functions/fulltextserviceproperty.sql | 20 + .../JDBC/input/functions/is_srvrolemember.sql | 62 + .../input/functions/sys-column-property.sql | 40 + .../JDBC/input/functions/sys-datefirst.sql | 11 + .../JDBC/input/functions/sys-lock_timeout.sql | 42 + .../input/functions/sys-max_connections.sql | 11 + .../input/functions/sys-original_login.sql | 2 + .../JDBC/input/functions/sys-schema-name.sql | 5 + .../JDBC/input/functions/sys-suser_sid.sql | 12 + .../JDBC/input/functions/sys-suser_sname.sql | 13 + .../input/functions/sys-trigger_nestlevel.sql | 34 + contrib/test/JDBC/input/insertbulk.txt | 274 + .../interoperability/TestBasicInterop.mix | 129 + .../TestInteropProcedures.mix | 161 + .../TestProcedureWithErrorHandling.mix | 292 + .../TestProcedureWithTransactions.mix | 457 + .../TestProcedureWithTriggers.mix | 597 + contrib/test/JDBC/input/pg_stat_activity.txt | 20 + contrib/test/JDBC/input/sp_columns_100.sql | 135 + contrib/test/JDBC/input/sp_statistics_100.sql | 69 + .../JDBC/input/sqlBatch/TestSQLQueries.txt | 234 + .../input/storedProcedures/BABEL-1365.sql | 23 + .../input/storedProcedures/Test-xp_qv.sql | 31 + .../storedProcedures/TestPrepareExec.txt | 15 + .../storedProcedures/TestSPExecutesql.sql | 194 + .../input/storedProcedures/TestSPPrepare.sql | 378 + .../storedProcedures/TestStoredProcedures.txt | 140 + .../test/JDBC/input/tds_faultinjection.txt | 33 + .../transactions/TestInsteadofTriggers.sql | 558 + .../transactions/TestIsolationLevels.sql | 36 + .../transactions/TestTransactionName.sql | 70 + .../TestTransactionSupportForProcedure.txt | 260 + .../transactions/TestTransactionsSQLBatch.txt | 189 + .../JDBC/input/transactions/TestTriggers.sql | 558 + .../test/JDBC/input/views/sys-all_columns.sql | 21 + .../input/views/sys-check_constraints.sql | 13 + .../JDBC/input/views/sys-computed_columns.sql | 18 + .../JDBC/input/views/sys-configurations.sql | 26 + .../test/JDBC/input/views/sys-databases.sql | 14 + .../input/views/sys-default_constraints.sql | 17 + .../test/JDBC/input/views/sys-endpoints.sql | 2 + .../input/views/sys-extended_properties.sql | 2 + .../input/views/sys-foreign_key_columns.sql | 65 + .../JDBC/input/views/sys-foreign_keys.sql | 89 + .../JDBC/input/views/sys-identity_columns.sql | 30 + .../JDBC/input/views/sys-index_columns.sql | 66 + contrib/test/JDBC/input/views/sys-indexes.sql | 60 + .../JDBC/input/views/sys-key_constraints.sql | 41 + .../test/JDBC/input/views/sys-procedures.sql | 101 + contrib/test/JDBC/input/views/sys-schemas.sql | 22 + .../input/views/sys-server_principals.sql | 23 + .../JDBC/input/views/sys-sp_tables_view.sql | 53 + .../test/JDBC/input/views/sys-syscolumns.sql | 267 + .../JDBC/input/views/sys-sysforeignkeys.sql | 65 + .../test/JDBC/input/views/sys-table_types.mix | 68 + contrib/test/JDBC/input/views/sys-tables.sql | 131 + contrib/test/JDBC/input/views/sys-types.sql | 88 + contrib/test/JDBC/input/views/sys-views.sql | 67 + contrib/test/JDBC/jdbc_schedule | 24 + contrib/test/JDBC/jdbc_upgrade_schedule | 24 + contrib/test/JDBC/pom.xml | 87 + contrib/test/JDBC/sql_expected/1034_1.out | 71 + contrib/test/JDBC/sql_expected/1049_1.out | 406 + contrib/test/JDBC/sql_expected/1051_1.out | 75 + contrib/test/JDBC/sql_expected/10610_1.out | 311 + contrib/test/JDBC/sql_expected/11700_1.out | 232 + contrib/test/JDBC/sql_expected/11701_1.out | 245 + contrib/test/JDBC/sql_expected/11702_1.out | 331 + contrib/test/JDBC/sql_expected/11703_1.out | 371 + contrib/test/JDBC/sql_expected/11703_2.out | 371 + contrib/test/JDBC/sql_expected/11705_1.out | 239 + contrib/test/JDBC/sql_expected/11706_1.out | 257 + contrib/test/JDBC/sql_expected/11708_1.out | 245 + contrib/test/JDBC/sql_expected/132_1.out | 455 + contrib/test/JDBC/sql_expected/133_1.out | 457 + contrib/test/JDBC/sql_expected/134_1.out | 444 + contrib/test/JDBC/sql_expected/135_1.out | 415 + contrib/test/JDBC/sql_expected/136_1.out | 415 + contrib/test/JDBC/sql_expected/141_1.out | 428 + contrib/test/JDBC/sql_expected/142_1.out | 367 + contrib/test/JDBC/sql_expected/1505_1.out | 267 + contrib/test/JDBC/sql_expected/16948_1.out | 603 + contrib/test/JDBC/sql_expected/16950_1.out | 398 + .../test/JDBC/sql_expected/1752_1750_2.out | 236 + .../test/JDBC/sql_expected/1765_1750_1.out | 371 + .../test/JDBC/sql_expected/1768_1750_1.out | 335 + contrib/test/JDBC/sql_expected/180_1.out | 4259 ++++ contrib/test/JDBC/sql_expected/180_2.out | 4255 ++++ contrib/test/JDBC/sql_expected/1946_1.out | 598 + contrib/test/JDBC/sql_expected/201_1.out | 503 + contrib/test/JDBC/sql_expected/206_1.out | 414 + contrib/test/JDBC/sql_expected/217_1.out | 329 + contrib/test/JDBC/sql_expected/219_1.out | 227 + contrib/test/JDBC/sql_expected/220_1.out | 268 + contrib/test/JDBC/sql_expected/220_2.out | 262 + contrib/test/JDBC/sql_expected/220_3.out | 262 + contrib/test/JDBC/sql_expected/220_4.out | 262 + contrib/test/JDBC/sql_expected/2732_1.out | 358 + contrib/test/JDBC/sql_expected/2733_1.out | 93 + contrib/test/JDBC/sql_expected/2747_1.out | 379 + contrib/test/JDBC/sql_expected/2787_1.out | 363 + contrib/test/JDBC/sql_expected/293_1.out | 491 + contrib/test/JDBC/sql_expected/3609_1.out | 474 + contrib/test/JDBC/sql_expected/3701_3.out | 243 + contrib/test/JDBC/sql_expected/3701_4.out | 243 + contrib/test/JDBC/sql_expected/3701_5.out | 243 + .../test/JDBC/sql_expected/3728_3727_1.out | 305 + contrib/test/JDBC/sql_expected/3729_1.out | 326 + contrib/test/JDBC/sql_expected/3902_1.out | 261 + contrib/test/JDBC/sql_expected/3903_1.out | 267 + contrib/test/JDBC/sql_expected/3930_1.out | 330 + contrib/test/JDBC/sql_expected/4708_1.out | 293 + contrib/test/JDBC/sql_expected/4712_1.out | 311 + contrib/test/JDBC/sql_expected/4901_1.out | 423 + contrib/test/JDBC/sql_expected/4920_1.out | 287 + contrib/test/JDBC/sql_expected/512_1.out | 276 + contrib/test/JDBC/sql_expected/515_1.out | 250 + contrib/test/JDBC/sql_expected/517_1.out | 374 + contrib/test/JDBC/sql_expected/545_1.out | 310 + contrib/test/JDBC/sql_expected/547_1.out | 217 + contrib/test/JDBC/sql_expected/547_2.out | 225 + contrib/test/JDBC/sql_expected/550_1.out | 328 + contrib/test/JDBC/sql_expected/556_1.out | 591 + contrib/test/JDBC/sql_expected/6401_1.out | 275 + contrib/test/JDBC/sql_expected/8106_1.out | 389 + contrib/test/JDBC/sql_expected/8107_1.out | 347 + contrib/test/JDBC/sql_expected/8143_1.out | 250 + contrib/test/JDBC/sql_expected/8143_2.out | 250 + contrib/test/JDBC/sql_expected/8144_1.out | 366 + contrib/test/JDBC/sql_expected/8145_1.out | 289 + contrib/test/JDBC/sql_expected/8146_1.out | 416 + contrib/test/JDBC/sql_expected/8152_1.out | 256 + contrib/test/JDBC/sql_expected/8152_2.out | 244 + contrib/test/JDBC/sql_expected/8159_1.out | 61 + contrib/test/JDBC/sql_expected/8179_1.out | 378 + contrib/test/JDBC/sql_expected/9809_1.out | 384 + contrib/test/JDBC/sql_expected/BABEL-1243.out | 33 + contrib/test/JDBC/sql_expected/BABEL-1654.out | 135 + contrib/test/JDBC/sql_expected/BABEL-1944.out | 312 + contrib/test/JDBC/sql_expected/BABEL-2079.out | 50 + contrib/test/JDBC/sql_expected/BABEL-2354.out | 15 + contrib/test/JDBC/sql_expected/BABEL-2419.out | 37 + contrib/test/JDBC/sql_expected/BABEL-2432.out | 51 + contrib/test/JDBC/sql_expected/BABEL-2437.out | 16 + contrib/test/JDBC/sql_expected/BABEL-2681.out | 29 + .../test/JDBC/sql_expected/BABEL-2787-2.out | 103 + contrib/test/JDBC/sql_expected/BABEL-2787.out | 136 + contrib/test/JDBC/sql_expected/BABEL-2944.out | 59 + contrib/test/JDBC/sql_expected/BABEL-2993.out | 28 + contrib/test/JDBC/sql_expected/BABEL-3119.out | 39 + contrib/test/JDBC/sql_expected/BABEL-383.out | 42 + .../sql_expected/BABEL-EXTENDEDPROPERTY.out | 29 + .../test/JDBC/sql_expected/BABEL-IDENTITY.out | 458 + .../BABEL-IMPLICIT_TRAN-PREPEXEC.out | 128 + .../JDBC/sql_expected/BABEL-IMPLICIT_TRAN.out | 564 + contrib/test/JDBC/sql_expected/TestAuth.out | 49 + .../sql_expected/TestErrorsWithTryCatch.out | 1682 ++ .../JDBC/sql_expected/TestSimpleErrors.out | 4844 +++++ .../TestSimpleErrorsWithImplicitTran.out | 4918 +++++ .../TestSimpleErrorsWithXactAbort.out | 4022 ++++ .../test/JDBC/sql_expected/TestTriggers.out | 1000 + .../JDBC/sql_expected/sys-all_columns.out | 33 + .../sql_expected/sys-check_constraints.out | 18 + .../sql_expected/sys-computed_columns.out | 28 + .../test/JDBC/sql_expected/sys-databases.out | 24 + .../sql_expected/sys-default_constraints.out | 27 + .../sql_expected/sys-extended_properties.out | 7 + .../sql_expected/sys-identity_columns.out | 45 + .../JDBC/sql_expected/sys-index_columns.out | 91 + .../test/JDBC/sql_expected/sys-indexes.out | 98 + .../test/JDBC/sql_expected/sys-schemas.out | 44 + .../sql_expected/sys-server_principals.out | 44 + .../test/JDBC/sql_expected/temp-tables.out | 433 + .../java/com/sqlsamples/CompareResults.java | 296 + .../src/main/java/com/sqlsamples/Config.java | 145 + .../java/com/sqlsamples/ExportResults.java | 33 + .../java/com/sqlsamples/HandleException.java | 55 + .../com/sqlsamples/JDBCAuthentication.java | 88 + .../java/com/sqlsamples/JDBCBulkCopy.java | 47 + .../com/sqlsamples/JDBCCallableStatement.java | 196 + .../java/com/sqlsamples/JDBCCrossDialect.java | 170 + .../main/java/com/sqlsamples/JDBCCursor.java | 192 + .../com/sqlsamples/JDBCPreparedStatement.java | 191 + .../java/com/sqlsamples/JDBCStatement.java | 53 + .../java/com/sqlsamples/JDBCTransaction.java | 121 + .../main/java/com/sqlsamples/Statistics.java | 34 + .../main/java/com/sqlsamples/batch_run.java | 294 + .../test/JDBC/src/main/resources/config.txt | 40 + .../main/resources/junit-platform.properties | 2 + .../java/com/sqlsamples/TestQueryFile.java | 356 + contrib/test/JDBC/utils/Blank.jpeg | Bin 0 -> 631 bytes .../test/JDBC/utils/TxnErrorHandingCases.py | 597 + contrib/test/JDBC/utils/blank.txt | 0 contrib/test/JDBC/utils/devanagari.txt | 1 + contrib/test/JDBC/utils/emojisText.txt | 1 + contrib/test/JDBC/utils/flower.jpg | Bin 0 -> 3572 bytes contrib/test/JDBC/utils/sample.txt | 6 + contrib/test/JDBC/utils/utf16.txt | 1 + .../ScalarFunctionsSQLBatch.out | 79 + .../ExpectedOutput/TestAuthentication.out | 46 + .../test/dotnet/ExpectedOutput/TestBIT.out | 21 + .../test/dotnet/ExpectedOutput/TestBigInt.out | 56 + .../test/dotnet/ExpectedOutput/TestBinary.out | 9 + .../test/dotnet/ExpectedOutput/TestChar.out | 38 + .../test/dotnet/ExpectedOutput/TestDate.out | 31 + .../dotnet/ExpectedOutput/TestDatetime.out | 86 + .../dotnet/ExpectedOutput/TestDatetime2.out | 22 + .../dotnet/ExpectedOutput/TestDecimal.out | 208 + .../test/dotnet/ExpectedOutput/TestFloat.out | 56 + .../test/dotnet/ExpectedOutput/TestInt.out | 59 + .../test/dotnet/ExpectedOutput/TestMoney.out | 55 + .../dotnet/ExpectedOutput/TestNumeric.out | 196 + .../dotnet/ExpectedOutput/TestPrepareExec.out | 32 + .../test/dotnet/ExpectedOutput/TestReal.out | 56 + .../dotnet/ExpectedOutput/TestSQLQueries.out | 257 + .../ExpectedOutput/TestSmallDatetime.out | 84 + .../dotnet/ExpectedOutput/TestSmallInt.out | 56 + .../dotnet/ExpectedOutput/TestSqlVariant.out | 77 + .../ExpectedOutput/TestStoredProcedure.out | 387 + .../test/dotnet/ExpectedOutput/TestText.out | 25 + .../test/dotnet/ExpectedOutput/TestTime.out | 140 + .../dotnet/ExpectedOutput/TestTinyInt.out | 56 + .../ExpectedOutput/TestTransactions.out | 263 + .../TestTransactionsSQLBatch.out | 322 + .../test/dotnet/ExpectedOutput/TestTvp.out | 6 + .../test/dotnet/ExpectedOutput/TestUDD.out | 51 + .../test/dotnet/ExpectedOutput/TestUID.out | 89 + .../dotnet/ExpectedOutput/TestVarChar.out | 10 + .../test/dotnet/ExpectedOutput/TestXML.out | 17 + .../test/dotnet/ExpectedOutput/insertBulk.out | 367 + contrib/test/dotnet/Info/.gitignore | 4 + contrib/test/dotnet/Output/.gitignore | 4 + contrib/test/dotnet/ReadMe.txt | 78 + contrib/test/dotnet/config.txt | 25 + contrib/test/dotnet/dotnet.csproj | 26 + .../Authentication/TestAuthentication.txt | 60 + .../test/dotnet/input/Datatypes/TestBIT.txt | 16 + .../dotnet/input/Datatypes/TestBigInt.txt | 24 + .../dotnet/input/Datatypes/TestBinary.txt | 28 + .../test/dotnet/input/Datatypes/TestChar.txt | 17 + .../test/dotnet/input/Datatypes/TestDate.txt | 18 + .../dotnet/input/Datatypes/TestDatetime.txt | 36 + .../dotnet/input/Datatypes/TestDatetime2.txt | 15 + .../dotnet/input/Datatypes/TestDecimal.txt | 158 + .../test/dotnet/input/Datatypes/TestFloat.txt | 24 + .../test/dotnet/input/Datatypes/TestInt.txt | 29 + .../test/dotnet/input/Datatypes/TestMoney.txt | 30 + .../dotnet/input/Datatypes/TestNumeric.txt | 178 + .../test/dotnet/input/Datatypes/TestReal.txt | 24 + .../input/Datatypes/TestSmallDatetime.txt | 35 + .../dotnet/input/Datatypes/TestSmallInt.txt | 24 + .../dotnet/input/Datatypes/TestSqlVariant.txt | 41 + .../test/dotnet/input/Datatypes/TestText.txt | 12 + .../test/dotnet/input/Datatypes/TestTime.txt | 104 + .../dotnet/input/Datatypes/TestTinyInt.txt | 24 + .../test/dotnet/input/Datatypes/TestTvp.txt | 5 + .../test/dotnet/input/Datatypes/TestUDD.txt | 78 + .../test/dotnet/input/Datatypes/TestUID.txt | 36 + .../dotnet/input/Datatypes/TestVarChar.txt | 6 + .../test/dotnet/input/Datatypes/TestXML.txt | 8 + .../dotnet/input/InsertBulk/insertBulk.txt | 286 + .../input/PrepareExec/TestPrepareExec.txt | 61 + .../dotnet/input/SQLBatch/TestSQLQueries.txt | 222 + .../ScalarFunctionsSQLBatch.txt | 59 + .../input/Storedproc/TestStoredProcedure.txt | 213 + .../input/Transaction/TestTransactions.txt | 189 + .../Transaction/TestTransactionsSQLBatch.txt | 189 + contrib/test/dotnet/src/BatchRun.cs | 383 + contrib/test/dotnet/src/ExecuteTests.cs | 130 + contrib/test/dotnet/src/PrepareExecBinding.cs | 453 + contrib/test/dotnet/utils/ConfigSetup.cs | 68 + contrib/test/dotnet/utils/TestUtils.cs | 563 + contrib/test/dotnet/utils/devanagari.txt | 1 + contrib/test/dotnet/utils/emojisText.txt | 1 + contrib/test/dotnet/utils/sample.txt | 6 + contrib/test/dotnet/utils/utf16.txt | 1 + contrib/test/odbc/CMakeLists.txt | 48 + contrib/test/odbc/README.md | 54 + contrib/test/odbc/config.txt | 6 + contrib/test/odbc/constants.cpp | 19 + contrib/test/odbc/constants.h | 34 + contrib/test/odbc/database_objects.cpp | 82 + contrib/test/odbc/database_objects.h | 54 + contrib/test/odbc/main.cpp | 8 + contrib/test/odbc/odbc_handler.cpp | 311 + contrib/test/odbc/odbc_handler.h | 138 + contrib/test/odbc/query_generator.cpp | 124 + contrib/test/odbc/query_generator.h | 67 + contrib/test/odbc/test_connection.cpp | 29 + contrib/test/odbc/test_data_types.cpp | 457 + contrib/test/odbc/test_diagnostics.cpp | 140 + .../odbc/test_direct_executed_statements.cpp | 268 + contrib/test/odbc/test_metadata.cpp | 949 + .../test/odbc/test_prepared_statements.cpp | 367 + contrib/test/odbc/test_resultset.cpp | 760 + contrib/test/odbc/test_sqlgetinfo.cpp | 146 + contrib/test/odbc/test_transactions.cpp | 129 + contrib/test/odbc/utils/blank.txt | 0 contrib/test/odbc/utils/devanagari.txt | 1 + contrib/test/odbc/utils/sample.txt | 6 + contrib/test/python/.gitignore | 5 + contrib/test/python/batch_run.py | 100 + contrib/test/python/compare_results.py | 141 + contrib/test/python/config.txt | 54 + contrib/test/python/execute_query.py | 376 + .../python/expected/pymssql/BABEL-1056.out | 23 + .../python/expected/pymssql/BABEL-1270.out | 14 + .../python/expected/pymssql/BABEL-1365.out | 52 + .../python/expected/pymssql/BABEL-1390.out | 25 + .../python/expected/pymssql/BABEL-1643.out | 18 + .../test/python/expected/pymssql/TestAuth.out | 4 + .../test/python/expected/pymssql/TestBIT.out | 49 + .../python/expected/pymssql/TestBigInt.out | 106 + .../python/expected/pymssql/TestBinary.out | 49 + .../test/python/expected/pymssql/TestChar.out | 64 + .../expected/pymssql/TestCursorFetchNext.out | 83 + .../pymssql/TestCursorPrepExecFetchNext.out | 83 + .../test/python/expected/pymssql/TestDate.out | 61 + .../python/expected/pymssql/TestDatetime.out | 160 + .../python/expected/pymssql/TestDatetime2.out | 45 + .../python/expected/pymssql/TestDecimal.out | 564 + .../python/expected/pymssql/TestFloat.out | 106 + .../test/python/expected/pymssql/TestInt.out | 115 + .../python/expected/pymssql/TestMoney.out | 115 + .../python/expected/pymssql/TestNumeric.out | 606 + .../test/python/expected/pymssql/TestReal.out | 106 + .../expected/pymssql/TestSPExecutesql.out | 253 + .../python/expected/pymssql/TestSPPrepare.out | 601 + .../expected/pymssql/TestSQLQueries.out | 897 + .../TestSimpleErrorsWithImplicitTran.out | 20 + .../expected/pymssql/TestSmallDatetime.out | 152 + .../python/expected/pymssql/TestSmallInt.out | 106 + .../expected/pymssql/TestStoredProcedures.out | 185 + .../test/python/expected/pymssql/TestText.out | 34 + .../test/python/expected/pymssql/TestTime.out | 265 + .../python/expected/pymssql/TestTinyInt.out | 105 + .../TestTransactionSupportForProcedure.out | 1096 + .../pymssql/TestTransactionsSQLBatch.out | 564 + .../test/python/expected/pymssql/TestUDD.out | 132 + .../expected/pymssql/TestUniqueIdentifier.out | 157 + .../python/expected/pymssql/TestVarChar.out | 168 + .../test/python/expected/pymssql/TestXML.out | 30 + .../expected/pymssql/pg_stat_activity.out | 79 + .../python/expected/pyodbc/BABEL-1056.out | 27 + .../python/expected/pyodbc/BABEL-1270.out | 14 + .../python/expected/pyodbc/BABEL-1365.out | 46 + .../python/expected/pyodbc/BABEL-1390.out | 25 + .../python/expected/pyodbc/BABEL-1643.out | 14 + .../test/python/expected/pyodbc/TestAuth.out | 34 + .../test/python/expected/pyodbc/TestBIT.out | 45 + .../python/expected/pyodbc/TestBigInt.out | 102 + .../python/expected/pyodbc/TestBinary.out | 43 + .../test/python/expected/pyodbc/TestChar.out | 60 + .../expected/pyodbc/TestCursorFetchNext.out | 83 + .../pyodbc/TestCursorPrepExecFetchNext.out | 83 + .../test/python/expected/pyodbc/TestDate.out | 57 + .../python/expected/pyodbc/TestDatetime.out | 156 + .../python/expected/pyodbc/TestDatetime2.out | 43 + .../python/expected/pyodbc/TestDecimal.out | 546 + .../test/python/expected/pyodbc/TestFloat.out | 102 + .../test/python/expected/pyodbc/TestInt.out | 111 + .../test/python/expected/pyodbc/TestMoney.out | 111 + .../python/expected/pyodbc/TestNumeric.out | 578 + .../test/python/expected/pyodbc/TestReal.out | 102 + .../expected/pyodbc/TestSPExecutesql.out | 243 + .../python/expected/pyodbc/TestSPPrepare.out | 567 + .../python/expected/pyodbc/TestSQLQueries.out | 839 + .../TestSimpleErrorsWithImplicitTran.out | 20 + .../expected/pyodbc/TestSmallDatetime.out | 148 + .../python/expected/pyodbc/TestSmallInt.out | 102 + .../expected/pyodbc/TestStoredProcedures.out | 181 + .../test/python/expected/pyodbc/TestText.out | 32 + .../test/python/expected/pyodbc/TestTime.out | 300 + .../python/expected/pyodbc/TestTinyInt.out | 101 + .../TestTransactionSupportForProcedure.out | 928 + .../pyodbc/TestTransactionsSQLBatch.out | 492 + .../test/python/expected/pyodbc/TestUDD.out | 128 + .../expected/pyodbc/TestUniqueIdentifier.out | 151 + .../python/expected/pyodbc/TestVarChar.out | 158 + .../test/python/expected/pyodbc/TestXML.out | 28 + .../python/expected/pyodbc/fk-contention.out | 27 + .../python/expected/pyodbc/fk-deadlock.out | 230 + .../expected/pyodbc/pg_stat_activity.out | 65 + contrib/test/python/file_handler.py | 146 + contrib/test/python/input/BABEL-1056.txt | 14 + contrib/test/python/input/BABEL-1270.txt | 12 + .../TestSimpleErrorsWithImplicitTran.txt | 11 + .../python/input/authentication/TestAuth.txt | 13 + .../test/python/input/cursors/BABEL-1390.txt | 15 + .../input/cursors/TestCursorFetchNext.txt | 86 + .../cursors/TestCursorPrepExecFetchNext.txt | 94 + .../python/input/datatypes/BABEL-1643.txt | 3 + .../test/python/input/datatypes/TestBIT.txt | 16 + .../python/input/datatypes/TestBigInt.txt | 24 + .../python/input/datatypes/TestBinary.txt | 19 + .../test/python/input/datatypes/TestChar.txt | 16 + .../test/python/input/datatypes/TestDate.txt | 14 + .../python/input/datatypes/TestDatetime.txt | 36 + .../python/input/datatypes/TestDatetime2.txt | 15 + .../python/input/datatypes/TestDecimal.txt | 180 + .../test/python/input/datatypes/TestFloat.txt | 24 + .../test/python/input/datatypes/TestInt.txt | 29 + .../test/python/input/datatypes/TestMoney.txt | 30 + .../python/input/datatypes/TestNumeric.txt | 187 + .../test/python/input/datatypes/TestReal.txt | 24 + .../input/datatypes/TestSmallDatetime.txt | 35 + .../python/input/datatypes/TestSmallInt.txt | 24 + .../test/python/input/datatypes/TestText.txt | 10 + .../test/python/input/datatypes/TestTime.txt | 104 + .../python/input/datatypes/TestTinyInt.txt | 24 + .../test/python/input/datatypes/TestUDD.txt | 78 + .../input/datatypes/TestUniqueIdentifier.txt | 34 + .../python/input/datatypes/TestVarChar.txt | 48 + .../test/python/input/datatypes/TestXML.txt | 11 + .../python/input/isolation/fk-contention.spec | 19 + .../python/input/isolation/fk-deadlock.spec | 46 + .../test/python/input/pg_stat_activity.txt | 20 + .../python/input/sqlBatch/TestSQLQueries.txt | 232 + .../input/storedProcedures/BABEL-1365.sql | 23 + .../storedProcedures/TestSPExecutesql.sql | 194 + .../input/storedProcedures/TestSPPrepare.sql | 357 + .../storedProcedures/TestStoredProcedures.txt | 140 + .../TestTransactionSupportForProcedure.txt | 260 + .../transactions/TestTransactionsSQLBatch.txt | 189 + contrib/test/python/isolationtest/README.md | 23 + contrib/test/python/isolationtest/__init__.py | 0 .../isolationtest/isolationTestHandler.py | 46 + .../python/isolationtest/isolationTester.py | 385 + .../python/isolationtest/parser/__init__.py | 0 .../python/isolationtest/parser/specLexer.g4 | 17 + .../python/isolationtest/parser/specParser.g4 | 20 + .../isolationtest/specParserVisitorImpl.py | 122 + contrib/test/python/logs/.gitkeep | 0 contrib/test/python/output/pymssql/.gitkeep | 0 contrib/test/python/output/pyodbc/.gitkeep | 0 contrib/test/python/python_authentication.py | 47 + .../test/python/sql_expected/pymssql/.gitkeep | 0 .../test/python/sql_expected/pyodbc/.gitkeep | 0 contrib/test/python/start.py | 24 + contrib/test/python/test_main.py | 79 + contrib/test/python/utils/__init__.py | 0 contrib/test/python/utils/base.py | 70 + contrib/test/python/utils/blank.txt | 0 contrib/test/python/utils/config.py | 28 + contrib/test/python/utils/db_client.py | 106 + contrib/test/python/utils/devanagari.txt | 1 + contrib/test/python/utils/emojisText.txt | 1 + contrib/test/python/utils/sample.txt | 6 + contrib/test/python/utils/utf16.txt | 1 + 1784 files changed, 471529 insertions(+), 20 deletions(-) create mode 100644 contrib/babelfishpg_common/Makefile create mode 100644 contrib/babelfishpg_common/Version.config create mode 100644 contrib/babelfishpg_common/babelfishpg_common.control.in create mode 100644 contrib/babelfishpg_common/sql/babelfishpg_common--1.0.0.sql create mode 100644 contrib/babelfishpg_common/sql/babelfishpg_common.in create mode 100644 contrib/babelfishpg_common/sql/binary.sql create mode 100644 contrib/babelfishpg_common/sql/bit.sql create mode 100644 contrib/babelfishpg_common/sql/bpchar.sql create mode 100644 contrib/babelfishpg_common/sql/coerce.sql create mode 100644 contrib/babelfishpg_common/sql/datetime.sql create mode 100644 contrib/babelfishpg_common/sql/datetime2.sql create mode 100644 contrib/babelfishpg_common/sql/datetimeoffset.sql create mode 100755 contrib/babelfishpg_common/sql/money/fixeddecimal--1.1.0_base_parallel.sql create mode 100644 contrib/babelfishpg_common/sql/money/fixeddecimal--brin.sql create mode 100644 contrib/babelfishpg_common/sql/money/fixeddecimal--parallelaggs.sql create mode 100644 contrib/babelfishpg_common/sql/numerics.sql create mode 100644 contrib/babelfishpg_common/sql/rowversion.sql create mode 100644 contrib/babelfishpg_common/sql/smalldatetime.sql create mode 100644 contrib/babelfishpg_common/sql/sqlvariant.sql create mode 100644 contrib/babelfishpg_common/sql/string_operators.sql create mode 100644 contrib/babelfishpg_common/sql/strings.sql create mode 100644 contrib/babelfishpg_common/sql/uniqueidentifier.sql create mode 100644 contrib/babelfishpg_common/sql/upgrades/Release.md create mode 100644 contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.0.0--1.1.0.sql create mode 100644 contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.1.0--1.2.0.sql create mode 100644 contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.2.0--1.2.1.sql create mode 100644 contrib/babelfishpg_common/sql/utils.sql create mode 100644 contrib/babelfishpg_common/sql/varbinary.sql create mode 100644 contrib/babelfishpg_common/sql/varchar.sql create mode 100644 contrib/babelfishpg_common/src/babelfishpg_common.c create mode 100644 contrib/babelfishpg_common/src/bit.c create mode 100644 contrib/babelfishpg_common/src/coerce.c create mode 100644 contrib/babelfishpg_common/src/datetime.c create mode 100644 contrib/babelfishpg_common/src/datetime.h create mode 100644 contrib/babelfishpg_common/src/datetime2.c create mode 100644 contrib/babelfishpg_common/src/datetime2.h create mode 100644 contrib/babelfishpg_common/src/datetimeoffset.c create mode 100644 contrib/babelfishpg_common/src/datetimeoffset.h create mode 100644 contrib/babelfishpg_common/src/instr.c create mode 100644 contrib/babelfishpg_common/src/instr.h create mode 100644 contrib/babelfishpg_common/src/numeric.c create mode 100644 contrib/babelfishpg_common/src/numeric.h create mode 100644 contrib/babelfishpg_common/src/smalldatetime.c create mode 100644 contrib/babelfishpg_common/src/sqlvariant.c create mode 100644 contrib/babelfishpg_common/src/typecode.c create mode 100644 contrib/babelfishpg_common/src/typecode.h create mode 100644 contrib/babelfishpg_common/src/uniqueidentifier.c create mode 100644 contrib/babelfishpg_common/src/varbinary.c create mode 100644 contrib/babelfishpg_common/src/varchar.c create mode 100755 contrib/babelfishpg_money/Makefile create mode 100755 contrib/babelfishpg_money/README.md create mode 100755 contrib/babelfishpg_money/babelfishpg_money.control create mode 100644 contrib/babelfishpg_money/fixeddecimal--1.0.0--1.1.0.sql create mode 100755 contrib/babelfishpg_money/fixeddecimal--1.0.0_base.sql create mode 100755 contrib/babelfishpg_money/fixeddecimal--1.1.0_base.sql create mode 100755 contrib/babelfishpg_money/fixeddecimal--1.1.0_base_parallel.sql create mode 100644 contrib/babelfishpg_money/fixeddecimal--aggs.sql create mode 100644 contrib/babelfishpg_money/fixeddecimal--brin.sql create mode 100644 contrib/babelfishpg_money/fixeddecimal--parallelaggs.sql create mode 100644 contrib/babelfishpg_money/fixeddecimal--xlaggs.sql create mode 100755 contrib/babelfishpg_money/fixeddecimal.c create mode 100644 contrib/babelfishpg_money/fixeddecimalaggstate.sql create mode 100755 contrib/babelfishpg_money/test/expected/aggregate.out create mode 100644 contrib/babelfishpg_money/test/expected/brin-xl.out create mode 100644 contrib/babelfishpg_money/test/expected/brin.out create mode 100755 contrib/babelfishpg_money/test/expected/cast.out create mode 100755 contrib/babelfishpg_money/test/expected/comparison.out create mode 100644 contrib/babelfishpg_money/test/expected/index-xl.out create mode 100644 contrib/babelfishpg_money/test/expected/index.out create mode 100755 contrib/babelfishpg_money/test/expected/overflow.out create mode 100755 contrib/babelfishpg_money/test/sql/aggregate.sql create mode 100644 contrib/babelfishpg_money/test/sql/brin-xl.sql create mode 100644 contrib/babelfishpg_money/test/sql/brin.sql create mode 100755 contrib/babelfishpg_money/test/sql/cast.sql create mode 100755 contrib/babelfishpg_money/test/sql/comparison.sql create mode 100644 contrib/babelfishpg_money/test/sql/index-xl.sql create mode 100644 contrib/babelfishpg_money/test/sql/index.sql create mode 100755 contrib/babelfishpg_money/test/sql/overflow.sql create mode 100644 contrib/babelfishpg_tds/Makefile create mode 100644 contrib/babelfishpg_tds/README create mode 100644 contrib/babelfishpg_tds/README.err create mode 100644 contrib/babelfishpg_tds/babelfishpg_tds--1.0.0.sql create mode 100644 contrib/babelfishpg_tds/babelfishpg_tds.control create mode 100644 contrib/babelfishpg_tds/error_mapping.txt create mode 100644 contrib/babelfishpg_tds/generate_error_mapping.pl create mode 100644 contrib/babelfishpg_tds/src/backend/encoding/encoding_utils.c create mode 100644 contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection.c create mode 100644 contrib/babelfishpg_tds/src/backend/fault_injection/fault_injection_tests.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/err_handler.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/guc.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/support_funcs.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tds-secure-openssl.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tds.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tds_data_map.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tds_srv.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdsbulkload.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdscomm.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdslogin.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdsprinttup.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdsprotocol.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdsrpc.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdssecure.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdssqlbatch.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdstimestamp.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdsutils.c create mode 100644 contrib/babelfishpg_tds/src/backend/tds/tdsxact.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/adt/README create mode 100644 contrib/babelfishpg_tds/src/backend/utils/adt/numeric.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/adt/varchar.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/adt/xml.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/mb/README create mode 100644 contrib/babelfishpg_tds/src/backend/utils/mb/conv.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_big5/utf8_and_big5.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_gbk/utf8_and_gbk.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_sjis/utf8_and_sjis.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_uhc/utf8_and_uhc.c create mode 100644 contrib/babelfishpg_tds/src/backend/utils/mb/conversion_procs/utf8_and_win/utf8_and_win.c create mode 100644 contrib/babelfishpg_tds/src/include/.gitignore create mode 100644 contrib/babelfishpg_tds/src/include/err_handler.h create mode 100644 contrib/babelfishpg_tds/src/include/faultinjection.h create mode 100644 contrib/babelfishpg_tds/src/include/guc.h create mode 100644 contrib/babelfishpg_tds/src/include/tds.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_debug.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_instr.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_int.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_iofuncmap.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_protocol.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_request.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_response.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_secure.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_timestamp.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_typecode.h create mode 100644 contrib/babelfishpg_tds/src/include/tds_typeio.h create mode 100644 contrib/babelfishpg_tds/src/include/tdsprinttup.h create mode 100644 contrib/babelfishpg_tsql/Makefile create mode 100644 contrib/babelfishpg_tsql/Release.md create mode 100644 contrib/babelfishpg_tsql/Version.config create mode 100644 contrib/babelfishpg_tsql/antlr/CMakeLists.txt create mode 100644 contrib/babelfishpg_tsql/antlr/TSqlLexer.g4 create mode 100644 contrib/babelfishpg_tsql/antlr/TSqlParser.g4 create mode 100644 contrib/babelfishpg_tsql/antlr/cmake-dir/FindANTLR.cmake create mode 100644 contrib/babelfishpg_tsql/antlr/cmake-dir/README.md create mode 100644 contrib/babelfishpg_tsql/antlr/thirdparty/antlr/antlr-4.9.3-complete.jar create mode 100644 contrib/babelfishpg_tsql/babelfishpg_tsql.control.in create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_219.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_collation.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_datatype.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_ddl.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_delete.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_function.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_init.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_like.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_procedures.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_set_command.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_table_type.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_transaction.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_typecode.out create mode 100644 contrib/babelfishpg_tsql/expected/test/babel_uniqueidentifier.out create mode 100644 contrib/babelfishpg_tsql/results/test/babel_219.out create mode 100644 contrib/babelfishpg_tsql/results/test/babel_init.out create mode 100644 contrib/babelfishpg_tsql/results/test/babel_like.out create mode 100644 contrib/babelfishpg_tsql/runtime/basic.sql create mode 100644 contrib/babelfishpg_tsql/runtime/functions.c create mode 100644 contrib/babelfishpg_tsql/sql/babelfishpg_tsql--1.0.0.sql create mode 100644 contrib/babelfishpg_tsql/sql/babelfishpg_tsql.in create mode 100644 contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql create mode 100644 contrib/babelfishpg_tsql/sql/coerce.sql create mode 100644 contrib/babelfishpg_tsql/sql/collation.sql create mode 100644 contrib/babelfishpg_tsql/sql/datatype.sql create mode 100644 contrib/babelfishpg_tsql/sql/datatype_string_operators.sql create mode 100644 contrib/babelfishpg_tsql/sql/import_export_compatibility.sql create mode 100644 contrib/babelfishpg_tsql/sql/information_schema_tsql.sql create mode 100644 contrib/babelfishpg_tsql/sql/ownership.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys_babelfish_configurations.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys_cast.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys_function_helpers.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys_functions.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys_languages.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys_procedures.sql create mode 100644 contrib/babelfishpg_tsql/sql/sys_views.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_219.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_collation.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_datatype.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_ddl.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_delete.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_function.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_init.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_like.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_procedures.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_set_command.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_table_type.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_transaction.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_typecode.sql create mode 100644 contrib/babelfishpg_tsql/sql/test/babel_uniqueidentifier.sql create mode 100644 contrib/babelfishpg_tsql/sql/upgrades/.gitignore create mode 100644 contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.0.0--1.1.0.sql create mode 100644 contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.1.0--1.2.0.sql create mode 100644 contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.2.0--1.2.1.sql create mode 100644 contrib/babelfishpg_tsql/src/analyzer.c create mode 100644 contrib/babelfishpg_tsql/src/analyzer.h create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/decl.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/ifelse.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/inval.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/nestedBlocks.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/oneBlock.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/print.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/print2.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/proc_simple.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/returnsTable.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/series.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/simple.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/simple2.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/single.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/throw.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/tryCatch.sql create mode 100644 contrib/babelfishpg_tsql/src/antlrTests/while.sql create mode 100644 contrib/babelfishpg_tsql/src/applock.c create mode 100644 contrib/babelfishpg_tsql/src/babelfish_version.h create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-decl.y create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-epilogue.y.c create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-nonassoc-ident-tokens create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-prologue.y.h create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-rule.y create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/gram_hook.c create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/gramparse.h create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/include.pl create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/keywords.c create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/kwlist.h create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/parser.c create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/scan-tsql-decl.l create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/scan-tsql-epilogue.l.c create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/scan-tsql-prologue-top.l.h create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/scan-tsql-prologue.l.h create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/scan-tsql-rule.l create mode 100644 contrib/babelfishpg_tsql/src/backend_parser/scanner.h create mode 100644 contrib/babelfishpg_tsql/src/catalog.c create mode 100644 contrib/babelfishpg_tsql/src/catalog.h create mode 100644 contrib/babelfishpg_tsql/src/codegen.c create mode 100644 contrib/babelfishpg_tsql/src/codegen.h create mode 100644 contrib/babelfishpg_tsql/src/collation.c create mode 100644 contrib/babelfishpg_tsql/src/collation.h create mode 100644 contrib/babelfishpg_tsql/src/collationproperty.c create mode 100644 contrib/babelfishpg_tsql/src/compile_context.c create mode 100644 contrib/babelfishpg_tsql/src/compile_context.h create mode 100644 contrib/babelfishpg_tsql/src/cursor.c create mode 100644 contrib/babelfishpg_tsql/src/databasepropertyex.c create mode 100644 contrib/babelfishpg_tsql/src/datatype_info.h create mode 100644 contrib/babelfishpg_tsql/src/datatypes.h create mode 100644 contrib/babelfishpg_tsql/src/dbcmds.c create mode 100644 contrib/babelfishpg_tsql/src/dbcmds.h create mode 100644 contrib/babelfishpg_tsql/src/dynastack.c create mode 100644 contrib/babelfishpg_tsql/src/dynastack.h create mode 100644 contrib/babelfishpg_tsql/src/dynavec.c create mode 100644 contrib/babelfishpg_tsql/src/dynavec.h create mode 100644 contrib/babelfishpg_tsql/src/err_handler.c create mode 100644 contrib/babelfishpg_tsql/src/err_handler.h create mode 100644 contrib/babelfishpg_tsql/src/forxml.c create mode 100644 contrib/babelfishpg_tsql/src/generate-plerrcodes.pl create mode 100644 contrib/babelfishpg_tsql/src/guc.c create mode 100644 contrib/babelfishpg_tsql/src/guc.h create mode 100644 contrib/babelfishpg_tsql/src/hooks.c create mode 100644 contrib/babelfishpg_tsql/src/hooks.h create mode 100644 contrib/babelfishpg_tsql/src/iterative_exec.c create mode 100644 contrib/babelfishpg_tsql/src/iterative_exec.h create mode 100644 contrib/babelfishpg_tsql/src/json_funcs.c create mode 100644 contrib/babelfishpg_tsql/src/multidb.c create mode 100644 contrib/babelfishpg_tsql/src/multidb.h create mode 100644 contrib/babelfishpg_tsql/src/pl_comp-2.c create mode 100644 contrib/babelfishpg_tsql/src/pl_comp.c create mode 100644 contrib/babelfishpg_tsql/src/pl_exec-2.c create mode 100644 contrib/babelfishpg_tsql/src/pl_exec.c create mode 100644 contrib/babelfishpg_tsql/src/pl_funcs-2.c create mode 100644 contrib/babelfishpg_tsql/src/pl_funcs-2.h create mode 100644 contrib/babelfishpg_tsql/src/pl_funcs.c create mode 100644 contrib/babelfishpg_tsql/src/pl_gram.y create mode 100644 contrib/babelfishpg_tsql/src/pl_handler.c create mode 100644 contrib/babelfishpg_tsql/src/pl_reserved_kwlist.h create mode 100644 contrib/babelfishpg_tsql/src/pl_scanner.c create mode 100644 contrib/babelfishpg_tsql/src/pl_unreserved_kwlist.h create mode 100644 contrib/babelfishpg_tsql/src/plan_inval.c create mode 100644 contrib/babelfishpg_tsql/src/plerrcodes.h create mode 100644 contrib/babelfishpg_tsql/src/pltsql-2.h create mode 100644 contrib/babelfishpg_tsql/src/pltsql.h create mode 100644 contrib/babelfishpg_tsql/src/pltsql_coerce.c create mode 100644 contrib/babelfishpg_tsql/src/pltsql_function_probin_handler.c create mode 100644 contrib/babelfishpg_tsql/src/pltsql_identity.c create mode 100644 contrib/babelfishpg_tsql/src/pltsql_instr.h create mode 100644 contrib/babelfishpg_tsql/src/pltsql_utils.c create mode 100644 contrib/babelfishpg_tsql/src/prepare.c create mode 100644 contrib/babelfishpg_tsql/src/procedures.c create mode 100644 contrib/babelfishpg_tsql/src/properties.c create mode 100644 contrib/babelfishpg_tsql/src/rolecmds.c create mode 100644 contrib/babelfishpg_tsql/src/rolecmds.h create mode 100644 contrib/babelfishpg_tsql/src/schemacmds.c create mode 100644 contrib/babelfishpg_tsql/src/schemacmds.h create mode 100644 contrib/babelfishpg_tsql/src/session.c create mode 100644 contrib/babelfishpg_tsql/src/session.h create mode 100644 contrib/babelfishpg_tsql/src/special_keywords.c create mode 100644 contrib/babelfishpg_tsql/src/stmt_walker.c create mode 100644 contrib/babelfishpg_tsql/src/stmt_walker.h create mode 100644 contrib/babelfishpg_tsql/src/string.c create mode 100644 contrib/babelfishpg_tsql/src/tablecmds.c create mode 100644 contrib/babelfishpg_tsql/src/tsqlHandler.c create mode 100644 contrib/babelfishpg_tsql/src/tsqlIface.cpp create mode 100644 contrib/babelfishpg_tsql/src/tsqlIface.hpp create mode 100644 contrib/babelfishpg_tsql/src/tsqlNodes.c create mode 100644 contrib/babelfishpg_tsql/src/tsqlNodes.h create mode 100644 contrib/babelfishpg_tsql/src/tsqlUnsupportedFeatureHandler.cpp create mode 100644 contrib/babelfishpg_tsql/src/tsqlUnsupportedFeatureHandler.h create mode 100755 contrib/babelfishpg_tsql/src/utils/update_pg_files create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_at_prefixed_vars.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_case.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_if.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_localtempobjs.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_optsemicolterm.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_print.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_print_0.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/pltsql_sanitychecks.out create mode 100644 contrib/babelfishpg_tsql/test/old_expected/top_percent_keywords.out create mode 100644 contrib/babelfishpg_tsql/test/old_sql/pltsql_at_prefixed_vars.sql create mode 100644 contrib/babelfishpg_tsql/test/old_sql/pltsql_case.sql create mode 100644 contrib/babelfishpg_tsql/test/old_sql/pltsql_if.sql create mode 100644 contrib/babelfishpg_tsql/test/old_sql/pltsql_localtempobjs.sql create mode 100644 contrib/babelfishpg_tsql/test/old_sql/pltsql_optsemicolterm.sql create mode 100644 contrib/babelfishpg_tsql/test/old_sql/pltsql_print.sql create mode 100644 contrib/babelfishpg_tsql/test/old_sql/pltsql_sanitychecks.sql create mode 100644 contrib/babelfishpg_tsql/test/old_sql/top_percent_keywords.sql create mode 100644 contrib/test/JDBC/BABEL-2267.sql create mode 100644 contrib/test/JDBC/BABEL-2455.sql create mode 100644 contrib/test/JDBC/Info/.gitignore create mode 100644 contrib/test/JDBC/README.md create mode 100755 contrib/test/JDBC/cleanup.sh create mode 100644 contrib/test/JDBC/expected/102_1.out create mode 100644 contrib/test/JDBC/expected/102_2.out create mode 100644 contrib/test/JDBC/expected/102_3.out create mode 100644 contrib/test/JDBC/expected/102_6.out create mode 100644 contrib/test/JDBC/expected/113_1.out create mode 100644 contrib/test/JDBC/expected/11704_1.out create mode 100644 contrib/test/JDBC/expected/11704_2.out create mode 100644 contrib/test/JDBC/expected/129_1.out create mode 100644 contrib/test/JDBC/expected/1770_1750_1.out create mode 100644 contrib/test/JDBC/expected/1774_1750_1.out create mode 100644 contrib/test/JDBC/expected/208_1.out create mode 100644 contrib/test/JDBC/expected/218_1.out create mode 100644 contrib/test/JDBC/expected/222_1.out create mode 100644 contrib/test/JDBC/expected/257_2.out create mode 100644 contrib/test/JDBC/expected/2760_1.out create mode 100644 contrib/test/JDBC/expected/2812_1.out create mode 100644 contrib/test/JDBC/expected/2812_2.out create mode 100644 contrib/test/JDBC/expected/2812_3.out create mode 100644 contrib/test/JDBC/expected/4924_1.out create mode 100644 contrib/test/JDBC/expected/4924_2.out create mode 100644 contrib/test/JDBC/expected/4936_1.out create mode 100644 contrib/test/JDBC/expected/4936_2.out create mode 100644 contrib/test/JDBC/expected/BABEL-1000.out create mode 100644 contrib/test/JDBC/expected/BABEL-101.out create mode 100644 contrib/test/JDBC/expected/BABEL-1044.out create mode 100644 contrib/test/JDBC/expected/BABEL-1056.out create mode 100644 contrib/test/JDBC/expected/BABEL-1066.out create mode 100644 contrib/test/JDBC/expected/BABEL-1073.out create mode 100644 contrib/test/JDBC/expected/BABEL-1091.out create mode 100644 contrib/test/JDBC/expected/BABEL-1094.out create mode 100644 contrib/test/JDBC/expected/BABEL-1096.out create mode 100644 contrib/test/JDBC/expected/BABEL-1098.out create mode 100644 contrib/test/JDBC/expected/BABEL-1100.out create mode 100644 contrib/test/JDBC/expected/BABEL-1111.out create mode 100644 contrib/test/JDBC/expected/BABEL-1113.out create mode 100644 contrib/test/JDBC/expected/BABEL-1114.out create mode 100644 contrib/test/JDBC/expected/BABEL-1149.out create mode 100644 contrib/test/JDBC/expected/BABEL-1161.out create mode 100644 contrib/test/JDBC/expected/BABEL-1164.out create mode 100644 contrib/test/JDBC/expected/BABEL-1167.out create mode 100644 contrib/test/JDBC/expected/BABEL-1173.out create mode 100644 contrib/test/JDBC/expected/BABEL-1179.out create mode 100644 contrib/test/JDBC/expected/BABEL-1184.out create mode 100644 contrib/test/JDBC/expected/BABEL-1185.out create mode 100644 contrib/test/JDBC/expected/BABEL-1189.out create mode 100644 contrib/test/JDBC/expected/BABEL-1193.out create mode 100644 contrib/test/JDBC/expected/BABEL-1206.out create mode 100644 contrib/test/JDBC/expected/BABEL-1208.out create mode 100644 contrib/test/JDBC/expected/BABEL-1210.out create mode 100644 contrib/test/JDBC/expected/BABEL-1212.out create mode 100644 contrib/test/JDBC/expected/BABEL-1231.out create mode 100644 contrib/test/JDBC/expected/BABEL-1234.out create mode 100644 contrib/test/JDBC/expected/BABEL-1239.out create mode 100644 contrib/test/JDBC/expected/BABEL-1241.out create mode 100644 contrib/test/JDBC/expected/BABEL-1249.out create mode 100644 contrib/test/JDBC/expected/BABEL-1251.out create mode 100644 contrib/test/JDBC/expected/BABEL-1252.out create mode 100644 contrib/test/JDBC/expected/BABEL-1261.out create mode 100644 contrib/test/JDBC/expected/BABEL-1270.out create mode 100644 contrib/test/JDBC/expected/BABEL-1287.out create mode 100644 contrib/test/JDBC/expected/BABEL-1291.out create mode 100644 contrib/test/JDBC/expected/BABEL-1299.out create mode 100644 contrib/test/JDBC/expected/BABEL-1309.out create mode 100644 contrib/test/JDBC/expected/BABEL-1311.out create mode 100644 contrib/test/JDBC/expected/BABEL-1319.out create mode 100644 contrib/test/JDBC/expected/BABEL-1320.out create mode 100644 contrib/test/JDBC/expected/BABEL-1329.out create mode 100644 contrib/test/JDBC/expected/BABEL-1331.out create mode 100644 contrib/test/JDBC/expected/BABEL-1363.out create mode 100644 contrib/test/JDBC/expected/BABEL-1365.out create mode 100644 contrib/test/JDBC/expected/BABEL-1381.out create mode 100644 contrib/test/JDBC/expected/BABEL-1389.out create mode 100644 contrib/test/JDBC/expected/BABEL-1390.out create mode 100644 contrib/test/JDBC/expected/BABEL-1400.out create mode 100644 contrib/test/JDBC/expected/BABEL-1435.out create mode 100644 contrib/test/JDBC/expected/BABEL-1437.out create mode 100644 contrib/test/JDBC/expected/BABEL-1438.out create mode 100644 contrib/test/JDBC/expected/BABEL-1442.out create mode 100644 contrib/test/JDBC/expected/BABEL-1444.out create mode 100644 contrib/test/JDBC/expected/BABEL-1446.out create mode 100644 contrib/test/JDBC/expected/BABEL-1454.out create mode 100644 contrib/test/JDBC/expected/BABEL-1465.out create mode 100644 contrib/test/JDBC/expected/BABEL-1466.out create mode 100644 contrib/test/JDBC/expected/BABEL-1475.out create mode 100644 contrib/test/JDBC/expected/BABEL-1481.out create mode 100644 contrib/test/JDBC/expected/BABEL-1499.out create mode 100644 contrib/test/JDBC/expected/BABEL-1502.out create mode 100644 contrib/test/JDBC/expected/BABEL-1510.out create mode 100644 contrib/test/JDBC/expected/BABEL-1513.out create mode 100644 contrib/test/JDBC/expected/BABEL-1515.out create mode 100644 contrib/test/JDBC/expected/BABEL-1524.out create mode 100644 contrib/test/JDBC/expected/BABEL-1531.out create mode 100644 contrib/test/JDBC/expected/BABEL-1544.out create mode 100644 contrib/test/JDBC/expected/BABEL-1566.out create mode 100644 contrib/test/JDBC/expected/BABEL-1569.out create mode 100644 contrib/test/JDBC/expected/BABEL-1577.out create mode 100644 contrib/test/JDBC/expected/BABEL-1603.out create mode 100644 contrib/test/JDBC/expected/BABEL-1621.out create mode 100644 contrib/test/JDBC/expected/BABEL-1631.out create mode 100644 contrib/test/JDBC/expected/BABEL-1636.out create mode 100644 contrib/test/JDBC/expected/BABEL-1661.out create mode 100644 contrib/test/JDBC/expected/BABEL-1666.out create mode 100644 contrib/test/JDBC/expected/BABEL-1671.out create mode 100644 contrib/test/JDBC/expected/BABEL-1682.out create mode 100644 contrib/test/JDBC/expected/BABEL-1683.out create mode 100644 contrib/test/JDBC/expected/BABEL-1708.out create mode 100644 contrib/test/JDBC/expected/BABEL-1712.out create mode 100644 contrib/test/JDBC/expected/BABEL-1715.out create mode 100644 contrib/test/JDBC/expected/BABEL-1719.out create mode 100644 contrib/test/JDBC/expected/BABEL-1746.out create mode 100644 contrib/test/JDBC/expected/BABEL-1756.out create mode 100644 contrib/test/JDBC/expected/BABEL-1757.out create mode 100644 contrib/test/JDBC/expected/BABEL-1771.out create mode 100644 contrib/test/JDBC/expected/BABEL-1792.out create mode 100644 contrib/test/JDBC/expected/BABEL-1797.out create mode 100644 contrib/test/JDBC/expected/BABEL-1808.out create mode 100644 contrib/test/JDBC/expected/BABEL-1813.out create mode 100644 contrib/test/JDBC/expected/BABEL-1839.out create mode 100644 contrib/test/JDBC/expected/BABEL-1845.out create mode 100644 contrib/test/JDBC/expected/BABEL-1858.out create mode 100644 contrib/test/JDBC/expected/BABEL-1887.out create mode 100644 contrib/test/JDBC/expected/BABEL-1963.out create mode 100644 contrib/test/JDBC/expected/BABEL-1975.out create mode 100644 contrib/test/JDBC/expected/BABEL-1978.out create mode 100644 contrib/test/JDBC/expected/BABEL-1994-CHAR.out create mode 100644 contrib/test/JDBC/expected/BABEL-1994-VARCHAR.out create mode 100644 contrib/test/JDBC/expected/BABEL-1997.out create mode 100644 contrib/test/JDBC/expected/BABEL-2010.out create mode 100644 contrib/test/JDBC/expected/BABEL-2011.out create mode 100644 contrib/test/JDBC/expected/BABEL-2020-DELETE.out create mode 100644 contrib/test/JDBC/expected/BABEL-2020-UPDATE.out create mode 100644 contrib/test/JDBC/expected/BABEL-2034.out create mode 100644 contrib/test/JDBC/expected/BABEL-2049.out create mode 100644 contrib/test/JDBC/expected/BABEL-2051.out create mode 100644 contrib/test/JDBC/expected/BABEL-2060.out create mode 100644 contrib/test/JDBC/expected/BABEL-2086.out create mode 100644 contrib/test/JDBC/expected/BABEL-2089.out create mode 100644 contrib/test/JDBC/expected/BABEL-2117.out create mode 100644 contrib/test/JDBC/expected/BABEL-213.out create mode 100644 contrib/test/JDBC/expected/BABEL-2145.out create mode 100644 contrib/test/JDBC/expected/BABEL-2203.out create mode 100644 contrib/test/JDBC/expected/BABEL-2208.out create mode 100644 contrib/test/JDBC/expected/BABEL-2218.out create mode 100644 contrib/test/JDBC/expected/BABEL-2225.out create mode 100644 contrib/test/JDBC/expected/BABEL-2234.out create mode 100644 contrib/test/JDBC/expected/BABEL-2257.out create mode 100644 contrib/test/JDBC/expected/BABEL-2258.out create mode 100644 contrib/test/JDBC/expected/BABEL-2262.out create mode 100644 contrib/test/JDBC/expected/BABEL-2266.out create mode 100644 contrib/test/JDBC/expected/BABEL-2267.out create mode 100644 contrib/test/JDBC/expected/BABEL-2276.out create mode 100644 contrib/test/JDBC/expected/BABEL-2288.out create mode 100644 contrib/test/JDBC/expected/BABEL-2296.out create mode 100644 contrib/test/JDBC/expected/BABEL-2303.out create mode 100644 contrib/test/JDBC/expected/BABEL-2303_upgrade.out create mode 100644 contrib/test/JDBC/expected/BABEL-2317.out create mode 100644 contrib/test/JDBC/expected/BABEL-2325.out create mode 100644 contrib/test/JDBC/expected/BABEL-2328.out create mode 100644 contrib/test/JDBC/expected/BABEL-2337.out create mode 100644 contrib/test/JDBC/expected/BABEL-2347.out create mode 100644 contrib/test/JDBC/expected/BABEL-2349.out create mode 100644 contrib/test/JDBC/expected/BABEL-235.out create mode 100644 contrib/test/JDBC/expected/BABEL-2350.out create mode 100644 contrib/test/JDBC/expected/BABEL-2355.out create mode 100644 contrib/test/JDBC/expected/BABEL-2357.out create mode 100644 contrib/test/JDBC/expected/BABEL-2371.out create mode 100644 contrib/test/JDBC/expected/BABEL-2372.out create mode 100644 contrib/test/JDBC/expected/BABEL-2381.out create mode 100644 contrib/test/JDBC/expected/BABEL-2390.out create mode 100644 contrib/test/JDBC/expected/BABEL-2392.out create mode 100644 contrib/test/JDBC/expected/BABEL-2403.out create mode 100644 contrib/test/JDBC/expected/BABEL-2409.out create mode 100644 contrib/test/JDBC/expected/BABEL-2410.out create mode 100644 contrib/test/JDBC/expected/BABEL-2412.out create mode 100644 contrib/test/JDBC/expected/BABEL-2416.out create mode 100644 contrib/test/JDBC/expected/BABEL-2417.out create mode 100644 contrib/test/JDBC/expected/BABEL-2418.out create mode 100644 contrib/test/JDBC/expected/BABEL-2433.out create mode 100644 contrib/test/JDBC/expected/BABEL-244.out create mode 100644 contrib/test/JDBC/expected/BABEL-2440.out create mode 100644 contrib/test/JDBC/expected/BABEL-2445.out create mode 100644 contrib/test/JDBC/expected/BABEL-2451.out create mode 100644 contrib/test/JDBC/expected/BABEL-2455.out create mode 100644 contrib/test/JDBC/expected/BABEL-2458.out create mode 100644 contrib/test/JDBC/expected/BABEL-2470.out create mode 100644 contrib/test/JDBC/expected/BABEL-2482.out create mode 100644 contrib/test/JDBC/expected/BABEL-2485.out create mode 100644 contrib/test/JDBC/expected/BABEL-2496.out create mode 100644 contrib/test/JDBC/expected/BABEL-2513.out create mode 100644 contrib/test/JDBC/expected/BABEL-2514.out create mode 100644 contrib/test/JDBC/expected/BABEL-2515.out create mode 100644 contrib/test/JDBC/expected/BABEL-2535.out create mode 100644 contrib/test/JDBC/expected/BABEL-2555.out create mode 100644 contrib/test/JDBC/expected/BABEL-2557.out create mode 100644 contrib/test/JDBC/expected/BABEL-2565.out create mode 100644 contrib/test/JDBC/expected/BABEL-2569.out create mode 100644 contrib/test/JDBC/expected/BABEL-2576.out create mode 100644 contrib/test/JDBC/expected/BABEL-2578.out create mode 100644 contrib/test/JDBC/expected/BABEL-2585.out create mode 100644 contrib/test/JDBC/expected/BABEL-2593.out create mode 100644 contrib/test/JDBC/expected/BABEL-2596.out create mode 100644 contrib/test/JDBC/expected/BABEL-2602.out create mode 100644 contrib/test/JDBC/expected/BABEL-2604.out create mode 100644 contrib/test/JDBC/expected/BABEL-2607.out create mode 100644 contrib/test/JDBC/expected/BABEL-2622.out create mode 100644 contrib/test/JDBC/expected/BABEL-2647.out create mode 100644 contrib/test/JDBC/expected/BABEL-2649.out create mode 100644 contrib/test/JDBC/expected/BABEL-265.out create mode 100644 contrib/test/JDBC/expected/BABEL-2659.out create mode 100644 contrib/test/JDBC/expected/BABEL-2674.out create mode 100644 contrib/test/JDBC/expected/BABEL-2676.out create mode 100644 contrib/test/JDBC/expected/BABEL-2687.out create mode 100644 contrib/test/JDBC/expected/BABEL-2687_upgrade.out create mode 100644 contrib/test/JDBC/expected/BABEL-2701.out create mode 100644 contrib/test/JDBC/expected/BABEL-2706.out create mode 100644 contrib/test/JDBC/expected/BABEL-2716.out create mode 100644 contrib/test/JDBC/expected/BABEL-2724.out create mode 100644 contrib/test/JDBC/expected/BABEL-2725.out create mode 100644 contrib/test/JDBC/expected/BABEL-2730.out create mode 100644 contrib/test/JDBC/expected/BABEL-2732.out create mode 100644 contrib/test/JDBC/expected/BABEL-2747.out create mode 100644 contrib/test/JDBC/expected/BABEL-2748.out create mode 100644 contrib/test/JDBC/expected/BABEL-2765.out create mode 100644 contrib/test/JDBC/expected/BABEL-2785.out create mode 100644 contrib/test/JDBC/expected/BABEL-2824.out create mode 100644 contrib/test/JDBC/expected/BABEL-2829.out create mode 100644 contrib/test/JDBC/expected/BABEL-2833.out create mode 100644 contrib/test/JDBC/expected/BABEL-2835.out create mode 100644 contrib/test/JDBC/expected/BABEL-2845.out create mode 100644 contrib/test/JDBC/expected/BABEL-2857.out create mode 100644 contrib/test/JDBC/expected/BABEL-2869.out create mode 100644 contrib/test/JDBC/expected/BABEL-2875.out create mode 100644 contrib/test/JDBC/expected/BABEL-2884.out create mode 100644 contrib/test/JDBC/expected/BABEL-2917.out create mode 100644 contrib/test/JDBC/expected/BABEL-2924.out create mode 100644 contrib/test/JDBC/expected/BABEL-2936.out create mode 100644 contrib/test/JDBC/expected/BABEL-2955.out create mode 100644 contrib/test/JDBC/expected/BABEL-2964.out create mode 100644 contrib/test/JDBC/expected/BABEL-2968.out create mode 100644 contrib/test/JDBC/expected/BABEL-2977.out create mode 100644 contrib/test/JDBC/expected/BABEL-2979.out create mode 100644 contrib/test/JDBC/expected/BABEL-2983.out create mode 100644 contrib/test/JDBC/expected/BABEL-2983_upgrade.out create mode 100644 contrib/test/JDBC/expected/BABEL-2984.out create mode 100644 contrib/test/JDBC/expected/BABEL-2988.out create mode 100644 contrib/test/JDBC/expected/BABEL-3004.out create mode 100644 contrib/test/JDBC/expected/BABEL-3005.out create mode 100644 contrib/test/JDBC/expected/BABEL-3006.out create mode 100644 contrib/test/JDBC/expected/BABEL-3019.out create mode 100644 contrib/test/JDBC/expected/BABEL-3036.out create mode 100644 contrib/test/JDBC/expected/BABEL-3101.out create mode 100644 contrib/test/JDBC/expected/BABEL-3108.out create mode 100644 contrib/test/JDBC/expected/BABEL-338.out create mode 100644 contrib/test/JDBC/expected/BABEL-381.out create mode 100644 contrib/test/JDBC/expected/BABEL-388.out create mode 100644 contrib/test/JDBC/expected/BABEL-405.out create mode 100644 contrib/test/JDBC/expected/BABEL-407.out create mode 100644 contrib/test/JDBC/expected/BABEL-453.out create mode 100644 contrib/test/JDBC/expected/BABEL-479.out create mode 100644 contrib/test/JDBC/expected/BABEL-480.out create mode 100644 contrib/test/JDBC/expected/BABEL-492.out create mode 100644 contrib/test/JDBC/expected/BABEL-493.out create mode 100644 contrib/test/JDBC/expected/BABEL-559.out create mode 100644 contrib/test/JDBC/expected/BABEL-565.out create mode 100644 contrib/test/JDBC/expected/BABEL-586.out create mode 100644 contrib/test/JDBC/expected/BABEL-588.out create mode 100644 contrib/test/JDBC/expected/BABEL-597.out create mode 100644 contrib/test/JDBC/expected/BABEL-604.out create mode 100644 contrib/test/JDBC/expected/BABEL-625.out create mode 100644 contrib/test/JDBC/expected/BABEL-627.out create mode 100644 contrib/test/JDBC/expected/BABEL-632.out create mode 100644 contrib/test/JDBC/expected/BABEL-637.out create mode 100644 contrib/test/JDBC/expected/BABEL-646.out create mode 100644 contrib/test/JDBC/expected/BABEL-662.out create mode 100644 contrib/test/JDBC/expected/BABEL-672.out create mode 100644 contrib/test/JDBC/expected/BABEL-690.out create mode 100644 contrib/test/JDBC/expected/BABEL-694.out create mode 100644 contrib/test/JDBC/expected/BABEL-701.out create mode 100644 contrib/test/JDBC/expected/BABEL-708.out create mode 100644 contrib/test/JDBC/expected/BABEL-719.out create mode 100644 contrib/test/JDBC/expected/BABEL-728.out create mode 100644 contrib/test/JDBC/expected/BABEL-735.out create mode 100644 contrib/test/JDBC/expected/BABEL-741.out create mode 100644 contrib/test/JDBC/expected/BABEL-758.out create mode 100644 contrib/test/JDBC/expected/BABEL-768.out create mode 100644 contrib/test/JDBC/expected/BABEL-775.out create mode 100644 contrib/test/JDBC/expected/BABEL-776.out create mode 100644 contrib/test/JDBC/expected/BABEL-785.out create mode 100644 contrib/test/JDBC/expected/BABEL-788.out create mode 100644 contrib/test/JDBC/expected/BABEL-798.out create mode 100644 contrib/test/JDBC/expected/BABEL-807.out create mode 100644 contrib/test/JDBC/expected/BABEL-820.out create mode 100644 contrib/test/JDBC/expected/BABEL-872.out create mode 100644 contrib/test/JDBC/expected/BABEL-889.out create mode 100644 contrib/test/JDBC/expected/BABEL-909.out create mode 100644 contrib/test/JDBC/expected/BABEL-911.out create mode 100644 contrib/test/JDBC/expected/BABEL-931.out create mode 100644 contrib/test/JDBC/expected/BABEL-941.out create mode 100644 contrib/test/JDBC/expected/BABEL-942.out create mode 100644 contrib/test/JDBC/expected/BABEL-955.out create mode 100644 contrib/test/JDBC/expected/BABEL-APPLOCK.out create mode 100644 contrib/test/JDBC/expected/BABEL-COLUMN-ALIAS.out create mode 100644 contrib/test/JDBC/expected/BABEL-COLUMN-CONSTRAINT.out create mode 100644 contrib/test/JDBC/expected/BABEL-DATABASEPROPERTYEX.out create mode 100644 contrib/test/JDBC/expected/BABEL-GRANT.out create mode 100644 contrib/test/JDBC/expected/BABEL-GUC-PLAN.out create mode 100644 contrib/test/JDBC/expected/BABEL-IDENTITY-BIFS.out create mode 100644 contrib/test/JDBC/expected/BABEL-IDENTITY-COLUMN.out create mode 100644 contrib/test/JDBC/expected/BABEL-INSERT-EXECUTE.out create mode 100644 contrib/test/JDBC/expected/BABEL-JOIN.out create mode 100644 contrib/test/JDBC/expected/BABEL-JSON-FUNCS.out create mode 100644 contrib/test/JDBC/expected/BABEL-LIKE2ILIKE.out create mode 100644 contrib/test/JDBC/expected/BABEL-LOGIN-USER-EXT.out create mode 100644 contrib/test/JDBC/expected/BABEL-OBJECT-FUNCTIONS.out create mode 100644 contrib/test/JDBC/expected/BABEL-PROCID.out create mode 100644 contrib/test/JDBC/expected/BABEL-RAND.out create mode 100644 contrib/test/JDBC/expected/BABEL-RECURSIVE-CTE.out create mode 100644 contrib/test/JDBC/expected/BABEL-ROWCOUNT.out create mode 100644 contrib/test/JDBC/expected/BABEL-ROWVERSION.out create mode 100644 contrib/test/JDBC/expected/BABEL-SCHEMABINDING.out create mode 100644 contrib/test/JDBC/expected/BABEL-SEQUENCE.out create mode 100644 contrib/test/JDBC/expected/BABEL-SERVERPROPERTY.out create mode 100644 contrib/test/JDBC/expected/BABEL-SERVICENAME.out create mode 100644 contrib/test/JDBC/expected/BABEL-SESSION.out create mode 100644 contrib/test/JDBC/expected/BABEL-SET-COMMAND.out create mode 100644 contrib/test/JDBC/expected/BABEL-SPCOLUMNS.out create mode 100644 contrib/test/JDBC/expected/BABEL-SPCURSOR.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_COLUMN_PRIVILEGES.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_DATABASES.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_DATATYPE_INFO.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_DESCRIBE_FRIST_RESULT_SET.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_FKEYS.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_PKEYS.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_SPECIAL_COLUMNS.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_STATISTICS.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_STORED_PROCEDURES.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_TABLES.out create mode 100644 contrib/test/JDBC/expected/BABEL-SP_TABLE_PRIVILEGES.out create mode 100644 contrib/test/JDBC/expected/BABEL-SQL-CONFIG-FUNCTIONS.out create mode 100644 contrib/test/JDBC/expected/BABEL-SQLvariant.out create mode 100644 contrib/test/JDBC/expected/BABEL-SQUARE.out create mode 100644 contrib/test/JDBC/expected/BABEL-SYS-DATABASES.out create mode 100644 contrib/test/JDBC/expected/BABEL-SYSCHARSETS.out create mode 100644 contrib/test/JDBC/expected/BABEL-TABLEHINT.out create mode 100644 contrib/test/JDBC/expected/BABEL-TARGETLIST.out create mode 100644 contrib/test/JDBC/expected/BABEL-UNSUPPORTED.out create mode 100644 contrib/test/JDBC/expected/BABEL-USER.out create mode 100644 contrib/test/JDBC/expected/BABEL_2429.out create mode 100644 contrib/test/JDBC/expected/Babel-2655.out create mode 100644 contrib/test/JDBC/expected/HAS_DBACCESS.out create mode 100644 contrib/test/JDBC/expected/ISC-Domains.out create mode 100644 contrib/test/JDBC/expected/ISC-Table_Constraints.out create mode 100644 contrib/test/JDBC/expected/ISC-Views.out create mode 100644 contrib/test/JDBC/expected/Test-xp_qv.out create mode 100644 contrib/test/JDBC/expected/TestBIT.out create mode 100644 contrib/test/JDBC/expected/TestBasicInterop.out create mode 100644 contrib/test/JDBC/expected/TestBigInt.out create mode 100644 contrib/test/JDBC/expected/TestBinary.out create mode 100644 contrib/test/JDBC/expected/TestChar.out create mode 100644 contrib/test/JDBC/expected/TestCompileTimeErrorWithDefaultBehave.out create mode 100644 contrib/test/JDBC/expected/TestCursorFetchNext.out create mode 100644 contrib/test/JDBC/expected/TestCursorPrepExecFetchNext.out create mode 100644 contrib/test/JDBC/expected/TestDate.out create mode 100644 contrib/test/JDBC/expected/TestDatetime.out create mode 100644 contrib/test/JDBC/expected/TestDatetime2.out create mode 100644 contrib/test/JDBC/expected/TestDecimal.out create mode 100644 contrib/test/JDBC/expected/TestErrorFunctions.out create mode 100644 contrib/test/JDBC/expected/TestErrorHelperFunctions.out create mode 100644 contrib/test/JDBC/expected/TestFloat.out create mode 100644 contrib/test/JDBC/expected/TestForXML.out create mode 100644 contrib/test/JDBC/expected/TestInsteadofTriggers.out create mode 100644 contrib/test/JDBC/expected/TestInt.out create mode 100644 contrib/test/JDBC/expected/TestInteropProcedures.out create mode 100644 contrib/test/JDBC/expected/TestIsolationLevels.out create mode 100644 contrib/test/JDBC/expected/TestMoney.out create mode 100644 contrib/test/JDBC/expected/TestNotNull.out create mode 100644 contrib/test/JDBC/expected/TestNumeric.out create mode 100644 contrib/test/JDBC/expected/TestPrepareExec.out create mode 100644 contrib/test/JDBC/expected/TestProcedureWithErrorHandling.out create mode 100644 contrib/test/JDBC/expected/TestProcedureWithTransactions.out create mode 100644 contrib/test/JDBC/expected/TestProcedureWithTriggers.out create mode 100644 contrib/test/JDBC/expected/TestRaiserror.out create mode 100644 contrib/test/JDBC/expected/TestRaiserrorFormat.out create mode 100644 contrib/test/JDBC/expected/TestReal.out create mode 100644 contrib/test/JDBC/expected/TestRunTimeErrorWithDefaultBehave.out create mode 100644 contrib/test/JDBC/expected/TestSPExecutesql.out create mode 100644 contrib/test/JDBC/expected/TestSPPrepare.out create mode 100644 contrib/test/JDBC/expected/TestSQLQueries.out create mode 100644 contrib/test/JDBC/expected/TestSmallDatetime.out create mode 100644 contrib/test/JDBC/expected/TestSmallInt.out create mode 100644 contrib/test/JDBC/expected/TestStoredProcedures.out create mode 100644 contrib/test/JDBC/expected/TestText.out create mode 100644 contrib/test/JDBC/expected/TestThrow.out create mode 100644 contrib/test/JDBC/expected/TestTime.out create mode 100644 contrib/test/JDBC/expected/TestTinyInt.out create mode 100644 contrib/test/JDBC/expected/TestTransactionName.out create mode 100644 contrib/test/JDBC/expected/TestTransactionSupportForProcedure.out create mode 100644 contrib/test/JDBC/expected/TestTransactionsSQLBatch.out create mode 100644 contrib/test/JDBC/expected/TestUDD.out create mode 100644 contrib/test/JDBC/expected/TestUniqueIdentifier.out create mode 100644 contrib/test/JDBC/expected/TestVarChar.out create mode 100644 contrib/test/JDBC/expected/TestXML.out create mode 100644 contrib/test/JDBC/expected/babel_1127.out create mode 100644 contrib/test/JDBC/expected/babel_404.out create mode 100644 contrib/test/JDBC/expected/babel_417.out create mode 100644 contrib/test/JDBC/expected/babel_613.out create mode 100644 contrib/test/JDBC/expected/babel_621.out create mode 100644 contrib/test/JDBC/expected/babel_bit_comp.out create mode 100644 contrib/test/JDBC/expected/babel_ceiling_floor.out create mode 100644 contrib/test/JDBC/expected/babel_char.out create mode 100644 contrib/test/JDBC/expected/babel_choose.out create mode 100644 contrib/test/JDBC/expected/babel_collation.out create mode 100644 contrib/test/JDBC/expected/babel_cursor.out create mode 100644 contrib/test/JDBC/expected/babel_databasepropertyex.out create mode 100644 contrib/test/JDBC/expected/babel_datatype_sqlvariant.out create mode 100644 contrib/test/JDBC/expected/babel_datetime.out create mode 100644 contrib/test/JDBC/expected/babel_datetime2.out create mode 100644 contrib/test/JDBC/expected/babel_datetimeoffset.out create mode 100644 contrib/test/JDBC/expected/babel_declare.out create mode 100644 contrib/test/JDBC/expected/babel_error_handling.out create mode 100644 contrib/test/JDBC/expected/babel_exec_batch.out create mode 100644 contrib/test/JDBC/expected/babel_function_string.out create mode 100644 contrib/test/JDBC/expected/babel_functions_cast.out create mode 100644 contrib/test/JDBC/expected/babel_hashbytes.out create mode 100644 contrib/test/JDBC/expected/babel_iif.out create mode 100644 contrib/test/JDBC/expected/babel_isnull.out create mode 100644 contrib/test/JDBC/expected/babel_isnumeric.out create mode 100644 contrib/test/JDBC/expected/babel_iterative_exec.out create mode 100644 contrib/test/JDBC/expected/babel_iterative_exec_goto_label.out create mode 100644 contrib/test/JDBC/expected/babel_iterative_exec_loop.out create mode 100644 contrib/test/JDBC/expected/babel_iterative_exec_return.out create mode 100644 contrib/test/JDBC/expected/babel_iterative_exec_try_catch.out create mode 100644 contrib/test/JDBC/expected/babel_money.out create mode 100644 contrib/test/JDBC/expected/babel_money_upgrade.out create mode 100644 contrib/test/JDBC/expected/babel_numeric.out create mode 100644 contrib/test/JDBC/expected/babel_operators.out create mode 100644 contrib/test/JDBC/expected/babel_print.out create mode 100644 contrib/test/JDBC/expected/babel_smalldatetime.out create mode 100644 contrib/test/JDBC/expected/babel_sqlvariant_cast_compare.out create mode 100644 contrib/test/JDBC/expected/babel_temp_table.out create mode 100644 contrib/test/JDBC/expected/babel_top.out create mode 100644 contrib/test/JDBC/expected/babel_trigger.out create mode 100644 contrib/test/JDBC/expected/babel_update.out create mode 100644 contrib/test/JDBC/expected/babel_varbinary.out create mode 100644 contrib/test/JDBC/expected/fulltextserviceproperty.out create mode 100644 contrib/test/JDBC/expected/insertbulk.out create mode 100644 contrib/test/JDBC/expected/is_srvrolemember.out create mode 100644 contrib/test/JDBC/expected/pg_stat_activity.out create mode 100644 contrib/test/JDBC/expected/sp_columns_100.out create mode 100644 contrib/test/JDBC/expected/sp_statistics_100.out create mode 100644 contrib/test/JDBC/expected/sys-column-property.out create mode 100644 contrib/test/JDBC/expected/sys-configurations.out create mode 100644 contrib/test/JDBC/expected/sys-datefirst.out create mode 100644 contrib/test/JDBC/expected/sys-endpoints.out create mode 100644 contrib/test/JDBC/expected/sys-foreign_key_columns.out create mode 100644 contrib/test/JDBC/expected/sys-foreign_keys.out create mode 100644 contrib/test/JDBC/expected/sys-key_constraints.out create mode 100644 contrib/test/JDBC/expected/sys-lock_timeout.out create mode 100644 contrib/test/JDBC/expected/sys-max_connections.out create mode 100644 contrib/test/JDBC/expected/sys-original_login.out create mode 100644 contrib/test/JDBC/expected/sys-procedures.out create mode 100644 contrib/test/JDBC/expected/sys-schema-name.out create mode 100644 contrib/test/JDBC/expected/sys-sp_tables_view.out create mode 100644 contrib/test/JDBC/expected/sys-suser_sid.out create mode 100644 contrib/test/JDBC/expected/sys-suser_sname.out create mode 100644 contrib/test/JDBC/expected/sys-syscolumns.out create mode 100644 contrib/test/JDBC/expected/sys-sysforeignkeys.out create mode 100644 contrib/test/JDBC/expected/sys-table_types.out create mode 100644 contrib/test/JDBC/expected/sys-tables.out create mode 100644 contrib/test/JDBC/expected/sys-trigger_nestlevel.out create mode 100644 contrib/test/JDBC/expected/sys-types.out create mode 100644 contrib/test/JDBC/expected/sys-views.out create mode 100644 contrib/test/JDBC/expected/tds_faultinjection.out create mode 100755 contrib/test/JDBC/init.sh create mode 100644 contrib/test/JDBC/input/BABEL-1000.sql create mode 100644 contrib/test/JDBC/input/BABEL-101.sql create mode 100644 contrib/test/JDBC/input/BABEL-1044.sql create mode 100644 contrib/test/JDBC/input/BABEL-1056.sql create mode 100644 contrib/test/JDBC/input/BABEL-1066.sql create mode 100644 contrib/test/JDBC/input/BABEL-1073.sql create mode 100644 contrib/test/JDBC/input/BABEL-1091.sql create mode 100644 contrib/test/JDBC/input/BABEL-1094.sql create mode 100644 contrib/test/JDBC/input/BABEL-1096.sql create mode 100644 contrib/test/JDBC/input/BABEL-1098.sql create mode 100644 contrib/test/JDBC/input/BABEL-1100.sql create mode 100644 contrib/test/JDBC/input/BABEL-1111.sql create mode 100644 contrib/test/JDBC/input/BABEL-1113.sql create mode 100644 contrib/test/JDBC/input/BABEL-1114.sql create mode 100644 contrib/test/JDBC/input/BABEL-1149.sql create mode 100644 contrib/test/JDBC/input/BABEL-1161.sql create mode 100644 contrib/test/JDBC/input/BABEL-1164.sql create mode 100644 contrib/test/JDBC/input/BABEL-1167.sql create mode 100644 contrib/test/JDBC/input/BABEL-1173.sql create mode 100644 contrib/test/JDBC/input/BABEL-1179.sql create mode 100644 contrib/test/JDBC/input/BABEL-1184.sql create mode 100644 contrib/test/JDBC/input/BABEL-1185.sql create mode 100644 contrib/test/JDBC/input/BABEL-1189.sql create mode 100644 contrib/test/JDBC/input/BABEL-1193.sql create mode 100644 contrib/test/JDBC/input/BABEL-1206.sql create mode 100644 contrib/test/JDBC/input/BABEL-1208.sql create mode 100644 contrib/test/JDBC/input/BABEL-1210.sql create mode 100644 contrib/test/JDBC/input/BABEL-1212.sql create mode 100644 contrib/test/JDBC/input/BABEL-1231.sql create mode 100644 contrib/test/JDBC/input/BABEL-1234.sql create mode 100644 contrib/test/JDBC/input/BABEL-1239.sql create mode 100644 contrib/test/JDBC/input/BABEL-1241.sql create mode 100644 contrib/test/JDBC/input/BABEL-1243.sql create mode 100644 contrib/test/JDBC/input/BABEL-1249.sql create mode 100644 contrib/test/JDBC/input/BABEL-1251.sql create mode 100644 contrib/test/JDBC/input/BABEL-1252.sql create mode 100644 contrib/test/JDBC/input/BABEL-1261.sql create mode 100644 contrib/test/JDBC/input/BABEL-1270.txt create mode 100644 contrib/test/JDBC/input/BABEL-1287.sql create mode 100644 contrib/test/JDBC/input/BABEL-1291.sql create mode 100644 contrib/test/JDBC/input/BABEL-1299.sql create mode 100644 contrib/test/JDBC/input/BABEL-1309.mix create mode 100644 contrib/test/JDBC/input/BABEL-1311.sql create mode 100644 contrib/test/JDBC/input/BABEL-1319.sql create mode 100644 contrib/test/JDBC/input/BABEL-1320.sql create mode 100644 contrib/test/JDBC/input/BABEL-1329.sql create mode 100644 contrib/test/JDBC/input/BABEL-1331.sql create mode 100644 contrib/test/JDBC/input/BABEL-1363.mix create mode 100644 contrib/test/JDBC/input/BABEL-1381.sql create mode 100644 contrib/test/JDBC/input/BABEL-1389.sql create mode 100644 contrib/test/JDBC/input/BABEL-1400.sql create mode 100644 contrib/test/JDBC/input/BABEL-1435.sql create mode 100644 contrib/test/JDBC/input/BABEL-1437.sql create mode 100644 contrib/test/JDBC/input/BABEL-1438.sql create mode 100644 contrib/test/JDBC/input/BABEL-1442.sql create mode 100644 contrib/test/JDBC/input/BABEL-1444.sql create mode 100644 contrib/test/JDBC/input/BABEL-1446.sql create mode 100644 contrib/test/JDBC/input/BABEL-1454.mix create mode 100644 contrib/test/JDBC/input/BABEL-1465.sql create mode 100644 contrib/test/JDBC/input/BABEL-1466.sql create mode 100644 contrib/test/JDBC/input/BABEL-1475.sql create mode 100644 contrib/test/JDBC/input/BABEL-1481.sql create mode 100644 contrib/test/JDBC/input/BABEL-1499.sql create mode 100644 contrib/test/JDBC/input/BABEL-1502.sql create mode 100644 contrib/test/JDBC/input/BABEL-1510.sql create mode 100644 contrib/test/JDBC/input/BABEL-1513.sql create mode 100644 contrib/test/JDBC/input/BABEL-1515.sql create mode 100644 contrib/test/JDBC/input/BABEL-1524.sql create mode 100644 contrib/test/JDBC/input/BABEL-1531.sql create mode 100644 contrib/test/JDBC/input/BABEL-1544.sql create mode 100644 contrib/test/JDBC/input/BABEL-1566.sql create mode 100644 contrib/test/JDBC/input/BABEL-1569.sql create mode 100644 contrib/test/JDBC/input/BABEL-1577.sql create mode 100644 contrib/test/JDBC/input/BABEL-1603.sql create mode 100644 contrib/test/JDBC/input/BABEL-1621.sql create mode 100644 contrib/test/JDBC/input/BABEL-1631.sql create mode 100644 contrib/test/JDBC/input/BABEL-1636.sql create mode 100644 contrib/test/JDBC/input/BABEL-1654.sql create mode 100644 contrib/test/JDBC/input/BABEL-1661.sql create mode 100644 contrib/test/JDBC/input/BABEL-1666.sql create mode 100644 contrib/test/JDBC/input/BABEL-1671.sql create mode 100644 contrib/test/JDBC/input/BABEL-1682.sql create mode 100644 contrib/test/JDBC/input/BABEL-1683.sql create mode 100644 contrib/test/JDBC/input/BABEL-1708.sql create mode 100644 contrib/test/JDBC/input/BABEL-1712.sql create mode 100644 contrib/test/JDBC/input/BABEL-1715.sql create mode 100644 contrib/test/JDBC/input/BABEL-1719.sql create mode 100644 contrib/test/JDBC/input/BABEL-1746.sql create mode 100644 contrib/test/JDBC/input/BABEL-1756.sql create mode 100644 contrib/test/JDBC/input/BABEL-1757.sql create mode 100644 contrib/test/JDBC/input/BABEL-1771.sql create mode 100644 contrib/test/JDBC/input/BABEL-1792.sql create mode 100644 contrib/test/JDBC/input/BABEL-1797.sql create mode 100644 contrib/test/JDBC/input/BABEL-1808.sql create mode 100644 contrib/test/JDBC/input/BABEL-1813.sql create mode 100644 contrib/test/JDBC/input/BABEL-1839.sql create mode 100644 contrib/test/JDBC/input/BABEL-1845.sql create mode 100644 contrib/test/JDBC/input/BABEL-1858.sql create mode 100644 contrib/test/JDBC/input/BABEL-1887.mix create mode 100644 contrib/test/JDBC/input/BABEL-1944.sql create mode 100644 contrib/test/JDBC/input/BABEL-1963.sql create mode 100644 contrib/test/JDBC/input/BABEL-1975.sql create mode 100644 contrib/test/JDBC/input/BABEL-1978.sql create mode 100644 contrib/test/JDBC/input/BABEL-1994-CHAR.sql create mode 100644 contrib/test/JDBC/input/BABEL-1994-VARCHAR.sql create mode 100644 contrib/test/JDBC/input/BABEL-1997.sql create mode 100644 contrib/test/JDBC/input/BABEL-2010.sql create mode 100644 contrib/test/JDBC/input/BABEL-2011.sql create mode 100644 contrib/test/JDBC/input/BABEL-2020-DELETE.sql create mode 100644 contrib/test/JDBC/input/BABEL-2020-UPDATE.sql create mode 100644 contrib/test/JDBC/input/BABEL-2034.sql create mode 100644 contrib/test/JDBC/input/BABEL-2049.sql create mode 100644 contrib/test/JDBC/input/BABEL-2051.sql create mode 100644 contrib/test/JDBC/input/BABEL-2060.sql create mode 100644 contrib/test/JDBC/input/BABEL-2079.sql create mode 100644 contrib/test/JDBC/input/BABEL-2086.sql create mode 100644 contrib/test/JDBC/input/BABEL-2089.sql create mode 100644 contrib/test/JDBC/input/BABEL-2117.sql create mode 100644 contrib/test/JDBC/input/BABEL-213.mix create mode 100644 contrib/test/JDBC/input/BABEL-2145.sql create mode 100644 contrib/test/JDBC/input/BABEL-2203.sql create mode 100644 contrib/test/JDBC/input/BABEL-2208.sql create mode 100644 contrib/test/JDBC/input/BABEL-2218.sql create mode 100644 contrib/test/JDBC/input/BABEL-2225.sql create mode 100644 contrib/test/JDBC/input/BABEL-2234.sql create mode 100644 contrib/test/JDBC/input/BABEL-2257.sql create mode 100644 contrib/test/JDBC/input/BABEL-2258.sql create mode 100644 contrib/test/JDBC/input/BABEL-2262.sql create mode 100644 contrib/test/JDBC/input/BABEL-2266.sql create mode 100644 contrib/test/JDBC/input/BABEL-2276.sql create mode 100644 contrib/test/JDBC/input/BABEL-2288.sql create mode 100644 contrib/test/JDBC/input/BABEL-2296.mix create mode 100644 contrib/test/JDBC/input/BABEL-2303.sql create mode 100644 contrib/test/JDBC/input/BABEL-2303_upgrade.txt create mode 100644 contrib/test/JDBC/input/BABEL-2317.sql create mode 100644 contrib/test/JDBC/input/BABEL-2325.sql create mode 100644 contrib/test/JDBC/input/BABEL-2328.sql create mode 100644 contrib/test/JDBC/input/BABEL-2337.sql create mode 100644 contrib/test/JDBC/input/BABEL-2347.sql create mode 100644 contrib/test/JDBC/input/BABEL-2349.sql create mode 100644 contrib/test/JDBC/input/BABEL-235.sql create mode 100644 contrib/test/JDBC/input/BABEL-2350.sql create mode 100644 contrib/test/JDBC/input/BABEL-2354.sql create mode 100644 contrib/test/JDBC/input/BABEL-2355.sql create mode 100644 contrib/test/JDBC/input/BABEL-2357.sql create mode 100644 contrib/test/JDBC/input/BABEL-2371.sql create mode 100644 contrib/test/JDBC/input/BABEL-2372.sql create mode 100644 contrib/test/JDBC/input/BABEL-2381.sql create mode 100644 contrib/test/JDBC/input/BABEL-2390.sql create mode 100644 contrib/test/JDBC/input/BABEL-2392.sql create mode 100644 contrib/test/JDBC/input/BABEL-2403.mix create mode 100644 contrib/test/JDBC/input/BABEL-2409.mix create mode 100644 contrib/test/JDBC/input/BABEL-2410.sql create mode 100644 contrib/test/JDBC/input/BABEL-2412.sql create mode 100644 contrib/test/JDBC/input/BABEL-2416.sql create mode 100644 contrib/test/JDBC/input/BABEL-2417.sql create mode 100644 contrib/test/JDBC/input/BABEL-2418.sql create mode 100644 contrib/test/JDBC/input/BABEL-2419.sql create mode 100644 contrib/test/JDBC/input/BABEL-2432.sql create mode 100644 contrib/test/JDBC/input/BABEL-2433.sql create mode 100644 contrib/test/JDBC/input/BABEL-2437.sql create mode 100644 contrib/test/JDBC/input/BABEL-244.sql create mode 100644 contrib/test/JDBC/input/BABEL-2440.mix create mode 100644 contrib/test/JDBC/input/BABEL-2445.sql create mode 100644 contrib/test/JDBC/input/BABEL-2451.sql create mode 100644 contrib/test/JDBC/input/BABEL-2458.mix create mode 100644 contrib/test/JDBC/input/BABEL-2470.sql create mode 100644 contrib/test/JDBC/input/BABEL-2482.sql create mode 100644 contrib/test/JDBC/input/BABEL-2485.sql create mode 100644 contrib/test/JDBC/input/BABEL-2496.sql create mode 100644 contrib/test/JDBC/input/BABEL-2513.sql create mode 100644 contrib/test/JDBC/input/BABEL-2514.sql create mode 100644 contrib/test/JDBC/input/BABEL-2515.sql create mode 100644 contrib/test/JDBC/input/BABEL-2535.sql create mode 100644 contrib/test/JDBC/input/BABEL-2555.sql create mode 100644 contrib/test/JDBC/input/BABEL-2557.sql create mode 100644 contrib/test/JDBC/input/BABEL-2565.sql create mode 100644 contrib/test/JDBC/input/BABEL-2569.sql create mode 100644 contrib/test/JDBC/input/BABEL-2576.sql create mode 100644 contrib/test/JDBC/input/BABEL-2578.mix create mode 100644 contrib/test/JDBC/input/BABEL-2585.sql create mode 100644 contrib/test/JDBC/input/BABEL-2593.sql create mode 100644 contrib/test/JDBC/input/BABEL-2596.sql create mode 100644 contrib/test/JDBC/input/BABEL-2602.mix create mode 100644 contrib/test/JDBC/input/BABEL-2604.sql create mode 100644 contrib/test/JDBC/input/BABEL-2607.sql create mode 100644 contrib/test/JDBC/input/BABEL-2622.mix create mode 100644 contrib/test/JDBC/input/BABEL-2647.sql create mode 100644 contrib/test/JDBC/input/BABEL-2649.sql create mode 100644 contrib/test/JDBC/input/BABEL-265.sql create mode 100644 contrib/test/JDBC/input/BABEL-2659.sql create mode 100644 contrib/test/JDBC/input/BABEL-2674.sql create mode 100644 contrib/test/JDBC/input/BABEL-2676.sql create mode 100644 contrib/test/JDBC/input/BABEL-2681.sql create mode 100644 contrib/test/JDBC/input/BABEL-2687.sql create mode 100644 contrib/test/JDBC/input/BABEL-2687_upgrade.txt create mode 100644 contrib/test/JDBC/input/BABEL-2701.sql create mode 100644 contrib/test/JDBC/input/BABEL-2706.sql create mode 100644 contrib/test/JDBC/input/BABEL-2716.sql create mode 100644 contrib/test/JDBC/input/BABEL-2724.sql create mode 100644 contrib/test/JDBC/input/BABEL-2725.sql create mode 100644 contrib/test/JDBC/input/BABEL-2730.sql create mode 100644 contrib/test/JDBC/input/BABEL-2732.sql create mode 100644 contrib/test/JDBC/input/BABEL-2747.sql create mode 100644 contrib/test/JDBC/input/BABEL-2748.sql create mode 100644 contrib/test/JDBC/input/BABEL-2765.sql create mode 100644 contrib/test/JDBC/input/BABEL-2785.sql create mode 100644 contrib/test/JDBC/input/BABEL-2787-2.sql create mode 100644 contrib/test/JDBC/input/BABEL-2787.sql create mode 100644 contrib/test/JDBC/input/BABEL-2824.sql create mode 100644 contrib/test/JDBC/input/BABEL-2829.sql create mode 100644 contrib/test/JDBC/input/BABEL-2833.sql create mode 100644 contrib/test/JDBC/input/BABEL-2835.sql create mode 100644 contrib/test/JDBC/input/BABEL-2845.sql create mode 100644 contrib/test/JDBC/input/BABEL-2857.sql create mode 100644 contrib/test/JDBC/input/BABEL-2869.sql create mode 100644 contrib/test/JDBC/input/BABEL-2875.sql create mode 100644 contrib/test/JDBC/input/BABEL-2884.sql create mode 100644 contrib/test/JDBC/input/BABEL-2917.sql create mode 100644 contrib/test/JDBC/input/BABEL-2924.sql create mode 100644 contrib/test/JDBC/input/BABEL-2936.sql create mode 100644 contrib/test/JDBC/input/BABEL-2944.sql create mode 100644 contrib/test/JDBC/input/BABEL-2955.sql create mode 100644 contrib/test/JDBC/input/BABEL-2964.sql create mode 100644 contrib/test/JDBC/input/BABEL-2968.sql create mode 100644 contrib/test/JDBC/input/BABEL-2977.sql create mode 100644 contrib/test/JDBC/input/BABEL-2979.sql create mode 100644 contrib/test/JDBC/input/BABEL-2983.sql create mode 100644 contrib/test/JDBC/input/BABEL-2983_upgrade.txt create mode 100644 contrib/test/JDBC/input/BABEL-2984.sql create mode 100644 contrib/test/JDBC/input/BABEL-2988.sql create mode 100644 contrib/test/JDBC/input/BABEL-2993.txt create mode 100644 contrib/test/JDBC/input/BABEL-3004.sql create mode 100644 contrib/test/JDBC/input/BABEL-3005.sql create mode 100644 contrib/test/JDBC/input/BABEL-3006.sql create mode 100644 contrib/test/JDBC/input/BABEL-3019.mix create mode 100644 contrib/test/JDBC/input/BABEL-3036.sql create mode 100644 contrib/test/JDBC/input/BABEL-3101.sql create mode 100644 contrib/test/JDBC/input/BABEL-3108.sql create mode 100644 contrib/test/JDBC/input/BABEL-3119.sql create mode 100644 contrib/test/JDBC/input/BABEL-338.sql create mode 100644 contrib/test/JDBC/input/BABEL-381.sql create mode 100644 contrib/test/JDBC/input/BABEL-383.sql create mode 100644 contrib/test/JDBC/input/BABEL-388.sql create mode 100644 contrib/test/JDBC/input/BABEL-405.sql create mode 100644 contrib/test/JDBC/input/BABEL-407.sql create mode 100644 contrib/test/JDBC/input/BABEL-453.sql create mode 100644 contrib/test/JDBC/input/BABEL-479.sql create mode 100644 contrib/test/JDBC/input/BABEL-480.sql create mode 100644 contrib/test/JDBC/input/BABEL-492.sql create mode 100644 contrib/test/JDBC/input/BABEL-493.sql create mode 100644 contrib/test/JDBC/input/BABEL-559.sql create mode 100644 contrib/test/JDBC/input/BABEL-565.sql create mode 100644 contrib/test/JDBC/input/BABEL-586.sql create mode 100644 contrib/test/JDBC/input/BABEL-588.sql create mode 100644 contrib/test/JDBC/input/BABEL-597.sql create mode 100644 contrib/test/JDBC/input/BABEL-604.sql create mode 100644 contrib/test/JDBC/input/BABEL-625.sql create mode 100644 contrib/test/JDBC/input/BABEL-627.sql create mode 100644 contrib/test/JDBC/input/BABEL-632.sql create mode 100644 contrib/test/JDBC/input/BABEL-637.sql create mode 100644 contrib/test/JDBC/input/BABEL-646.sql create mode 100644 contrib/test/JDBC/input/BABEL-662.sql create mode 100644 contrib/test/JDBC/input/BABEL-672.sql create mode 100644 contrib/test/JDBC/input/BABEL-690.sql create mode 100644 contrib/test/JDBC/input/BABEL-694.sql create mode 100644 contrib/test/JDBC/input/BABEL-701.sql create mode 100644 contrib/test/JDBC/input/BABEL-708.sql create mode 100644 contrib/test/JDBC/input/BABEL-719.sql create mode 100644 contrib/test/JDBC/input/BABEL-728.sql create mode 100644 contrib/test/JDBC/input/BABEL-735.sql create mode 100644 contrib/test/JDBC/input/BABEL-741.sql create mode 100644 contrib/test/JDBC/input/BABEL-758.sql create mode 100644 contrib/test/JDBC/input/BABEL-768.sql create mode 100644 contrib/test/JDBC/input/BABEL-775.sql create mode 100644 contrib/test/JDBC/input/BABEL-776.sql create mode 100644 contrib/test/JDBC/input/BABEL-785.sql create mode 100644 contrib/test/JDBC/input/BABEL-788.sql create mode 100644 contrib/test/JDBC/input/BABEL-798.sql create mode 100644 contrib/test/JDBC/input/BABEL-807.sql create mode 100644 contrib/test/JDBC/input/BABEL-820.sql create mode 100644 contrib/test/JDBC/input/BABEL-872.sql create mode 100644 contrib/test/JDBC/input/BABEL-889.sql create mode 100644 contrib/test/JDBC/input/BABEL-909.sql create mode 100644 contrib/test/JDBC/input/BABEL-911.sql create mode 100644 contrib/test/JDBC/input/BABEL-931.sql create mode 100644 contrib/test/JDBC/input/BABEL-941.sql create mode 100644 contrib/test/JDBC/input/BABEL-942.sql create mode 100644 contrib/test/JDBC/input/BABEL-955.sql create mode 100644 contrib/test/JDBC/input/BABEL-APPLOCK.sql create mode 100644 contrib/test/JDBC/input/BABEL-COLUMN-ALIAS.sql create mode 100644 contrib/test/JDBC/input/BABEL-COLUMN-CONSTRAINT.sql create mode 100644 contrib/test/JDBC/input/BABEL-DATABASEPROPERTYEX.sql create mode 100644 contrib/test/JDBC/input/BABEL-EXTENDEDPROPERTY.sql create mode 100644 contrib/test/JDBC/input/BABEL-GRANT.sql create mode 100644 contrib/test/JDBC/input/BABEL-GUC-PLAN.sql create mode 100644 contrib/test/JDBC/input/BABEL-IDENTITY-BIFS.sql create mode 100644 contrib/test/JDBC/input/BABEL-IDENTITY-COLUMN.sql create mode 100644 contrib/test/JDBC/input/BABEL-IDENTITY.sql create mode 100644 contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN-PREPEXEC.txt create mode 100644 contrib/test/JDBC/input/BABEL-IMPLICIT_TRAN.sql create mode 100644 contrib/test/JDBC/input/BABEL-INSERT-EXECUTE.sql create mode 100644 contrib/test/JDBC/input/BABEL-JOIN.sql create mode 100644 contrib/test/JDBC/input/BABEL-JSON-FUNCS.sql create mode 100644 contrib/test/JDBC/input/BABEL-LIKE2ILIKE.sql create mode 100644 contrib/test/JDBC/input/BABEL-LOGIN-USER-EXT.mix create mode 100644 contrib/test/JDBC/input/BABEL-OBJECT-FUNCTIONS.sql create mode 100644 contrib/test/JDBC/input/BABEL-PROCID.sql create mode 100644 contrib/test/JDBC/input/BABEL-RAND.sql create mode 100644 contrib/test/JDBC/input/BABEL-RECURSIVE-CTE.sql create mode 100644 contrib/test/JDBC/input/BABEL-ROWCOUNT.sql create mode 100644 contrib/test/JDBC/input/BABEL-ROWVERSION.sql create mode 100644 contrib/test/JDBC/input/BABEL-SCHEMABINDING.sql create mode 100644 contrib/test/JDBC/input/BABEL-SEQUENCE.sql create mode 100644 contrib/test/JDBC/input/BABEL-SERVERPROPERTY.sql create mode 100644 contrib/test/JDBC/input/BABEL-SERVICENAME.sql create mode 100644 contrib/test/JDBC/input/BABEL-SESSION.mix create mode 100644 contrib/test/JDBC/input/BABEL-SET-COMMAND.sql create mode 100644 contrib/test/JDBC/input/BABEL-SPCOLUMNS.sql create mode 100644 contrib/test/JDBC/input/BABEL-SPCURSOR.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_COLUMN_PRIVILEGES.mix create mode 100644 contrib/test/JDBC/input/BABEL-SP_DATABASES.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_DATATYPE_INFO.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_DESCRIBE_FRIST_RESULT_SET.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_FKEYS.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_PKEYS.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_SPECIAL_COLUMNS.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_STATISTICS.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_STORED_PROCEDURES.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_TABLES.sql create mode 100644 contrib/test/JDBC/input/BABEL-SP_TABLE_PRIVILEGES.sql create mode 100644 contrib/test/JDBC/input/BABEL-SQL-CONFIG-FUNCTIONS.sql create mode 100644 contrib/test/JDBC/input/BABEL-SQLvariant.sql create mode 100644 contrib/test/JDBC/input/BABEL-SQUARE.sql create mode 100644 contrib/test/JDBC/input/BABEL-SYS-DATABASES.mix create mode 100644 contrib/test/JDBC/input/BABEL-SYSCHARSETS.sql create mode 100644 contrib/test/JDBC/input/BABEL-TABLEHINT.sql create mode 100644 contrib/test/JDBC/input/BABEL-TARGETLIST.sql create mode 100644 contrib/test/JDBC/input/BABEL-UNSUPPORTED.sql create mode 100644 contrib/test/JDBC/input/BABEL-USER.sql create mode 100644 contrib/test/JDBC/input/BABEL_2429.sql create mode 100644 contrib/test/JDBC/input/Babel-2655.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/102_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/102_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/102_3.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/102_6.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1034_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1049_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1051_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/10610_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/113_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11700_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11701_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11702_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11703_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11703_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11704_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11704_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11705_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11706_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/11708_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/129_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/132_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/133_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/134_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/135_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/136_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/141_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/142_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1505_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/16948_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/16950_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1752_1750_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1765_1750_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1768_1750_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1770_1750_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1774_1750_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/180_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/180_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/1946_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/201_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/206_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/208_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/217_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/218_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/219_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/220_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/220_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/220_3.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/220_4.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/222_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/257_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2732_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2733_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2747_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2760_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2787_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2812_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2812_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/2812_3.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/293_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3609_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3701_3.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3701_4.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3701_5.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3728_3727_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3729_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3902_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3903_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/3930_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4708_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4712_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4901_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4920_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4924_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4924_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4936_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/4936_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/512_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/515_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/517_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/545_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/547_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/547_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/550_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/556_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/6401_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8106_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8107_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8143_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8143_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8144_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8145_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8146_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8152_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8152_2.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8159_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/8179_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/9809_1.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/TestCompileTimeErrorWithDefaultBehave.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/TestErrorHelperFunctions.sql create mode 100644 contrib/test/JDBC/input/ErrorMapping/TestRunTimeErrorWithDefaultBehave.sql create mode 100644 contrib/test/JDBC/input/HAS_DBACCESS.sql create mode 100644 contrib/test/JDBC/input/ISC-Domains.sql create mode 100644 contrib/test/JDBC/input/ISC-Table_Constraints.mix create mode 100644 contrib/test/JDBC/input/ISC-Views.sql create mode 100644 contrib/test/JDBC/input/TestNotNull.txt create mode 100644 contrib/test/JDBC/input/authentication/TestAuth.txt create mode 100644 contrib/test/JDBC/input/babel_1127.sql create mode 100644 contrib/test/JDBC/input/babel_404.sql create mode 100644 contrib/test/JDBC/input/babel_417.sql create mode 100644 contrib/test/JDBC/input/babel_613.sql create mode 100644 contrib/test/JDBC/input/babel_621.sql create mode 100644 contrib/test/JDBC/input/babel_bit_comp.sql create mode 100644 contrib/test/JDBC/input/babel_ceiling_floor.sql create mode 100644 contrib/test/JDBC/input/babel_char.sql create mode 100644 contrib/test/JDBC/input/babel_choose.sql create mode 100644 contrib/test/JDBC/input/babel_collation.sql create mode 100644 contrib/test/JDBC/input/babel_cursor.sql create mode 100644 contrib/test/JDBC/input/babel_databasepropertyex.sql create mode 100644 contrib/test/JDBC/input/babel_datatype_sqlvariant.sql create mode 100644 contrib/test/JDBC/input/babel_datetime.sql create mode 100644 contrib/test/JDBC/input/babel_datetime2.sql create mode 100644 contrib/test/JDBC/input/babel_datetimeoffset.sql create mode 100644 contrib/test/JDBC/input/babel_declare.sql create mode 100644 contrib/test/JDBC/input/babel_error_handling.sql create mode 100644 contrib/test/JDBC/input/babel_exec_batch.sql create mode 100644 contrib/test/JDBC/input/babel_function_string.sql create mode 100644 contrib/test/JDBC/input/babel_functions_cast.sql create mode 100644 contrib/test/JDBC/input/babel_hashbytes.sql create mode 100644 contrib/test/JDBC/input/babel_iif.sql create mode 100644 contrib/test/JDBC/input/babel_isnull.mix create mode 100644 contrib/test/JDBC/input/babel_isnumeric.sql create mode 100644 contrib/test/JDBC/input/babel_iterative_exec.sql create mode 100644 contrib/test/JDBC/input/babel_iterative_exec_goto_label.sql create mode 100644 contrib/test/JDBC/input/babel_iterative_exec_loop.sql create mode 100644 contrib/test/JDBC/input/babel_iterative_exec_return.sql create mode 100644 contrib/test/JDBC/input/babel_iterative_exec_try_catch.sql create mode 100644 contrib/test/JDBC/input/babel_money.sql create mode 100644 contrib/test/JDBC/input/babel_money_upgrade.txt create mode 100644 contrib/test/JDBC/input/babel_numeric.sql create mode 100644 contrib/test/JDBC/input/babel_operators.sql create mode 100644 contrib/test/JDBC/input/babel_print.sql create mode 100644 contrib/test/JDBC/input/babel_smalldatetime.sql create mode 100644 contrib/test/JDBC/input/babel_sqlvariant_cast_compare.sql create mode 100644 contrib/test/JDBC/input/babel_temp_table.sql create mode 100644 contrib/test/JDBC/input/babel_top.sql create mode 100644 contrib/test/JDBC/input/babel_trigger.sql create mode 100644 contrib/test/JDBC/input/babel_update.sql create mode 100644 contrib/test/JDBC/input/babel_varbinary.sql create mode 100644 contrib/test/JDBC/input/cursors/BABEL-1390.txt create mode 100644 contrib/test/JDBC/input/cursors/TestCursorFetchNext.txt create mode 100644 contrib/test/JDBC/input/cursors/TestCursorPrepExecFetchNext.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestBIT.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestBigInt.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestBinary.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestChar.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestDate.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestDatetime.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestDatetime2.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestDecimal.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestFloat.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestInt.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestMoney.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestNumeric.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestReal.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestSmallDatetime.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestSmallInt.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestText.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestTime.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestTinyInt.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestUDD.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestUniqueIdentifier.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestVarChar.txt create mode 100644 contrib/test/JDBC/input/datatypes/TestXML.txt create mode 100644 contrib/test/JDBC/input/ddl/temp-tables.sql create mode 100644 contrib/test/JDBC/input/errorHandling/TestErrorFunctions.sql create mode 100644 contrib/test/JDBC/input/errorHandling/TestErrorsWithTryCatch.sql create mode 100644 contrib/test/JDBC/input/errorHandling/TestRaiserror.sql create mode 100644 contrib/test/JDBC/input/errorHandling/TestRaiserrorFormat.sql create mode 100644 contrib/test/JDBC/input/errorHandling/TestSimpleErrors.sql create mode 100644 contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithImplicitTran.txt create mode 100644 contrib/test/JDBC/input/errorHandling/TestSimpleErrorsWithXactAbort.sql create mode 100644 contrib/test/JDBC/input/errorHandling/TestThrow.sql create mode 100644 contrib/test/JDBC/input/forxml/TestForXML.sql create mode 100644 contrib/test/JDBC/input/functions/fulltextserviceproperty.sql create mode 100644 contrib/test/JDBC/input/functions/is_srvrolemember.sql create mode 100644 contrib/test/JDBC/input/functions/sys-column-property.sql create mode 100644 contrib/test/JDBC/input/functions/sys-datefirst.sql create mode 100644 contrib/test/JDBC/input/functions/sys-lock_timeout.sql create mode 100644 contrib/test/JDBC/input/functions/sys-max_connections.sql create mode 100644 contrib/test/JDBC/input/functions/sys-original_login.sql create mode 100644 contrib/test/JDBC/input/functions/sys-schema-name.sql create mode 100644 contrib/test/JDBC/input/functions/sys-suser_sid.sql create mode 100644 contrib/test/JDBC/input/functions/sys-suser_sname.sql create mode 100644 contrib/test/JDBC/input/functions/sys-trigger_nestlevel.sql create mode 100644 contrib/test/JDBC/input/insertbulk.txt create mode 100644 contrib/test/JDBC/input/interoperability/TestBasicInterop.mix create mode 100644 contrib/test/JDBC/input/interoperability/TestInteropProcedures.mix create mode 100644 contrib/test/JDBC/input/interoperability/TestProcedureWithErrorHandling.mix create mode 100644 contrib/test/JDBC/input/interoperability/TestProcedureWithTransactions.mix create mode 100644 contrib/test/JDBC/input/interoperability/TestProcedureWithTriggers.mix create mode 100644 contrib/test/JDBC/input/pg_stat_activity.txt create mode 100644 contrib/test/JDBC/input/sp_columns_100.sql create mode 100644 contrib/test/JDBC/input/sp_statistics_100.sql create mode 100644 contrib/test/JDBC/input/sqlBatch/TestSQLQueries.txt create mode 100644 contrib/test/JDBC/input/storedProcedures/BABEL-1365.sql create mode 100644 contrib/test/JDBC/input/storedProcedures/Test-xp_qv.sql create mode 100644 contrib/test/JDBC/input/storedProcedures/TestPrepareExec.txt create mode 100644 contrib/test/JDBC/input/storedProcedures/TestSPExecutesql.sql create mode 100644 contrib/test/JDBC/input/storedProcedures/TestSPPrepare.sql create mode 100644 contrib/test/JDBC/input/storedProcedures/TestStoredProcedures.txt create mode 100644 contrib/test/JDBC/input/tds_faultinjection.txt create mode 100644 contrib/test/JDBC/input/transactions/TestInsteadofTriggers.sql create mode 100644 contrib/test/JDBC/input/transactions/TestIsolationLevels.sql create mode 100644 contrib/test/JDBC/input/transactions/TestTransactionName.sql create mode 100644 contrib/test/JDBC/input/transactions/TestTransactionSupportForProcedure.txt create mode 100644 contrib/test/JDBC/input/transactions/TestTransactionsSQLBatch.txt create mode 100644 contrib/test/JDBC/input/transactions/TestTriggers.sql create mode 100644 contrib/test/JDBC/input/views/sys-all_columns.sql create mode 100644 contrib/test/JDBC/input/views/sys-check_constraints.sql create mode 100644 contrib/test/JDBC/input/views/sys-computed_columns.sql create mode 100644 contrib/test/JDBC/input/views/sys-configurations.sql create mode 100644 contrib/test/JDBC/input/views/sys-databases.sql create mode 100644 contrib/test/JDBC/input/views/sys-default_constraints.sql create mode 100644 contrib/test/JDBC/input/views/sys-endpoints.sql create mode 100644 contrib/test/JDBC/input/views/sys-extended_properties.sql create mode 100644 contrib/test/JDBC/input/views/sys-foreign_key_columns.sql create mode 100644 contrib/test/JDBC/input/views/sys-foreign_keys.sql create mode 100644 contrib/test/JDBC/input/views/sys-identity_columns.sql create mode 100644 contrib/test/JDBC/input/views/sys-index_columns.sql create mode 100644 contrib/test/JDBC/input/views/sys-indexes.sql create mode 100644 contrib/test/JDBC/input/views/sys-key_constraints.sql create mode 100644 contrib/test/JDBC/input/views/sys-procedures.sql create mode 100644 contrib/test/JDBC/input/views/sys-schemas.sql create mode 100644 contrib/test/JDBC/input/views/sys-server_principals.sql create mode 100644 contrib/test/JDBC/input/views/sys-sp_tables_view.sql create mode 100644 contrib/test/JDBC/input/views/sys-syscolumns.sql create mode 100644 contrib/test/JDBC/input/views/sys-sysforeignkeys.sql create mode 100644 contrib/test/JDBC/input/views/sys-table_types.mix create mode 100644 contrib/test/JDBC/input/views/sys-tables.sql create mode 100644 contrib/test/JDBC/input/views/sys-types.sql create mode 100644 contrib/test/JDBC/input/views/sys-views.sql create mode 100644 contrib/test/JDBC/jdbc_schedule create mode 100644 contrib/test/JDBC/jdbc_upgrade_schedule create mode 100644 contrib/test/JDBC/pom.xml create mode 100644 contrib/test/JDBC/sql_expected/1034_1.out create mode 100644 contrib/test/JDBC/sql_expected/1049_1.out create mode 100644 contrib/test/JDBC/sql_expected/1051_1.out create mode 100644 contrib/test/JDBC/sql_expected/10610_1.out create mode 100644 contrib/test/JDBC/sql_expected/11700_1.out create mode 100644 contrib/test/JDBC/sql_expected/11701_1.out create mode 100644 contrib/test/JDBC/sql_expected/11702_1.out create mode 100644 contrib/test/JDBC/sql_expected/11703_1.out create mode 100644 contrib/test/JDBC/sql_expected/11703_2.out create mode 100644 contrib/test/JDBC/sql_expected/11705_1.out create mode 100644 contrib/test/JDBC/sql_expected/11706_1.out create mode 100644 contrib/test/JDBC/sql_expected/11708_1.out create mode 100644 contrib/test/JDBC/sql_expected/132_1.out create mode 100644 contrib/test/JDBC/sql_expected/133_1.out create mode 100644 contrib/test/JDBC/sql_expected/134_1.out create mode 100644 contrib/test/JDBC/sql_expected/135_1.out create mode 100644 contrib/test/JDBC/sql_expected/136_1.out create mode 100644 contrib/test/JDBC/sql_expected/141_1.out create mode 100644 contrib/test/JDBC/sql_expected/142_1.out create mode 100644 contrib/test/JDBC/sql_expected/1505_1.out create mode 100644 contrib/test/JDBC/sql_expected/16948_1.out create mode 100644 contrib/test/JDBC/sql_expected/16950_1.out create mode 100644 contrib/test/JDBC/sql_expected/1752_1750_2.out create mode 100644 contrib/test/JDBC/sql_expected/1765_1750_1.out create mode 100644 contrib/test/JDBC/sql_expected/1768_1750_1.out create mode 100644 contrib/test/JDBC/sql_expected/180_1.out create mode 100644 contrib/test/JDBC/sql_expected/180_2.out create mode 100644 contrib/test/JDBC/sql_expected/1946_1.out create mode 100644 contrib/test/JDBC/sql_expected/201_1.out create mode 100644 contrib/test/JDBC/sql_expected/206_1.out create mode 100644 contrib/test/JDBC/sql_expected/217_1.out create mode 100644 contrib/test/JDBC/sql_expected/219_1.out create mode 100644 contrib/test/JDBC/sql_expected/220_1.out create mode 100644 contrib/test/JDBC/sql_expected/220_2.out create mode 100644 contrib/test/JDBC/sql_expected/220_3.out create mode 100644 contrib/test/JDBC/sql_expected/220_4.out create mode 100644 contrib/test/JDBC/sql_expected/2732_1.out create mode 100644 contrib/test/JDBC/sql_expected/2733_1.out create mode 100644 contrib/test/JDBC/sql_expected/2747_1.out create mode 100644 contrib/test/JDBC/sql_expected/2787_1.out create mode 100644 contrib/test/JDBC/sql_expected/293_1.out create mode 100644 contrib/test/JDBC/sql_expected/3609_1.out create mode 100644 contrib/test/JDBC/sql_expected/3701_3.out create mode 100644 contrib/test/JDBC/sql_expected/3701_4.out create mode 100644 contrib/test/JDBC/sql_expected/3701_5.out create mode 100644 contrib/test/JDBC/sql_expected/3728_3727_1.out create mode 100644 contrib/test/JDBC/sql_expected/3729_1.out create mode 100644 contrib/test/JDBC/sql_expected/3902_1.out create mode 100644 contrib/test/JDBC/sql_expected/3903_1.out create mode 100644 contrib/test/JDBC/sql_expected/3930_1.out create mode 100644 contrib/test/JDBC/sql_expected/4708_1.out create mode 100644 contrib/test/JDBC/sql_expected/4712_1.out create mode 100644 contrib/test/JDBC/sql_expected/4901_1.out create mode 100644 contrib/test/JDBC/sql_expected/4920_1.out create mode 100644 contrib/test/JDBC/sql_expected/512_1.out create mode 100644 contrib/test/JDBC/sql_expected/515_1.out create mode 100644 contrib/test/JDBC/sql_expected/517_1.out create mode 100644 contrib/test/JDBC/sql_expected/545_1.out create mode 100644 contrib/test/JDBC/sql_expected/547_1.out create mode 100644 contrib/test/JDBC/sql_expected/547_2.out create mode 100644 contrib/test/JDBC/sql_expected/550_1.out create mode 100644 contrib/test/JDBC/sql_expected/556_1.out create mode 100644 contrib/test/JDBC/sql_expected/6401_1.out create mode 100644 contrib/test/JDBC/sql_expected/8106_1.out create mode 100644 contrib/test/JDBC/sql_expected/8107_1.out create mode 100644 contrib/test/JDBC/sql_expected/8143_1.out create mode 100644 contrib/test/JDBC/sql_expected/8143_2.out create mode 100644 contrib/test/JDBC/sql_expected/8144_1.out create mode 100644 contrib/test/JDBC/sql_expected/8145_1.out create mode 100644 contrib/test/JDBC/sql_expected/8146_1.out create mode 100644 contrib/test/JDBC/sql_expected/8152_1.out create mode 100644 contrib/test/JDBC/sql_expected/8152_2.out create mode 100644 contrib/test/JDBC/sql_expected/8159_1.out create mode 100644 contrib/test/JDBC/sql_expected/8179_1.out create mode 100644 contrib/test/JDBC/sql_expected/9809_1.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-1243.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-1654.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-1944.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2079.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2354.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2419.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2432.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2437.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2681.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2787-2.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2787.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2944.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-2993.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-3119.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-383.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-EXTENDEDPROPERTY.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-IDENTITY.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN-PREPEXEC.out create mode 100644 contrib/test/JDBC/sql_expected/BABEL-IMPLICIT_TRAN.out create mode 100644 contrib/test/JDBC/sql_expected/TestAuth.out create mode 100644 contrib/test/JDBC/sql_expected/TestErrorsWithTryCatch.out create mode 100644 contrib/test/JDBC/sql_expected/TestSimpleErrors.out create mode 100644 contrib/test/JDBC/sql_expected/TestSimpleErrorsWithImplicitTran.out create mode 100644 contrib/test/JDBC/sql_expected/TestSimpleErrorsWithXactAbort.out create mode 100644 contrib/test/JDBC/sql_expected/TestTriggers.out create mode 100644 contrib/test/JDBC/sql_expected/sys-all_columns.out create mode 100644 contrib/test/JDBC/sql_expected/sys-check_constraints.out create mode 100644 contrib/test/JDBC/sql_expected/sys-computed_columns.out create mode 100644 contrib/test/JDBC/sql_expected/sys-databases.out create mode 100644 contrib/test/JDBC/sql_expected/sys-default_constraints.out create mode 100644 contrib/test/JDBC/sql_expected/sys-extended_properties.out create mode 100644 contrib/test/JDBC/sql_expected/sys-identity_columns.out create mode 100644 contrib/test/JDBC/sql_expected/sys-index_columns.out create mode 100644 contrib/test/JDBC/sql_expected/sys-indexes.out create mode 100644 contrib/test/JDBC/sql_expected/sys-schemas.out create mode 100644 contrib/test/JDBC/sql_expected/sys-server_principals.out create mode 100644 contrib/test/JDBC/sql_expected/temp-tables.out create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/CompareResults.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/Config.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/ExportResults.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/HandleException.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCAuthentication.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCBulkCopy.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCallableStatement.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCrossDialect.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCCursor.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCPreparedStatement.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCStatement.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/JDBCTransaction.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/Statistics.java create mode 100644 contrib/test/JDBC/src/main/java/com/sqlsamples/batch_run.java create mode 100644 contrib/test/JDBC/src/main/resources/config.txt create mode 100644 contrib/test/JDBC/src/main/resources/junit-platform.properties create mode 100644 contrib/test/JDBC/src/test/java/com/sqlsamples/TestQueryFile.java create mode 100644 contrib/test/JDBC/utils/Blank.jpeg create mode 100644 contrib/test/JDBC/utils/TxnErrorHandingCases.py create mode 100644 contrib/test/JDBC/utils/blank.txt create mode 100644 contrib/test/JDBC/utils/devanagari.txt create mode 100644 contrib/test/JDBC/utils/emojisText.txt create mode 100644 contrib/test/JDBC/utils/flower.jpg create mode 100644 contrib/test/JDBC/utils/sample.txt create mode 100644 contrib/test/JDBC/utils/utf16.txt create mode 100644 contrib/test/dotnet/ExpectedOutput/ScalarFunctionsSQLBatch.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestAuthentication.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestBIT.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestBigInt.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestBinary.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestChar.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestDate.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestDatetime.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestDatetime2.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestDecimal.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestFloat.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestInt.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestMoney.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestNumeric.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestPrepareExec.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestReal.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestSQLQueries.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestSmallDatetime.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestSmallInt.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestSqlVariant.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestStoredProcedure.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestText.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestTime.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestTinyInt.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestTransactions.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestTransactionsSQLBatch.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestTvp.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestUDD.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestUID.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestVarChar.out create mode 100644 contrib/test/dotnet/ExpectedOutput/TestXML.out create mode 100644 contrib/test/dotnet/ExpectedOutput/insertBulk.out create mode 100644 contrib/test/dotnet/Info/.gitignore create mode 100644 contrib/test/dotnet/Output/.gitignore create mode 100644 contrib/test/dotnet/ReadMe.txt create mode 100644 contrib/test/dotnet/config.txt create mode 100644 contrib/test/dotnet/dotnet.csproj create mode 100644 contrib/test/dotnet/input/Authentication/TestAuthentication.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestBIT.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestBigInt.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestBinary.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestChar.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestDate.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestDatetime.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestDatetime2.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestDecimal.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestFloat.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestInt.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestMoney.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestNumeric.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestReal.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestSmallDatetime.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestSmallInt.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestSqlVariant.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestText.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestTime.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestTinyInt.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestTvp.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestUDD.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestUID.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestVarChar.txt create mode 100644 contrib/test/dotnet/input/Datatypes/TestXML.txt create mode 100644 contrib/test/dotnet/input/InsertBulk/insertBulk.txt create mode 100644 contrib/test/dotnet/input/PrepareExec/TestPrepareExec.txt create mode 100644 contrib/test/dotnet/input/SQLBatch/TestSQLQueries.txt create mode 100644 contrib/test/dotnet/input/ScalarFunctions/ScalarFunctionsSQLBatch.txt create mode 100644 contrib/test/dotnet/input/Storedproc/TestStoredProcedure.txt create mode 100644 contrib/test/dotnet/input/Transaction/TestTransactions.txt create mode 100644 contrib/test/dotnet/input/Transaction/TestTransactionsSQLBatch.txt create mode 100644 contrib/test/dotnet/src/BatchRun.cs create mode 100644 contrib/test/dotnet/src/ExecuteTests.cs create mode 100644 contrib/test/dotnet/src/PrepareExecBinding.cs create mode 100644 contrib/test/dotnet/utils/ConfigSetup.cs create mode 100644 contrib/test/dotnet/utils/TestUtils.cs create mode 100644 contrib/test/dotnet/utils/devanagari.txt create mode 100644 contrib/test/dotnet/utils/emojisText.txt create mode 100644 contrib/test/dotnet/utils/sample.txt create mode 100644 contrib/test/dotnet/utils/utf16.txt create mode 100644 contrib/test/odbc/CMakeLists.txt create mode 100644 contrib/test/odbc/README.md create mode 100644 contrib/test/odbc/config.txt create mode 100644 contrib/test/odbc/constants.cpp create mode 100644 contrib/test/odbc/constants.h create mode 100644 contrib/test/odbc/database_objects.cpp create mode 100644 contrib/test/odbc/database_objects.h create mode 100644 contrib/test/odbc/main.cpp create mode 100644 contrib/test/odbc/odbc_handler.cpp create mode 100644 contrib/test/odbc/odbc_handler.h create mode 100644 contrib/test/odbc/query_generator.cpp create mode 100644 contrib/test/odbc/query_generator.h create mode 100644 contrib/test/odbc/test_connection.cpp create mode 100644 contrib/test/odbc/test_data_types.cpp create mode 100644 contrib/test/odbc/test_diagnostics.cpp create mode 100644 contrib/test/odbc/test_direct_executed_statements.cpp create mode 100644 contrib/test/odbc/test_metadata.cpp create mode 100644 contrib/test/odbc/test_prepared_statements.cpp create mode 100644 contrib/test/odbc/test_resultset.cpp create mode 100644 contrib/test/odbc/test_sqlgetinfo.cpp create mode 100644 contrib/test/odbc/test_transactions.cpp create mode 100644 contrib/test/odbc/utils/blank.txt create mode 100644 contrib/test/odbc/utils/devanagari.txt create mode 100644 contrib/test/odbc/utils/sample.txt create mode 100644 contrib/test/python/.gitignore create mode 100644 contrib/test/python/batch_run.py create mode 100644 contrib/test/python/compare_results.py create mode 100644 contrib/test/python/config.txt create mode 100644 contrib/test/python/execute_query.py create mode 100644 contrib/test/python/expected/pymssql/BABEL-1056.out create mode 100644 contrib/test/python/expected/pymssql/BABEL-1270.out create mode 100644 contrib/test/python/expected/pymssql/BABEL-1365.out create mode 100644 contrib/test/python/expected/pymssql/BABEL-1390.out create mode 100644 contrib/test/python/expected/pymssql/BABEL-1643.out create mode 100644 contrib/test/python/expected/pymssql/TestAuth.out create mode 100644 contrib/test/python/expected/pymssql/TestBIT.out create mode 100644 contrib/test/python/expected/pymssql/TestBigInt.out create mode 100644 contrib/test/python/expected/pymssql/TestBinary.out create mode 100644 contrib/test/python/expected/pymssql/TestChar.out create mode 100644 contrib/test/python/expected/pymssql/TestCursorFetchNext.out create mode 100644 contrib/test/python/expected/pymssql/TestCursorPrepExecFetchNext.out create mode 100644 contrib/test/python/expected/pymssql/TestDate.out create mode 100644 contrib/test/python/expected/pymssql/TestDatetime.out create mode 100644 contrib/test/python/expected/pymssql/TestDatetime2.out create mode 100644 contrib/test/python/expected/pymssql/TestDecimal.out create mode 100644 contrib/test/python/expected/pymssql/TestFloat.out create mode 100644 contrib/test/python/expected/pymssql/TestInt.out create mode 100644 contrib/test/python/expected/pymssql/TestMoney.out create mode 100644 contrib/test/python/expected/pymssql/TestNumeric.out create mode 100644 contrib/test/python/expected/pymssql/TestReal.out create mode 100644 contrib/test/python/expected/pymssql/TestSPExecutesql.out create mode 100644 contrib/test/python/expected/pymssql/TestSPPrepare.out create mode 100644 contrib/test/python/expected/pymssql/TestSQLQueries.out create mode 100644 contrib/test/python/expected/pymssql/TestSimpleErrorsWithImplicitTran.out create mode 100644 contrib/test/python/expected/pymssql/TestSmallDatetime.out create mode 100644 contrib/test/python/expected/pymssql/TestSmallInt.out create mode 100644 contrib/test/python/expected/pymssql/TestStoredProcedures.out create mode 100644 contrib/test/python/expected/pymssql/TestText.out create mode 100644 contrib/test/python/expected/pymssql/TestTime.out create mode 100644 contrib/test/python/expected/pymssql/TestTinyInt.out create mode 100644 contrib/test/python/expected/pymssql/TestTransactionSupportForProcedure.out create mode 100644 contrib/test/python/expected/pymssql/TestTransactionsSQLBatch.out create mode 100644 contrib/test/python/expected/pymssql/TestUDD.out create mode 100644 contrib/test/python/expected/pymssql/TestUniqueIdentifier.out create mode 100644 contrib/test/python/expected/pymssql/TestVarChar.out create mode 100644 contrib/test/python/expected/pymssql/TestXML.out create mode 100644 contrib/test/python/expected/pymssql/pg_stat_activity.out create mode 100644 contrib/test/python/expected/pyodbc/BABEL-1056.out create mode 100644 contrib/test/python/expected/pyodbc/BABEL-1270.out create mode 100644 contrib/test/python/expected/pyodbc/BABEL-1365.out create mode 100644 contrib/test/python/expected/pyodbc/BABEL-1390.out create mode 100644 contrib/test/python/expected/pyodbc/BABEL-1643.out create mode 100644 contrib/test/python/expected/pyodbc/TestAuth.out create mode 100644 contrib/test/python/expected/pyodbc/TestBIT.out create mode 100644 contrib/test/python/expected/pyodbc/TestBigInt.out create mode 100644 contrib/test/python/expected/pyodbc/TestBinary.out create mode 100644 contrib/test/python/expected/pyodbc/TestChar.out create mode 100644 contrib/test/python/expected/pyodbc/TestCursorFetchNext.out create mode 100644 contrib/test/python/expected/pyodbc/TestCursorPrepExecFetchNext.out create mode 100644 contrib/test/python/expected/pyodbc/TestDate.out create mode 100644 contrib/test/python/expected/pyodbc/TestDatetime.out create mode 100644 contrib/test/python/expected/pyodbc/TestDatetime2.out create mode 100644 contrib/test/python/expected/pyodbc/TestDecimal.out create mode 100644 contrib/test/python/expected/pyodbc/TestFloat.out create mode 100644 contrib/test/python/expected/pyodbc/TestInt.out create mode 100644 contrib/test/python/expected/pyodbc/TestMoney.out create mode 100644 contrib/test/python/expected/pyodbc/TestNumeric.out create mode 100644 contrib/test/python/expected/pyodbc/TestReal.out create mode 100644 contrib/test/python/expected/pyodbc/TestSPExecutesql.out create mode 100644 contrib/test/python/expected/pyodbc/TestSPPrepare.out create mode 100644 contrib/test/python/expected/pyodbc/TestSQLQueries.out create mode 100644 contrib/test/python/expected/pyodbc/TestSimpleErrorsWithImplicitTran.out create mode 100644 contrib/test/python/expected/pyodbc/TestSmallDatetime.out create mode 100644 contrib/test/python/expected/pyodbc/TestSmallInt.out create mode 100644 contrib/test/python/expected/pyodbc/TestStoredProcedures.out create mode 100644 contrib/test/python/expected/pyodbc/TestText.out create mode 100644 contrib/test/python/expected/pyodbc/TestTime.out create mode 100644 contrib/test/python/expected/pyodbc/TestTinyInt.out create mode 100644 contrib/test/python/expected/pyodbc/TestTransactionSupportForProcedure.out create mode 100644 contrib/test/python/expected/pyodbc/TestTransactionsSQLBatch.out create mode 100644 contrib/test/python/expected/pyodbc/TestUDD.out create mode 100644 contrib/test/python/expected/pyodbc/TestUniqueIdentifier.out create mode 100644 contrib/test/python/expected/pyodbc/TestVarChar.out create mode 100644 contrib/test/python/expected/pyodbc/TestXML.out create mode 100644 contrib/test/python/expected/pyodbc/fk-contention.out create mode 100644 contrib/test/python/expected/pyodbc/fk-deadlock.out create mode 100644 contrib/test/python/expected/pyodbc/pg_stat_activity.out create mode 100644 contrib/test/python/file_handler.py create mode 100644 contrib/test/python/input/BABEL-1056.txt create mode 100644 contrib/test/python/input/BABEL-1270.txt create mode 100644 contrib/test/python/input/TestSimpleErrorsWithImplicitTran.txt create mode 100644 contrib/test/python/input/authentication/TestAuth.txt create mode 100644 contrib/test/python/input/cursors/BABEL-1390.txt create mode 100644 contrib/test/python/input/cursors/TestCursorFetchNext.txt create mode 100644 contrib/test/python/input/cursors/TestCursorPrepExecFetchNext.txt create mode 100644 contrib/test/python/input/datatypes/BABEL-1643.txt create mode 100644 contrib/test/python/input/datatypes/TestBIT.txt create mode 100644 contrib/test/python/input/datatypes/TestBigInt.txt create mode 100644 contrib/test/python/input/datatypes/TestBinary.txt create mode 100644 contrib/test/python/input/datatypes/TestChar.txt create mode 100644 contrib/test/python/input/datatypes/TestDate.txt create mode 100644 contrib/test/python/input/datatypes/TestDatetime.txt create mode 100644 contrib/test/python/input/datatypes/TestDatetime2.txt create mode 100644 contrib/test/python/input/datatypes/TestDecimal.txt create mode 100644 contrib/test/python/input/datatypes/TestFloat.txt create mode 100644 contrib/test/python/input/datatypes/TestInt.txt create mode 100644 contrib/test/python/input/datatypes/TestMoney.txt create mode 100644 contrib/test/python/input/datatypes/TestNumeric.txt create mode 100644 contrib/test/python/input/datatypes/TestReal.txt create mode 100644 contrib/test/python/input/datatypes/TestSmallDatetime.txt create mode 100644 contrib/test/python/input/datatypes/TestSmallInt.txt create mode 100644 contrib/test/python/input/datatypes/TestText.txt create mode 100644 contrib/test/python/input/datatypes/TestTime.txt create mode 100644 contrib/test/python/input/datatypes/TestTinyInt.txt create mode 100644 contrib/test/python/input/datatypes/TestUDD.txt create mode 100644 contrib/test/python/input/datatypes/TestUniqueIdentifier.txt create mode 100644 contrib/test/python/input/datatypes/TestVarChar.txt create mode 100644 contrib/test/python/input/datatypes/TestXML.txt create mode 100644 contrib/test/python/input/isolation/fk-contention.spec create mode 100644 contrib/test/python/input/isolation/fk-deadlock.spec create mode 100644 contrib/test/python/input/pg_stat_activity.txt create mode 100644 contrib/test/python/input/sqlBatch/TestSQLQueries.txt create mode 100644 contrib/test/python/input/storedProcedures/BABEL-1365.sql create mode 100644 contrib/test/python/input/storedProcedures/TestSPExecutesql.sql create mode 100644 contrib/test/python/input/storedProcedures/TestSPPrepare.sql create mode 100644 contrib/test/python/input/storedProcedures/TestStoredProcedures.txt create mode 100644 contrib/test/python/input/transactions/TestTransactionSupportForProcedure.txt create mode 100644 contrib/test/python/input/transactions/TestTransactionsSQLBatch.txt create mode 100644 contrib/test/python/isolationtest/README.md create mode 100644 contrib/test/python/isolationtest/__init__.py create mode 100644 contrib/test/python/isolationtest/isolationTestHandler.py create mode 100644 contrib/test/python/isolationtest/isolationTester.py create mode 100644 contrib/test/python/isolationtest/parser/__init__.py create mode 100644 contrib/test/python/isolationtest/parser/specLexer.g4 create mode 100644 contrib/test/python/isolationtest/parser/specParser.g4 create mode 100644 contrib/test/python/isolationtest/specParserVisitorImpl.py create mode 100644 contrib/test/python/logs/.gitkeep create mode 100644 contrib/test/python/output/pymssql/.gitkeep create mode 100644 contrib/test/python/output/pyodbc/.gitkeep create mode 100644 contrib/test/python/python_authentication.py create mode 100644 contrib/test/python/sql_expected/pymssql/.gitkeep create mode 100644 contrib/test/python/sql_expected/pyodbc/.gitkeep create mode 100644 contrib/test/python/start.py create mode 100644 contrib/test/python/test_main.py create mode 100644 contrib/test/python/utils/__init__.py create mode 100644 contrib/test/python/utils/base.py create mode 100644 contrib/test/python/utils/blank.txt create mode 100644 contrib/test/python/utils/config.py create mode 100644 contrib/test/python/utils/db_client.py create mode 100644 contrib/test/python/utils/devanagari.txt create mode 100644 contrib/test/python/utils/emojisText.txt create mode 100644 contrib/test/python/utils/sample.txt create mode 100644 contrib/test/python/utils/utf16.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5ab388a05b0..046c65a47a8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,34 +1,168 @@ -name: CI - +name: JDBC Unit Tests on: push: + branches: + - BABEL_1_X_DEV__PG_13_6__distro pull_request: branches: - - BABEL_1_X_DEV__13_4 + - BABEL_1_X_DEV__PG_13_6__distro + +env: + ANTLR4_VERSION: 4.9.3 + REPOSITORY: BABEL_1_X_DEV__PG_13_6 + PG_SRC: /home/runner/work/${REPOSITORY}/${REPOSITORY} + jobs: - build-and-run-tests: - name: Build and run tests + extension-tests: + name: Build and test runs-on: ubuntu-latest steps: - - name: clone-repository - uses: actions/checkout@v2 - - name: build-postgres + - 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: | - ./configure - make world-bin -j8 - - name: run-tests + # 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 + # Removed due to incompatibility with BABEL-2785 test, as host_os function + # uses the version string to extract the OS. + #--with-extra-version=" Babelfish for PostgreSQL" + make clean && make DESTDIR=~/postgres/ -j 4 2>error.txt + # make check + sudo make install + cd contrib && make -j 4 && 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/${REPOSITORY} + export PG_SRC=/home/runner/work/${REPOSITORY}/${REPOSITORY}/ + 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 . + make all + - 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/${REPOSITORY}/${REPOSITORY}/ + export PG_CONFIG=~/postgres/bin/pg_config + # cmake=$(which cmake) + + cd ${PG_SRC}/contrib + for ext in babelfishpg_common babelfishpg_money babelfishpg_tds babelfishpg_tsql + do + cd $ext + make -j ${JOBS} + sudo make PG_CONFIG=${PG_CONFIG} install + cd .. + done + - 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 "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: | + export PG_SRC=/home/runner/work/${REPOSITORY}/${REPOSITORY}/ + cd ${PG_SRC}/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: | - make check -j8 - - name: upload-test-summary - if: failure() + cd ${PG_SRC}/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: regression-summary - path: src/test/regress/regression.out - - name: upload-test-differences - if: failure() + 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: regression-differences - path: src/test/regress/regression.diffs + name: output-diff.diff + path: contrib/test/JDBC/Info/output-diff.diff diff --git a/contrib/babelfishpg_common/Makefile b/contrib/babelfishpg_common/Makefile new file mode 100644 index 00000000000..9a16c467d9d --- /dev/null +++ b/contrib/babelfishpg_common/Makefile @@ -0,0 +1,89 @@ +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 + +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) + +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..da334b7bc1e --- /dev/null +++ b/contrib/babelfishpg_common/Version.config @@ -0,0 +1,4 @@ +BBFPGCMN_MAJOR_VERSION=1 +BBFPGCMN_MINOR_VERSION=2 +BBFPGCMN_MICRO_VERSION=1 + diff --git a/contrib/babelfishpg_common/babelfishpg_common.control.in b/contrib/babelfishpg_common/babelfishpg_common.control.in new file mode 100644 index 00000000000..d36603ccdbc --- /dev/null +++ b/contrib/babelfishpg_common/babelfishpg_common.control.in @@ -0,0 +1,7 @@ +# TSQL Datatype extension +comment = 'Transact SQL Datatype Support' +default_version = '@EXTVERSION@' +module_pathname = '@MODULEPATH@' +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..19fec1338c8 --- /dev/null +++ b/contrib/babelfishpg_common/sql/babelfishpg_common.in @@ -0,0 +1,40 @@ +/* + * 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 "rowversion.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..228a73a0272 --- /dev/null +++ b/contrib/babelfishpg_common/sql/bit.sql @@ -0,0 +1,490 @@ +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; + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_max(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for max operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_min(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for min operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_sum(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for sum operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_avg(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for avg operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.BIT) +( + sfunc = sys.bit_unsupported_max, + stype = sys.bit, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.BIT) +( + sfunc = sys.bit_unsupported_min, + stype = sys.bit, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.sum(sys.BIT) +( + sfunc = sys.bit_unsupported_sum, + stype = sys.bit, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.avg(sys.BIT) +( + sfunc = sys.bit_unsupported_avg, + stype = sys.bit, + parallel = safe +); diff --git a/contrib/babelfishpg_common/sql/bpchar.sql b/contrib/babelfishpg_common/sql/bpchar.sql new file mode 100644 index 00000000000..cc88b5f8453 --- /dev/null +++ b/contrib/babelfishpg_common/sql/bpchar.sql @@ -0,0 +1,350 @@ +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 +); + +CREATE OR REPLACE FUNCTION sys.bpchar_larger(sys.BPCHAR, sys.BPCHAR) +RETURNS sys.BPCHAR +AS 'bpchar_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bpchar_smaller(sys.BPCHAR, sys.BPCHAR) +RETURNS sys.BPCHAR +AS 'bpchar_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.BPCHAR) +( + sfunc = sys.bpchar_larger, + stype = sys.bpchar, + combinefunc = sys.bpchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.BPCHAR) +( + sfunc = sys.bpchar_smaller, + stype = sys.bpchar, + combinefunc = sys.bpchar_smaller, + parallel = safe +); + +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'; + +CREATE OR REPLACE FUNCTION sys.nchar_larger(sys.NCHAR, sys.NCHAR) +RETURNS sys.NCHAR +AS 'bpchar_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.nchar_smaller(sys.NCHAR, sys.NCHAR) +RETURNS sys.NCHAR +AS 'bpchar_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.NCHAR) +( + sfunc = sys.nchar_larger, + stype = sys.nchar, + combinefunc = sys.nchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.NCHAR) +( + sfunc = sys.nchar_smaller, + stype = sys.nchar, + combinefunc = sys.nchar_smaller, + parallel = safe +); 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..4a7d986a50c --- /dev/null +++ b/contrib/babelfishpg_common/sql/datetime.sql @@ -0,0 +1,418 @@ +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 +); + +CREATE OR REPLACE FUNCTION sys.datetime_larger(sys.DATETIME, sys.DATETIME) +RETURNS sys.DATETIME +AS 'timestamp_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime_smaller(sys.DATETIME, sys.DATETIME) +RETURNS sys.DATETIME +AS 'timestamp_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.DATETIME) +( + sfunc = sys.datetime_larger, + stype = sys.datetime, + combinefunc = sys.datetime_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.DATETIME) +( + sfunc = sys.datetime_smaller, + stype = sys.datetime, + combinefunc = sys.datetime_smaller, + parallel = safe +); + +-- 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..f38a7790fad --- /dev/null +++ b/contrib/babelfishpg_common/sql/datetime2.sql @@ -0,0 +1,347 @@ +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); + +CREATE OR REPLACE FUNCTION sys.datetime2_larger(sys.DATETIME2, sys.DATETIME2) +RETURNS sys.DATETIME2 +AS 'timestamp_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2_smaller(sys.DATETIME2, sys.DATETIME2) +RETURNS sys.DATETIME2 +AS 'timestamp_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.DATETIME2) +( + sfunc = sys.datetime2_larger, + stype = sys.datetime2, + combinefunc = sys.datetime2_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.DATETIME2) +( + sfunc = sys.datetime2_smaller, + stype = sys.datetime2, + combinefunc = sys.datetime2_smaller, + parallel = safe +); + +-- 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..801615c9550 --- /dev/null +++ b/contrib/babelfishpg_common/sql/datetimeoffset.sql @@ -0,0 +1,330 @@ +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); + +CREATE OR REPLACE FUNCTION sys.datetimeoffset_larger(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_larger' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset_smaller(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_smaller' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.DATETIMEOFFSET) +( + sfunc = sys.datetimeoffset_larger, + stype = sys.datetimeoffset, + combinefunc = sys.datetimeoffset_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.DATETIMEOFFSET) +( + sfunc = sys.datetimeoffset_smaller, + stype = sys.datetimeoffset, + combinefunc = sys.datetimeoffset_smaller, + parallel = safe +); + +-- 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..93a9974d6a0 --- /dev/null +++ b/contrib/babelfishpg_common/sql/money/fixeddecimal--1.1.0_base_parallel.sql @@ -0,0 +1,1982 @@ +------------------ +-- 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); + + +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); + +-- +-- 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 FUNCTION sys.int8fixeddecimaldiv_money(INT8, FIXEDDECIMAL) +RETURNS sys.MONEY +AS $$ + SELECT sys.int8fixeddecimaldiv($1, $2)::sys.MONEY; +$$ LANGUAGE SQL 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_money +); + +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 FUNCTION sys.int4fixeddecimaldiv_money(INT4, FIXEDDECIMAL) +RETURNS sys.MONEY +AS $$ + SELECT sys.int4fixeddecimaldiv($1, $2)::sys.MONEY; +$$ LANGUAGE SQL 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_money +); + +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 FUNCTION sys.int2fixeddecimaldiv_money(INT2, FIXEDDECIMAL) +RETURNS sys.MONEY +AS $$ + SELECT sys.int2fixeddecimaldiv($1, $2)::sys.MONEY; +$$ LANGUAGE SQL 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_money +); + +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 FUNCTION sys.fixeddecimalum(sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalum' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalpl(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalmi(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalmul(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimaldiv(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = fixeddecimalmi +); + +CREATE OPERATOR sys.- ( + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = fixeddecimalum +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = fixeddecimaldiv +); + +CREATE FUNCTION sys.fixeddecimalint8pl(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8mi(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8mul(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8div(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint8pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + PROCEDURE = fixeddecimalint8mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint8mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + PROCEDURE = fixeddecimalint8div +); + +CREATE FUNCTION sys.fixeddecimalint4pl(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4mi(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4mul(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4div(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint4pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint4mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4div +); + +CREATE FUNCTION sys.fixeddecimalint2pl(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2mi(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2mul(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2div(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint2pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint2mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2div +); + + +CREATE FUNCTION sys.int8fixeddecimalpl(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int8fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimalmi(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int8fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimalmul(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int8fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimaldiv(INT8, sys.SMALLMONEY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int8fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimaldiv_smallmoney(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int8fixeddecimaldiv($1, $2)::sys.SMALLMONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = int8fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int8fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = int8fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int8fixeddecimaldiv_smallmoney +); + +CREATE FUNCTION sys.int4fixeddecimalpl(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int4fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimalmi(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int4fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimalmul(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int4fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimaldiv(INT4, sys.SMALLMONEY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int4fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimaldiv_smallmoney(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int4fixeddecimaldiv($1, $2)::sys.SMALLMONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = int4fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int4fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = int4fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int4fixeddecimaldiv_smallmoney +); + +CREATE FUNCTION sys.int2fixeddecimalpl(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int2fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimalmi(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int2fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimalmul(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int2fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimaldiv(INT2, sys.SMALLMONEY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int2fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimaldiv_smallmoney(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimaldiv($1, $2)::sys.SMALLMONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = int2fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int2fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = int2fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int2fixeddecimaldiv_smallmoney +); + +CREATE FUNCTION sys.smallmoneylarger(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimallarger' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneysmaller(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalsmaller' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; 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..a4207326b07 --- /dev/null +++ b/contrib/babelfishpg_common/sql/money/fixeddecimal--parallelaggs.sql @@ -0,0 +1,82 @@ + +-- 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 +); + +CREATE AGGREGATE sys.min(sys.smallmoney) ( + SFUNC = sys.smallmoneysmaller, + STYPE = sys.smallmoney, + COMBINEFUNC = sys.smallmoneysmaller, + PARALLEL = SAFE +); + +CREATE AGGREGATE sys.max(sys.smallmoney) ( + SFUNC = sys.smallmoneylarger, + STYPE = sys.smallmoney, + COMBINEFUNC = sys.smallmoneylarger, + PARALLEL = SAFE +); diff --git a/contrib/babelfishpg_common/sql/numerics.sql b/contrib/babelfishpg_common/sql/numerics.sql new file mode 100644 index 00000000000..d8b3ba5382a --- /dev/null +++ b/contrib/babelfishpg_common/sql/numerics.sql @@ -0,0 +1,309 @@ +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 = ^ +); + +-- tinyint operator definitions to force return type to tinyyint + +CREATE OR REPLACE FUNCTION sys.tinyint_larger(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS 'int2larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.tinyint_smaller(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS 'int2smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.TINYINT) +( + sfunc = sys.tinyint_larger, + stype = sys.tinyint, + combinefunc = sys.tinyint_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.TINYINT) +( + sfunc = sys.tinyint_smaller, + stype = sys.tinyint, + combinefunc = sys.tinyint_smaller, + parallel = safe +); + +CREATE FUNCTION sys.tinyintum(sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2um($1)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintpl(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2pl($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintmi(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2mi($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintmul(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2mul($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintdiv(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2div($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + COMMUTATOR = +, + PROCEDURE = sys.tinyintpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.tinyintmi +); + +CREATE OPERATOR sys.- ( + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.tinyintum +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + COMMUTATOR = *, + PROCEDURE = sys.tinyintmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.tinyintdiv +); + + +CREATE FUNCTION sys.smallmoneytinyintpl(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2pl($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneytinyintmi(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2mi($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneytinyintmul(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2mul($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneytinyintdiv(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2div($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + COMMUTATOR = +, + PROCEDURE = sys.smallmoneytinyintpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.smallmoneytinyintmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + COMMUTATOR = *, + PROCEDURE = sys.smallmoneytinyintmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.smallmoneytinyintdiv +); + +CREATE FUNCTION sys.tinyintsmallmoneypl(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimalpl($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintsmallmoneymi(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimalmi($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintsmallmoneymul(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimalmul($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintsmallmoneydiv(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimaldiv($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = sys.tinyintsmallmoneypl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = sys.tinyintsmallmoneymi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = sys.tinyintsmallmoneymul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = sys.tinyintsmallmoneydiv +); + + +-- function definition on REAL datatype to force return type to REAL + +CREATE OR REPLACE FUNCTION sys.real_larger(sys.REAL, sys.REAL) +RETURNS sys.REAL +AS 'float4larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.real_smaller(sys.REAL, sys.REAL) +RETURNS sys.REAL +AS 'float4smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.REAL) +( + sfunc = sys.real_larger, + stype = sys.real, + combinefunc = sys.real_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.REAL) +( + sfunc = sys.real_smaller, + stype = sys.real, + combinefunc = sys.real_smaller, + parallel = safe +); diff --git a/contrib/babelfishpg_common/sql/rowversion.sql b/contrib/babelfishpg_common/sql/rowversion.sql new file mode 100644 index 00000000000..a22cb19ad45 --- /dev/null +++ b/contrib/babelfishpg_common/sql/rowversion.sql @@ -0,0 +1,263 @@ +CREATE TYPE sys.ROWVERSION; + +CREATE OR REPLACE FUNCTION sys.rowversionin(cstring, oid, integer) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.rowversionout(sys.ROWVERSION) +RETURNS cstring +AS 'babelfishpg_common', 'varbinaryout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.rowversionrecv(internal, oid, integer) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.rowversionsend(sys.ROWVERSION) +RETURNS bytea +AS 'babelfishpg_common', 'varbinarysend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.ROWVERSION ( + INPUT = sys.rowversionin, + OUTPUT = sys.rowversionout, + RECEIVE = sys.rowversionrecv, + SEND = sys.rowversionsend, + INTERNALLENGTH = 12, + ALIGNMENT = 'int4', + STORAGE = 'plain', + CATEGORY = 'U', + PREFERRED = false, + COLLATABLE = false +); + +-- casting functions for sys.ROWVERSION + +CREATE OR REPLACE FUNCTION sys.binaryrowversion(sys.BBF_BINARY, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY AS sys.ROWVERSION) +WITH FUNCTION sys.binaryrowversion (sys.BBF_BINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryrowversion(sys.BBF_VARBINARY, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY AS sys.ROWVERSION) +WITH FUNCTION sys.varbinaryrowversion (sys.BBF_VARBINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionbinary(sys.ROWVERSION, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'rowversionbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS sys.BBF_BINARY) +WITH FUNCTION sys.rowversionbinary (sys.ROWVERSION, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionvarbinary(sys.ROWVERSION, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'rowversionvarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS sys.BBF_VARBINARY) +WITH FUNCTION sys.rowversionvarbinary (sys.ROWVERSION, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varcharrowversion(sys.VARCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.varcharrowversion (sys.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varcharrowversion(pg_catalog.VARCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.varcharrowversion (pg_catalog.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharrowversion(pg_catalog.BPCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'bpcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.BPCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.bpcharrowversion (pg_catalog.BPCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharrowversion(sys.BPCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'bpcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.bpcharrowversion (sys.BPCHAR, integer, boolean) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.rowversionsysvarchar(sys.ROWVERSION) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS sys.VARCHAR) +WITH FUNCTION sys.rowversionsysvarchar (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionvarchar(sys.ROWVERSION) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS pg_catalog.VARCHAR) +WITH FUNCTION sys.rowversionvarchar (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int2rowversion(INT2, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int2rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT2 AS sys.ROWVERSION) +WITH FUNCTION sys.int2rowversion (INT2, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int4rowversion(INT4, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int4rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT4 AS sys.ROWVERSION) +WITH FUNCTION sys.int4rowversion (INT4, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.xid8rowversion(XID8, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int8rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (XID8 AS sys.ROWVERSION) +WITH FUNCTION sys.xid8rowversion (XID8, integer, boolean) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.int8rowversion(INT8, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int8rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT8 AS sys.ROWVERSION) +WITH FUNCTION sys.int8rowversion (INT8, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionint2(sys.ROWVERSION) +RETURNS INT2 +AS 'babelfishpg_common', 'binaryint2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION as INT2) +WITH FUNCTION sys.rowversionint2 (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionint4(sys.ROWVERSION) +RETURNS INT4 +AS 'babelfishpg_common', 'binaryint4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION as INT4) +WITH FUNCTION sys.rowversionint4 (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionint8(sys.ROWVERSION) +RETURNS INT8 +AS 'babelfishpg_common', 'binaryint8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION as INT8) +WITH FUNCTION sys.rowversionint8 (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE DOMAIN sys.TIMESTAMP AS sys.ROWVERSION; + +CREATE FUNCTION sys.rowversion_eq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_eq, + COMMUTATOR = = +); + + +CREATE FUNCTION sys.rowversion_neq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_neq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_neq, + COMMUTATOR = <> +); + +CREATE FUNCTION sys.rowversion_gt(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.> ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_gt, + COMMUTATOR = < +); + +CREATE FUNCTION sys.rowversion_geq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_geq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_geq, + COMMUTATOR = <= +); + +CREATE FUNCTION sys.rowversion_lt(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.< ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_lt, + COMMUTATOR = > +); + +CREATE FUNCTION sys.rowversion_leq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_leq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_leq, + COMMUTATOR = >= +); + +CREATE FUNCTION sys.rowversion_cmp(sys.rowversion, sys.rowversion) +RETURNS int +AS 'babelfishpg_common', 'varbinary_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.rowversion_ops +DEFAULT FOR TYPE sys.rowversion USING btree AS + OPERATOR 1 < (sys.rowversion, sys.rowversion), + OPERATOR 2 <= (sys.rowversion, sys.rowversion), + OPERATOR 3 = (sys.rowversion, sys.rowversion), + OPERATOR 4 >= (sys.rowversion, sys.rowversion), + OPERATOR 5 > (sys.rowversion, sys.rowversion), + FUNCTION 1 sys.rowversion_cmp(sys.rowversion, sys.rowversion); + diff --git a/contrib/babelfishpg_common/sql/smalldatetime.sql b/contrib/babelfishpg_common/sql/smalldatetime.sql new file mode 100644 index 00000000000..6f41a19756d --- /dev/null +++ b/contrib/babelfishpg_common/sql/smalldatetime.sql @@ -0,0 +1,614 @@ +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 +); + +CREATE OR REPLACE FUNCTION sys.smalldatetime_larger(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS sys.SMALLDATETIME +AS 'timestamp_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetime_smaller(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS sys.SMALLDATETIME +AS 'timestamp_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.SMALLDATETIME) +( + sfunc = sys.smalldatetime_larger, + stype = sys.smalldatetime, + combinefunc = sys.smalldatetime_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.SMALLDATETIME) +( + sfunc = sys.smalldatetime_smaller, + stype = sys.smalldatetime, + combinefunc = sys.smalldatetime_smaller, + parallel = safe +); + +-- 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..c03f93596e5 --- /dev/null +++ b/contrib/babelfishpg_common/sql/string_operators.sql @@ -0,0 +1,152 @@ +-- 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; + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper_outer(leftarg text, rightarg text) RETURNS sys.varchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = text, + RIGHTARG = text, + FUNCTION = sys.babelfish_concat_wrapper_outer +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.varchar, rightarg sys.varchar) RETURNS sys.varchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.varchar, + RIGHTARG = sys.varchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.nvarchar, rightarg sys.nvarchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.nvarchar, + RIGHTARG = sys.nvarchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.bpchar, rightarg sys.bpchar) RETURNS sys.varchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.bpchar, + RIGHTARG = sys.bpchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.nchar, rightarg sys.nchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.nchar, + RIGHTARG = sys.nchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +-- if one of input is nvarchar, resolve it as nvarchar. as varchar is a base type of nvarchar, we need to define this function explictly. +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.varchar, rightarg sys.nvarchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.varchar, + RIGHTARG = sys.nvarchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.nvarchar, rightarg sys.varchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.nvarchar, + RIGHTARG = sys.varchar, + 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/upgrades/babelfishpg_common--1.1.0--1.2.0.sql b/contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.1.0--1.2.0.sql new file mode 100644 index 00000000000..39b87bf8f51 --- /dev/null +++ b/contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.1.0--1.2.0.sql @@ -0,0 +1,1319 @@ +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION ""babelfishpg_common"" UPDATE TO '1.2.0'" to load this file. \quit + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +CREATE OR REPLACE FUNCTION sys.byteavarbinary(pg_catalog.BYTEA, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'byteavarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.BYTEA AS sys.BBF_VARBINARY) +WITH FUNCTION sys.byteavarbinary(pg_catalog.BYTEA, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinarybytea(sys.BBF_VARBINARY, integer, boolean) +RETURNS pg_catalog.BYTEA +AS 'babelfishpg_common', 'byteavarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY AS pg_catalog.BYTEA) +WITH FUNCTION sys.varbinarybytea(sys.BBF_VARBINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bbfvarbinary(sys.BBF_VARBINARY, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- typmod cast for sys.BBF_VARBINARY +CREATE CAST (sys.BBF_VARBINARY AS sys.BBF_VARBINARY) +WITH FUNCTION sys.bbfvarbinary(sys.BBF_VARBINARY, integer, BOOLEAN) AS ASSIGNMENT; + +CREATE TYPE sys.ROWVERSION; + +CREATE OR REPLACE FUNCTION sys.rowversionin(cstring, oid, integer) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryin' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.rowversionout(sys.ROWVERSION) +RETURNS cstring +AS 'babelfishpg_common', 'varbinaryout' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.rowversionrecv(internal, oid, integer) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryrecv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.rowversionsend(sys.ROWVERSION) +RETURNS bytea +AS 'babelfishpg_common', 'varbinarysend' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE sys.ROWVERSION ( + INPUT = sys.rowversionin, + OUTPUT = sys.rowversionout, + RECEIVE = sys.rowversionrecv, + SEND = sys.rowversionsend, + INTERNALLENGTH = 12, + ALIGNMENT = 'int4', + STORAGE = 'plain', + CATEGORY = 'U', + PREFERRED = false, + COLLATABLE = false +); + +-- casting functions for sys.ROWVERSION + +CREATE OR REPLACE FUNCTION sys.binaryrowversion(sys.BBF_BINARY, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_BINARY AS sys.ROWVERSION) +WITH FUNCTION sys.binaryrowversion (sys.BBF_BINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinaryrowversion(sys.BBF_VARBINARY, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varbinaryrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY AS sys.ROWVERSION) +WITH FUNCTION sys.varbinaryrowversion (sys.BBF_VARBINARY, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionbinary(sys.ROWVERSION, integer, boolean) +RETURNS sys.BBF_BINARY +AS 'babelfishpg_common', 'rowversionbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS sys.BBF_BINARY) +WITH FUNCTION sys.rowversionbinary (sys.ROWVERSION, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionvarbinary(sys.ROWVERSION, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'rowversionvarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS sys.BBF_VARBINARY) +WITH FUNCTION sys.rowversionvarbinary (sys.ROWVERSION, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varcharrowversion(sys.VARCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.VARCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.varcharrowversion (sys.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varcharrowversion(pg_catalog.VARCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'varcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.VARCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.varcharrowversion (pg_catalog.VARCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharrowversion(pg_catalog.BPCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'bpcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.BPCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.bpcharrowversion (pg_catalog.BPCHAR, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.bpcharrowversion(sys.BPCHAR, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'bpcharrowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BPCHAR AS sys.ROWVERSION) +WITH FUNCTION sys.bpcharrowversion (sys.BPCHAR, integer, boolean) AS ASSIGNMENT; + + +CREATE OR REPLACE FUNCTION sys.rowversionsysvarchar(sys.ROWVERSION) +RETURNS sys.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS sys.VARCHAR) +WITH FUNCTION sys.rowversionsysvarchar (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionvarchar(sys.ROWVERSION) +RETURNS pg_catalog.VARCHAR +AS 'babelfishpg_common', 'varbinaryvarchar' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION AS pg_catalog.VARCHAR) +WITH FUNCTION sys.rowversionvarchar (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int2rowversion(INT2, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int2rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT2 AS sys.ROWVERSION) +WITH FUNCTION sys.int2rowversion (INT2, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.int4rowversion(INT4, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int4rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT4 AS sys.ROWVERSION) +WITH FUNCTION sys.int4rowversion (INT4, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.xid8rowversion(XID8, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int8rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (XID8 AS sys.ROWVERSION) +WITH FUNCTION sys.xid8rowversion (XID8, integer, boolean) AS IMPLICIT; + +CREATE OR REPLACE FUNCTION sys.int8rowversion(INT8, integer, boolean) +RETURNS sys.ROWVERSION +AS 'babelfishpg_common', 'int8rowversion' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (INT8 AS sys.ROWVERSION) +WITH FUNCTION sys.int8rowversion (INT8, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionint2(sys.ROWVERSION) +RETURNS INT2 +AS 'babelfishpg_common', 'binaryint2' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION as INT2) +WITH FUNCTION sys.rowversionint2 (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionint4(sys.ROWVERSION) +RETURNS INT4 +AS 'babelfishpg_common', 'binaryint4' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION as INT4) +WITH FUNCTION sys.rowversionint4 (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.rowversionint8(sys.ROWVERSION) +RETURNS INT8 +AS 'babelfishpg_common', 'binaryint8' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.ROWVERSION as INT8) +WITH FUNCTION sys.rowversionint8 (sys.ROWVERSION) AS ASSIGNMENT; + +CREATE DOMAIN sys.TIMESTAMP AS sys.ROWVERSION; + +CREATE FUNCTION sys.rowversion_eq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_eq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.= ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_eq, + COMMUTATOR = = +); + + +CREATE FUNCTION sys.rowversion_neq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_neq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<> ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_neq, + COMMUTATOR = <> +); + +CREATE FUNCTION sys.rowversion_gt(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_gt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.> ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_gt, + COMMUTATOR = < +); + +CREATE FUNCTION sys.rowversion_geq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_geq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.>= ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_geq, + COMMUTATOR = <= +); + +CREATE FUNCTION sys.rowversion_lt(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_lt' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.< ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_lt, + COMMUTATOR = > +); + +CREATE FUNCTION sys.rowversion_leq(leftarg sys.rowversion, rightarg sys.rowversion) +RETURNS boolean +AS 'babelfishpg_common', 'varbinary_leq' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.<= ( + LEFTARG = sys.rowversion, + RIGHTARG = sys.rowversion, + FUNCTION = sys.rowversion_leq, + COMMUTATOR = >= +); + +CREATE FUNCTION sys.rowversion_cmp(sys.rowversion, sys.rowversion) +RETURNS int +AS 'babelfishpg_common', 'varbinary_cmp' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS sys.rowversion_ops +DEFAULT FOR TYPE sys.rowversion USING btree AS + OPERATOR 1 < (sys.rowversion, sys.rowversion), + OPERATOR 2 <= (sys.rowversion, sys.rowversion), + OPERATOR 3 = (sys.rowversion, sys.rowversion), + OPERATOR 4 >= (sys.rowversion, sys.rowversion), + OPERATOR 5 > (sys.rowversion, sys.rowversion), + FUNCTION 1 sys.rowversion_cmp(sys.rowversion, sys.rowversion); + + +CREATE OR REPLACE FUNCTION sys.int8fixeddecimaldiv_money(INT8, FIXEDDECIMAL) +RETURNS sys.MONEY +AS $$ + SELECT sys.int8fixeddecimaldiv($1, $2)::sys.MONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.int4fixeddecimaldiv_money(INT4, FIXEDDECIMAL) +RETURNS sys.MONEY +AS $$ + SELECT sys.int4fixeddecimaldiv($1, $2)::sys.MONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.int2fixeddecimaldiv_money(INT2, FIXEDDECIMAL) +RETURNS sys.MONEY +AS $$ + SELECT sys.int2fixeddecimaldiv($1, $2)::sys.MONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +-- DROP OPERATOR can't work if other DB objects already refer to the operator. +-- We keep the operator as it is for minor version upgrade. +-- It will be fixed in major version upgrade by pg_dump and pg_restore. + +--DROP OPERATOR IF EXISTS sys./ (INT8, FIXEDDECIMAL); + +--CREATE OPERATOR sys./ ( +-- LEFTARG = INT8, +-- RIGHTARG = FIXEDDECIMAL, +-- PROCEDURE = int8fixeddecimaldiv_money +--); + +--DROP OPERATOR IF EXISTS sys./ (INT4, FIXEDDECIMAL); + +--CREATE OPERATOR sys./ ( +-- LEFTARG = INT4, +-- RIGHTARG = FIXEDDECIMAL, +-- PROCEDURE = int4fixeddecimaldiv_money +--); + +--DROP OPERATOR IF EXISTS sys./ (INT2, FIXEDDECIMAL); + +--CREATE OPERATOR sys./ ( +-- LEFTARG = INT2, +-- RIGHTARG = FIXEDDECIMAL, +-- PROCEDURE = int2fixeddecimaldiv_money +--); + +CREATE FUNCTION sys.fixeddecimalum(sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalum' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalpl(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalmi(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalmul(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimaldiv(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = fixeddecimalmi +); + +CREATE OPERATOR sys.- ( + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = fixeddecimalum +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = fixeddecimaldiv +); + +CREATE FUNCTION sys.fixeddecimalint8pl(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8mi(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8mul(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint8div(sys.SMALLMONEY, INT8) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint8div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint8pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + PROCEDURE = fixeddecimalint8mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint8mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT8, + PROCEDURE = fixeddecimalint8div +); + +CREATE FUNCTION sys.fixeddecimalint4pl(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4mi(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4mul(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint4div(sys.SMALLMONEY, INT4) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint4div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint4pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint4mul +); + + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT4, + PROCEDURE = fixeddecimalint4div +); + +CREATE FUNCTION sys.fixeddecimalint2pl(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2pl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2mi(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2mi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2mul(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2mul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.fixeddecimalint2div(sys.SMALLMONEY, INT2) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalint2div' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + COMMUTATOR = +, + PROCEDURE = fixeddecimalint2pl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2mi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + COMMUTATOR = *, + PROCEDURE = fixeddecimalint2mul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = INT2, + PROCEDURE = fixeddecimalint2div +); + + +CREATE FUNCTION sys.int8fixeddecimalpl(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int8fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimalmi(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int8fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimalmul(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int8fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimaldiv(INT8, sys.SMALLMONEY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int8fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int8fixeddecimaldiv_smallmoney(INT8, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int8fixeddecimaldiv($1, $2)::sys.SMALLMONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = int8fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int8fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = int8fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT8, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int8fixeddecimaldiv_smallmoney +); + +CREATE FUNCTION sys.int4fixeddecimalpl(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int4fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimalmi(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int4fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimalmul(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int4fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimaldiv(INT4, sys.SMALLMONEY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int4fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int4fixeddecimaldiv_smallmoney(INT4, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int4fixeddecimaldiv($1, $2)::sys.SMALLMONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + + +CREATE OPERATOR sys.+ ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = int4fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int4fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = int4fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT4, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int4fixeddecimaldiv_smallmoney +); + +CREATE FUNCTION sys.int2fixeddecimalpl(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int2fixeddecimalpl' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimalmi(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int2fixeddecimalmi' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimalmul(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'int2fixeddecimalmul' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimaldiv(INT2, sys.SMALLMONEY) +RETURNS DOUBLE PRECISION +AS 'babelfishpg_money', 'int2fixeddecimaldiv' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.int2fixeddecimaldiv_smallmoney(INT2, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimaldiv($1, $2)::sys.SMALLMONEY; +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = int2fixeddecimalpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int2fixeddecimalmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = int2fixeddecimalmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = INT2, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = int2fixeddecimaldiv_smallmoney +); + + +-- tinyint operator definitions to force return type to tinyyint + +CREATE FUNCTION sys.tinyintum(sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2um($1)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintpl(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2pl($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintmi(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2mi($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintmul(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2mul($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintdiv(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS $$ + SELECT int2div($1,$2)::sys.TINYINT; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + COMMUTATOR = +, + PROCEDURE = sys.tinyintpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.tinyintmi +); + +CREATE OPERATOR sys.- ( + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.tinyintum +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + COMMUTATOR = *, + PROCEDURE = sys.tinyintmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.tinyintdiv +); + +CREATE FUNCTION sys.smallmoneytinyintpl(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2pl($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneytinyintmi(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2mi($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneytinyintmul(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2mul($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneytinyintdiv(sys.SMALLMONEY, sys.TINYINT) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.fixeddecimalint2div($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + COMMUTATOR = +, + PROCEDURE = sys.smallmoneytinyintpl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.smallmoneytinyintmi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + COMMUTATOR = *, + PROCEDURE = sys.smallmoneytinyintmul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.SMALLMONEY, + RIGHTARG = sys.TINYINT, + PROCEDURE = sys.smallmoneytinyintdiv +); + +CREATE FUNCTION sys.tinyintsmallmoneypl(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimalpl($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintsmallmoneymi(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimalmi($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintsmallmoneymul(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimalmul($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.tinyintsmallmoneydiv(sys.TINYINT, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS $$ + SELECT sys.int2fixeddecimaldiv($1,$2)::sys.SMALLMONEY; +$$ +LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR sys.+ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = +, + PROCEDURE = sys.tinyintsmallmoneypl +); + +CREATE OPERATOR sys.- ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = sys.tinyintsmallmoneymi +); + +CREATE OPERATOR sys.* ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + COMMUTATOR = *, + PROCEDURE = sys.tinyintsmallmoneymul +); + +CREATE OPERATOR sys./ ( + LEFTARG = sys.TINYINT, + RIGHTARG = sys.SMALLMONEY, + PROCEDURE = sys.tinyintsmallmoneydiv +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper_outer(leftarg text, rightarg text) RETURNS sys.varchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- DROP OPERATOR can't work if other DB objects already refer to the operator. +-- We keep the operator as it is for minor version upgrade. +-- It will be fixed in major version upgrade by pg_dump and pg_restore. + +--DROP OPERATOR IF EXISTS sys.+(text, text); + +--CREATE OPERATOR sys.+ ( +-- LEFTARG = text, +-- RIGHTARG = text, +-- FUNCTION = sys.babelfish_concat_wrapper_outer +--); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.varchar, rightarg sys.varchar) RETURNS sys.varchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.varchar, + RIGHTARG = sys.varchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.nvarchar, rightarg sys.nvarchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.nvarchar, + RIGHTARG = sys.nvarchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.bpchar, rightarg sys.bpchar) RETURNS sys.varchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.bpchar, + RIGHTARG = sys.bpchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.nchar, rightarg sys.nchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.nchar, + RIGHTARG = sys.nchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +-- if one of input is nvarchar, resolve it as nvarchar. as varchar is a base type of nvarchar, we need to define this function explictly. +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.varchar, rightarg sys.nvarchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.varchar, + RIGHTARG = sys.nvarchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg sys.nvarchar, rightarg sys.varchar) RETURNS sys.nvarchar(8000) AS +$$ + SELECT sys.babelfish_concat_wrapper(cast(leftarg as text), cast(rightarg as text)) +$$ +LANGUAGE SQL VOLATILE; + +-- Support strings for + operator. +CREATE OPERATOR sys.+ ( + LEFTARG = sys.nvarchar, + RIGHTARG = sys.varchar, + FUNCTION = sys.babelfish_concat_wrapper +); + +CREATE OR REPLACE FUNCTION sys.varchar_larger(sys.VARCHAR, sys.VARCHAR) +RETURNS sys.VARCHAR +AS 'text_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varchar_smaller(sys.VARCHAR, sys.VARCHAR) +RETURNS sys.VARCHAR +AS 'text_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.VARCHAR) +( + sfunc = sys.varchar_larger, + stype = sys.varchar, + combinefunc = sys.varchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.VARCHAR) +( + sfunc = sys.varchar_smaller, + stype = sys.varchar, + combinefunc = sys.varchar_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.nvarchar_larger(sys.NVARCHAR, sys.NVARCHAR) +RETURNS sys.NVARCHAR +AS 'text_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.nvarchar_smaller(sys.NVARCHAR, sys.NVARCHAR) +RETURNS sys.NVARCHAR +AS 'text_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.NVARCHAR) +( + sfunc = sys.nvarchar_larger, + stype = sys.nvarchar, + combinefunc = sys.nvarchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.NVARCHAR) +( + sfunc = sys.nvarchar_smaller, + stype = sys.nvarchar, + combinefunc = sys.nvarchar_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.bpchar_larger(sys.BPCHAR, sys.BPCHAR) +RETURNS sys.BPCHAR +AS 'bpchar_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.bpchar_smaller(sys.BPCHAR, sys.BPCHAR) +RETURNS sys.BPCHAR +AS 'bpchar_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.BPCHAR) +( + sfunc = sys.bpchar_larger, + stype = sys.bpchar, + combinefunc = sys.bpchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.BPCHAR) +( + sfunc = sys.bpchar_smaller, + stype = sys.bpchar, + combinefunc = sys.bpchar_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.nchar_larger(sys.NCHAR, sys.NCHAR) +RETURNS sys.NCHAR +AS 'bpchar_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.nchar_smaller(sys.NCHAR, sys.NCHAR) +RETURNS sys.NCHAR +AS 'bpchar_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.NCHAR) +( + sfunc = sys.nchar_larger, + stype = sys.nchar, + combinefunc = sys.nchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.NCHAR) +( + sfunc = sys.nchar_smaller, + stype = sys.nchar, + combinefunc = sys.nchar_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.tinyint_larger(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS 'int2larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.tinyint_smaller(sys.TINYINT, sys.TINYINT) +RETURNS sys.TINYINT +AS 'int2smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.TINYINT) +( + sfunc = sys.tinyint_larger, + stype = sys.tinyint, + combinefunc = sys.tinyint_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.TINYINT) +( + sfunc = sys.tinyint_smaller, + stype = sys.tinyint, + combinefunc = sys.tinyint_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.real_larger(sys.REAL, sys.REAL) +RETURNS sys.REAL +AS 'float4larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.real_smaller(sys.REAL, sys.REAL) +RETURNS sys.REAL +AS 'float4smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.REAL) +( + sfunc = sys.real_larger, + stype = sys.real, + combinefunc = sys.real_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.REAL) +( + sfunc = sys.real_smaller, + stype = sys.real, + combinefunc = sys.real_smaller, + parallel = safe +); + +CREATE FUNCTION sys.smallmoneylarger(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimallarger' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION sys.smallmoneysmaller(sys.SMALLMONEY, sys.SMALLMONEY) +RETURNS sys.SMALLMONEY +AS 'babelfishpg_money', 'fixeddecimalsmaller' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE AGGREGATE sys.min(sys.smallmoney) ( + SFUNC = sys.smallmoneysmaller, + STYPE = sys.smallmoney, + COMBINEFUNC = sys.smallmoneysmaller, + PARALLEL = SAFE +); + +CREATE AGGREGATE sys.max(sys.smallmoney) ( + SFUNC = sys.smallmoneylarger, + STYPE = sys.smallmoney, + COMBINEFUNC = sys.smallmoneylarger, + PARALLEL = SAFE +); + +CREATE OR REPLACE FUNCTION sys.datetime_larger(sys.DATETIME, sys.DATETIME) +RETURNS sys.DATETIME +AS 'timestamp_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime_smaller(sys.DATETIME, sys.DATETIME) +RETURNS sys.DATETIME +AS 'timestamp_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.DATETIME) +( + sfunc = sys.datetime_larger, + stype = sys.datetime, + combinefunc = sys.datetime_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.DATETIME) +( + sfunc = sys.datetime_smaller, + stype = sys.datetime, + combinefunc = sys.datetime_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.datetime2_larger(sys.DATETIME2, sys.DATETIME2) +RETURNS sys.DATETIME2 +AS 'timestamp_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetime2_smaller(sys.DATETIME2, sys.DATETIME2) +RETURNS sys.DATETIME2 +AS 'timestamp_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.DATETIME2) +( + sfunc = sys.datetime2_larger, + stype = sys.datetime2, + combinefunc = sys.datetime2_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.DATETIME2) +( + sfunc = sys.datetime2_smaller, + stype = sys.datetime2, + combinefunc = sys.datetime2_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.datetimeoffset_larger(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_larger' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.datetimeoffset_smaller(sys.DATETIMEOFFSET, sys.DATETIMEOFFSET) +RETURNS sys.DATETIMEOFFSET +AS 'babelfishpg_common', 'datetimeoffset_smaller' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.DATETIMEOFFSET) +( + sfunc = sys.datetimeoffset_larger, + stype = sys.datetimeoffset, + combinefunc = sys.datetimeoffset_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.DATETIMEOFFSET) +( + sfunc = sys.datetimeoffset_smaller, + stype = sys.datetimeoffset, + combinefunc = sys.datetimeoffset_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.smalldatetime_larger(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS sys.SMALLDATETIME +AS 'timestamp_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.smalldatetime_smaller(sys.SMALLDATETIME, sys.SMALLDATETIME) +RETURNS sys.SMALLDATETIME +AS 'timestamp_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.SMALLDATETIME) +( + sfunc = sys.smalldatetime_larger, + stype = sys.smalldatetime, + combinefunc = sys.smalldatetime_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.SMALLDATETIME) +( + sfunc = sys.smalldatetime_smaller, + stype = sys.smalldatetime, + combinefunc = sys.smalldatetime_smaller, + parallel = safe +); + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_max(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for max operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_min(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for min operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_sum(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for sum operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.bit_unsupported_avg(IN b1 sys.BIT, IN b2 sys.BIT) +RETURNS sys.BIT +AS $$ +BEGIN + RAISE EXCEPTION 'Operand data type bit is invalid for avg operator.'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.BIT) +( + sfunc = sys.bit_unsupported_max, + stype = sys.bit, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.BIT) +( + sfunc = sys.bit_unsupported_min, + stype = sys.bit, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.sum(sys.BIT) +( + sfunc = sys.bit_unsupported_sum, + stype = sys.bit, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.avg(sys.BIT) +( + sfunc = sys.bit_unsupported_avg, + stype = sys.bit, + parallel = safe +); + + + +CREATE OR REPLACE FUNCTION sys.translate_pg_type_to_tsql(pgoid oid) RETURNS TEXT +AS 'babelfishpg_common', 'translate_pg_type_to_tsql' +LANGUAGE C PARALLEL SAFE IMMUTABLE; + +-- 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_common/sql/upgrades/babelfishpg_common--1.2.0--1.2.1.sql b/contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.2.0--1.2.1.sql new file mode 100644 index 00000000000..95bcf521971 --- /dev/null +++ b/contrib/babelfishpg_common/sql/upgrades/babelfishpg_common--1.2.0--1.2.1.sql @@ -0,0 +1,3 @@ +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION ""babelfishpg_common"" UPDATE TO '1.2.1'" to load this file. \quit + \ 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..1cde5ae3c85 --- /dev/null +++ b/contrib/babelfishpg_common/sql/utils.sql @@ -0,0 +1,20 @@ +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; + +CREATE OR REPLACE FUNCTION sys.translate_pg_type_to_tsql(pgoid oid) RETURNS TEXT +AS 'babelfishpg_common', 'translate_pg_type_to_tsql' +LANGUAGE C PARALLEL SAFE IMMUTABLE; \ No newline at end of file diff --git a/contrib/babelfishpg_common/sql/varbinary.sql b/contrib/babelfishpg_common/sql/varbinary.sql new file mode 100644 index 00000000000..136785e053f --- /dev/null +++ b/contrib/babelfishpg_common/sql/varbinary.sql @@ -0,0 +1,303 @@ +-- 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.bbfvarbinary(sys.BBF_VARBINARY, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'varbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- typmod cast for sys.BBF_VARBINARY +CREATE CAST (sys.BBF_VARBINARY AS sys.BBF_VARBINARY) +WITH FUNCTION sys.bbfvarbinary(sys.BBF_VARBINARY, integer, BOOLEAN) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.byteavarbinary(pg_catalog.BYTEA, integer, boolean) +RETURNS sys.BBF_VARBINARY +AS 'babelfishpg_common', 'byteavarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (pg_catalog.BYTEA AS sys.BBF_VARBINARY) +WITH FUNCTION sys.byteavarbinary(pg_catalog.BYTEA, integer, boolean) AS ASSIGNMENT; + +CREATE OR REPLACE FUNCTION sys.varbinarybytea(sys.BBF_VARBINARY, integer, boolean) +RETURNS pg_catalog.BYTEA +AS 'babelfishpg_common', 'byteavarbinary' +LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE CAST (sys.BBF_VARBINARY AS pg_catalog.BYTEA) +WITH FUNCTION sys.varbinarybytea(sys.BBF_VARBINARY, integer, boolean) AS ASSIGNMENT; + +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..75e402159bf --- /dev/null +++ b/contrib/babelfishpg_common/sql/varchar.sql @@ -0,0 +1,295 @@ +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; + +CREATE OR REPLACE FUNCTION sys.varchar_larger(sys.VARCHAR, sys.VARCHAR) +RETURNS sys.VARCHAR +AS 'text_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.varchar_smaller(sys.VARCHAR, sys.VARCHAR) +RETURNS sys.VARCHAR +AS 'text_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.VARCHAR) +( + sfunc = sys.varchar_larger, + stype = sys.varchar, + combinefunc = sys.varchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.VARCHAR) +( + sfunc = sys.varchar_smaller, + stype = sys.varchar, + combinefunc = sys.varchar_smaller, + parallel = safe +); + +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'; + +CREATE OR REPLACE FUNCTION sys.nvarchar_larger(sys.NVARCHAR, sys.NVARCHAR) +RETURNS sys.NVARCHAR +AS 'text_larger' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.nvarchar_smaller(sys.NVARCHAR, sys.NVARCHAR) +RETURNS sys.NVARCHAR +AS 'text_smaller' +LANGUAGE INTERNAL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE AGGREGATE sys.max(sys.NVARCHAR) +( + sfunc = sys.nvarchar_larger, + stype = sys.nvarchar, + combinefunc = sys.nvarchar_larger, + parallel = safe +); + +CREATE OR REPLACE AGGREGATE sys.min(sys.NVARCHAR) +( + sfunc = sys.nvarchar_smaller, + stype = sys.nvarchar, + combinefunc = sys.nvarchar_smaller, + parallel = safe +); 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..6e79f890d4a --- /dev/null +++ b/contrib/babelfishpg_common/src/datetimeoffset.c @@ -0,0 +1,803 @@ +/*------------------------------------------------------------------------- + * + * 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_larger); +PG_FUNCTION_INFO_V1(datetimeoffset_smaller); + +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)); +} + +Datum +datetimeoffset_smaller(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + tsql_datetimeoffset *result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + + if (datetimeoffset_cmp_internal(df1, df2) < 0) + *result = *df1; + else + *result = *df2; + PG_RETURN_DATETIMEOFFSET(result); +} + +Datum +datetimeoffset_larger(PG_FUNCTION_ARGS) +{ + tsql_datetimeoffset *df1 = PG_GETARG_DATETIMEOFFSET(0); + tsql_datetimeoffset *df2 = PG_GETARG_DATETIMEOFFSET(1); + tsql_datetimeoffset *result = (tsql_datetimeoffset *) palloc(DATETIMEOFFSET_LEN); + + if (datetimeoffset_cmp_internal(df1, df2) > 0) + *result = *df1; + else + *result = *df2; + PG_RETURN_DATETIMEOFFSET(result); +} + +/* 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..8169b434dba --- /dev/null +++ b/contrib/babelfishpg_common/src/sqlvariant.c @@ -0,0 +1,2382 @@ +/*------------------------------------------------------------------------- + * + * 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_DECIMAL 106 +#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 + +/* + * macros to store length of metadata (including metadata for base type) for sqlvariant datatypes. + */ +#define VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES 2 /* for BIT, TINYINT, SMALLINT, INT, BIGINT, REAL, FLOAT, [SMALL]MONEY and UID */ +#define VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES 9 /* for [N][VAR]CHAR */ +#define VARIANT_TYPE_METALEN_FOR_BIN_DATATYPES 4 /* for [VAR]BINARY */ +#define VARIANT_TYPE_METALEN_FOR_NUMERIC_DATATYPES 5 /* for NUMERIC */ +#define VARIANT_TYPE_METALEN_FOR_DATE 2 /* for DATE */ +#define VARIANT_TYPE_METALEN_FOR_SMALLDATETIME 2 /* for SMALLDATETIME */ +#define VARIANT_TYPE_METALEN_FOR_DATETIME 2 /* for DATETIME */ +#define VARIANT_TYPE_METALEN_FOR_TIME 3 /* for TIME */ +#define VARIANT_TYPE_METALEN_FOR_DATETIME2 3 /* for DATETIME2 */ +#define VARIANT_TYPE_METALEN_FOR_DATETIMEOFFSET 3 /* for DATETIMEOFFSET */ + + +/* 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: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = BIT_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_TINYINT: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = TINYINT_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_SMALLINT: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = SMALLINT_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_INT: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = INT_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_BIGINT: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = BIGINT_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_REAL: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = REAL_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_FLOAT: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = FLOAT_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_CHAR: + /* + * dataformat: totalLen(4B) + metadata(9B)( baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) ) + data(dataLen) + */ + *pgBaseType = CHAR_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES; + break; + case VARIANT_TYPE_NCHAR: + *pgBaseType = NCHAR_T; + /* + * dataformat: totalLen(4B) + metadata(9B)( baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) ) + data(dataLen) + * Data is in UTF16 format. + */ + *dataLen = (tempLen - VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES) / 2; + break; + case VARIANT_TYPE_VARCHAR: + /* + * dataformat: totalLen(4B) + metadata(9B)( baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) ) + data(dataLen) + */ + *pgBaseType = VARCHAR_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES; + break; + case VARIANT_TYPE_NVARCHAR: + *pgBaseType = NVARCHAR_T; + /* + * dataformat: totalLen(4B) + metadata(9B)( baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) ) + data(dataLen) + * Data is in UTF16 format. + */ + *dataLen = (tempLen - VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES) / 2; + break; + case VARIANT_TYPE_BINARY: + /* + * dataformat : totalLen(4B) + metadata(4B)( baseType(1B) + metadatalen(1B) + + * dataLen(2B) ) + data(dataLen) + */ + *pgBaseType = BINARY_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_BIN_DATATYPES; + break; + case VARIANT_TYPE_VARBINARY: + /* + * dataformat : totalLen(4B) + metadata(4B)( baseType(1B) + metadatalen(1B) + + * dataLen(2B) ) + data(dataLen) + */ + *pgBaseType = VARBINARY_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_BIN_DATATYPES; + break; + case VARIANT_TYPE_DATE: + /* + * dataformat : totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(3B) + */ + *pgBaseType = DATE_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_DATE; + break; + case VARIANT_TYPE_TIME: + /* + * dataformat : totalLen(4B) + metadata(3B)( baseType(1B) + metadatalen(1B) + + * scale(1B) ) + data(3B-5B) + */ + *pgBaseType = TIME_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_TIME; + break; + case VARIANT_TYPE_SMALLDATETIME: + /* + * dataformat : totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(4B) + */ + *pgBaseType = SMALLDATETIME_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_SMALLDATETIME; + break; + case VARIANT_TYPE_DATETIME: + /* + * dataformat : totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(8B) + */ + *pgBaseType = DATETIME_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_DATETIME; + break; + case VARIANT_TYPE_DATETIME2: + /* + * dataformat : totalLen(4B) + metadata(3B)( baseType(1B) + metadatalen(1B) + + * scale(1B) ) + data(6B-8B) + */ + *pgBaseType = DATETIME2_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_DATETIME2; + break; + case VARIANT_TYPE_UNIQUEIDENTIFIER: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = UNIQUEIDENTIFIER_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_NUMERIC: + case VARIANT_TYPE_DECIMAL: + /* + * dataformat : totalLen(4B) + metdata(5B)( baseType(1B) + metadatalen(1B) + + * precision(1B) + scale(1B) + sign(1B) ) + data(dataLen) + */ + *pgBaseType = NUMERIC_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUMERIC_DATATYPES; + break; + case VARIANT_TYPE_MONEY: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = MONEY_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_SMALLMONEY: + /* + * dataformat: totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(dataLen) + */ + *pgBaseType = SMALLMONEY_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + break; + case VARIANT_TYPE_DATETIMEOFFSET: + /* + * dataformat : totalLen(4B) + metadata(3B)(baseType(1B) + metadatalen(1B) + + * scale(1B)) + data(8B-10B) + */ + *pgBaseType = DATETIMEOFFSET_T; + *dataLen = tempLen - VARIANT_TYPE_METALEN_FOR_DATETIMEOFFSET; + break; + default: + elog(ERROR, "0x%02X : datatype as basetype for SQL_VARIANT is not supported", 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) +{ + if (pgBaseType == TIME_T || pgBaseType == DATETIME2_T || + pgBaseType == DATETIMEOFFSET_T) + { + /* For datatypes having sql_variant specific header of length 2 bytes */ + svhdr_2B_t *svhdr2; + svhdr2 = SV_HDR_2B(result); + SV_SET_METADATA(svhdr2, pgBaseType, HDR_VER); + svhdr2->typmod = scale; + } + else if (pgBaseType == NUMERIC_T) + { + /* For datatypes having sql_variant specific header of length 3 bytes */ + svhdr_3B_t *svhdr3; + svhdr3 = SV_HDR_3B(result); + SV_SET_METADATA(svhdr3, pgBaseType, HDR_VER); + svhdr3->typmod = (precision << 8) | scale; + } + else if (pgBaseType == BINARY_T || pgBaseType == VARBINARY_T || + pgBaseType == CHAR_T || pgBaseType == NCHAR_T || + pgBaseType == VARCHAR_T || pgBaseType == NVARCHAR_T) + { + /* For datatypes having sql_variant specific header of length 5 bytes */ + svhdr_5B_t *svhdr5; + svhdr5 = SV_HDR_5B(result); + SV_SET_METADATA(svhdr5, pgBaseType, HDR_VER); + svhdr5->typmod = (int16)maxLen; + } + else + { + /* For all other fixed-length datatypes */ + svhdr_2B_t *svhdr2; + svhdr2 = SV_HDR_2B(result); + SV_SET_METADATA(svhdr2, pgBaseType, HDR_VER); + } +} + +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) >> 8; + } + 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..5f3fd5f4552 --- /dev/null +++ b/contrib/babelfishpg_common/src/typecode.c @@ -0,0 +1,337 @@ +#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, 1, "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}, + {0, 0, "text" , "text" , 5, 25, 5}, + {0, 1, "ntext" , "ntext" , 5, 26, 5}, + {0, 1, "image" , "image" , 5, 27, 5}, + {0, 0, "xml" , "xml" , 5, 28, 5}, + {0, 0, "bpchar" , "char" , 5, 29, 5}, + {0, 1, "decimal" , "decimal" , 5, 30, 5}, + {0, 1, "sysname" , "sysname" , 5, 31, 5}, + {0, 1, "rowversion" , "timestamp" , 8, 32, 3}, + {0, 1, "timestamp" , "timestamp" , 8, 33, 3} +}; + +/* 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(); +} + +PG_FUNCTION_INFO_V1(translate_pg_type_to_tsql); + +Datum +translate_pg_type_to_tsql(PG_FUNCTION_ARGS) +{ + Oid pg_type = PG_GETARG_OID(0); + ht_oid2typecode_entry_t *entry; + + if (OidIsValid(pg_type)) + { + entry = hash_search(ht_oid2typecode, &pg_type, HASH_FIND, NULL); + + if (entry && entry->persist_id < TOTAL_TYPECODE_COUNT) + PG_RETURN_TEXT_P(CStringGetTextDatum(type_infos[entry->persist_id].tsql_typname)); + } + 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 tsql_rowversion_oid = InvalidOid; +Oid tsql_timestamp_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; +} + +bool +is_tsql_rowversion_datatype(Oid oid) +{ + if (tsql_rowversion_oid == InvalidOid) + tsql_rowversion_oid = lookup_tsql_datatype_oid("rowversion"); + return tsql_rowversion_oid == oid; +} + +bool +is_tsql_timestamp_datatype(Oid oid) +{ + if (tsql_timestamp_oid == InvalidOid) + tsql_timestamp_oid = lookup_tsql_datatype_oid("timestamp"); + return tsql_timestamp_oid == oid; +} + +bool +is_tsql_rowversion_or_timestamp_datatype(Oid oid) +{ + return (is_tsql_rowversion_datatype(oid) || is_tsql_timestamp_datatype(oid)); +} + diff --git a/contrib/babelfishpg_common/src/typecode.h b/contrib/babelfishpg_common/src/typecode.h new file mode 100644 index 00000000000..5b922e3f07d --- /dev/null +++ b/contrib/babelfishpg_common/src/typecode.h @@ -0,0 +1,89 @@ +#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 33 + +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 tsql_rowversion_oid; +extern Oid tsql_timestamp_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); +extern bool is_tsql_rowversion_datatype(Oid oid); +extern bool is_tsql_timestamp_datatype(Oid oid); +extern bool is_tsql_rowversion_or_timestamp_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..43b37ae86dc --- /dev/null +++ b/contrib/babelfishpg_common/src/varbinary.c @@ -0,0 +1,1421 @@ +/*------------------------------------------------------------------------- + * + * 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(byteavarbinary); +PG_FUNCTION_INFO_V1(varbinarybytea); +PG_FUNCTION_INFO_V1(varbinaryrowversion); +PG_FUNCTION_INFO_V1(rowversionbinary); +PG_FUNCTION_INFO_V1(rowversionvarbinary); +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(varcharrowversion); +PG_FUNCTION_INFO_V1(bpcharrowversion); +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(int2rowversion); +PG_FUNCTION_INFO_V1(int4rowversion); +PG_FUNCTION_INFO_V1(int8rowversion); +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 +#define ROWVERSION_SIZE 8 + +/* + * 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 +byteavarbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + + PG_RETURN_BYTEA_P(source); +} + +Datum +varbinarybytea(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + + PG_RETURN_BYTEA_P(source); +} + +Datum +varbinaryrowversion(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + bytea *result; + char *data = VARDATA_ANY(source); + size_t len = VARSIZE_ANY_EXHDR(source); + char *rp; + + if (len > ROWVERSION_SIZE) + len = ROWVERSION_SIZE; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +rowversionbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + int32 typmod = PG_GETARG_INT32(1); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 maxlen; + bytea *result; + + /* 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 *) palloc0(maxlen + VARHDRSZ); + SET_VARSIZE(result, maxlen + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(source); +} + +Datum +rowversionvarbinary(PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + int32 typmod = PG_GETARG_INT32(1); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + int32 maxlen; + bytea *result; + + /* 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(source); +} + +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 +varcharrowversion(PG_FUNCTION_ARGS) +{ + VarChar *source = PG_GETARG_VARCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + bool isExplicit = PG_GETARG_BOOL(2); + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type varchar to " + "rowversion is not allowed. Use the CONVERT function " + "to run this query."))); + + if (len > ROWVERSION_SIZE) + len = ROWVERSION_SIZE; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +bpcharrowversion(PG_FUNCTION_ARGS) +{ + BpChar *source = PG_GETARG_BPCHAR_PP(0); + char *data = VARDATA_ANY(source); + char *rp; + size_t len = VARSIZE_ANY_EXHDR(source); + bool isExplicit = PG_GETARG_BOOL(2); + bytea *result; + + if (!isExplicit) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("Implicit conversion from data type bpchar to " + "rowversion is not allowed. Use the CONVERT function " + "to run this query."))); + + if (len > ROWVERSION_SIZE) + len = ROWVERSION_SIZE; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + memcpy(rp, data, 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 +int2rowversion(PG_FUNCTION_ARGS) +{ + int16 input = PG_GETARG_INT16(0); + int len = sizeof(int16); + bytea *result; + char *rp; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in T-SQL */ + reverse_memcpy(rp+ROWVERSION_SIZE-len, (char *) &input , len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int4rowversion(PG_FUNCTION_ARGS) +{ + int32 input = PG_GETARG_INT32(0); + int len = sizeof(int32); + bytea *result; + char *rp; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in T-SQL */ + reverse_memcpy(rp+ROWVERSION_SIZE-len, (char *) &input , len); + + PG_RETURN_BYTEA_P(result); +} + +Datum +int8rowversion(PG_FUNCTION_ARGS) +{ + int64 input = PG_GETARG_INT64(0); + int len = sizeof(int64); + bytea *result; + char *rp; + + result = (bytea *) palloc0(ROWVERSION_SIZE + VARHDRSZ); + SET_VARSIZE(result, ROWVERSION_SIZE + VARHDRSZ); + + rp = VARDATA(result); + /* Need reverse copy because endianness is different in T-SQL */ + reverse_memcpy(rp, (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)); +} + + +PG_FUNCTION_INFO_V1(varbinary_length); + +Datum +varbinary_length (PG_FUNCTION_ARGS) +{ + bytea *source = PG_GETARG_BYTEA_PP(0); + int32 limit = VARSIZE_ANY_EXHDR(source); + PG_RETURN_INT32(limit); +} 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..2dcd6921d28 --- /dev/null +++ b/contrib/babelfishpg_money/Makefile @@ -0,0 +1,41 @@ +MODULE_big = babelfishpg_money +OBJS = fixeddecimal.o + +EXTENSION = babelfishpg_money + +#subdir = contrib/babelfishpg_money + +DATA = fixeddecimal--1.0.0--1.1.0.sql +DATA_built = babelfishpg_money--1.1.0.sql + +#include ../Makefile.common + +CFLAGS = `$(PG_CONFIG) --includedir-server` + +TESTS = $(wildcard test/sql/*.sql) + +REGRESS_BRIN := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "XL 9\.[5-9]| 10\.0| 11\.[0-9]| 12\.[0-9]" && echo brin-xl) +REGRESS_BRIN += $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -E "9\.[5-9]| 10\.0| 11\.[0-9]| 12\.[0-9]" | grep -qEv "XL" && echo brin) +REGRESS_VERSION_SPECIFIC := $(shell LD_LIBRARY_PATH=$(LD_LIBRARY_PATH) $(PG_CONFIG) --version | grep -qE "XL" && echo index-xl || echo index) +REGRESS = $(shell echo aggregate cast comparison overflow $(REGRESS_BRIN) $(REGRESS_VERSION_SPECIFIC)) + +REGRESS_OPTS = --inputdir=test --outputdir=test --load-extension=babelfishpg_money + +#PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) + +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..2457a797c1b --- /dev/null +++ b/contrib/babelfishpg_money/fixeddecimal.c @@ -0,0 +1,3039 @@ +/*------------------------------------------------------------------------- + * + * 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. + * For first digit, apply "Round half away from zero" + * XXX These are ignored, should we error instead? + */ + if (isdigit((unsigned char) *ptr) && (unsigned char) *ptr >= '5') + { + fractionalpart++; + ptr++, vscale++; + } + + 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..9ccf744b97e --- /dev/null +++ b/contrib/babelfishpg_tds/Makefile @@ -0,0 +1,39 @@ +# contrib/babelfishpg_tds/Makefile +MODULE_big = babelfishpg_tds +EXTENSION = babelfishpg_tds +DATA = babelfishpg_tds--1.0.0.sql +PGFILEDESC = "babelfishpg_tds - TDS Listener Extension" +#REGRESS = babelfishpg_tds + +tds_top_dir = . +tds_backend = $(tds_top_dir)/src/backend +tds_include = $(tds_top_dir)/src/include +TSQL_SRC = ../babelfishpg_tsql + +PG_CPPFLAGS += -I$(TSQL_SRC) -I$(PG_SRC) -I$(tds_top_dir) -DFAULT_INJECTOR + +# Exclude the following files from the build (sometimes these +# files are included in another c file) +tds_exclude_files = $(tds_backend)/tds/support_funcs.c \ + $(tds_backend)/tds/tds_data_map.c \ + $(tds_backend)/tds/tdsprinttup.c + +tds_temp_srcs = $(shell find $(tds_top_dir) -name "*.c") +tds_srcs = $(filter-out $(tds_exclude_files), $(tds_temp_srcs)) + +OBJS = $(patsubst %.c, %.o, $(tds_srcs)) +OBJS += $(WIN32RES) + +$(tds_include)/error_mapping.h: error_mapping.txt generate_error_mapping.pl + $(PERL) generate_error_mapping.pl $< > $@ +$(tds_backend)/tds/err_handler.o: $(tds_include)/error_mapping.h + +# Disable for now +#NO_PGXS = 1 + +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) + +#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..4c0b9938d90 --- /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 sys.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..6b750378a56 --- /dev/null +++ b/contrib/babelfishpg_tds/error_mapping.txt @@ -0,0 +1,175 @@ +# +# 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 +42883 ERRCODE_UNDEFINED_FUNCTION "%s %s expects parameter \"%s\", which was not supplied." SQL_ERROR_201 16 +42883 ERRCODE_UNDEFINED_FUNCTION "%s %s has no parameters and arguments were supplied." SQL_ERROR_8146 16 +42883 ERRCODE_UNDEFINED_FUNCTION "%s %s has too many arguments specified." SQL_ERROR_8144 16 +42883 ERRCODE_UNDEFINED_FUNCTION "\"%s\" is not an parameter for %s %s." SQL_ERROR_8145 16 +42883 ERRCODE_UNDEFINED_FUNCTION "The %s %s is found but cannot be used. Possibly due to datatype mismatch and implicit casting is not allowed." SQL_ERROR_206 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 +42P13 ERRCODE_INVALID_FUNCTION_DEFINITION "PL/tsql functions cannot return type %s" SQL_ERROR_2733 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..a23cb6e1574 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/err_handler.c @@ -0,0 +1,337 @@ +#include + +#include "postgres.h" + +#include "common/hashfn.h" +#include "miscadmin.h" +#include "nodes/bitmapset.h" +#include "pgstat.h" +#include "storage/proc.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; + } + + if (MyProc != NULL) + { + error_lineno = 1; + get_tsql_error_details(edata, &tsql_error_code, &tsql_error_sev, &tsql_error_state, "TDS"); + 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); + } + else + { + /* We are not in position to load the error mapping hash table. */ + error_lineno = 0; + tsql_error_code = ERRCODE_PLTSQL_ERROR_NOT_MAPPED; + tsql_error_sev = 16; + tsql_error_state = 1; + } + + 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..f9c6cb69a67 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/guc.c @@ -0,0 +1,292 @@ +/*------------------------------------------------------------------------- + * + * 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; +#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); + +/* 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..24cdb399157 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds.c @@ -0,0 +1,800 @@ + +/*------------------------------------------------------------------------- + * + * 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 "funcapi.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 "pgstat.h" +#include "libpq/libpq.h" +#include "libpq/libpq-be.h" +#include "miscadmin.h" +#include "parser/parse_expr.h" +#include "postmaster/postmaster.h" +#include "storage/backendid.h" +#include "storage/ipc.h" +#include "storage/lwlock.h" +#include "storage/shmem.h" +#include "storage/sinvaladt.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/elog.h" +#include "utils/pidfile.h" +#include "utils/lsyscache.h" + +#include "src/include/err_handler.h" + + +/* ---------- + * Total number of backends including auxiliary + * + * We reserve a slot for each possible BackendId, plus one for each + * possible auxiliary process type. (This scheme assumes there is not + * more than one of any auxiliary process type at a time.) MaxBackends + * includes autovacuum workers and background workers as well. + * ---------- + */ +#define NumBackendStatSlots (MaxBackends + NUM_AUXPROCTYPES) + +#define LIBDATALEN 32 +#define LANGDATALEN 128 + +PG_MODULE_MAGIC; + +/* Shmem state */ +typedef struct TdsStatus +{ + /* + * To avoid locking overhead, we use the following protocol: a backend + * increments st_changecount before modifying its entry, and again after + * finishing a modification. A would-be reader should note the value of + * st_changecount, copy the entry into private memory, then check + * st_changecount again. If the value hasn't changed, and if it's even, + * the copy is valid; otherwise start over. This makes updates cheap + * while reads are potentially expensive, but that's the tradeoff we want. + * + * The above protocol needs memory barriers to ensure that the apparent + * order of execution is as it desires. Otherwise, for example, the CPU + * might rearrange the code so that st_changecount is incremented twice + * before the modification on a machine with weak memory ordering. Hence, + * use the macros defined below for manipulating st_changecount, rather + * than touching it directly. + */ + int st_changecount; + + /* The entry is valid iff st_procpid > 0, unused if st_procpid == 0 */ + int st_procpid; + + /* Add more TDS info */ + uint32_t client_version; + + bool quoted_identifier; + bool arithabort; + bool ansi_null_dflt_on; + bool ansi_defaults; + bool ansi_warnings; + bool ansi_padding; + bool ansi_nulls; + bool concat_null_yields_null; + int textsize; + int datefirst; + int lock_timeout; + int transaction_isolation; + + char *st_library_name; /* Library */ + char *st_language; /* Language */ + + uint32_t client_pid; + + uint64 rowcount; + int error; + int trancount; + + uint32_t protocol_version; + uint32_t packet_size; + int encrypt_option; + + int16 database_id; +} TdsStatus; + +typedef struct LocalTdsStatus +{ + /* + * Local version of the tds status entry. + */ + TdsStatus tdsStatus; + + /* + * The xid of the current transaction if available, InvalidTransactionId + * if not. + */ + TransactionId backend_xid; + + /* + * The xmin of the current session if available, InvalidTransactionId if + * not. + */ + TransactionId backend_xmin; +} LocalTdsStatus; + +static TdsStatus *TdsStatusArray = NULL; +static TdsStatus *MyTdsStatusEntry; +static LocalTdsStatus *localTdsStatusTable = NULL; + +uint32_t MyTdsClientVersion = 0; +uint32_t MyTdsClientPid = -1; +char *MyTdsLibraryName = NULL; +uint32_t MyTdsProtocolVersion = TDS_DEFAULT_VERSION; +uint32_t MyTdsPacketSize = 0; +int MyTdsEncryptOption = TDS_ENCRYPT_OFF; +static char *TdsLibraryNameBuffer = NULL; +static char *TdsLanguageBuffer = NULL; + +static int localNumBackends = 0; +static bool isLocalStatusTableValid = false; + +TdsInstrPlugin **tds_instr_plugin_ptr = NULL; + +extern void _PG_init(void); +extern void _PG_fini(void); + +/* 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; + +/* Shmem hook */ +static shmem_startup_hook_type next_shmem_startup_hook = NULL; + +/* Shmem init interfaces */ +static void tds_status_shmem_startup(void); +static void tds_stats_shmem_shutdown(int code, Datum arg); + +static void tdsstat_read_current_status(void); +static LocalTdsStatus * tdsstat_fetch_stat_local_tdsentry (int beid); + +/* + * 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; + + /* Hooks */ + next_shmem_startup_hook = shmem_startup_hook; + shmem_startup_hook = tds_status_shmem_startup; + + inited = true; +} + +/* + * Module unload function + */ +void +_PG_fini(void) +{ + pe_fin(); + relname_lookup_hook = prev_relname_lookup_hook; +} + +static Size +TdsStatusArraySize() +{ + return mul_size(sizeof(TdsStatus), NumBackendStatSlots); +} + +static Size +TdsLibraryNameBufferSize() +{ + return mul_size(LIBDATALEN, NumBackendStatSlots); +} + +static Size +TdsLanguageBufferSize() +{ + return mul_size(LANGDATALEN, NumBackendStatSlots); +} + +/* + * tds_status_shmem_startup hook: allocate or attach to shared memory, + * the TDS status array and string buffers + */ +static void +tds_status_shmem_startup(void) +{ + bool found; + char *buffer; + + /* + * Create or attach to the shared memory state + */ + LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); + + TdsStatusArray = (TdsStatus *) ShmemInitStruct("TDS Status Array", + TdsStatusArraySize(), + &found); + if (!found) + { + /* + * We're the first - initialize. + */ + MemSet(TdsStatusArray, 0, TdsStatusArraySize()); + } + + Assert(TdsStatusArray != NULL); + + /* Create or attach to the shared TDS library name buffer */ + TdsLibraryNameBuffer = (char *) + ShmemInitStruct("TDS library name buffer", TdsLibraryNameBufferSize(), &found); + + if (!found) + { + int i; + + MemSet(TdsLibraryNameBuffer, 0, TdsLibraryNameBufferSize()); + + /* Initialize st_library_name pointers. */ + buffer = TdsLibraryNameBuffer; + for (i = 0; i < MaxBackends; i++) + { + TdsStatusArray[i].st_library_name = buffer; + buffer += LIBDATALEN; + } + } + + /* Create or attach to the shared TDS language buffer */ + TdsLanguageBuffer = (char *) + ShmemInitStruct("TDS language buffer", TdsLanguageBufferSize(), &found); + + if (!found) + { + int i; + + MemSet(TdsLanguageBuffer, 0, TdsLanguageBufferSize()); + + /* Initialize st_language pointers. */ + buffer = TdsLanguageBuffer; + for (i = 0; i < MaxBackends; i++) + { + TdsStatusArray[i].st_language = buffer; + buffer += LANGDATALEN; + } + } + + LWLockRelease(AddinShmemInitLock); + + /* If we're in the postmaster (or a standalone backend...), set up a shmem + * exit hook to persist the dirty outlines + */ + if (!IsUnderPostmaster) + on_shmem_exit(tds_stats_shmem_shutdown, (Datum) 0); + + if (next_shmem_startup_hook) + next_shmem_startup_hook(); + + return; +} + +/* + * tds_status_shmem_shutdown hook: if we want to persist any data + * across database restarts, write additional logic here. No-op + * for now. + */ +static void +tds_stats_shmem_shutdown(int code, Datum arg) +{ + /* Don't try to save the outlines during a crash. */ + if (code) + return; + + /* Safety check ... shouldn't get here unless shmem is set up. */ + if (TdsStatusArray == NULL) + return; + + return; +} + +/* ---------- + * tdsstat_initialize() - + * + * Initialize tdsstats state, and set up our on-proc-exit hook. + * ---------- + */ +void +tdsstat_initialize(void) +{ + /* Initialize MyTdsStatusEntry */ + Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends); + MyTdsStatusEntry = &TdsStatusArray[MyBackendId - 1]; + + /* Set up a process-exit hook to clean up */ + on_shmem_exit(tds_stats_shmem_shutdown, 0); +} + +void +tdsstat_bestart(void) +{ + volatile TdsStatus *vtdsentry = MyTdsStatusEntry; + TdsStatus ltdsentry; + + int len; + char *library_name = NULL; + const char *language = NULL; + + /* + * To minimize the time spent modifying the TdsStatus entry, and + * avoid risk of errors inside the critical section, we first copy the + * shared-memory struct to a local variable, then modify the data in the + * local variable, then copy the local variable back to shared memory. + * Only the last step has to be inside the critical section. + * + * Most of the data we copy from shared memory is just going to be + * overwritten, but the struct's not so large that it's worth the + * maintenance hassle to copy only the needful fields. + */ + memcpy(<dsentry, + unvolatize(TdsStatus *, vtdsentry), + sizeof(TdsStatus)); + + + ltdsentry.st_procpid = MyProcPid; + ltdsentry.client_version = MyTdsClientVersion; + ltdsentry.client_pid = MyTdsClientPid; + ltdsentry.protocol_version = MyTdsProtocolVersion; + ltdsentry.packet_size = MyTdsPacketSize; + + /* Set the boot GUC values */ + ltdsentry.quoted_identifier = strcmp(GetConfigOption("babelfishpg_tsql.quoted_identifier", true, true), "on") == 0 ? true : false; + ltdsentry.arithabort = strcmp(GetConfigOption("babelfishpg_tsql.arithabort", true, true), "on") == 0 ? true : false; + ltdsentry.ansi_null_dflt_on = strcmp(GetConfigOption("babelfishpg_tsql.ansi_null_dflt_on", true, true), "on") == 0 ? true : false; + ltdsentry.ansi_defaults = strcmp(GetConfigOption("babelfishpg_tsql.ansi_defaults", true, true), "on") == 0 ? true : false; + ltdsentry.ansi_warnings = strcmp(GetConfigOption("babelfishpg_tsql.ansi_warnings", true, true), "on") == 0 ? true : false; + ltdsentry.ansi_padding = strcmp(GetConfigOption("babelfishpg_tsql.ansi_padding", true, true), "on") == 0 ? true : false; + ltdsentry.ansi_nulls = strcmp(GetConfigOption("babelfishpg_tsql.ansi_nulls", true, true), "on") == 0 ? true : false; + ltdsentry.concat_null_yields_null = strcmp(GetConfigOption("babelfishpg_tsql.concat_null_yields_null", true, true), "on") == 0 ? true : false; + ltdsentry.textsize = atoi(GetConfigOption("babelfishpg_tsql.textsize", true, true)); + ltdsentry.datefirst = atoi(GetConfigOption("babelfishpg_tsql.datefirst", true, true)); + ltdsentry.lock_timeout = atoi(GetConfigOption("lock_timeout", true, true)); + ltdsentry.transaction_isolation = atoi(GetConfigOption("default_transaction_isolation", true, true)); + + language = GetConfigOption("babelfishpg_tsql.language", true, true); + + if (language != NULL) + { + len = pg_mbcliplen(language, strlen(language), LANGDATALEN - 1); + memcpy((char *) ltdsentry.st_language, language, len); + ltdsentry.st_language[len] = '\0'; + } + + library_name = MyTdsLibraryName; + + if (library_name != NULL) + { + len = pg_mbcliplen(library_name, strlen(library_name), LIBDATALEN - 1); + memcpy((char *) ltdsentry.st_library_name, library_name, len); + ltdsentry.st_library_name[len] = '\0'; + } + + ltdsentry.encrypt_option = MyTdsEncryptOption; + ltdsentry.database_id = 0; + + /* + * We're ready to enter the critical section that fills the shared-memory + * status entry. We follow the protocol of bumping st_changecount before + * and after; and make sure it's even afterwards. We use a volatile + * pointer here to ensure the compiler doesn't try to get cute. + */ + PGSTAT_BEGIN_WRITE_ACTIVITY(vtdsentry); + + /* make sure we'll memcpy the same st_changecount back */ + ltdsentry.st_changecount = vtdsentry->st_changecount; + + memcpy(unvolatize(TdsStatus *, vtdsentry), + <dsentry, + sizeof(TdsStatus)); + + PGSTAT_END_WRITE_ACTIVITY(vtdsentry); +} + +static LocalTdsStatus * +tdsstat_fetch_stat_local_tdsentry (int beid) +{ + LocalTdsStatus *localentry; + + tdsstat_read_current_status(); + + if (beid < 1 || beid > localNumBackends) + return NULL; + + localentry = &localTdsStatusTable[beid - 1]; + + if (localentry->tdsStatus.st_procpid <= 0) + return NULL; + + return localentry; +} + +/* ---------- + * tdsstat_read_current_status() - + * + * Copy the current contents of the TdsStatus array to local memory, + * if not already done in this transaction. + * ---------- + */ +static void +tdsstat_read_current_status(void) +{ + volatile TdsStatus *tdsentry; + LocalTdsStatus *localtable; + LocalTdsStatus *localentry; + int i; + + if (isLocalStatusTableValid) + return; /* already done */ + + /* + * Allocate storage for local copy of state data. + */ + localtable = (LocalTdsStatus *) + palloc(sizeof(LocalTdsStatus) * NumBackendStatSlots); + + localNumBackends = 0; + + tdsentry = TdsStatusArray; + localentry = localtable; + + for (i = 1; i <= NumBackendStatSlots; i++) + { + /* + * Follow the protocol of retrying if st_changecount changes while we + * copy the entry, or if it's odd. (The check for odd is needed to + * cover the case where we are able to completely copy the entry while + * the source backend is between increment steps.) We use a volatile + * pointer here to ensure the compiler doesn't try to get cute. + */ + for (;;) + { + int before_changecount; + int after_changecount; + + pgstat_begin_read_activity(tdsentry, before_changecount); + + localentry->tdsStatus.st_procpid = tdsentry->st_procpid; + + /* Skip all the data-copying work if entry is not in use */ + if (localentry->tdsStatus.st_procpid > 0) + { + memcpy(&localentry->tdsStatus, unvolatize(TdsStatus *, tdsentry), sizeof(TdsStatus)); + + if (tdsentry->client_version) + localentry->tdsStatus.client_version = tdsentry->client_version; + + if (tdsentry->st_library_name) + localentry->tdsStatus.st_library_name = tdsentry->st_library_name; + + if (tdsentry->st_language) + localentry->tdsStatus.st_language = tdsentry->st_language; + + if (tdsentry->quoted_identifier) + localentry->tdsStatus.quoted_identifier = tdsentry->quoted_identifier; + + if (tdsentry->arithabort) + localentry->tdsStatus.arithabort = tdsentry->arithabort; + + if (tdsentry->ansi_null_dflt_on) + localentry->tdsStatus.ansi_null_dflt_on = tdsentry->ansi_null_dflt_on; + + if (tdsentry->ansi_defaults) + localentry->tdsStatus.ansi_defaults = tdsentry->ansi_defaults; + + if (tdsentry->ansi_warnings) + localentry->tdsStatus.ansi_warnings = tdsentry->ansi_warnings; + + if (tdsentry->ansi_padding) + localentry->tdsStatus.ansi_padding = tdsentry->ansi_padding; + + if (tdsentry->ansi_nulls) + localentry->tdsStatus.ansi_nulls = tdsentry->ansi_nulls; + + if (tdsentry->concat_null_yields_null) + localentry->tdsStatus.concat_null_yields_null = tdsentry->concat_null_yields_null; + + if (tdsentry->textsize) + localentry->tdsStatus.textsize = tdsentry->textsize; + + if (tdsentry->datefirst) + localentry->tdsStatus.datefirst = tdsentry->datefirst; + + if (tdsentry->lock_timeout) + localentry->tdsStatus.lock_timeout = tdsentry->lock_timeout; + + if (tdsentry->transaction_isolation) + localentry->tdsStatus.transaction_isolation = tdsentry->transaction_isolation; + + if (tdsentry->client_pid) + localentry->tdsStatus.client_pid = tdsentry->client_pid; + + if (tdsentry->rowcount) + localentry->tdsStatus.rowcount = tdsentry->rowcount; + + if (tdsentry->error) + localentry->tdsStatus.error = tdsentry->error; + + if (tdsentry->trancount) + localentry->tdsStatus.trancount = tdsentry->trancount; + + if (tdsentry->protocol_version) + localentry->tdsStatus.protocol_version = tdsentry->protocol_version; + + if (tdsentry->packet_size) + localentry->tdsStatus.packet_size = tdsentry->packet_size; + + if (tdsentry->encrypt_option) + localentry->tdsStatus.encrypt_option = tdsentry->encrypt_option; + + if (tdsentry->database_id) + localentry->tdsStatus.database_id = tdsentry->database_id; + } + + pgstat_end_read_activity(tdsentry, after_changecount); + + if (pgstat_read_activity_complete(before_changecount, after_changecount)) + break; + + /* Make sure we can break out of loop if stuck... */ + CHECK_FOR_INTERRUPTS(); + } + + tdsentry++; + + /* Only valid entries get included into the local array */ + if (localentry->tdsStatus.st_procpid > 0) + BackendIdGetTransactionIds(i, &localentry->backend_xid, &localentry->backend_xmin); + + localentry++; + localNumBackends++; + } + + localTdsStatusTable = localtable; + isLocalStatusTableValid = true; +} + +bool +tds_stat_get_activity(Datum *values, bool *nulls, int len, int pid, int curr_backend) +{ + LocalTdsStatus *local_tdsentry; + TdsStatus *tdsentry; + + MemSet(values, 0, len); + MemSet(nulls, false, len); + + /* Get the next one in the list */ + local_tdsentry = tdsstat_fetch_stat_local_tdsentry(curr_backend); + if (!local_tdsentry) + return false; + + tdsentry = &local_tdsentry->tdsStatus; + + /* If looking for specific PID, ignore all the others */ + if (pid != -1 && tdsentry->st_procpid != pid) + return false; + + values[0] = Int32GetDatum(tdsentry->st_procpid); + + /* TDS Client Version must be valid */ + if (tdsentry->client_version != 0) + values[1] = Int32GetDatum(tdsentry->client_version); + + /* Library name must be valid */ + if(tdsentry->st_library_name) + values[2] = CStringGetTextDatum(tdsentry->st_library_name); + else + nulls[2] = true; + + /* Language must be valid */ + if(tdsentry->st_language) + values[3] = CStringGetTextDatum(tdsentry->st_language); + else + nulls[3] = true; + + values[4] = BoolGetDatum(tdsentry->quoted_identifier); + values[5] = BoolGetDatum(tdsentry->arithabort); + values[6] = BoolGetDatum(tdsentry->ansi_null_dflt_on); + values[7] = BoolGetDatum(tdsentry->ansi_defaults); + values[8] = BoolGetDatum(tdsentry->ansi_warnings); + values[9] = BoolGetDatum(tdsentry->ansi_padding); + values[10] = BoolGetDatum(tdsentry->ansi_nulls); + values[11] = BoolGetDatum(tdsentry->concat_null_yields_null); + values[12] = Int32GetDatum(tdsentry->textsize); + values[13] = Int32GetDatum(tdsentry->datefirst); + values[14] = Int32GetDatum(tdsentry->lock_timeout); + + /* + * In postgres, transaction isolation level mapping is as follows: + * XACT_READ_UNCOMMITTED 0 + * XACT_READ_COMMITTED 1 + * XACT_REPEATABLE_READ 2 + * XACT_SERIALIZABLE 3 + * + * In T-SQL, transaction isolation level mapping is as follows: + * XACT_READ_UNCOMMITTED 1 + * XACT_READ_COMMITTED 2 + * XACT_REPEATABLE_READ 3 + * XACT_SERIALIZABLE 4 + * + * So adding 1 while storing value in tuples + */ + values[15] = Int16GetDatum(tdsentry->transaction_isolation + 1); + + /* Client PID must be valid */ + if (tdsentry->client_pid != 0) + values[16] = Int32GetDatum(tdsentry->client_pid); + else + nulls[16] = true; + + values[17] = Int64GetDatum(tdsentry->rowcount); + values[18] = Int32GetDatum(tdsentry->error); + values[19] = Int32GetDatum(tdsentry->trancount); + + /* ValidateLoginRequest() already checks if protocol version is valid or not */ + values[20] = Int32GetDatum(tdsentry->protocol_version); + + /* ValidateLoginRequest() already checks if packet size is valid or not */ + values[21] = Int32GetDatum(tdsentry->packet_size); + + values[22] = CStringGetTextDatum(tdsentry->encrypt_option == 1 ? "TRUE" : "FALSE"); + values[23] = Int16GetDatum(tdsentry->database_id); + + return true; +} + +void +TdsSetGucStatVariable(const char *guc, bool boolVal, const char *strVal, int intVal) +{ + volatile TdsStatus *vtdsentry = MyTdsStatusEntry; + int len; + + PGSTAT_BEGIN_WRITE_ACTIVITY(vtdsentry); + + if (strcmp(guc, "babelfishpg_tsql.language") == 0) + { + len = pg_mbcliplen(strVal, strlen(strVal), LANGDATALEN - 1); + memcpy((char *) vtdsentry->st_language, strVal, len); + vtdsentry->st_language[len] = '\0'; + } + else if (strcmp(guc, "babelfishpg_tsql.quoted_identifier") == 0) + vtdsentry->quoted_identifier = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.arithabort") == 0) + vtdsentry->arithabort = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.ansi_null_dflt_on") == 0) + vtdsentry->ansi_null_dflt_on = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.ansi_defaults") == 0) + vtdsentry->ansi_defaults = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.ansi_warnings") == 0) + vtdsentry->ansi_warnings = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.ansi_padding") == 0) + vtdsentry->ansi_padding = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.ansi_nulls") == 0) + vtdsentry->ansi_nulls = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.concat_null_yields_null") == 0) + vtdsentry->concat_null_yields_null = boolVal; + else if (strcmp(guc, "babelfishpg_tsql.textsize") == 0) + vtdsentry->textsize = intVal; + else if (strcmp(guc, "babelfishpg_tsql.datefirst") == 0) + vtdsentry->datefirst = intVal; + else if (strcmp(guc, "lock_timeout") == 0) + vtdsentry->lock_timeout = intVal; + else if (strcmp(guc, "default_transaction_isolation") == 0) + vtdsentry->transaction_isolation = intVal; + + PGSTAT_END_WRITE_ACTIVITY(vtdsentry); +} + +void +TdsSetAtAtStatVariable(const char *at_at_var, int intVal, uint64 bigintVal) +{ + volatile TdsStatus *vtdsentry = MyTdsStatusEntry; + + PGSTAT_BEGIN_WRITE_ACTIVITY(vtdsentry); + + if (strcmp(at_at_var, "rowcount") == 0) + vtdsentry->rowcount = bigintVal; + else if (strcmp(at_at_var, "error") == 0) + vtdsentry->error = intVal; + else if (strcmp(at_at_var, "trancount") == 0) + vtdsentry->trancount = intVal; + + PGSTAT_END_WRITE_ACTIVITY(vtdsentry); +} + +void +TdsSetDatabaseStatVariable(int16 db_id) +{ + volatile TdsStatus *vtdsentry = MyTdsStatusEntry; + PGSTAT_BEGIN_WRITE_ACTIVITY(vtdsentry); + + vtdsentry->database_id = db_id; + + PGSTAT_END_WRITE_ACTIVITY(vtdsentry); +} + +/* + * 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; +} + +void +invalidate_stat_table(void) +{ + isLocalStatusTableValid = false; +} \ No newline at end of file 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..a3a296eac31 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds_data_map.c @@ -0,0 +1,225 @@ +#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", "bbf_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}, + {"sys", "rowversion", TDS_TYPE_BINARY, 8, 2, TDS_SEND_BINARY, TDS_RECV_BINARY}, + {"sys", "timestamp", TDS_TYPE_BINARY, 8, 2, TDS_SEND_BINARY, TDS_RECV_BINARY}, + + /* 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}, + {"pg_catalog", "bytea", TDS_TYPE_VARBINARY, -1, 2, TDS_SEND_VARBINARY, 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..50040e6275a --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tds_srv.c @@ -0,0 +1,492 @@ +/*------------------------------------------------------------------------- + * + * 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; + pltsql_plugin_handler_ptr->set_guc_stat_var = &TdsSetGucStatVariable; + pltsql_plugin_handler_ptr->set_at_at_stat_var = &TdsSetAtAtStatVariable; + pltsql_plugin_handler_ptr->set_db_stat_var = &TdsSetDatabaseStatVariable; + pltsql_plugin_handler_ptr->get_stat_values = &tds_stat_get_activity; + pltsql_plugin_handler_ptr->invalidate_stat_view = &invalidate_stat_table; + + invalidate_stat_table_hook = invalidate_stat_table; + guc_newval_hook = TdsSetGucStatVariable; + + /* 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) +{ + + /* Initialize the TDS backend status array in shmem */ + tdsstat_initialize(); + + /* 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; + + /* Initialize the TDS backend information */ + tdsstat_bestart(); + + /* 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..ade1deefc42 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdslogin.c @@ -0,0 +1,2345 @@ +/*------------------------------------------------------------------------- + * + * 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; + } + + MyTdsEncryptOption = *loadEncryption; + 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); + MyTdsLibraryName = request->library; + 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 = 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 not "none" then + * database name specified in login request is overridden by + * "babelfish_pgtsql.database_name" + */ + if (gucDatabaseName != NULL && strcmp(gucDatabaseName, "none") != 0) + 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); + + MyTdsClientVersion = loginInfo->clientProVersion; + MyTdsClientPid = loginInfo->clientPid; + MyTdsProtocolVersion = loginInfo->tdsVersion; + MyTdsPacketSize = loginInfo->packetSize; + + 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]; + char *useDbCommand = NULL; + MemoryContext oldContext; + 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))); + } + + oldContext = CurrentMemoryContext; + + 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))); + + /* Any delimitated/quoted db name identifier requested in login must be already handled before this point. */ + useDbCommand = psprintf("USE [%s]", request->database); + } + else + { + char *temp = NULL; + + StartTransactionCommand(); + temp = pltsql_plugin_handler_ptr->pltsql_get_login_default_db(port->user_name); + MemoryContextSwitchTo(oldContext); + + if (temp == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("could not find default database for user \"%s\"", port->user_name))); + + useDbCommand = psprintf("USE [%s]", temp); + CommitTransactionCommand(); + MemoryContextSwitchTo(oldContext); + } + + /* + * Request has a database name provided, so we execute + * a "USE []" through pgtsql inline handler + */ + StartTransactionCommand(); + ExecuteSQLBatch(useDbCommand); + CommitTransactionCommand(); + if (useDbCommand) + pfree(useDbCommand); + + /* + * 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..d3d4952e209 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsprotocol.c @@ -0,0 +1,713 @@ +/*------------------------------------------------------------------------- + * + * 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(); + + if (ret != 0) + { + TdsErrorContext->reqType = 0; + TdsErrorContext->err_text = "EOF reached on TDS socket"; + TDS_DEBUG(TDS_DEBUG1, "EOF on TDS socket"); + pfree(message.data); + return NULL; + } + TdsErrorContext->err_text = "Fetching TDS Request"; + 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); + SetConfigOption("babelfishpg_tsql.enable_tsql_information_schema", "true", PGC_USERSET, PGC_S_SESSION); + SetConfigOption("lock_timeout", "0", 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); + SetConfigOption("babelfishpg_tsql.enable_tsql_information_schema", "false", PGC_USERSET, PGC_S_SESSION); + SetConfigOption("lock_timeout", NULL, 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..c0c0164c8d5 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c @@ -0,0 +1,2955 @@ +/*------------------------------------------------------------------------- + * + * 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/syscache.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)) +#define ROWVERSION_SIZE 8 + +/* + * 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 void SetAttributesForColmetada(TdsColumnMetaData *col); + +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); +} + +static +int TdsGetGenericTypmod(Node *expr) +{ + FuncExpr *func; + Oid func_oid = InvalidOid; + int rettypmod = -1; + + if (!expr) + return rettypmod; + func = (FuncExpr *) expr; + + /* + * Look up the return type typmod from a persistent + * store using function oid. + */ + func_oid = func->funcid; + Assert(func_oid != InvalidOid); + + if (func->funcresulttype != VOIDOID) + rettypmod = pltsql_plugin_handler_ptr->pltsql_get_generic_typmod(func_oid, + func->args == NIL ? 0 : func->args->length, func->funcresulttype); + + return rettypmod; +} + +/* + * 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; + } + + SetAttributesForColmetada(col); + + 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: + if (atttypmod == -1 && tle != NULL) + atttypmod = TdsGetGenericTypmod((Node *)tle->expr); + + SetColMetadataForCharTypeHelper(col, TDS_TYPE_CHAR, + att->attcollation, (atttypmod - 4)); + break; + case TDS_SEND_NCHAR: + if (atttypmod == -1 && tle != NULL) + atttypmod = TdsGetGenericTypmod((Node *)tle->expr); + + 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: + /* Explicitly set typemod for rowversion because typmod won't be specified */ + if (finfo->ttmtdstypelen == ROWVERSION_SIZE) + atttypmod = ROWVERSION_SIZE + VARHDRSZ; + /* 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); +} + +/* + * Using the relation id and the attribute number, set the attributes + * required in the TDS Column Metadata from the attributes relation. + */ +static void +SetAttributesForColmetada(TdsColumnMetaData *col) +{ + HeapTuple tp; + Form_pg_attribute att_tup; + + /* Initialise to false if no valid heap tuple is found. */ + col->attNotNull = false; + col->attidentity = false; + col->attgenerated = false; + + /* + * Send the right column-metadata only for FMTONLY Statements. + * FIXME: We need to find a generic solution where we do not rely + * on the catalog for constraint information. + */ + if (pltsql_plugin_handler_ptr && + !(*pltsql_plugin_handler_ptr->pltsql_is_fmtonly_stmt)) + return; + + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(col->relOid), + Int16GetDatum(col->attrNum)); + + if (HeapTupleIsValid(tp)) + { + att_tup = (Form_pg_attribute) GETSTRUCT(tp); + col->attNotNull = att_tup->attnotnull; + if (att_tup->attgenerated != '\0') + col->attgenerated = true; + + if (att_tup->attidentity != '\0') + col->attidentity = true; + + ReleaseSysCache(tp); + } +} 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..f114f0752f3 --- /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]; + + snprintf(cursorHandle, INT32_STRLEN, "%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..7328480a7e8 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c @@ -0,0 +1,3696 @@ +/*------------------------------------------------------------------------- + * + * 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); + +/* + * macros to store length of metadata (including metadata for base type) for sqlvariant datatypes. + */ +#define VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES 2 /* for BIT, TINYINT, SMALLINT, INT, BIGINT, REAL, FLOAT, [SMALL]MONEY and UID */ +#define VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES 9 /* for [N][VAR]CHAR */ +#define VARIANT_TYPE_METALEN_FOR_BIN_DATATYPES 4 /* for [VAR]BINARY */ +#define VARIANT_TYPE_METALEN_FOR_NUMERIC_DATATYPES 5 /* for NUMERIC */ +#define VARIANT_TYPE_METALEN_FOR_DATE 2 /* for DATE */ +#define VARIANT_TYPE_METALEN_FOR_SMALLDATETIME 2 /* for SMALLDATETIME */ +#define VARIANT_TYPE_METALEN_FOR_DATETIME 2 /* for DATETIME */ +#define VARIANT_TYPE_METALEN_FOR_TIME 3 /* for TIME */ +#define VARIANT_TYPE_METALEN_FOR_DATETIME2 3 /* for DATETIME2 */ +#define VARIANT_TYPE_METALEN_FOR_DATETIMEOFFSET 3 /* for DATETIMEOFFSET */ + +/* + * macros to store length of metadata for base type of sqlvariant datatype. + */ +#define VARIANT_TYPE_BASE_METALEN_FOR_NUM_DATATYPES 0 /* for BIT, TINYINT, SMALLINT, INT, BIGINT, REAL, FLOAT, [SMALL]MONEY and UID */ +#define VARIANT_TYPE_BASE_METALEN_FOR_CHAR_DATATYPES 7 /* for [N][VAR]CHAR */ +#define VARIANT_TYPE_BASE_METALEN_FOR_BIN_DATATYPES 2 /* for [VAR]BINARY */ +#define VARIANT_TYPE_BASE_METALEN_FOR_NUMERIC_DATATYPES 2 /* for NUMERIC */ +#define VARIANT_TYPE_BASE_METALEN_FOR_DATE 0 /* for DATE */ +#define VARIANT_TYPE_BASE_METALEN_FOR_SMALLDATETIME 0 /* for SMALLDATETIME */ +#define VARIANT_TYPE_BASE_METALEN_FOR_DATETIME 0 /* for DATETIME */ +#define VARIANT_TYPE_BASE_METALEN_FOR_TIME 1 /* for TIME */ +#define VARIANT_TYPE_BASE_METALEN_FOR_DATETIME2 1 /* for DATETIME2 */ +#define VARIANT_TYPE_BASE_METALEN_FOR_DATETIMEOFFSET 1 /* for DATETIMEOFFSET */ + +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); + + /* + * Header formats: + * + * 3-byte Header (for datetime series types with typmod): + * 1. One byte varlena Header + * 2. One byte type code (5bit) + MD ver (3bit) + * 3. One byte scale + * + * 2-byte Header (for fixed length types without typmod): + * 1. One byte varlena Header + * 2. One byte type code (5bit) + MD ver (3bit) + * + * 4-byte Header (for decimal type): + * 1. One byte varlena Header + * 2. One byte type code (5bit) + MD ver (3bit) + * 3. Two bytes typmod (encoded precision and scale) + * + * Header for binary types: + * 1. 1 or 4 bytes varlena header + * 2. One byte type code ( 5bit ) + MD ver (3bit) + * 3. 2 Bytes max length + * + * Header for string types: + * 1. 1 or 4 bytes varlena Header + * 2. One byte type code (5bit) + MD ver (3bit) + * 3. Two bytes for max length + * 4. Two bytes for collation code + */ + + /* + * If base type is N[VAR]CHAR then we have to use length of data in UTF8 format as datalen. + */ + if (variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_NVARCHAR) + { + /* + * dataformat: totalLen(4B) + metadata(9B)( baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) ) + data(dataLen) + * Data is in UTF16 format. + */ + initStringInfo(&strbuf); + TdsUTF16toUTF8StringInfo(&strbuf, &buf->data[VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES], tempLen - VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES); + dataLen = strbuf.len; + } + + 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 || + variantBaseType == VARIANT_TYPE_DECIMAL || + variantBaseType == VARIANT_TYPE_TIME || + variantBaseType == VARIANT_TYPE_DATETIME2 || + variantBaseType == VARIANT_TYPE_DATETIMEOFFSET) + { + resLen += VARHDRSZ; + } + + /* common varlena header for SQL_VARIANT datatype */ + 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) + { + /* + * dataformat: totalLen(4B) + metadata(9B)( baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) ) + data(dataLen) + * Data is in UTF16 format. + */ + memcpy(VARDATA(READ_DATA(result, variantHeaderLen)), strbuf.data, dataLen); + pfree(strbuf.data); + } + else + { + /* + * dataformat: totalLen(4B) + metadata(9B)( baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) ) + data(dataLen) + */ + memcpy(VARDATA(READ_DATA(result, variantHeaderLen)), &buf->data[VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES], dataLen); + } + } + else if (variantBaseType == VARIANT_TYPE_BINARY || + variantBaseType == VARIANT_TYPE_VARBINARY) + { + /* + * dataformat : totalLen(4B) + metadata(4B)( baseType(1B) + metadatalen(1B) + + * dataLen(2B) ) + data(dataLen) + */ + SET_VARSIZE(READ_DATA(result, variantHeaderLen), VARHDRSZ + dataLen); + memcpy(&maxLen, &buf->data[2], 2); + memcpy(VARDATA(READ_DATA(result, variantHeaderLen)), &buf->data[VARIANT_TYPE_METALEN_FOR_BIN_DATATYPES], dataLen); + } + else if (variantBaseType == VARIANT_TYPE_DATE) + { + /* + * dataformat : totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(3B) + */ + memset(&date, 0, sizeof(date)); + memcpy(&date, &buf->data[VARIANT_TYPE_METALEN_FOR_DATE], 3); + TdsCheckDateValidity(date); + TdsTimeGetDatumFromDays(date, &dateval); + memcpy(READ_DATA(result, variantHeaderLen), &dateval, sizeof(date)); + } + else if (variantBaseType == VARIANT_TYPE_SMALLDATETIME) + { + /* + * dataformat : totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(4B) + */ + memcpy(&numDays, &buf->data[VARIANT_TYPE_METALEN_FOR_SMALLDATETIME], 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) + { + /* + * dataformat : totalLen(4B) + metadata(2B)( baseType(1B) + metadatalen(1B) ) + + * data(8B) + */ + memcpy(&numDays32, &buf->data[VARIANT_TYPE_METALEN_FOR_DATETIME], 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) + { + /* + * dataformat : totalLen(4B) + metadata(3B)( baseType(1B) + metadatalen(1B) + + * scale(1B) ) + data(3B-5B) + */ + 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[VARIANT_TYPE_METALEN_FOR_TIME], 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) + { + /* + * dataformat : totalLen(4B) + metadata(3B)( baseType(1B) + metadatalen(1B) + + * scale(1B) ) + data(6B-8B) + */ + 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[VARIANT_TYPE_METALEN_FOR_DATETIME2], 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) + { + /* + * dataformat : totalLen(4B) + metadata(3B)(baseType(1B) + metadatalen(1B) + + * scale(1B)) + data(8B-10B) + */ + 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 || variantBaseType == VARIANT_TYPE_DECIMAL) + { + /* + * dataformat : totalLen(4B) + metdata(5B)( baseType(1B) + metadatalen(1B) + + * precision(1B) + scale(1B) + sign(1B) ) + data(dataLen) + */ + 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[VARIANT_TYPE_METALEN_FOR_NUMERIC_DATATYPES], dataLen); + num = LEtoh128(n128); + decString = (char *)palloc0(sizeof(char) * 40); + if (num != 0) + Integer2String(num, decString); + else + decString[0] = '0'; + len = strlen(decString); + temp1 = '.'; + if (num != 0) + { + while (tempScale) + { + temp2 = decString[len - tempScale]; + decString[len - tempScale] = temp1; + temp1 = temp2; + tempScale--; + } + decString[len++] = temp1; + } + else + { + decString[len++] = temp1; + while (tempScale) + { + decString[len++] = '0'; + tempScale--; + } + } + + if (sign == 1 && num != 0) + decString++; + res = TdsSetVarFromStrWrapper(decString); + memcpy(READ_DATA(result, variantHeaderLen), (bytea *)DatumGetPointer(res), dataLen); + } + else + { + /* + * For all other fixed length datatypes + */ + memcpy(READ_DATA(result, variantHeaderLen), &buf->data[VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES], 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); + } + else if (variantBaseType == VARIANT_TYPE_UNIQUEIDENTIFIER) + { + /* SWAP to match TSQL behaviour */ + SwapByte(READ_DATA(result, variantHeaderLen), 0, 3); + SwapByte(READ_DATA(result, variantHeaderLen), 1, 2); + SwapByte(READ_DATA(result, variantHeaderLen), 4, 5); + SwapByte(READ_DATA(result, variantHeaderLen), 6, 7); + } + } + + pltsql_plugin_handler_ptr->sqlvariant_set_metadata(result, + pgBaseType, scale, precision, maxLen); + + buf->cursor += tempLen; + + pfree(tdt); + 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, 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; + 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); + + 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 + VARIANT_TYPE_METALEN_FOR_NUM_DATATYPES; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_NUM_DATATYPES); + rc |= TdsPutbytes(buf, dataLen); + } + else if (isBaseChar) + { + /* + * dataformat: totalLen(4B) + baseType(1B) + metadatalen(1B) + + * encodingLen(5B) + dataLen(2B) + data(dataLen) + */ + StringInfoData strbuf; + int actualDataLen = 0; /* Number of bytes that would be needed to store given string in given encoding. */ + char *destBuf = NULL; + dataLen -= VARHDRSZ; + if (variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_NVARCHAR) + { + initStringInfo(&strbuf); + TdsUTF8toUTF16StringInfo(&strbuf, buf + VARHDRSZ, dataLen); + actualDataLen = strbuf.len; + } + else + { + /* + * TODO: [BABEL-1069] Remove collation related hardcoding + * from sql_variant sender for char class basetypes + */ + if (dataLen > 0) + { + destBuf = server_to_any(buf + VARHDRSZ, dataLen, PG_WIN1252); + actualDataLen = strlen(destBuf); + } + else + /* We can not assume that buf would be NULL terminated. */ + actualDataLen = 0; + } + + totalLen = actualDataLen + VARIANT_TYPE_METALEN_FOR_CHAR_DATATYPES; + + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_CHAR_DATATYPES); + /* + * 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(actualDataLen); + + if (variantBaseType == VARIANT_TYPE_NCHAR || + variantBaseType == VARIANT_TYPE_NVARCHAR) + { + rc |= TdsPutbytes(strbuf.data, actualDataLen); + pfree(strbuf.data); + } + else + rc |= TdsPutbytes(destBuf, actualDataLen); + + if (destBuf) + pfree(destBuf); + } + else if (isBaseBin) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * dataLen(2B) + data(dataLen) + */ + dataLen = dataLen - VARHDRSZ; + totalLen = dataLen + VARIANT_TYPE_METALEN_FOR_BIN_DATATYPES; + + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_BIN_DATATYPES); + rc |= TdsPutUInt16LE(maxLen); + rc |= TdsPutbytes(buf + VARHDRSZ, dataLen); + } + else if (isBaseDec) + { + /* + * dataformat : totalLen(4B) + baseType(1B) + metadatalen(1B) + + * precision(1B) + scale(1B) + sign(1B) + data(dataLen) + */ + dataLen = 16; + totalLen = dataLen + VARIANT_TYPE_METALEN_FOR_NUMERIC_DATATYPES; + + 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); + + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_NUMERIC_DATATYPES); + 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 + VARIANT_TYPE_METALEN_FOR_DATE; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_DATE); + 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 + VARIANT_TYPE_METALEN_FOR_SMALLDATETIME; + TdsTimeDifferenceSmalldatetime(timestamp, &numDays16, &numMins); + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_SMALLDATETIME); + 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 + VARIANT_TYPE_METALEN_FOR_DATETIME; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_DATETIME); + 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; + + memcpy(&numMicro, buf, sizeof(numMicro)); + temp = scale; + if (scale == 7 || scale == 0xff) + numMicro *= 10; + + while (temp < 6) + { + numMicro /= 10; + temp++; + } + totalLen = dataLen + VARIANT_TYPE_METALEN_FOR_TIME; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_TIME); + 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); + + totalLen = dataLen + VARIANT_TYPE_METALEN_FOR_DATETIME2; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_DATETIME2); + 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; + + totalLen = dataLen + VARIANT_TYPE_METALEN_FOR_DATETIMEOFFSET; + rc = TdsPutUInt32LE(totalLen); + rc |= TdsPutInt8(variantBaseType); + rc |= TdsPutInt8(VARIANT_TYPE_BASE_METALEN_FOR_DATETIMEOFFSET); + rc |= TdsPutInt8(scale); + rc |= TdsPutbytes(&numMicro, dataLen - 5); + rc |= TdsPutDate(numDays); + rc |= TdsPutInt16LE(timezone); + } + } + + if (vlena) + pfree(vlena); + 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..1fd2d91a8f1 --- /dev/null +++ b/contrib/babelfishpg_tds/src/backend/tds/tdsutils.c @@ -0,0 +1,543 @@ +/*------------------------------------------------------------------------- + * + * 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; + case TDS_BULK_LOAD: /* Bulk Load request */ + { + errcontext("TDS Protocol: Message Type: Bulk Load, 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..9fd95e661cf --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/guc.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * 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 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..37b1882950b --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_int.h @@ -0,0 +1,360 @@ +/*------------------------------------------------------------------------- + * + * 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; + +extern PGDLLIMPORT uint32_t MyTdsClientVersion; +extern PGDLLIMPORT char* MyTdsLibraryName; +extern PGDLLIMPORT uint32_t MyTdsClientPid; +extern PGDLLIMPORT uint32_t MyTdsProtocolVersion; +extern PGDLLIMPORT uint32_t MyTdsPacketSize; +extern PGDLLIMPORT int MyTdsEncryptOption; + +/* 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); + +extern void tdsstat_initialize(void); +extern void tdsstat_bestart(void); +extern void TdsSetGucStatVariable(const char *guc, bool boolVal, const char *strVal, int intVal); +extern void TdsSetAtAtStatVariable(const char *at_at_var, int intVal, uint64 bigintVal); +extern void TdsSetDatabaseStatVariable(int16 db_id); +extern bool tds_stat_get_activity(Datum *values, bool *nulls, int len, int pid, int curr_backend); +extern void invalidate_stat_table(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..7a0bfd7f4fa --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_iofuncmap.h @@ -0,0 +1,160 @@ +/*------------------------------------------------------------------------- + * + * 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_DECIMAL 106 +#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..d1e00d8f5b3 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_request.h @@ -0,0 +1,907 @@ +/*------------------------------------------------------------------------- + * + * 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 +#define TDS_COL_METADATA_IDENTITY_FLAGS TDS_COLMETA_IDENTITY +#define TDS_COL_METADATA_COMPUTED_FLAGS TDS_COLMETA_NULLABLE | \ + TDS_COLMETA_COMPUTED + +/* 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. + * This needs to be done for identity contraints as well. + */ + if (col->attNotNull && tdsType != TDS_TYPE_UNIQUEIDENTIFIER && tdsType != TDS_TYPE_XML) + { + col->metaLen = sizeof(col->metaEntry.type1) - 1; + + if (col->attidentity) + col->metaEntry.type1.flags = TDS_COL_METADATA_IDENTITY_FLAGS; + else + col->metaEntry.type1.flags = TDS_COL_METADATA_NOT_NULL_FLAGS; + } + else + { + col->metaLen = sizeof(col->metaEntry.type1); + + if (col->attgenerated) + col->metaEntry.type1.flags = TDS_COL_METADATA_COMPUTED_FLAGS; + else + 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..5e5ba208105 --- /dev/null +++ b/contrib/babelfishpg_tds/src/include/tds_typeio.h @@ -0,0 +1,476 @@ +/*------------------------------------------------------------------------- + * + * 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 */ + bool attidentity; /* true if it is an identity column */ + bool attgenerated; /* true if it is a computed column */ +} 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..135d6dd611c --- /dev/null +++ b/contrib/babelfishpg_tsql/Makefile @@ -0,0 +1,246 @@ +# 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 += src/special_keywords.o +OBJS += antlr/libantlr_tsql.a +OBJS += src/multidb.o +OBJS += src/json_funcs.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)) + +REGRESS = test/babel_like +REGRESS += test/babel_219 +REGRESS += test/babel_init +REGRESS += test/babel_select_distinct_top +REGRESS += test/babel_transaction +REGRESS += test/babel_typecode +REGRESS += test/babel_uniqueidentifier +REGRESS += test/babel_collation +REGRESS += test/babel_ddl +REGRESS += test/babel_delete +REGRESS += test/babel_set_command +# REGRESS += test/babel_datatype TODO: fix BABEL-2636 "Money datatype doesn't support any currency symbol other than Dollar" before enabling this test +REGRESS += test/babel_function +REGRESS += test/babel_table_type +REGRESS += test/babel_procedures + +# 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) + +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 $< $@ + + +# $(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..ff2c74dfd5c --- /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=2 +PGTSQL_MICRO_VERSION=1 + 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..68e64523c43 --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/TSqlLexer.g4 @@ -0,0 +1,1310 @@ +/* +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. +*/ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +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; +ACCELERATED_DATABASE_RECOVERY: A C C E L E R A T E D UNDERLINE D A T A B A S E UNDERLINE R E C O V E R Y; +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; +ALL_SPARSE_COLUMNS: A L L UNDERLINE S P A R S E UNDERLINE C O L U M N S; +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: C L A S S I F I E R; +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; +COLUMN_SET: C O L U M N UNDERLINE S E T; +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; +COLUMN_ENCRYPTION_KEY: C O L U M N UNDERLINE E N C R Y P T I O N 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; +DETERMINISTIC: D E T E R M I N I S T I C; +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; +DISTRIBUTION: D I S T R I B U T I O N; +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: E N C R Y P T E D; +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; +ENCRYPTION_TYPE: E N C R Y P T I O N UNDERLINE T Y P E; +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; +FOR_APPEND: F O R UNDERLINE A P P E N D; +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_LOG: N O UNDERLINE L O G; +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; +PERSISTENT_VERSION_STORE_FILEGROUP: P E R S I S T E N T UNDERLINE V E R S I O N UNDERLINE S T O R E UNDERLINE F I L E G R O U P; +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; +RANDOMIZED: R A N D O M I Z E D; +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; +ROUND_ROBIN: R O U N D UNDERLINE R O B I N; +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; +TIMESTAMP: T I M E S T A M P; +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]+ -> channel(HIDDEN); // Thus error messages have spaces + +// the following are ignored by SQL Server +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://en.wikipedia.org/wiki/Whitespace_character +CHAR_ZWSP: '\u200b' -> skip; // zero width space + +// 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 + Greek 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..724bbf5a58d --- /dev/null +++ b/contrib/babelfishpg_tsql/antlr/TSqlParser.g4 @@ -0,0 +1,5098 @@ +/* +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. +*/ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +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 +// | insert_bulk_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_materialized_view + | alter_master_key + | alter_message_type + | alter_partition_function + | alter_partition_scheme + | alter_queue + | 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_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_availability_group + | create_column_encryption_key + | create_column_master_key + | create_contract + | 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_materialized_view + | 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_queue + | 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_classifier + | 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_cryptographic_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_classifier + | drop_workload_group + | drop_xml_schema_collection + | disable_trigger + | enable_trigger + | 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 + : char_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=raiseerror_msg 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_msg + : DECIMAL | char_string | LOCAL_ID + ; + +raiseerror_option + : (LOG | SETERROR | NOWAIT) + ; + +another_statement + : declare_statement + | declare_xmlnamespaces_statement + | execute_statement + | cursor_statement + | conversation_statement + | kill_statement + | create_message_type + | 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=char_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=char_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 ((char_string|id) (COMMA (char_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|char_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? (char_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 char_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 char_string |EXECUTABLE_FILE EQUAL char_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=char_string | CREATION_DISPOSITION EQUAL (CREATE_NEW|OPEN_EXISTING) ) )? + (ENCRYPTION BY PASSWORD EQUAL asymmetric_key_password=char_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 )? + ; + +create_materialized_view //Azure + : CREATE MATERIALIZED VIEW simple_name WITH LR_BRACKET DISTRIBUTION EQUAL (ROUND_ROBIN | HASH LR_BRACKET id RR_BRACKET) (COMMA FOR_APPEND)? RR_BRACKET AS select_statement_standalone + ; + +alter_materialized_view + : ALTER MATERIALIZED VIEW simple_name (REBUILD | DISABLE) + ; + +// 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 + ; + +create_availability_group + : CREATE AVAILABILITY GROUP group_name=id WITH .+? EOF // quick & dirty, good enough for now + ; + +// 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=char_string (WITH LR_BRACKET ( (ENDPOINT_URL EQUAL char_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 ( ( char_string) ) RR_BRACKET ) ) + |PRIMARY_ROLE LR_BRACKET (ALLOW_CONNECTIONS EQUAL (NO|READ_ONLY|ALL) | READ_ONLY_ROUTING_LIST EQUAL ( LR_BRACKET ( (COMMA? char_string)*|NONE ) RR_BRACKET ) + | SESSION_TIMEOUT EQUAL session_timeout=DECIMAL +) + | MODIFY REPLICA ON server_instance=char_string (WITH LR_BRACKET (ENDPOINT_URL EQUAL char_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 ( ( char_string) ) RR_BRACKET ) ) + |PRIMARY_ROLE LR_BRACKET (ALLOW_CONNECTIONS EQUAL (NO|READ_ONLY|ALL) | READ_ONLY_ROUTING_LIST EQUAL ( LR_BRACKET ( (COMMA? char_string)*|NONE ) RR_BRACKET ) + | SESSION_TIMEOUT EQUAL session_timeout=DECIMAL +) ) RR_BRACKET + | REMOVE REPLICA ON char_string + | JOIN + | JOIN AVAILABILITY GROUP ON (COMMA? ag_name=char_string WITH LR_BRACKET ( LISTENER_URL EQUAL char_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=char_string WITH LR_BRACKET (LISTENER_URL EQUAL char_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=char_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 char_string + |REMOVE LISTENER char_string + |OFFLINE + | WITH LR_BRACKET DTC_SUPPORT EQUAL PER_DB RR_BRACKET + ; + +ip_v4_failover + : char_string + ; + +ip_v6_failover + : char_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=char_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 char_string COMMA? | DECRYPTION BY PASSWORD EQUAL char_string COMMA?| ENCRYPTION BY PASSWORD EQUAL char_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=char_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=char_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_cryptographic_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 char_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) + ; + +// 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=char_string COMMA + KEY_PATH EQUAL key_path=char_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=char_string + ( COMMA SECRET EQUAL secret=char_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=char_string + ( COMMA SECRET EQUAL secret=char_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=char_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=char_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=char_string COMMA + broker_service_specifier_or_current_database=char_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|char_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? |char_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 | char_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 | char_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=char_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=char_string COMMA? + | RESOURCE_MANAGER_LOCATION EQUAL resource_manager_location=char_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 char_string)? + RR_BRACKET SEMI + ; + +external_file_format_option + : FIELD_TERMINATOR EQUAL char_string + | STRING_DELIMITER EQUAL char_string + | FIRST_ROW EQUAL DECIMAL + | DATE_FORMAT EQUAL char_string + | USE_TYPE_DEFAULT EQUAL (TRUE | FALSE) + | ENCODING EQUAL char_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=char_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=char_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=char_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 (char_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=char_string LANGUAGE (char_string|DECIMAL|BINARY) | DROP ( stopword=char_string LANGUAGE (char_string|DECIMAL|BINARY) |ALL (char_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)? + ; + +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=char_string | password_hash=BINARY HASHED ) + ( (MUST_CHANGE|UNLOCK)+ + | OLD_PASSWORD EQUAL old_password=char_string )? + | DEFAULT_DATABASE EQUAL default_database=id + | DEFAULT_LANGUAGE EQUAL (id | char_string | DECIMAL) + | NAME EQUAL login_name=id + | CHECK_POLICY EQUAL (ON|OFF) + | CHECK_EXPIRATION EQUAL (ON|OFF) + | CREDENTIAL EQUAL credential_name=id + | NO CREDENTIAL + ; + +create_login + : CREATE LOGIN login_name=id + ( WITH PASSWORD EQUAL ( password=char_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 | char_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 | char_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=char_string |(ADD|DROP) ENCRYPTION BY (SERVICE MASTER KEY | PASSWORD EQUAL encryption_password=char_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=char_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=char_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=char_string)? + (COMMA? BROKER_INSTANCE EQUAL broker_instance_identifier=char_string)? + (COMMA? LIFETIME EQUAL DECIMAL)? + COMMA? ADDRESS EQUAL char_string + (COMMA MIRROR_ADDRESS EQUAL char_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 + | deny_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_kwd=CACHE cache_value=DECIMAL? | no_cache=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_kwd=CACHE cache_value=DECIMAL? | no_cache=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=char_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 | char_string) + | COMMA? (AND|OR) NOT? (EQUAL + |(LESS GREATER) + | (EXCLAMATION EQUAL) + | GREATER + | (GREATER EQUAL) + | LESS + | LESS EQUAL) + (DECIMAL | char_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=char_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 | char_string) + | COMMA? (AND|OR) NOT? (EQUAL + |(LESS GREATER) + | (EXCLAMATION EQUAL) + | GREATER + | (GREATER EQUAL) + | LESS + | LESS EQUAL) + (DECIMAL | char_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 (char_string | DEFAULT) |MAX_SIZE EQUAL (DECIMAL MB |DEFAULT)|MAX_FILES EQUAL (DECIMAL|DEFAULT) ) | FAILOVER CLUSTER PROPERTY (VERBOSELOGGING EQUAL (char_string|DEFAULT) |SQLDUMPERFLAGS EQUAL (char_string|DEFAULT) | SQLDUMPERPATH EQUAL (char_string|DEFAULT) | SQLDUMPERTIMEOUT (char_string|DEFAULT) | FAILURECONDITIONLEVEL EQUAL (char_string|DEFAULT) | HEALTHCHECKTIMEOUT EQUAL (DECIMAL|DEFAULT) ) | HADR CLUSTER CONTEXT EQUAL (char_string|LOCAL) | BUFFER POOL EXTENSION (ON LR_BRACKET FILENAME EQUAL char_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_name=id (AUTHORIZATION server_principal=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=char_string COMMA OLD_PASSWORD EQUAL old_password=char_string | NEW_ACCOUNT EQUAL new_account_name=char_string COMMA NEW_PASSWORD EQUAL new_password=char_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=char_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 + : CREATE SYMMETRIC KEY key_name=id + (AUTHORIZATION user_name=id)? + (FROM PROVIDER provider_name=id)? + WITH ((key_options | ENCRYPTION BY encryption_mechanism)COMMA?)+ + ; + +// 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 char_string (OLD_PASSWORD EQUAL char_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=char_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)? + )? + ; + +create_workload_classifier + : CREATE WORKLOAD CLASSIFIER id WITH LR_BRACKET?.+? RR_BRACKET EOF // quick & dirty, good enough for now + ; + +drop_workload_classifier + : DROP WORKLOAD CLASSIFIER id + ; + +// 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 (char_string|id|LOCAL_ID) + ; + +alter_xml_schema_collection + : ALTER XML SCHEMA COLLECTION simple_name ADD char_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=char_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 + ; + +create_message_type + : 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? + final_char=(SEMI | RR_BRACKET) /// semicolon is required for stand-alone statement, but not inside INSERT-SELECT FROM (MERGE), where we expect a bracket instead + ; + +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 + : derived_table + | execute_statement + | DEFAULT VALUES + ; + +insert_bulk_statement + : INSERT BULK table_name LR_BRACKET schema_declaration RR_BRACKET ( WITH LR_BRACKET insert_bulk_option (COMMA? insert_bulk_option)* RR_BRACKET )? SEMI? + ; + +insert_bulk_option + : id + | bulk_insert_option + ; + +bulk_insert_statement + : BULK INSERT ddl_object FROM char_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_clause? + ; + +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 col=full_column_name text_ptr=(LOCAL_ID|BINARY) offset=(LOCAL_ID|DECIMAL) size=(LOCAL_ID|DECIMAL) HOLDLOCK? + ; + +writetext_statement + : WRITETEXT BULK? col=full_column_name text_ptr=(LOCAL_ID|BINARY) (WITH LOG)? (LOCAL_ID|char_string) + ; + +updatetext_statement + : UPDATETEXT BULK? col=full_column_name text_ptr=(LOCAL_ID|BINARY) (NULL_P|DECIMAL|LOCAL_ID) (NULL_P|DECIMAL|LOCAL_ID) (WITH LOG)? ((LOCAL_ID|char_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 containment=( NONE | PARTIAL ) )? + ( ON PRIMARY? database_file_spec ( COMMA database_file_spec )* )? + ( LOG ON database_file_spec ( COMMA database_file_spec )* )? + collation? + ( 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 char_string AS id (COMMA char_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 char_string (AS (XQUERY char_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 proc_version=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 char_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)? param_option=(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_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? 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)? + | char_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 (colname=id | column_definition) ((ADD | DROP) special_column_option)? (WITH ( LR_BRACKET online_clause 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)? + | SPLIT RANGE LR_BRACKET expression RR_BRACKET + | MERGE RANGE LR_BRACKET expression 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 + | alter_table_drop_constraint_id + | 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_clause + | MOVE TO storage_partition_clause + ; + +online_clause + : ONLINE EQUAL on_off (LR_BRACKET low_priority_lock_wait RR_BRACKET)? + ; + +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)? + | collation + | 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 +database_optionspec + : auto_option + | accelerated_database_recovery + | 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 ) + ; + +accelerated_database_recovery + : ACCELERATED_DATABASE_RECOVERY EQUAL on_off ( LR_BRACKET PERSISTENT_VERSION_STORE_FILEGROUP EQUAL id RR_BRACKET )? + ; + +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=char_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 char_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 | char_string | DECIMAL ) + | DEFAULT_FULLTEXT_LANGUAGE EQUAL ( id | char_string | DECIMAL ) + | NESTED_TRIGGERS EQUAL on_off + | TRANSFORM_NOISE_WORDS EQUAL on_off + | 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 on_off + ; + +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 (COMMA? table_indices)* 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=char_string RR_BRACKET + ; + +// https://msdn.microsoft.com/en-us/library/ms179856.aspx +open_datasource + : OPENDATASOURCE LR_BRACKET provider=char_string COMMA init=char_string RR_BRACKET + (DOT id)+ + ; + +// https://msdn.microsoft.com/en-us/library/ms190312.aspx +open_rowset + : OPENROWSET LR_BRACKET provider_name = char_string COMMA connectionString = char_string COMMA sql = char_string RR_BRACKET + | OPENROWSET LR_BRACKET BULK data_file=char_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=char_string AS id + | DEFAULT char_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 (char_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 (char_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 (char_string|id|LOCAL_ID) + | NAME EQUAL (id|LOCAL_ID) + | CREDENTIAL + | FILE_SNAPSHOT + | (EXPIREDATE EQUAL (char_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 (char_string|LOCAL_ID)) + | NO_TRUNCATE + | NO_LOG + ; + +restore_option + : (RECOVERY | NORECOVERY | STANDBY) EQUAL (char_string |LOCAL_ID) + | MOVE (char_string|LOCAL_ID) TO (char_string|LOCAL_ID) + | REPLACE + | RESTART + | RESTRICTED_USER + | CREDENTIAL + | FILE EQUAL (char_string|LOCAL_ID) + | PASSWORD EQUAL (char_string|LOCAL_ID) + | KEEP_REPLICATION + | KEEP_CDC + | FILESTREAM LR_BRACKET DIRECTORY_NAME EQUAL (char_string|LOCAL_ID) RR_BRACKET + | ENABLE_BROKER + | ERROR_BROKER_CONVERSATIONS + | NEW_BROKER + | STOPAT EQUAL (char_string|LOCAL_ID) + | STOPATMARK EQUAL (char_string) (AFTER (char_string|LOCAL_ID))? + | STOPBEFOREMARK EQUAL (char_string) (AFTER (char_string|LOCAL_ID))? + ; + +backup_restore_option + : MEDIADESCRIPTION EQUAL (char_string|id|LOCAL_ID) + | MEDIANAME EQUAL (char_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=char_string + ( WITH PRIVATE KEY + LR_BRACKET + (COMMA? FILE EQUAL private_key_file=char_string + |COMMA? ENCRYPTION BY PASSWORD EQUAL encryption_password=char_string + |COMMA? DECRYPTION BY PASSWORD EQUAL decryption_pasword=char_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=char_string + ENCRYPTION BY PASSWORD EQUAL encryption_password=char_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=char_string + ENCRYPTION BY PASSWORD EQUAL encryption_password=char_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 + : ((DECIMAL|char_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? (WITH execute_option (COMMA execute_option)* )? SEMI? + ; + +execute_body + : (return_status=LOCAL_ID EQUAL)? (func_proc_name_server_database_schema (SEMI proc_version=DECIMAL)? | proc_var=LOCAL_ID) 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 char_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 + | char_string + ; + +execute_option + : RECOMPILE + | RESULT SETS (NONE |UNDEFINED) + | RESULT SETS LR_BRACKET (COMMA? 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_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=char_string (WITH PRIVATE KEY LR_BRACKET private_key_options RR_BRACKET )? + ; + +private_key_options + : (FILE | BINARY) EQUAL path=char_string (COMMA (DECRYPTION | ENCRYPTION) BY PASSWORD EQUAL password=char_string)? + ; + +generate_new_keys + : (ENCRYPTION BY PASSWORD EQUAL password=char_string)? + WITH SUBJECT EQUAL certificate_subject_name=char_string (COMMA date_options)* + ; + +date_options + : (START_DATE | EXPIRY_DATE) EQUAL char_string + ; + +open_key + : OPEN SYMMETRIC KEY key_name=id DECRYPTION BY decryption_mechanism + | OPEN MASTER KEY DECRYPTION BY PASSWORD EQUAL password=char_string + ; + +close_key + : CLOSE SYMMETRIC KEY key_name=id + | CLOSE ALL SYMMETRIC KEYS + | CLOSE MASTER KEY + ; + +key_options + : KEY_SOURCE EQUAL pass_phrase=char_string + | ALGORITHM EQUAL algorithm + | IDENTITY_VALUE EQUAL identity_phrase=char_string + | PROVIDER_KEY_NAME EQUAL key_name_in_provider=char_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 char_string + ; + +decryption_mechanism + : CERTIFICATE certificate_name=id (WITH PASSWORD EQUAL char_string)? + | ASYMMETRIC KEY asym_key_name=id (WITH PASSWORD EQUAL char_string)? + | SYMMETRIC KEY decrypting_Key_name=id + | PASSWORD EQUAL char_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 SEMI? + ; + +// 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 char_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=char_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 (CALLER | SELF | OWNER | char_string) + ; + +execute_as_statement + : (EXECUTE|EXEC) AS ( CALLER | ( LOGIN | USER ) EQUAL char_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 | UNIQUE) clustered?) LR_BRACKET column_name_list_with_order RR_BRACKET + | table_indices + | CHECK LR_BRACKET search_condition RR_BRACKET + ; + +xml_type_definition + : id LR_BRACKET ( CONTENT | DOCUMENT )? xml_schema_collection RR_BRACKET + | XML COLUMN_SET FOR ALL_SPARSE_COLUMNS + ; + +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 + | table_constraint + | period_for_system_time + ; + +// https://msdn.microsoft.com/en-us/library/ms187742.aspx +// empirically found: ROWGUIDCOL can be in various locations +column_definition + : simple_column_name (data_type system_versioning_column? | AS expression PERSISTED? ) ( special_column_option | collation | null_notnull )* + ( column_constraint? IDENTITY (LR_BRACKET sign? seed=DECIMAL COMMA sign? increment=DECIMAL RR_BRACKET)? )? for_replication? ROWGUIDCOL? + column_constraint* column_inline_index? + | TIMESTAMP null_notnull? column_constraint? + ; + +// 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 UNIQUE? clustered? (LR_BRACKET column_name_list_with_order RR_BRACKET)? + (WHERE where=search_condition)? + with_index_options? + (ON storage_partition_clause)? + (FILESTREAM_ON storage_partition_clause)? + ; + +special_column_option + : FILESTREAM + | SPARSE + | ROWGUIDCOL + | HIDDEN_RENAMED + | PERSISTED + | MASKED ( WITH LR_BRACKET FUNCTION EQUAL char_string RR_BRACKET )? + | column_encrypted + | for_replication + ; + +column_encrypted + : ENCRYPTED WITH LR_BRACKET column_encrypted_option (COMMA column_encrypted_option)* RR_BRACKET + ; + +column_encrypted_option + : COLUMN_ENCRYPTION_KEY EQUAL id + | ENCRYPTION_TYPE EQUAL ( DETERMINISTIC | RANDOMIZED ) + | ALGORITHM EQUAL char_string + ; + +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_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 (char_string|LOCAL_ID) + | BETWEEN (char_string|LOCAL_ID) AND (char_string|LOCAL_ID) + | CONTAINED IN LR_BRACKET (char_string|LOCAL_ID) COMMA (char_string|LOCAL_ID) RR_BRACKET + | FROM (char_string|LOCAL_ID) TO (char_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)* for_replication? + | 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)? + | 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). +index_option + : option_id=id (EQUAL (set_id=id | on_off | DECIMAL))? sub_options? + ; + +sub_options + : LR_BRACKET sub_option (sub_options? | (COMMA sub_option)*) RR_BRACKET + ; + +sub_option + : id EQUAL expression keyword? // keyword is for cases like 'RETENTION_PERIOD = 1 WEEKS' + | low_priority_lock_wait + ; + +// 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 +set_special + : SET set_on_off_option (COMMA set_on_off_option)* on_off + | SET STATISTICS set_statistics_keyword (COMMA set_statistics_keyword)* on_off + | SET OFFSETS set_offsets_keyword (COMMA set_offsets_keyword)* on_off + | SET id_set=id (id_val=id | constant_LOCAL_ID | on_off) + | SET ROWCOUNT (LOCAL_ID | MINUS? DECIMAL) + // https://msdn.microsoft.com/en-us/library/ms173763.aspx + | SET (TRAN | TRANSACTION) ISOLATION LEVEL (READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SNAPSHOT | SERIALIZABLE | DECIMAL) + // https://msdn.microsoft.com/en-us/library/ms188059.aspx + | SET IDENTITY_INSERT table_name on_off + | SET TEXTSIZE DECIMAL + | 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 + | CONCAT_NULL_YIELDS_NULL + | CURSOR_CLOSE_ON_COMMIT + | FIPS_FLAGGER + | FMTONLY + | FORCEPLAN + | IMPLICIT_TRANSACTIONS + | NOCOUNT + | NOEXEC + | NUMERIC_ROUNDABORT + | PARSEONLY + | QUOTED_IDENTIFIER + | REMOTE_PROC_TRANSACTIONS + | SHOWPLAN_ALL + | SHOWPLAN_TEXT + | SHOWPLAN_XML + | XACT_ABORT + ; + +set_statistics_keyword + : IO + | PROFILE + | TIME + | XML + ; + +set_offsets_keyword + : SELECT + | FROM + | ORDER + | TABLE + | (PROCEDURE|PROC) + | 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)* #local_id_expr + | subquery (DOT calls+=method_call)* #subquery_expr + | LR_BRACKET expression RR_BRACKET (DOT calls+=method_call)* #bracket_expr + | function_call (DOT calls+=method_call)* #func_call_expr + | expression collation #collate_expr + | expression AT_KEYWORD TIME ZONE expression #time_zone_expr + | op=(MINUS | PLUS | BIT_NOT) expression #unary_op_expr + | expression op=(STAR | DIVIDE | PERCENT_SIGN ) expression #mult_div_percent_expr + | expression op=(PLUS | MINUS | BIT_AND | BIT_XOR | BIT_OR ) expression #plus_minus_bit_expr + | constant #constant_expr + | full_column_name #full_col_name_expr + | DEFAULT #default_expr + | case_expression #case_expr + | hierarchyid_coloncolon_methods #hierarchyid_coloncolon + | over_clause #over_clause_expr + | odbc_literal #odbc_literal_expr + | DOLLAR_ACTION #dollar_action_expr + ; + +method_call + : xml_methods + | hierarchyid_methods + | spatial_methods + ; + +// 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 + ; + +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 (XMLNAMESPACES LR_BRACKET xml_dec+=xml_declaration (COMMA xml_dec+=xml_declaration)* RR_BRACKET COMMA?)? (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 + ; + +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 + ; + +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_qs=order_by_clause? | LR_BRACKET query_expression order_by_qe=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_qs=order_by_clause? | LR_BRACKET query_expression order_by_qe=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 char_string RR_BRACKET )? | AUTO) + ( xml_common_directives | (COMMA (XMLDATA | XMLSCHEMA ( LR_BRACKET char_string RR_BRACKET )?)) | (COMMA ELEMENTS (XSINIL | ABSENT)?) )* + | FOR XML EXPLICIT ( xml_common_directives | COMMA XMLDATA)* + | FOR XML PATH ( LR_BRACKET char_string RR_BRACKET )? (xml_common_directives | COMMA ELEMENTS (XSINIL | ABSENT)?)* + + | FOR JSON (AUTO | PATH) + ( (COMMA ROOT ( LR_BRACKET char_string RR_BRACKET )?) + | (COMMA INCLUDE_NULL_VALUES) + | (COMMA WITHOUT_ARRAY_WRAPPER) + )* + ; + +xml_common_directives + : COMMA (BINARY_KEYWORD BASE64 | TYPE | ROOT ( LR_BRACKET char_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 char_string + | USE HINT LR_BRACKET char_string (COMMA char_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 + ; + +expression_elem + : leftAlias=column_alias eq=EQUAL leftAssignment=expression + | expressionAs=expression as_column_alias? + ; + +select_list_elem + : asterisk + | expression_elem + | LOCAL_ID (assignment_operator | EQUAL) expression + ; + +table_sources +// : table_source_item (COMMA table_source_item)* + : table_source_item (COMMA table_source_item)* + | LR_BRACKET table_source_item_dml as_table_alias column_alias_list + ; + +// these DML statements must have an OUTPUT clause to be used in INSERT-SELECT FROM () +table_source_item_dml + : merge_statement + | delete_statement RR_BRACKET + | insert_statement RR_BRACKET + | update_statement RR_BRACKET + ; + +table_source_item + : table_source_item (ij=INNER join_hint?)? JOIN table_source_item ON search_condition + | table_source_item oj=(LEFT|RIGHT|FULL) OUTER? join_hint? JOIN table_source_item ON search_condition + | table_source_item cj=CROSS JOIN table_source_item + | table_source_item lj=(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 (collation | null_notnull)* char_string? + ; + +full_column_name_list + : column+=full_column_name (COMMA column+=full_column_name)* + ; + +table_name_with_hint + : table_name with_table_hints? + ; + +bulk_option + : id EQUAL (bulk_option_nr=DECIMAL | bulk_option_str=char_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 + | aggregate_windowed_function + | analytic_windowed_function + | func_proc_name_server_database_schema LR_BRACKET function_arg_list? RR_BRACKET + | built_in_functions + | freetext_function + | NEXT VALUE FOR full_object_name + | odbc_scalar_function + | partition_function_call + ; + +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 | (table_name DOT)? 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 | (table_name DOT)? 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 | (table_name DOT)? 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 | (table_name DOT)? 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 + : L_CURLY FN odbc_scalar_function_name R_CURLY + ; + +odbc_scalar_function_name + : 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 expression (COMMA expression)+ RR_BRACKET + | CURRENT_DATE LR_BRACKET expression? RR_BRACKET + | CURRENT_TIME LR_BRACKET expression? RR_BRACKET + | id (LR_BRACKET (COMMA? expression)* RR_BRACKET)? + ; + +odbc_literal + : L_CURLY op=(D | T | TS | GUID) char_string R_CURLY + | L_CURLY INTERVAL sign? char_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 + : method=( GETANCESTOR | GETDESCENDANT | GETLEVEL | ISDESCENDANTOF | READ | GETREPARENTEDVALUE | TOSTRING ) LR_BRACKET expression_list? RR_BRACKET + ; + +hierarchyid_coloncolon_methods + : id colon_colon method=(GETROOT | PARSE) LR_BRACKET expression? RR_BRACKET + ; + +// this is no longer used: +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=char_string COMMA sqltype=char_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=char_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=char_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=char_string RR_BRACKET + ; + +xml_nodes_method + : (loc_id=LOCAL_ID | value_id=id | subquery) DOT NODES LR_BRACKET xquery=char_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 +// NB: WITH keyword is mandatory these days in SQL Server, but customer applications running older or backward-compatible version may still use the old syntax without WITH +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 + | char_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 | char_string | DECIMAL ) + | DEFAULT_FULLTEXT_LANGUAGE EQUAL ( id | char_string | DECIMAL ) + | NESTED_TRIGGERS EQUAL on_off + | TRANSFORM_NOISE_WORDS EQUAL on_off + | TWO_DIGIT_YEAR_CUTOFF EQUAL DECIMAL + | DB_CHAINING on_off + | TRUSTWORTHY on_off + | 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 char_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 | char_string ) COMMA? + ( FILENAME EQUAL file = char_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=char_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 (failure_code_v=LOCAL_ID | failure_code_s=char_string) DESCRIPTION EQUAL (failure_text_v=LOCAL_ID | failure_text_s=char_string))? CLEANUP? )? + ; + +waitfor_conversation + : WAITFOR? LR_BRACKET get_conversation RR_BRACKET (COMMA? TIMEOUT timeout=time)? SEMI? + ; + +get_conversation + :GET CONVERSATION GROUP (char_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 (char_string | LOCAL_ID) + MESSAGE TYPE message_type_name=expression + ( LR_BRACKET (message_body_s=char_string | message_body_v=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 + : char_string // string, datetime or uniqueidentifier + | BINARY + | NULL_P + | sign? (REAL | MONEY | DECIMAL | FLOAT) + ; + +char_string + : STRING // single-quoted string; may also be a double-quoted string in case of SET QUOTED_IDENTIFIER OFF + ; + +sign + : PLUS + | MINUS + ; + +keyword + : ABORT + | ABORT_AFTER_WAIT + | ABSENT + | ABSOLUTE + | ACCELERATED_DATABASE_RECOVERY + | ACCENT_SENSITIVITY + | ACCESS + | ACTION + | ACTIVATION + | ACTIVE + | ADDRESS + | ADMINISTER + | AES + | AES_128 + | AES_192 + | AES_256 + | AFFINITY + | AFTER + | AGGREGATE + | ALGORITHM + | ALLOWED + | ALL_SPARSE_COLUMNS + | 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 + | ARITHIGNORE + | ASSEMBLY + | ASYMMETRIC + | ASYNCHRONOUS_COMMIT + | ATOMIC + | AT_KEYWORD + | AUDIT + | AUDIT_GUID + | AUTHENTICATE + | AUTHENTICATION + | AUTO + | AUTOGROW_ALL_FILES + | AUTOGROW_SINGLE_FILE + | 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 + | BINARY_KEYWORD + | 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 + | CLASSIFIER_FUNCTION + | CLEANUP + | CLEANUP_POLICY + | CLEAR + | CLUSTER + | COALESCE + | COLLECTION + | COLUMNS + | COLUMNSTORE + | COLUMN_SET + | COLUMN_ENCRYPTION_KEY + | COLUMN_MASTER_KEY + | COMMITTED + | COMPATIBILITY_LEVEL + | COMPRESSION + | CONCAT + | CONCAT_NULL_YIELDS_NULL + | CONFIGURATION + | CONNECT + | 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 + | DATE_FORMAT + | 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 + | DETERMINISTIC + | DISTRIBUTION + | DHCP + | DIAGNOSTICS + | DIALOG + | DIFFERENTIAL + | DIRECTORY_NAME + | DISABLE + | DISABLED + | DISABLE_BROKER + | DISK + | DISK_DRIVE + | DISTRIBUTED_AGG + | DISTRIBUTION + | DOCUMENT + | DTC_SUPPORT + | DYNAMIC + | EDGE + | ELEMENTS + | EMERGENCY + | EMPTY + | ENABLE + | ENABLED + | ENABLE_BROKER + | ENCODING + | ENCRYPTED + | ENCRYPTION_TYPE + | 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 + | EXTERNALPUSHDOWN + | EXTERNAL_ACCESS + | EXTRACT + | FAILOVER + | FAILOVER_MODE + | FAILURE + | FAILURECONDITIONLEVEL + | FAILURE_CONDITION_LEVEL + | FAIL_OPERATION + | FAIL_UNSUPPORTED + | FALSE + | FAN_IN + | FAST + | FAST_FORWARD + | FIELD_TERMINATOR + | FILEGROUP + | FILEGROWTH + | FILENAME + | FILEPATH + | FILESTREAM + | FILESTREAM_ON + | FILETABLE + | FILE_SNAPSHOT + | FILTER + | FIPS_FLAGGER + | FIRST + | FIRST_ROW + | FIRST_VALUE + | FMTONLY + | FN + | FOLLOWING + | FOR_APPEND + | FORCE + | FORCED + | FORCEPLAN + | 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 + | IMPLICIT_TRANSACTIONS + | 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 + | NEGOTIATE + | NESTED_TRIGGERS + | NEW_ACCOUNT + | NEW_BROKER + | NEW_PASSWORD + | NEXT + | NO + | NO_PERFORMANCE_SPOOL + | 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_LOG + | NO_PERFORMANCE_SPOOL + | 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 + | PARSE + | PARSEONLY + | PARTIAL + | PARTITION + | PARTITIONS + | PARTNER + | PASSWORD + | PATH + | PAUSE + | PERCENTILE_CONT + | PERCENTILE_DISC + | PERCENT_RANK + | PERIOD + | PERMISSION_SET + | PERSISTED + | PERSIST_SAMPLE_PERCENT + | PERSISTENT_LOG_BUFFER + | PERSISTENT_VERSION_STORE_FILEGROUP + | PER_CPU + | PER_DB + | PER_NODE + | PLATFORM + | POISON_MESSAGE_HANDLING + | POLICY + | POOL + | POPULATION + | PORT + | POSITION + | PRECEDING + | PREDICATE + | PREDICT + | PRIMARY_ROLE + | PRIOR + | PRIORITY + | PRIORITY_LEVEL + | PRIVATE + | PRIVATE_KEY + | PRIVILEGES + | PROCEDURE_CACHE + | PROCEDURE_NAME + | PROCESS + | PROFILE + | PROPERTY + | PROVIDER + | PROVIDER_KEY_NAME + | PYTHON + | QUERY + | QUERY_CAPTURE_MODE + | QUERY_CAPTURE_POLICY + | QUERY_STORE + | QUERYTRACEON + | QUEUE + | QUEUE_DELAY + | QUOTED_IDENTIFIER + | R + | RANDOMIZED + | RANGE + | RANK + | RAW + | RC2 + | RC4 + | RC4_128 + | READONLY + | READWRITE + | 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 + | ROUND_ROBIN + | ROUTE + | ROW + | ROWGUID + | ROWS + | ROW_NUMBER + | RSA_1024 + | RSA_2048 + | RSA_3072 + | RSA_4096 + | RSA_512 + | RUNTIME + | SAFE + | SAFETY + | SAMPLE + | SCALEOUTEXECUTION + | SCHEDULER + | SCHEMABINDING + | SCHEME + | SCOPED + | SCRIPT + | SCROLL + | SCROLL_LOCKS + | SEARCH + | SECOND + | SECONDARY + | SECONDARY_ONLY + | SECONDARY_ROLE + | SECONDS + | SECRET + | SECURABLES + | SECURITY + | SECURITY_LOG + | SEEDING_MODE + | SELECTIVE + | 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 + | SHRINKLOG + | 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 + | STALE_QUERY_THRESHOLD_DAYS + | STANDBY + | START + | STARTED + | STARTUP_STATE + | START_DATE + | STATE + | STATEMENT + | STATIC + | STATISTICAL_SEMANTICS + | 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 + | TIMESTAMP + | 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 + | TRY_PARSE + | TS + | TSQL + | TWO_DIGIT_YEAR_CUTOFF + | TYPE + | TYPE_WARNING + | UNBOUNDED + | UNCHECKED + | UNCOMMITTED + | UNDEFINED + | 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 + | WHEN_SUPPORTED + | WINDOWS + | WITHOUT + | WITHOUT_ARRAY_WRAPPER + | WITNESS + | WORK + | WORKLOAD + | XACT_ABORT + | 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 + ; + +collation + : COLLATE id + ; + +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, sql server 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 // this is a double-quoted identifier in case of SET QUOTED_IDENTIFIER ON + | SQUARE_BRACKET_ID + | keyword + | DOLLAR_IDENTITY + | DOLLAR_ROWGUID + | 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 0000000000000000000000000000000000000000..749296fe7b9021346079968d5d0b0ada93d48716 GIT binary patch literal 3508089 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\T=1W(x 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%0DGg7?QM;_fStX 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 -|kL6Hw7`A?#KFELfc185=>{;Va&Zpjw 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�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Ν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 z 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%0D`&hq0|nV{2mB+k@+eO~KSk>@?Vn2z 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

C<+N$VkE|3OkBJrz?&fJ;?~yv11ZDdHb_#=g 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

d(g3vN7qdzOb*`iDvs{q*`UlA;#Lkqwj-?% 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%0DF^&)k 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%0Dc@bqQouD$0-qL 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

8T^jM2 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μy4qI<0Yn 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

Dj@TEcovYS5%Erg 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%0DsajN8RE}8z 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 z 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�iZcChg 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

Gaenzy8AQXNg`y-9sY-C4#s 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

|V&SOKs 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ΠZgA}fmi68+eza?Ku}b9ke%;c6 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%0AqJvwL>0>x_-s|dvl zLnLlBA3=n!adig^h95Ss#D*D9WJbgyg<)980x{&YibWkM?+zuuH70ac-?kYC+6X!1 zGs!M#7Pt}IC;-M8Mn#C05`_rd$h#m`KFEPN#BxOqC8uBtD~Xg3 zxxW!uPTC3j#h;&1{66)w-Kl_<*ZSGkTSOk&VN_~Lggr7jbmYP>jcl9K!M4d!idVZ4 z`%6fC*05Mfcfw#0?Xbj@NkoD>2+0V@&TN_S$+8>u&B7dhoujv~Dem|#Si5Je|c6&1l=u)UxwN14(={-3%LBNcSr48PzC|qyVXGnPfg-lB;Ygem}5My-+{A zk|^F4v5XE=)#A*spZP~H^Q2YtUO zs&6t%M{)mo{I~p7UjfK!L28J%^{Vi!| zW+jf1X=p-%<_f2Ugo`+o{YCl+XdKFiv}iP8AuFWcAR(=P81k1nf2jq7<4AhOhieuJ zOkz6BGla(AYT5iV*EnvMpCB+_T34ELRVSz_QE~!O5VPSd+;d6=k~LDP(3N-3?=Q2cyN^bjs=_9ZrgLt897O0 z^59o!8&md#P{!EXfayjA2d+q#)*~$OZ)(PQmMjGe7lRZ{5QM0EvS~JpdZMV04sBv|^ z$GOGtp?HmdU6K7f&A8j;Nql|t#NHY4D$&VQf;otHW9{jGoq2ik_~|Z_T)oFAQjzhvU(_qd&+y_Rj$SFG#x z>p35Ur?eXj}z0>Q70uN(qNuhl+SJ=w2zJ{-BC?`p3|e$-FDpY*cISx)*6ub%cE_S|`?$=%p{ z!kH*~uzS+xk7Di)9vscp?zUwgm9J~IA8w#WnRbw`P4^!5ksa4~H3i>c3>4m*UX>I| zFqU!!r!ASxm#?TFllG9e6dlVCY)JgS3yQvzywe+qzu&!bzY{zKKUux=J#k*Lzd~Mz zUp8ea;MvglC)AMpC;Zw%S(G4WCha%A=Nb0+$u`=_YzeYm^hub+GMRVXp$2)j5S zPBfSn+#ya9Oug84Q3p|(s9#mCM{c@aS+A)tXSmgj)%zZ<-u5)EAv(ieBVS2A5?`fX z=@gp2t6hn{;$KtW47}=GS3IHmCBJ@t#SxTvSA8ceX&*_1A!XXq#_{Z4M>FD{E`IEuk z=Ge?t&sa}X?xD+$vGWt8U*N0fD_My%d*%#g%V>`dP!uR(6=+z@q5J@IfHo;_m1juQ zm*wSDV<^y<^{rm2Ho#CpzD=<<-cUhJi(XR7`vqLm5<6~x`N@7EV%sNm`; zRLjP?V-G>K%u<)Y`rcOvL+R>CUSkRP(Ze&7_d z&kHmvE7fkSNp;a`68HVVM_8xOOyE$ifnCx7maN%gooa(F)I+=BGVFnBCqoe8g2XXm z9~7(`e;FqvK0S^vE2vqULk#$A*4qrnA@|P_mO~f6>@4UvMY;<7Cf2E0UzSy?Xe}D( zJdd%41&s-7pEb)2O$#?LPqAzZ-C#Mifh7{%)S93_DaFU2)-j0N^%|m&3Upb=e+Q|- z9W0C^LuP``#UO}gQ*Wry^{QF>8FcNO;|yCsPfq&7aOeR`to+DUY(cNQQRy=SebVW! zped}$vg}+~w{Sy+yI0-Xj>d#%k=nMPDe@-8+NYr3@>V^)F#~0#&;UCE1^Ft}Ry8iQ zyC1z%ePtB}aKDhU#-CGq5qrz34B&OI67&*cB?Saj?3T3cueJwys@hFCM_L3st=RQCa~<9iLv5w(<)u9!{wPCnn3< zM<5yLOU`ppT9dQJUmALcL^tPGnZU7;-|&X~Fk5x?Dep3SAw_ydYqbMqcU+Zw;B^7^ z@jRb_XGa?JZZd}XyeW^3y$fzuSH$2+t7XIHfZ`gJxDSlacGw^ck&(#BrFmg>@g^X}=)icu4fk%yT$!O_93SG?rDik#vl{Bhs z7S#rMDxh?!Rc8B_qed<<8Eo2YEY%_Tmfpr6Ja)(RMd*RFZAX|~cd8TedV{wM{*vQN z4##E9neBx~WhI-+lRtXPO0?~d!AnZssg;caRTMX^!y3Y)U%@&dVM`s9;O*ce#iFRUj3Zd_UM-GZxTgN&u{k4kAaKI4aok z{Gqox>19#HIcz+{>FpNBmie2clX8LRqUHdVv@iV3E;7I*5yenx&}(%5Y-KVkyM`dO zNN=i55#&MG{H#GUtDYrghbX)bLCVJ^Q`7iUn}MpTGns6?!BQZK-(xrY?RzU{X015# zCIUbUdVkSLVUkQ@bhA*7z9hKA@0Jn#3N^9?hhPvT%$VI=-T;_6SMoL#%1`Ajp*66^+R66Y7jmZRz&)YFDg{K z+{G()K#iK~a!#1*?e5bqyF6uCck7;ceG$0O{=D3(pUBj@k!-C;Jc_ONvd6Qm^psek zUhS?I&(!he6wE8%y;Duc*`7I}@tbg;%H96yyLaucQzA+Ocg19#Uk!Ws=b_i~59HSj z543HIZ{L0$Y%sQT2&US1AdeHC6Yue!&pVZ@Cti5b8eY}=m^VD!J*A2;xw+OzMDtzTM}d_Tu0Q+&2dDDhdm zZoE)<3b!xg+x1p{KhmL|dEQ9&e{!2}?uNLw-+Z|4ylCv);@lJW|Can-sQNzq^SVVP ztf$2IU3mO9PL=UPZ2x|D4-UvquZ^wL^9GqYdsUuC%sCUHlbWO@^d2 zKQQDQBpxkHqdMQLe58n3G*+qDmQf0-W)TYnzM8sWxD1ql(rTb+ zxx$ut3OZMbQ?Pytww#)4u&Y{^VO}}$X(b*YN_Aih=&I!qJ&vrP78B&Gwz~yUF6~fc zg{NBs7o?-suq9dUdHI7D;k{HFFjJaJs?r2EqO9JZ0_S$;YTj6*=2-uC81L zcW^CgT?4<>axXu*%C~A_hJq+)mvW0tonQ3u^{b!5H7ZW}GZz9b71)8D8i;Vj>dWE& zYQY1Ci-@Q_cxaIoc?r)}nuSH}%s<5u<8D^(#mR_#npOThMmDQ`+H8k_>|OYk)7;ng;_Cya)WYg#k$IL z>8g_GRAks9;ao^NtG2;!Oa@iw0>Z^k3nq3@a^P=526g!S-9`R6ODiLn>UPd{RfccX zUmnDlONZv37HfDmpvg*mYd8Z(3HW#%UM3*{^7ce*w$+Jx&RIdOza z*2X>M*v5wo+71=iq{U0Z%3XjbEB;1xCQxk;`YE9**Jtl+Sjt z7yY(1pvB6|QQMAxSd@2ToGobNcRXv~YJ?9u+{NG&AVpc-RiziiMu;DmYF5WasvYyz z3HvfNmlF@#-4(2q_@Rv~I65k4qsY8v;i>l&nw1-<+$`Fc*{a!Ex!-voe{yAM(#)2r z`ck4|UM{GNUCvrIv6x#{vADV*e_XSWeXM(|f9HN|SYrBmlo4S8jT73bb4))|+AM{Ha)WM8*)WktvDSCSIJ)Wou*sGV`;s;>Sj zPlvl{YdV6g>AJJ2!+kZcZr5(KUsR&$I-jK8bvvy7cI&1de4C>_a4WoIdPH9JdK;-; zaJybLd=ynlbrh#1KfkOAd6ZdYcGOXMaFnYPdL&tib=0IjaT}#xAvSB3Ev0FThFH}c zxwNz&X{X*H=6~{S5T?S{xO0(|dEhu*)6i9h&&E}uvc6i*rm>yEO;Zm&SMxVocx45e zpw4gf!%8At{R(r5a#vRgy$TJ9yhYxEhDGXv(RM16bhTvW;p0r^#p5*Q<#vu;unPZS znUnVDyQP5Ws-@oOxJqGM=&BP|zeXtYtU9|FiWDg=#rvm33N1 z*1GD&>;iO~-{TjT%14jw)XWfTsmw5SlIm$I%PTXM_9{cwU6-7!WtOb0t(K@xmMXKC z8Y{b&ZYu|tqALxm*6XKrSd;v$)miL*=TzEHDKs}8P;_XzQ@m)oQ-$leQ%0FO)-QZd z%QBo&RM&E+KGcF|d~;R%1xzZE&}+!i+5{$|MJ3L zpRjQKC&@rhWnKt_e?1jSq?Uoi@6)$YNjRDD5(_YKlqxaP(ybqgvK_`iiVO;d>jVGG zv@MAI*yr;&r_S~Me>_MlnZHi?4|BXP&Osg|KX9Cp`fu>9Wn_T@kZ-j4IA54y^n|s= z2}GT6)g9qm@R@%CAaF4YaIA}GVIh;?ig9*WQ%yph5tR75rcrbpqJl_`v_UxoYpWRV zvfY+RC5tZbzw{5gDSP6w>MpY}ffO1(%jkZsoCfr8{x)98!V}!Fm)JH$fFQT%%<3Lf z2kJ)O0zRW;f(jwNVeLi6>-dL7ldrgpm&R8sdcwB#SzjJa|KXgcwJE_dtOKh^3g2Ks zfRCTWYUei68+{TT0^cQnpV1(ricW&(w z;2D-G|MF(jy@%084xYD_7Y0dB>rrR~8;8j^7eAm9HurH(99fAmU9677#L*&7`Zc1} z9@m9GO*XgW+Xza^+RTfV7wy`uS1!Trflx?A5VqdPMuG5> zfvowA7HwIKq~6qMPVsG$&rGWZ$VE|Dx}MO~$tQFtSkF#59^Sogqhy?C-QMBi<{^{^ z@qAR(b|z-gE>*?&m;l_n*apn%*b1^3XMWjqT-mfic*H*PTH0H$*x&lNT^o#4oyaK^JQ1aYoDP`T zw~O*+E_46(P`Hw;f;<%Hry_TeghA4OJrp9$$_`A7Y9Vuo;%U~<3q61Pwo>tDT^B(e zCyD=U$I1WPfzA3K{U)l`phzkd{uTzr!oKDJ3rvgzNjNE5jK6p`3rbp0B-N`6D3S_R znp)cNnfI0Q5ll%^ke5rqFJX5>z{~;{deY_m!0tS!!Q9X0bCS6UnoLRY5Dp?QZ}XkeopVq_)2XB)ESW zkhCn_Cmake8KU?I+mjBLc--adYPdz;@#hQX>8e%ObbI&=XmHNIeit5FYu;wm1T}s0 z0P}dyoBwJ0?rK!8pW>UduvY6B^%a-}Bp6v7hKl^A1Fp-hbN=GDLp7A!-1gR``!|y9 zsx&~`@y{%6OAF+Q19^o`y|44~teZSjjj6RJx9Ff7=Qld+6Y!Eu4>OSeD1&?uX66ua z20vY{L>a+p`g5Bn$bXbxXHq``*%<`bfisR^z0HlS*}^iT_@(XuJ;>%kl>qLi-lsPy z-CD9GaDWS*nHmBOhgD0nofkyOmw2fujHQU94C29}sh6dQFD~cUpW%4s(qt|F6XQ0c z+zN_u`*l-2F|jZNd=T50E`646 zDs#(RO?2oz9u@Ohqd0Ng0-xN0q0g_rpCNP(ucv#beoGx=6 z%KQO0ty1b(^SbbUk{h2^%Vx0woIzItDSqKF`$%*3uejTYAY{SZESNU(+!ASz8>EUB6I&JzKZZ%btH;$qPlM5F>bS*d3{bH0=7-mtD zm}7OSF!0iVpN+nea+zxvxAbml6zCuXn(~?eLtN%rhz=jz5e`3ZjwP0ew#@T&3=yjM zXk>$3q#Ut?EEDpYbLkr~CoyLoJ<=90j*o5KO zr5VEK-=N}k??N|5<0Epi!WEk4s3kfV**g(~&28v(exR!7!kce`{H}qEe#2s%*m8Weg4#MK;CIk@F+PBSZx#ITU0X_xHv5|G?xJy!$*N7{XPctvRZR0D{TFC;%X6 z5#U=ydI(VB2}re#SIueC2}!$yMg?4z$%93mCo=4|_?r)!92*AjsCroMeCMJVtn`ky8|MnR0asJHbw^*BZ!{m4l0)Z?h91|hR5vkc9vp)s4R%JKS=BaC%20fUe=g9#Rn|OlpYi=c zUUpG#rsNJ4srg_w-_`8GVI#pypVR*3ASdrUBd5B!LK8Q5 ztapT+!^3bfLDI}7Z7wZ>mYopevyps79Ozb1{Y30h^W;$FD~M4+ zW^pBac*6X)h*X6m25n^0xGqT@lo_P)Hivpyl*m*3y`S!4V{U1FKWc8#A&Ly7lKs+> z{*dO|>GnbD7HSa}{gf>bs!IXTeK11Inf=t3t;J5ERt@QW3gK}C`?ehf?a)jgT}|Dt zGXp-}fd@!Wp_szhD$o|ckVz3+pl9qvj zcMW`DasmE^i*wMIc?2`}EPuV?R1MVL^@lIh?$hV*?>BGQ0a`XPtwG*sWH^}2RHPA( zu;^dJ1}Q_B$HGHGKL5HHbir1opezK=%ja35I+GnhhNOYCqXE*+V|BonrO!D3XopEw z@+Iu$`x!_(iy-ZE{MF7j!V~dd?M%3S3p+_$hJzfCXPShKcLr%k>mTiyv5)Y7`KukT zEOps`w4>5;GHG6LhJy>Hp_w=9vizmvZ?R@#=BRuYPsrM|mzi3gk)wD*PGB>UC%!%@ zu+@oP)d|WpoFW(HXW`eIVz0f1iX*gG)L`UbpKiILHvng8kcnWKcOAHq_lsES{0w#p*RS)4o=N$xhM$U~dTwHBc77T@#oUckYpy}`aEP7E=r z^izpdU0PR9j4#XpZ#+QByVnZV=A(l%_K^Zbzen6XTg)HVq3F1p)r$2hJNKuzqd>{K zcntE_VZ+G{K1`Z*?E7ne`RdSa*RQvYAES-QG#4GkzCw{tu?If+!2^77gc{S~LrwQI zBFC0v!P8ElnoV1EI_+=GcG3_0O^13(9r0;1nFN+iWen~sFI5kjg9tI$?Hta*v<$)9 zEyFVQ7HxG>%44$~z@N%K;8gbyb&z+T<{o*MTFAAUi_N=`N1Tv211gWWIi_CMPi&W5 z5d|MnMHG7lE0A>7Q6lNV+E_wqR^P|j!f0tFXr&A3%CylS#1`T@S|fGX@bgunEHP-B za}3`$2}#zZwKHLn^4~+sQMhtv$v{4F-AkD2#gMr>G0u}uh5yWPh%!yW|Nnz`|L1cY z=Kq-Eq{2I>m|_LY_)?U}D}Q(HP5r5^{dpGd=NC}&E>xgM;7{crBtK`1|A4LFSV5m_6KdRf08Oge_Ts0gC)b-rOHO$osT|eeOj$x)_?Wk||dHtvP zKR$B3?JsB*lA&~n8kZr<5Kt^nrAg?x$PC^Kwivb;J_oxq ze~SrQ46+1J?hl2C7L7uHNq{X?^M?*Hl`plE(KLuQaL+ZNlMPoksg7;ceoYqcsJ{Hx zIJBA=rdxG1z7t_=0&T)dbrfqNwa(_qquHjmL-puA4Ag!`26tEA@?0wtOfW|x$u@}! zz$CK`LWCRn+HCTn?x7d!p{Mg}y zS1U?F_+gV~7}qa|*ykA}(gzG>`dVc0h|!%izUjLg7<|drPm25tG8}ToV@u0}5%xVE zVP*iH$OHBLPiWefX@~~O`_}+(3obMT1kT=ZrgWnKekyHz7Lq150$6!VeKtXiOrosK zQi)bJZFHL~)aX!3H>k70eg|FCw4r?YJk5MpRa;w{CI25_-HB2Z82Ixz(IcX?d8KT4 zI$v<2Dez1}86e;@$+z*?<&BEoiSXqBH`6pqHbOGB&G#irWf=sq6e4x0h6S{Ln5vMQ z0q~jV+r#}o9r*HosuZbesZypulf!>q+9;X9vyP?cj}i%u>c9Hlk5-~CE)^@!>+4)4 zFKFv9i+h9cMo1YIUh!?iP+p#fXy!x^hyny5-fd$SLxw|(ia)T|&#Cu)FL7+H`b2Ao z0-XIix{==zBvJJ5`jkAT0&;q+la$}nk@dqlQ8sS-bS|60hA~2V9=EtUgDWj80H?644%7TI7=Pn499Jtg*nDS!Ulmuk|llq=-( z;-n2%_#VuB=64R;2{JH%&c2x;NIF@=N2VMX?OU@btW(o#d1^2P9=33lO=me1K{(-k z{z$u#N8-rt-iwWr$~J7M7dcO3^`{Z2632|x-h0MN(A0#=1;sKs614{;nrf+nTf{$++{9DlYcsd5klayE zRdSKs(FsUA+6NMTE$$(?d!V%`CcI=KJ#@e#qjwiV6z=iJ#2%AGCMqglAM!{Q@72(( zau18<@LY3Q>R)G3#3db&+Y@?b8z_(BlV6|_x{zH+U;C8>t6ncA%t+rP*?)dg{}V9k zp6H{%UYx9z>=cQ_qas6CM87DEEyFGSSz%3Up)8t5?T93Xhpv}UU*lvMaE<5drBYFwT_ z3b?y8Cgn&@G`lpWodv_->4MJI@6Y{Bhi+2@&kFlI#2g7tvgvm-qZbf zi*NiT`Py$mSn&9_NSFPRFzMZ!zOM_)k5A=sNY5%HY2c7NH2J-TaJr?zU<)5q$Nm*a zP|8Y3nB10!Dsuo@r*_teZ^cS;e1y6};kfdn&Yl)s;f6@jofK`F-ZJ9TJ&_~oBX64CQR36K z#*Evp(>A{m=TkT#AeupM)Ssl}Q##Sj87$If+8*N*KjF(6H`T82#7nbAbb?e|=E8Ea@ATh!rS5YsapckYo~p?0}dUVmO=^kp0o#a2b#1`xCdRe@jbwp7S=|M zs+Zd%@LQFvL$Dpitdk2)e{suqrkiMEJ7=M5oBWz&(BAC~$^Kx2wcD3%;sCH7^}#f#a>cFLw{B!SboHrP!t^0DsBxv- z>^e8JVnsxKafM**O7o=@akC41H3ba@)A`cgvkNzHJ~UAn;Kuw@!K4F z37)v7S#+gnN@i%IDsCuIGu)*NXU9OTIQS__91abWE>Cif^asO&HbU%- zGaTmJky1~D%*hAGx~I>>lQqnCU{v%>96FXO#``;H6-Nf@|s(N;{Ph>lXpNW=N zr9G;CP|8;a{qEKyt2BTynf z$cEr~b|({|qE*nSr02S01On5Dzwz+A-{;Q6Lti2i%oGphnG!H3aeF(t&FsHSulju4 z9nl4l{z|EX*TU^^)E4Q9rkJMa^W#G`mWI)QF^HHE@kw&TI;6m1!PpDIiD!<;b3<_7 z;*{e7(A{G7VpR2oas6G$&M;Z{b9?ofqM8TFip$rP+gH=z4x=H?rLdUt z6V2q&Rt+&B&R9~R3)21+SUG+5?o_jSBL{C*_Elg5f9*TiOO4dpWBi{oFKHQ-o(}1K#%6+C7IM2Alsn)XIK?c|s5dMySVC$nL4rRDetrT@Qd&s;QlZB#_5=xH= zt6_cHfn03Tr|-ct2JibppanST5^-h~?)7RFYn_UHQ>63-!l}dbf$2=uf<@w;WWUF) z0kAAQCg(s*RL$49TgRuVZO`LJ>0+cerDL!*=e0aWipZv8T3PKZYuBNRyN6IU^<9;n zR>Pv~q_zs7ORh%6;9O~UN`6mbGl*k)d$@{tMUj9a3LDmSYdSk_Vaxjl7THorQ5*MF z8>fKulv=NhxEG9|SPjeMx5K;i5}>Ln`WA95v~XO%{B)5=xhOPwT)*N}zOtok4B~d! zOZ$wdfOCnmN?vwEyMxN|tS(jBn(#aQf~(60C}1cMgol1nxu`QC{H=XJxbQIhENEmoSPq{?>`NuTU`|^+$kU#B-81G`edo)eagLW>VSi7dbrgcjz(6 zcf`M2Q+rEqIkceUAjJRWiMX2HN!?OWB}ZS&&1E~*#E%RD9>jLjNo;{3XpwI7Bl*utGy(XJ4Ay?3yC zmx!tT`;K`SN4wI=bCXcq)&-zH+~MywlwJ z=2?x?W$El!J9@IyQ5msoI8vgR8b`4Ld5?Lq z$t^Y`hK&GhhcIL{vNAPdy4$1^lB; zX|hTj89M0@R*3^t>5#j|xm6J~{BK4axMrQQGdO0sMo5Da1x6bjlTJ3JVQt3g`ZjD= zP(+7I>^TQA2$`N?=Ef`i_?{*RI&I^b#-+|NoFlVa3_eWMhdj$BE5nG>l)r@6Q|#xu z#VQHuZcCcy>VnNZ4mG&CW63>@zG{=y{Qf_KDU@9&Q@#ubc0^Fue~L*sZyp?gd+7Qk|^ zr6Wkius%(H1RiTbyY4faPUojp>bZa-P!<}nAKGzyykTcqaUR_<+EE0{2!WpN>a4J^ zc$nOT!G9u)1Lof0|AamN(-QIxek5iUboY4sU%q>A|0jUyKf^5$Lk#~-0d#HnxX>8!~xGXvGwAtYU}sBXC$a z{Z>wyq=}9tr{z${xtBMdJNWuM5_MC)$v;5;Q#51`|NleUTL8t?Zh61By9bBh z?(Xhx!6kS%?iSpg;BLV!IKe%*YmngXF5&Cs%sY2x&N=Vg?_Q{`?p;*vruKiY-?IIz z=e<SB>|^Hg{)3aWB-jrK zc4Yaau~6b0yHE(kW>USCfz+hs0ra4O{LTn_b)-Mb0xfllDgk{gd+h0BOD)0s2y8lu z7h*kZhH@Uoq*2(Rb#HdWv<%wgUxciVzde6JX9@lh_ou&Iqt=Tr(RvY;GZa^vt9lki z9B+CX1ZvYM8UDZ^vME$PxzgG|;!@0UCwdNdt9vr!@n%;4(`cZ?&rq4XuDTTS9oc=A zs6qkm_8(fmBvjL@GF>oO#$h1pue~b3hCO5Pl^P<+AH}E!&uOK?X7B#qDyh(r;rCWa z>mi11+NNmAcw}|7pa%CBP=h-&w2RzDfrQLu3J-f?27&KTuZ*GF^#=ug6wmNKPT8*A#SfZ^5TlR;bJ5l zs>s=fC!I1>6_zvvbj}bcq;QSOx}d94pKWuUO;v`e9eznr1yJ^kba{)kr#CEMoJj3P zWCehxYEm;vfTn7a*nJN9T`jf6{l`>I#|$##|GirJf8S={W%+B8PdcoJhUDUqr5kr9 zV8a;$9iHr)JR&DI)*)#}h#^!FG#pwmlv=48G!zy#HWoCLC>lDnMn(Z?USJ+u7##$T zN*!q)mg;8a&m5o0it}s1^XJ`1;jXNWy|W+QmU|Nv%dgE%UDUy)v_yt=+CD$ANg|Uj zx8!RaBGr}1w7QII70eWKdhmj5VYoRK1dUmyj9Yr(f>!Z|w5;{H=VS6jhry6AkQ0&A zkPBeUkw@NXaFl+Gw>4Js+(v|)oy)z1u~M1IYY=@G#0L|Dl!3>=Ze?BjUBvna05J7^ zQ#J@1hJ@h7#zB2MrFB@Z7-5oeMjj1*A`=XEyS#)eJ%6n|_RmQt#WB`^^qVKK0^`Jod%6Tqw z1-Wycm6@q1Z>tCg0U(_($b2;uU8qO;1d+ygQJA@POloJ0lY8)kw2>AF((Z}57)jO( z9s7Zs>=y(-%;QZ}Hu54w<%1SrfXHoDga$xkT_Lyb&AdU!HIpk`XHi^z81$g8FuTZf zct!8#I7)4eCmi$hMb(4$V5*Q-@X{t)wz24oh9@tK880YmEL(VGV zhT;oYsTi}Y8-MN52=auHM^dOUVDWYBiJpd@<;5NM)n|KkqWu_eXUw#%7TZG;M2&=l zmlnj#k380sI!FtHvtL5*8VfadNo^h0M1!1Qu>!|A9pS;j&!TkICF%uKaSyL{<@h2}{ zT0q(tptE!Rb!OZc9nJ89^xW9kC`n3AvK|kcurHwMrr<|b&y&ER;K@1TSA_bJ*ZLS3 zHK_VfBwD{mrHY)aAH63pbsyf3%d$&rW<>go|CSwQW}SvKFGI!7}RF5i^9HN z^>BZ^hr5fj7d@gxlB~|c)4|w0&$!y&+`n;d-+Qp--F{E9Z~b7zv7A?DH=WDr;ov4J z%ImwELK)JmXQ=vX^F)jH%k)Lp4^P+GP9q;Cp}VNK{voygU)S(2TY(RphWa;A@>^fA zcHb(#ag4w1;dVvQUv8Bf>0>Us#<>CJI|uxk#tCj?aX&o_Aa{gEcD-zUX~AZ;doGSw zi;RA|UpgdcpS6B}%TnZy&q40(kdGDHp08F^+z8Y=>^kh(qB^_ro_%?#g!kfE`v7eL zcY|Ca82>oQ&QW@E&Q8vHnp=}DdqzLZbdr(HNx|&C10@(S`;>%%=Tu3`EkN!=Kblx^tgb)+ly#Wm#b=X?@QJXlc!#UYqsmb!+oj>k*-Fum9DNT zwLRgDVm*YHQ@)!*fi{_Y)wPCC=LK)|v%hXB(Wm)e!+L!VLk7lV$p(aghfm+;Hn!>P z(PLxU9Kv5S?5+#w&~gt}>`6#b!Y6OsYtrZMGUb6&HGIJ`<={?dC_hh5T>?EED;!)w z6_PrL{tSLs!th2ucN^|-S5m_+^`W38Fw^a9KJb1EG`+Jq6W=L*zNUl1^~E|#lDqs^ zMq;$wgQY~cuhp@PvZR@T0E9BCZj{Ui98nJ zYPaK93LcMAP|&w>2A+w4`Uu9|?q!9cl&ehu`~-v^oqs)C7ABw!ZbEi53m}irQxbS> zqDL2StxBXv7v$U-%c$Ai3YK*j4cbI)U&`UEz8IfJc($&Xk0&nRT3c_X=P@&u5r?NH zFiDyTGVAGnY_(|)ELD6cC1cYmU=%3N=s@W|YAFwN2$AcJ0Z2~rfaybaYkrg$cZh*qRMNu_u;p%f@WT%}W@(9Cnt{?E z>T&H&dG5Z&Zs}|)rfu;9TlCb+2sox|@$k>VZz_iF9|w1zOG(+RhC36}(+%*3 zXn9PqMm{s(Yia<$w{xJ^TM2w-@t6Tv!{&@^)&ZgX zXV3*;dz@!dls0**%;YUEe4y9yn_R%{87~%^is?O${hL!6*Sc46+g|~PDC^*xwm^B* zi$-W8em$?i8pwy1nsZpAPZxqfd1lZg2a?I`^-X>_Kf65Chtm3cqIRBSt30cu}vhXN58M6I*5UpfOk+m&7{L&$f29oaFO$?$18O*qKcKO z45cKXWx(ZIH-UyMAi)Wm%QFS%1WaM*U6D3`#w-BdA?GKf=>kXb;5s1kU7L2qtptOc zR@A`qPto=`i_mWBiGR(eki%^R@9^jiiaE8%GH#PD7n+TF$lM(+v4JN-@--<0@1Rj! zBk;o~Vv@CjzEspnoJfyK+{sL458E1nf_t{koQ|T||1+ICR6mD|?4n5~3N5nBl7)zz zG*MF+*{-O}yCE)2H7b#jYd}4zId#zwi%ewm6p?h?W#Rxf?mUId$vqJpc$tn&AX8CF zz??>tXZ#9+nqIviLKzWRBj&1E{EB=SP^jo`+Qges?fIxy5WMWwbtxx!jy9ez83WMsoXn|p(kW0R?61K}kY!sm?j5E54S0HdU8xa0$cSL71-hqa7hGMM2Frh4EB zZfJ+!?=U31M2lLewZuM>N?a{ziBgb|Lj#z_ci@Sd=Fz5cYeRv^5({EQ55EipNcI4$nLUGO9f(hVE$lxkOHoRxQ!^5$mo2 zZGB84%N$MODoqHV={uF6cl`oK27IMdc)^mRYED=*h+&hJzY+zEl;j;zG!?42B4~`7 z^TW(biQk$7hVq}BO8wGLp)}rz%5f*xm`Pu0if~tDYbkUFCYMq{53!i=Lu*0SuGDA4 zR0N@{zN_Usto4P~!X;{|={fq+cZaMa=(5xaee6fnRBwE{V8Rc(0Lcf~2$)w6JN2%G zIjDUF(@H^E^{j;n;tW2Ot_?k^eT7RjzG1tbK)E;O7Hy_pIdixgl2GgZ>uOC_lvYT2j$SP$>xT67wnT z+XFi`BjTdgdG2|N9*0huF^Pga4oAr(sNCiQTpAyi4YGfr@-Hr#wFeN_x-9COUk4W3 zA5mqZvO#^+0r(AjK(OJ|LX(>De!Ngf5iLz9@52F&CeMSJRDNC5i7Dd)fHgu5){(Zc z<{V2Xh@dXY9!UDp>>3;lfh|(9gp*aDOTEGpEmEShCLDXSz#R4G$!uMKB* zGQ?0OG()Ec-W$_aj~cV3?D1i%*CH*VD-6Ibsz#eeALFGAIO(SjTn!^uGK68*LM-mq zkzZ+%(;MSoiBZs-Qtl{9elanx9CM|t4Ag9`j-HRlwfF!%TjxPjZVGet<$Z;TV&#CV zYh}#6Q+BLtEz=^-LZ6qurLpf0qufF_%u@Vu#uA*Td}?DYE7x8d7nkTd%1*};R;Hr? zZ037=yRS@566-i01pzVbw8KwJIIE6pV5{$$Ex(dkkTY9+MR$|<1+C0AsI-)ROtl34 z&B>R?a>+7Q3h*N0!A?2RIAoHR7->mTGoKo;`;(aOq9BYW}UD2hzoC2a2l{YW{5+!U0#@ zG0AnFkJ@8>)B5gxtURH|XK8Wm93#X!{_T|Cb9BN(^f%%phY`P=gqS5Xt1J@?y=Tt; zimKEdp)c&8bU{}Ay!A1w*Ht(xasbuxQ ~3$g#KBi|yVBg_>SL%jpl#Euu*MDGWE z)WKz*%ww_#xGO2fdPkIrT`!u6KIvBC(72yw`lv&XJh{h&K7=IuUQj{-FZB67Xu18= zt;7+C7qr#VKWB$m1I7j{FWyyOJCHH$cyTcGO6te;SYCdpzP{x3i9LqUk8V#G7V`l& z+H8YM-NdLK+EUv&m5l8(AJ*$6jAJH$t0SY{bkd)=mCz4hb`*ry-jb^(>D6qBJpTT` zR=wwiP~D^5k`rm`$hbIrp}7;Ipft1?-^|JjX(YYgzth(O?unSh?udB>LuL6>Z+JB| zD<(66oB0vWPIA3%hww+NJ6Cnp*4o{9I8g_^J^H!1mLrhst$j7(8&?D>SJawjQ-q+B zmacn6mHW%8KZyWo4$- z8B{oBho;3DWL%|T11Oy8C))M>tekpTnsMRsHnAP{J6x)a)y!~64Mtx}AM_GOm4Ot)Dt8jLB_^Ve6fw}I^iDLjNW zW$%kpc;zZ^iIV4>hB|CQ3R}ItPk*R#xlykmd_OlD>uA$x_Ca#%nx(&THu1ZQHixZRj>?W6z-$L3V^q5_`eAiAGuYksHs3$~o_b<~jd;S@uMsP3pDYZNPQG%4vJqT}!)7&9U8W zenr;PaB{wo8>iN@{^`7*HuG%+y&jRnZbeXa*^&C9*%AAq>20fRBj1jfI_;v8WOdL{ z?J=Tj|7{1~(USpDRML@$zDjnuyp~aaaaodM<9w>)_*_|w(vge4kt-FGv@bTdmM_tL z?Gx3R>@Dzznk(0trYn_8*{1e$(10TtK(i7`h+tYWV5bE7&^0%qa z(yp_<25XV-6|0xB6kBE#+l)^A+*Fo6RIOXjD*7)DPD63U4-e^|(cZ8;w^Eb1f4N=_ zbjkmEc*P1AR_b{;r@?yT55Xjaf;Z_6X8W9`lAMTfG;074ER_&bZsRwBOI8>z@y7U~ zS{6J+%WDFktmKibhS8*!9*j!M`IAKwqch*CS2B|FN5NNO;pFSj1y?y7<(sXkbY8kD z871_ZS4c%8Ir`;Wp5f(Qd#aq1&Khg=88d?R3A>N}Yy!FJ$dlIPhmYwto^~ogdx*7T zMp4)H-O}o2nEC5<>DvyXvVdl0&YRdgsrAdbCH~9|kKFv{c4k%ea~PXF-?$8KR29T? z+_{-&$i^$f;sqhx#!IrsTglSw8otJR&LRaP{-e!fNg`kIvZR`LzGn*++>NBU`R3@h zN5jIi&eOTRd)C_{BkH>1CKGEZEo-0AV2GNWP zXTUs`Oc}qKarD0lm_Hlk=l>e#80!iPea7B>=qf<}g?N+q#*F{&Vy09?b_7`?uU&)@ zpU6te3?iFWcq$hG;(ZwlOF|@svlYM?JqEF|;4(mwgRC}*3Pz|{Hh}pszdMeD*CG20 z)EWn8n#61%YX)(Hl~gUOAmLb1dXEqXJx`VeY}7%*rb`B9ma=nDl(pf7xfe%E0x{xQ z-eZsNjmHI?Ao@#24$?0%#K2Ee>iIlgd{8$EG$A;lbkhYYKrgPj&pC*$c?0?DH$F6( zhHx?Yl=&_E^-$rVaH_2q)Wi@n`{9f7&HxzEF6A@lP!o znwDnC77BfAK*gE$^GFv8h;adPn-X1;&h15iLhAf7`4g89>Qj6#4>=THG_4C1{j_SO z;!rIu(GZFYg!e3Nnj*C$l5a%PA=X6kY)kyaCc+cK;_PBeOlMEW4I^@jW$0XWsEUc* zD9j1n)*pk*gB>UW!Xw4S;9u2M)#7;=ig-*vc?}5j(@F4%5MB2l&6QgL1O|wK-QB ztP9rs>frg<9nMuJRs#7qOgApFG(|9QGE%q#V=H(f>=X(S77A8qXlwGP*vP<0|0Oh4 zaM4ILDCi(QUmmHcl)!obGV%Imsh8p>yzuu4(Dh6rR86X8LrWFUry3e z$4%(lPh6X32DCiWW2%~1xz4Rk)I)%~O~&!Hjz8ARcHFFpn`JF%Mc!x=nW&oZ z_J+dK?`Y9RYE3@LgOxRtk_8^ zjRNLJeZ-^trU^-X zjA2s*@UD*W1QU1l$aIxsZ;XM4j>f)%RnJL|Bxac(Yk$|aQddh<)vq}+LHtVIdmt3+ ziat#qpY9x;VE@xF$KkKkwoev*` zn5Q`^T3mEEv@{HL@QLObbX7ItM^_{6QgVsRcI!eH7pHa2;OpAkA9l-i1)(K(ABzi3 zLkgxV(d$&}tRy`}O*ks@Dzj2+sOXMW#}LhjB10qG-Y+)kfzD&d!Y0i3HIIRN<->e( z67yM=>*<43YX8vEOPd?pn`Cm;>J|QL-H#tHU%!U7?;jrGU+-aQf8N`3r$bAo`UTvv zF7Olf4(s@7>AXyU&;H9B((jIT^~^N)aT%pa-R$QlPC3!?u2+B!oPXy1My%_r4#QQbAyFmws<3#Vx7 zYw%>m33>Xr;Jw%v?o^cEup2N?QoMC^RM#cie$#JEdLTdUaPD_@e#Nb-NiWE{nh7j0 zX*b&)_%MKaB~7I@XwRAS=BX#QaO-}V0kL=!Bs@Tg?Gvj@_2wM;Yf|?4nG$E`O{;UF zpG4TL-|ZuQ<`C_OFz2sBtA@zKQIDzR%pciXacNau<0(tVob~%R&D2U@TqSgT{*0UDBTB~S4w~k;wb=Ta%g6vmZB3<}5H7G) z@_^J81VA&CA%M9y2t%Md7L1>&2l0g8n403j|FxU42la%S2_D?IBp^E>9_)l6HQ1(` z3G5pM8Tt~5J_guQu095u4yBcXnCq0w6b9uS7;-Dc98`=m5qzUa1+1=4FLEAb(1MH@ za0)*th3Ti%R7Svpm>AKN9duPya9f`1W@2|m8)O4NYn^Q`17h0sTraMNi2AsRzIGMYaVR;M_{V0lNxX<)1tS0U&6lXn3PIB3*8ih8!6#nMGTB0E0RAzAmdLBwnK9g8)nSs4{k?Odw&X^ zkB?SPn@=5V=dYq7#uc1MT9gw)2q*db%@EB7DIWmeLDtVHB1Qs-yp=eGD;9!AmokMa zW=8r^_5d}Xnv6rU7YuGZbczlhlB@wn%*JQ~GT&Dp<1Azf9L|^drx09raJ=rIsIiqk z2279%Y#%KdOeic;O)#19Lyt^+JfakNYOnwcS~Nb!RZl7OU<^iRpek;(7e;6RIz(SQ z3t@DqJ_bTJODjb**tP`|{CyBQbYWSr7>jP;v~I5q%SvEAN*Fl{Uyx&SuqjL1?;{Kr zr*0MYConu8tX2%Up3Q8Ucoi6!8sDN|Czc0boJ=X085>@lEFN?y^q>noY9K3Q^al)L z2s19cYl>)?ZAO*_Sv<%=7#Rq7RB<&|8MS(LoWvthwXeaB@t+Z7sVSqwii}8QiPkYm z4;d)3D_sqGnSLw#1-n-pTM+T59Ls5B4&*HK7rp6gP>UXkCIiKSJZ&Dk z1I&UEG>tt2+`^YhlD!82wg-%~2w2XMpN=wi1G`p|tnWF2w(zMof^ddQElBMl94S7) zW;x$iyP->y)Q>cTtcM!AjNJ3*MIYP8>Oi47f1r=t3;f=dQ+R;P@)!agxfjq1H-?Jc z0lx>s!cFUiICkEG)q%X{$nr^rgM=K3{PLYX7&^6tKqQV>KWajFfjem;M1q236zL2O zxjVfsQbM(U$xbbTgk0veX}BNxP0 zpk*pFZpb81#crYt;z+%)3og$WM4y21>q^sJGP&@F^;kfEB$#)Xk1u#^Gp$&_;OH{_ zNG{Pq$Kmki+IJZon`#ZvZFc_Emv=&;FYmPa zB$C}pNlV)?A9F{()$%Q(ZU~5Y#q}cwb6IPhRZn;2xK?qaJT7(wz^%;F0uz zBbmzV23Z*=j)c{cg=z{#Qw_R_te$z9!2Y2{a`OL+ELk2GIQGV&Bny3Q?>r z8!<9tZt%=*#uYZ>KKXLRyhOC^eZz6rokN$~0~k|%)IJI6 z&=iXDt*sf^uO+$DeiT6I2B$OX4&YJQjq*TUuehRYN8K)&GbfA=()@*Nh9_L(H>5wN z{)n(=_OkN?8TQj5xyBScR^?H-odi+y7oxQ33r9u2DMLc~8;cK^yyikJ(x~P_9fWil zpbz$vEcuiNU%KL?o0Uh(oLVRvwc@0=jz`O!J67D)&W1S4A5-&g>phRGIf+o@QP$MU z1D;T|6Bs;WGUf%mkJ_%y1D291FM95vr3Oq^KMgdVQ3{z zNV+JMd?B2zs}av~aZnwlte7XT?@|`gDciD6d17 zsBzaNtSN`jn*TuQ6mG59S2?@!dQQ^PrI|R7p##Nr{{5Vyz0}(6-JJNjl;CZ^=PJ|^ z!j3?%!=%z?q2#9nW_2T9@Y`8!wNJj7$W!-IBCmLBg}DXZx1%n(<4QpQksu(q>R5`L zIDnCM(G7NY`77-PS9s_Of^`~@1G!mH3A$DUKobo62oUuL;)N9QVq+!`PUcKr`-f2g2g?SQ6`$ZH$hChG~E$dB;wr zGBZEHH2nn5O?mzMj=d9E=dAnbT5EuzDJ&MXQdMEZ0AG9eKoJu*4RCM_bG^d z+=eO7#C1o*0!b!*MmMsKV}uzDZG1*o^BFd6T-~!H>GRgmndfRJ-Jcl}^ait5$;X|$v9r?E&J7deRvr8STrJ1bLuXPA(`$)bt^Cs#?#wG&#A3NE z@>Uhz0zhZweC4%z>u=sdzI%qU{68|SOP-xqc7KJseN&z#zSmqizMR?5S!tLwoAFa- z=i6+}M)C5RBAfJeSyGpg7BD+NjFU9-vY1kx6yV$Ypk(C9HMN*ya1*mn<9p02x#8(o z_~w!A@9_RJoRJx?y^Cqb|*zYd3*r**R`%bBZeVzBF!W}*P zw)%_n`nb#bw=gD;xEaZF*|wbykyPJkzw5_CcOziimaWXEhnd52=A>UYYm%QHy;O_s zYX^KnQue%uXMB?wGwn0X?>hjzKG%#T=f0!6tH!Oq{0wpDwY#@O%|4w*j9usD*cacb zpgtj2WC&YPJ`w0`+FR#$6>6?+^p*eY8Y({|v75h2)#HAk)@^)MuOxV_ujKpXsb%n{ zR*~~2UeWm}(>3W+*L?R|4ED_%AmpX%gH*R2B{W7=Xn>a%Pdp$!z(R{+YC;a>;|JOR zKdnNMoMe2OI(*ncU2a$gdPbJDNSHmH!vIMwDdWa@irQrx{3SC^M8ZZp_^gHuSSB4t zjI?>S9wk|Ly9!MRohnS559#pVwKB_bKQyN0Y+8^tg)>?(fa?J7nDlx%nlw6h)HW{J z>88h1-p&cWdMW(6d|S8LdYP&A6#Y_vUv8MuF^QwmK{&fa*c3Z%x&HQNV|#RJUZr$Y zFt8H*zsvsl!N9=moh+G6>|6j&%x)aa4kk{{=FHMgCP1KxlZKPIxt6uFwTrzIg^Rfp z(Av%fVB}=(;_77gkM#iADvI)bB51yuUm~jJZLkFp5mth{Yqvx%)l`DF@)> z0V5x0-*#m$HbHyZM~*AblYFy3XRZN@ty`0VT*J#`^+K3#bzLx`;XpyL zMEJ?l^*-cmn6Hhtp7(NYOg-X8%F}cv-9+!Q#`u5)KxU(y^}%vRkILw)`hh;bw19%5 zYCN2hH1CG(w$F$c?-XJm^dUXh2Mdo833ff5-0Sr?Axnf)SwknW)F%Aq>u);;(+rBR zWEreYSW3S*rSsnmhKjGb-k(a=R1`R-&$KKkCi;-sp1fJZxuFkBYfFDmMjnvrbr}z^ zczbswE8?#M(16~Brw`=Mg=m1%d~95)Hq-Rca{}k;Zx;HqpIqlTaCaQWxdP&RjGMF5+4y(f z*6;g^&#?Z^C%Ey0Z9$5#dPsm1<1jM!p?Oj?q_YmS-Z@C(Q1XwZT#cSLEnfpJsc?zo zDv2FOJ2LGmdVDL!oz1UHrHj-}yloD3r7 zW~;Uwk0z2Ep?5;?VCj{TV$J%cMBkEe@>aO!(?VAz&MP(FS!&gLW#hczLVCvt-6&c+rT5@~a)X@8>5a9?ah4pU3h@wr5y)BD7=+XYQGBA1_#k)lehQ9np1%@c#%zgwg zFZhM~cZKXzl02j|NFQ;~P=bH2k>J19=wH|fz}&;!Nep0bX8Ye1`)wtVtZ2T6iz}#A zRe_eGJ$IYLSQ1#MI9T}}R`NB8)j?AAvgzYEZz7MrMqez}_ z9=?zc{^KwsR?-I&2Xu2vG3-AT^4k|P=`>(|rC_ayZy)d}TlQlL*7v%E2~E3|FJnDq zRt9?~(Yi1QqUY-63aqn)WMzvTkMM2<%zuy~=Oy;)jA}$o*&Z+vSjAh6uUDFfZzBHlAU6r?NmyyZfQ5EIl+e+HU^hid zevRkS`NPM1WwY9tL?3s@&-%-%riv=<{UVKT5Vs7H@Cp68qFmlO32nEOA_nCJU2V~} zR&sVSHNmdmZPZ)Q1BIJpAMMFB1f*3K^f z?eZt9uF8QRCAb<@$J%)7hl?e8929ksNe(57E|%PMquJzIMr*CcMbITLisf?w`9h^i zt*SzR?z^gna_0aY7BiRr0;8{Zc#Yq8MX9snJhrKQGJ$ro_s@Y7cLvcjM` zsC%1y?nNiUP?_f2`jQ#mio35i3Cl-rI>iEF7+eWP-fHFw)f!4o@dkmBXns*W55@c+ zJilc2Zi)%i6eV*S1W#M?g|yYAM=WcVe2mJ)m4ovs#wOfZee=b>pW5`5We-&A%HNZ^ zN=Y9{eH~ko+GRdgz^J6JhPn^l_*qg_4X~BVyd_lwdhQICj~Wfy4HzT#(dgg>nwTJM z!{EGYdSgsAc}*%LH~J>{ePfyvVi7~7gXl)K_v(DDPHcZ{Hs+mRq9m8RP#J}-TK{33 z?c=z2ErAmoo^0M{2BALk4KLWdaHpAY9Qg$MgjQnGt}oxsGlwy0uZX?|?c`EfvZOE3 zem>wXOX(w&QuToRE$sOyON0wZ)SKGZ^6N)sB$tLd@cA2DWx177RiWP*B8BH2h3aNHtqy;}^HE ze8!^cM4*zwWcC3gj`V2CK+|Ii$MxPDOF!q{=TGnGe0)UgW?FbZcTp4zHi{mWjAl!g zg8;RT4li)7%l&!8<88TlFe!NOuivKQaT8f+V zCVv=a=gB6o_3KSED^EqG*=%Y zf8eX@Wz<^utOP%hCM`Hf*ozmXgK()}eEw+-5wKBE%_1}|lQQ{!G(ZVKks8y0xh@rO zH1PUl9F+TQ@XdcSF2g2K@)8Y9Oor7f4N51vx>q(6^h-71u`1<3}*Lp1zRQ+VDKM#yq%UIlcB|7i3q? zwPb*n4+FI7MK5T|OaHRwg$nv5dD8ctH@3I<{nR09u;aA)gaHqfcM3~bRE!FGX(q#X zjFf$*j8jBRK7|}zbk3Mikx^3@zKm@K`SjxpIN}>k``#8|WF75J3RavvlpIFaJ|E)R zfMt3GRi$idi2C`lA$MEX-mGR5?eQiG-)+kX5)!}BIWQ|Va3tKl%YxiUZ7Y|+lS>5p z+&a^`(0CTFmvhE$X{7r3gbL^qS?hk6Dc())LeivIZf(KPhwth`UEQDI)M&~SEi!1` zA6Q4s<*OMmg?%07JqX%0#(R%r@owbJajft=)J{3^{01{y2Wy*!Y_{}Elv>B7dGP?>Y-2|T3=z!SfK|DthhLI^bMhqBn~ z@MLD+Iep#5?iN}&rYXcW?85q>(3KFR)_6o%k%XyMND)$)&(q-H*T5yXW-5c=*&bE)2R^K zcxn!vgHUzhB*CUP4(Lpp??$+w027;13;GZmhHlNWl$us9oKg$%>t#4)S!OQ8ToK*V zXjDsJ=uX>>oRxgF@1F=W?F?m?03^Z!==JZTmw%cH^UsbG6tI{8oYfu7&HlaL#65lu zj5IzDqCd(c#}Y-C`{Otr%Mv(fGZp`GoHULY(2dyBVcFTEyzG`~sf#6+VBR`h34;nS zv|||`_mCLybUw;hQ&RSMjcXe)37paiE$y$|8!$nPD<>b^2A6Z`!6KQ3#qJ-x5B=CW zLv>X5Npenq{PLsViEEKfM67(e>3DOWj9QcIWp% zMeF**u#psMr6S%p^CLH?CYIRf5LP@K1R8uD)1}86-OzF6NCd&t8;Vb`++x~XrYf^{ z&qc<&_j@{pdS4E^qlwGNGbwT<5;#1m4zaqtGH0ZI}IH;Qx-uG1v;{wy0k)0|xT?Kk`X*0}!P zN2MQe zWA+(Q!nTH($j^TTrEj31G%C`^-SbaSY7lQ|nz!&N#4Dj7P{*``qL6E$$j%pWD!%-r z88q$cI5Bgk!dTsD8}WDSf>GrR_YS1N-v_1t25|r6!^G`D*|gn9B@>|ef3vXc{}P$n zRNd3l1TMhdi=vC7NB;3)KFPpub248+7Is+z1#)6Ns27F(vRLgJl0N?#oBDxb(*RF` z4+=Z+Jw=KZBfZffA3~$2)z}!4A=mcA-YH+7QE`iII-=wIm4=)n8kEnw<2|_<>}Cjt z-q>7zE`1h_D(y4Z{w}w}T~r+vDxEIIYlU#GVZ|N0k@(K}EEFjzr~R{+a9(w}CVHp9 z?2LpiE046C>Hww*4SdV%2UY0%$A;VEkcvs3t~Sz`hGW} zogYq`htjZxA)2YXc!pz`ajkc8l8S3cP)t%*`@VwZ__VF-c1nB_nuP^}K|=|et`UFU z4#5)cGZWr)OtwMp`WoW1CO?rHF0dlrDgH$N`0YDwG^HD1-_5GVs4>3yaK|5jzCu|G z|Mp_PM>_s{fJ*#NFJ@-{(cIG9j@iY;$#;tCQ_Qm1FC=Q{UaEw4XfR0QPtzU1GL zPdSst_Eo}+L>A>J3lGWzqO*`O!uJ<`o5bGV1)Cm zhO*rHq2~!D_;3s{lm&}AJ;W*lPh;xO-pcrG3$w}TOUTH`(aL+a`;D6+LnB#UgARUV zv;GZSm0lY9AJ$0Q0;VvS>t>1H0bRUjuyu8CWk8Npw~6iY<4 z{et*Om8+^BqHYgyiIhSJ%F!~hpcEdX7jeUTGRynSZ11>6IT!p#P^vh}Vtq?OPzWah zcYRB;q^xxF?KBaAIRSR)*fuG`9A>CX>(@Tx#x22~czf6ckMx^wVs_VdGU2PSzQkt^ z?+!d5bZvz3W|pZ)E@Kl#=e!vU1j#}^Iv7eM#8<_mh6VD(t<;@(3aI7ZNZkwOj@(G> z&@y?8c}{Kvw?A7XXCQsRj$;iScY5}xF&r+;xi9x7^_kBS8*G42EG4~BSS|RFPRUxU zGs#&f-O&|eo?DSraMr$~VDI=Zxm8YjrE=`xmEdo`g;hoCD*>k3U?q4~^kb z`V#L5Ob@OD98@G@Om1C1gwEOZ8lO`U5TNQt1o7dcOynDzW&iy7$u2Fwh&>EOqd*w0 z6+?H6gYo@3wLsA*!&qy`2!V|Pg4~^Tf5~k0f>0{a2zCN<#;3mhb(KRSsa|}XL)l|y z$@9!6_!U-3;tx`8`CdqO(}Xos!e-s}!?PRjFl!_0Kc=tmQg}+J;Vc(qmXnaZfYN2;@&@|qRLv6%4HILK0VTADAvVgANGL96!?lMXFda{`hoIGK!=qXxPOZ+_s#VT3 z3;PkEa1=`xHxtLvebL<=VJ80EdrDGau;`%s?gwaD_y1quu>F4mPLNkrI4PW*zRKl# z#3>KkVSO`2dL~pQ`bfvxhm>_qG?dG;jPJeGkQGB?(aH=oiGrLnB)mUj`%Q8 zitnplaP`0yFWI*weVSeo#+jqqwMu$ts$)IGzks9q7jXDwR{sTXzWh(% zD6V|TcYFUEIOQPVNHK{B!(63N0$xGD!O0RG4btMY|9^n9H$Cv*fn)HWz`395`wg6e zI1q67x6J+kXXa|;4{%_A1BVC%9Mj?7z+nLa2dWSR9J6(^tbYIpv=aD_=&Ic?e*Zgg zSpS`u0fM{@fLTF-RoTJC+8$JZ|C0>=!=*H;jVkqmAX7Lz3L6<1m(3KtX=du8EmlC) zQufuqh2xz~PeQ(UC@uWk=41<-<4;f!H@I4W7;#xwtGMEPo|Tis?rvz9n*&a0@;=JC z@=I+>FQ52Va!Bh6q(%N}E+Qs~3M3L@XXT9tBH_*3sXK9R(d2UYNgH-2AE7fO8+o&Y6 zcagbC(N84S<(NN4-0KQ_Bd_NgcvTn55}m$~@dwoAlhr+c%9`C|xi4xcG|No;v{%IXz75a{RV`%52Se)|); zoGvbZkUz2cTbur;D%W5Bm^-<6s+n6DnYnoUN2^8k zuOhMyuu3bA_-0R+S8|+vEQv zW2-uq1>uiy<8X*b$-clwVPhVBMYdEYlMY9p_C9kim9nos63Z6>iJ|W8qs78hc+}da z3@zwf$dygj?F(i!L)dvKo8M0I%hwvrhuc9e0DjRGkuN>Ma&feqS zC;*J7Q1j{NKUrK^SXJf+ zv-O>QI%NmEt~`|?)6-0yhYVQF`b1)vD@7ptG;~gTL3xwvh)C4Qy(>+!!ch-S*)UK0 zf~hctWaA<$;yQx`$4#~7{KwoFl5a36KzgwKt$+WgtoUE)0V+Q#+1n}n&W=T0T>c}B zYgAXX#{{KKNq|HuDBYEtsG6oMIT}1s`!e!!@*){k3aq(r-|D!S2$-GPv0pI-FQvUo z=^Qs1O5ud8FGzR&mp8aY6w@jT|r=jEhfzB+8C7>eyXnHSNbhL9)@{; z_@V}qMwVBCfSWyDlbzhgM?OqCcRp+bLANUEu;2s#TexcjpawGu5$&@fad8h2XY@uz z_w-)-<4&lTg2yAj?IjC0iG)$Bp+(?er*jHD<6x}qkMv?XB`dMxayK5Ki?%EulQyK@ zks3o~VZ&!%LF{k$mfO#Dv!L#!E>JkHQz_b|)x34`&6kRu;D){Ow*JT9QPmwK_ZPmm zAJnCVp;de%rE{!dbUz0wAt?ZF;8}z}uYZ=S}IyGN}kyc80IM80^p(r(G|Q<1ELd=gllrfIPdxbM1m2JP|&``c`5o2|S0e%~fgf9Hz- z7b=UJIDmSg=1S%sE*ig+?0;BlvZ|soh{{>o^vS5;syC6`k6&RsOj_a{#KHp{jE;0G z%;gstL1x;M=5Yc0P#9ekl7t_$ykl;$agw>b7B|`K4ra+S(j!F~#dyNcldXfmjWyyvBn3Nij>d)?fS`~%CHPKa#XW>A-z{~&8rMMHh|V1=Qxd?G9^(5m zT}If|Z=y^Y-Yt$Zc!pHP32)L;O}U>z!S}s#uzG$_a1A%mnLq$-r7Lp+lfRi7hibuz zhGd+gEj4nRkGldX_-W7)zSR-SR{1WdzxB!Y#M&tvf{+lWHU=sGF-{?Ph2*I2P6|=X z_PV)B*>P;c^4cAPd7@ql#jV96-Jr)5I^LecNHk^vvC{Gjj+1_rb;U5O{MhI)dqovU zJzN>pgm0e<`nyf_m8egA|I`(yv9njWL8{38t?@Gby|2_TcXk1puB;uv+RpqRl2mB{ zoiR1B{OrDd9}fk3BD)4$snHhE*0$pS=~d{atgPoX;Pi`fe^_JyEgfuJsclGzqWg zo17PO#J!{HsbS(0nTdQfLcemG=sY}Bbmcy;pZ)i0t?Y7wr$(CZQJIKlP~Ao zcl#UtUfhG4@g!0W>w8O3)ty%OwApil7B_WHPjCXi}Ap77$zhB%b_=UdVGIh z4c*E;Ybx#~?V1C77sn-Pz7{tnP4;xLY^qN_M99So1{0XY-Tb%s;Fm6l<*3^4tSpDm!qjKvPAHhm2i#|kBi z#Kvlv<(w17kj&@LsYDLtS+ml*V!cPM@+$(HT1Y7F-f zR*8F<>Ow3-c?3iganspgu_(^S0MAiChVxJHceE3H&m6Fu&IQn zB8NoWP-8vw(6BkW3eB4*@(~?GFDtZ34aQnMD!SsQKtQk5sxx6ab=fem7yT%jTWtb< zdf7QKBEezya@?Q-IeR5TOjz``Oz3xE#m02@pPFKQ4_OhUs-gHup^(<9yLW<`zjL4f z$eaqc;2CJYw9PsPaY$8^Jv)Rm)i%dgzL{l?WZ~&e_9^|TQpK8uzLjOJwP_vAPUQP> zO2VqGIR9d8$j((KiIzO82ipBg?2RIbx3D1(Z?4!+Qk*;uR*2j&;z+WM&A)#Qm_sq& zbxbWtTsVn4*Tqt3){VP{rXa5#9_WG91+7PV)?z80liQHcr5w%(#CDyEi{88tXtDy$x+33WUI8=gpK7`+D0`(;4WV?b8+oKGO~%jkR4}6Ndbh+ zw$d-(#?pY&ZQ=bS3H4of%+6qtHto$)mhFI4agT85(gEa38_1wi5tA9#$b!;h_=FRs z?7=j!sq*{Z-@bb!HlXKZ7CV|mSGY>spPFH(t$Umy+cUJ`kq|=jGLpiOscfR$ymOAk zYh&Hqc69tO5Qe$JAXW8uIyEsm^LLqM0uuNgyR)j|*{P+jO0x_BK@ST2B*t*rMc(-SO7CUS3 z%g108O+C~WhYGuHXZK;diFfJg2`=(z}Fjn-*t=K_* z9ej`X3ck3}YbfVB9^QWwTK|hR{ME~i{1wmgY;yqN`zZxvN|w;l24;c6=++2ND4hn8 zc@@}>e=sd(vO_gGb9uP$CZOP}Pc|$)#sMM^xbNQ`lHGuSM5sYzq(nunr-g~n_#z?e zZ;;>14FT;?JuQE5{&tF?&=pT&FG?siUzm*#h-b7di!fjG1ZtlKR|shIU;|8_A%~%r zVz9I8B(#oK4J_MjS|uRgK8?({LL?#5avXyl4@57VVcRrSv^?yyg!T+&m`5znF{~aU$*D7Av z&dAo$$jZRX$o{{mV*mO#cdGn$!Bj%?B56+-V~%GQTW>Ivuym7X6H{AI#Vw7UA!cF( z7uEj-bY{pP6?{cBF|||_0-URvP_CH((STp*0|C6&P^c+BSmqPrGbU9Ml8kc(`xyK{ zHt>{@nYk&&yt#%ilQl8LcFeJT_|bLmWs>!Fb6fnw1Ks;}Ur;l5Pn-v$rWTb?vvALx z$74m+@3&25%1e=phXiL8g8g!NOD`-HnN9w$PBGHfRT*FF@LejR7Nq9H9VZVF&aQHO z1#t3Pu3D$!u=atSD}6=1dK`@#PoyUo=7@-hkVBl1RQJJkWuB{kW}d6zCjBn1onilH zJvgs};pp3#n)jNTW-U%^xVz=>;e7s2IwTIoiw6VLc0@$SW~avn=SC;elufvL5I?sC z7>(XW=-v=CSm1Ta{k;G}ZT6%b~nmBV)~L zNQ@7NgnL9pYP$TQ%?e!EbiGM&K}!aGAj@%DKQ0f4!B^-s+6U43K5gKwLQIO36+Pj| zD#3#38n*yxU?i;C80@P>DuswlvFgsX0)+7TesH-#A2?{?XAGF8pvVxoMuv_nru`zU zdeo@t2pD1uU?!1Myj?8XizzwOiBhc;p4r09CT#WjQdssXnyGfP3 zZe3Nji}hO6{i&E;W^YpDQ-YGrS9V~}#uv<~W-mmzw~#ibf$gP(>Lu(*^rg8PQq(+V zqI^>DaZa*9%Pkkyp@fMsy&MKT$eXo%{?5Gn?r2A2v|GUx4)S^mLsMEMqH%k*G2g3L zDrT10V#Nz(8)xnZJL4#NAIGV<;Ag4uc%lR)L}M(+-ZS3aPV%jwNlT{6V=hUlXJfdLB^cWtLJYU*d`egH zb0SPOYSjBYYQj^0lc{rQnxjIZH98kQr#sWUePW^hS}G}pmS(tAv<3tw&t-FX$lBQxG2U^ z%_~r3{Ba(JR~|8C7(A=6eOn2|XF^Y_36L&Kts1O*z+1f>Tr)!F_8(NVKk(1pvxRoQ z4G2=GQSB$WC4(-`lk1On5$|nqfNLoD_Go25&l=)}p4ROSn$FkiPqys}%HB~!;#su$ z!9!qe{@kki3H+zjB?=`NGoxbHHTMYkUA}jMAhtFLC2*(OCO0gZ@g1E8;km&+VF%bp zyVFmF=^cdT_cK+N>@5d$^{yk5wW$k&b?I|wQ1dO!?v+|=++oEo?q|-fR_-fQ#f(js z%e$QJQx=%FSCWK(8xo7WBk>iuG`mYQN78yo7?0UA!=L+#!dNpCvQv4J)sy?-e!4UH zVFRX=kZR+>CF6`GhBs<@ey6*I!O6!UO6uNm|Df6+;Qsc0R6Y7cH0wyHH39jwrZ?jnGO=8Zhkd~h>=IiUgFkP~gcrb?2Gi!kjrg_+%*~@EZ9fZ?0 zrkx@~@z9n1Y44;XBB~DQtC_L>E0}2HrRdJF7Q~#Q3eLz@Q7CL!_rsQ(b^FIDe@@WF zFZJoqe4 zI9NYb6{qnrGCfSYIimgyOTEVl^hPKG7jT#DL*I^w>zpy|elTlg8nM$ew8OjN`gWE@Pg?XhZTQIUVakFdzr+W>C7_50oY%%;x7i#hJ4v;2<(hI zl1$cjJtl1Jndl5#ME)+D)dZ`gPpeg-c-a$?nNn+cFxW=7o<6j>byB{E(`;S z^9E;ggi1MOyvyI^I1aCDi{_a0y;}I35Jx1dEr;P(+BNO^+3gimWEDhoslvKqw zDvSn;zh4h=Jb6B2AQN&m0rR4~WN!FHHIougN4K3aD~#7k2>6WLICNnly7$ikl*K$Q zh-LzhT(Z|@{5A6zyHeNducOEg??h$Bz5zi)UA1YAW@3J7U;1O?fO3`2BJ>1gKrCx{$8KrQh|wPSYCu@~p=$5 TX~DzTL# zJGGL8OuL5@MP7nFoV&vDc<*W>eySBt$aQA&mXBn3&yvU)aVC4&u!)O)eX?px`zoT{ zT85p3w6L;wk(eB}G~btf?I85t+ed8l-P^~EyeqFGnk`t?2K948A$UbSPAN}81BZfT zGzf1BEUg;ul3=i9;u_PKEIuH!)ZrUY(I~8!ooMlM-I1?j+jpzJK)ImZB+Q^z?3&O*S z?X-yz9m>LKFI^arTK8g%nyGmWlDNeZu($fRwC>Y?ppw_L{`y!DMzOqNMR<|q<6yJA zF3v5eFE1=&f|nwRgk|L>9z$;Wp)NHO`9ETX4NLBAH4n`!bE4yyyjW z5%SDhK1OkY_J|ej8u;BuVfd_KOVjPGU{Zrz7?%yqtD!~P#I6fQjf~P+pjh2sfzBCB zCj@_#VZMfGXMeBaNB#pco((xMvk@)LgUuQ;?b8IzQw*%0DYM-O%I~zrWK{c;y}HLb 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%0DPuHnXcXWQ?5cl6Hr} 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

k8s${f1S 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%0DKqPmW4PCM%Yv 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

1ZGVd^05)E{M)w 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 &i*y9|rJ1;#pxki|%| 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

SSs0 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�v`oB7*x*|C#== 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 z

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�d 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

s5Sr5nWP!zf;bQllMBU5`uSl2dH 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 +zA2z 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

M4KsLGLYF`T=mtL%TdshXO6;g4yQm# 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

%lTDCYypAz5|ZgHH#59pi8)Nw1#}d+p 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͙TQLD_pZ#%M_h5I!=#UOL3+I1;f6WZGG-a1k@^ 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 xC6{rb<Z?z@GSy_d{67X#5A^^5 literal 0 HcmV?d00001 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/expected/test/babel_219.out b/contrib/babelfishpg_tsql/expected/test/babel_219.out new file mode 100644 index 00000000000..cdf9b9b5a1c --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_219.out @@ -0,0 +1,84 @@ +-- This test should be run without installing the babelfishpg_tsql extension +-- BABEL-219 test a domain named varchar in schema other than sys +-- is not affected by the fix of BABEL-219 +create domain public.varchar as pg_catalog.varchar(2) check (char_length(value) < 1); +select cast('a' as public.varchar); -- throw error +ERROR: value for domain public."varchar" violates check constraint "varchar_check" +select cast('' as public.varchar); + varchar +--------- + +(1 row) + +select cast('a' as varchar); -- pg_catalog.varchar should work + varchar +--------- + a +(1 row) + +show search_path; + search_path +----------------- + "$user", public +(1 row) + +-- Explicitly add pg_catalog to tail of search_path, +-- to force varchar default to public.varchar +select set_config('search_path', current_setting('search_path') || ', pg_catalog', false); + set_config +----------------------------- + "$user", public, pg_catalog +(1 row) + +-- Set tsql dialet so the fix for BABEL-219 can kick in +SET babelfishpg_tsql.sql_dialect = 'tsql'; +select cast('a' as varchar); -- varchar default to public.varchar. should fail exactly the same way as explicitly specifying public.varchar +ERROR: value for domain "varchar" violates check constraint "varchar_check" +select cast('' as varchar); -- varchar default to public.varchar. should pass + varchar +--------- + +(1 row) + +create table t1(col varchar); +insert into t1 (col) select 'a'; -- fail +ERROR: value for domain "varchar" violates check constraint "varchar_check" +insert into t1 (col) select ''; -- pass +select * from t1; + col +----- + +(1 row) + +-- verify behavior of public.varchar is unchanged in tsql dialect +select cast('a' as public.varchar); -- fail +ERROR: value for domain "varchar" violates check constraint "varchar_check" +select cast('' as public.varchar); -- pass + varchar +--------- + +(1 row) + +create table t2(col public.varchar); +insert into t1 (col) select 'a'; -- fail +ERROR: value for domain "varchar" violates check constraint "varchar_check" +insert into t1 (col) select ''; -- pass +select * from t1; + col +----- + + +(2 rows) + +-- Clean up +drop table t1; +drop table t2; +set babelfishpg_tsql.sql_dialect = 'postgres'; +-- Reset search_path +set search_path to "$user", public; +show search_path; + search_path +----------------- + "$user", public +(1 row) + diff --git a/contrib/babelfishpg_tsql/expected/test/babel_collation.out b/contrib/babelfishpg_tsql/expected/test/babel_collation.out new file mode 100644 index 00000000000..6f3e3ac60a9 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_collation.out @@ -0,0 +1,826 @@ +-- nvarchar is not supported in PG +create table testing1(col nvarchar(60)); -- expect this to fail in the Postgres dialect +ERROR: type "nvarchar" does not exist +LINE 1: create table testing1(col nvarchar(60)); + ^ +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql" CASCADE; +NOTICE: extension "babelfishpg_tsql" already exists, skipping +set babelfishpg_tsql.sql_dialect = "tsql"; +-- nvarchar is supported in tsql dialect +create table testing1(col nvarchar(60)); +insert into testing1 (col) select N'Muffler'; +insert into testing1 (col) select N'Mülle'; +insert into testing1 (col) select N'MX Systems'; +insert into testing1 (col) select N'Magic'; +select * from testing1 order by col; + col +------------ + Magic + Muffler + Mülle + MX Systems +(4 rows) + +-- test case insensitive collation +create table testing2 (col varchar(20) collate SQL_Latin1_General_CP1_CI_AS); +insert into testing2 values ('JONES'); +insert into testing2 values ('jones'); +insert into testing2 values ('Jones'); +insert into testing2 values ('JoNes'); +insert into testing2 values ('JoNés'); +select * from testing2 where col collate BBF_Unicode_General_CS_AS = 'JoNes'; + col +------- + JoNes +(1 row) + +select * from testing2 where col collate BBF_Unicode_General_CI_AS = 'JoNes'; + col +------- + JONES + jones + Jones + JoNes +(4 rows) + +select * from testing2 where col collate BBF_Unicode_General_CI_AI = 'JoNes'; + col +------- + JONES + jones + Jones + JoNes + JoNés +(5 rows) + +select * from testing2 where col collate BBF_Unicode_General_CS_AI = 'JoNes'; + col +------- + JoNes + JoNés +(2 rows) + +-- test case insensitivity for default collation +create table testing3 (c1 varchar(20), c2 char(20), c3 nvarchar(20)); +\d testing3 + Table "public.testing3" + Column | Type | Collation | Nullable | Default +--------+---------------+-----------------------+----------+--------- + c1 | "varchar"(20) | bbf_unicode_cp1_ci_as | | + c2 | bpchar(20) | bbf_unicode_cp1_ci_as | | + c3 | nvarchar(20) | bbf_unicode_cp1_ci_as | | + +insert into testing3 values ('JONES','JONES','JONES'); +insert into testing3 values ('JoneS','JoneS','JoneS'); +insert into testing3 values ('jOnes','jOnes','jOnes'); +select c1 from testing3 where c1='jones'; + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +select c2 from testing3 where c2='jones'; + c2 +---------------------- + JONES + JoneS + jOnes +(3 rows) + +select c3 from testing3 where c3='jones'; + c3 +------- + JONES + JoneS + jOnes +(3 rows) + +-- test LIKE to ILIKE transformation +create table testing4 (c1 varchar(20), c2 char(20), c3 nvarchar(20)); +create index c1_idx on testing4 (c1); +insert into testing4 values ('JONES','JONES','JONES'); +insert into testing4 values ('JoneS','JoneS','JoneS'); +insert into testing4 values ('jOnes','jOnes','jOnes'); +insert into testing4 values ('abcD','AbcD','ABCd'); +insert into testing4 values ('äbĆD','äḃcD','äƀCd'); +-- set enable_seqscan doesn't work from the TSQL dialect, so switch +-- dialects, disable sequential scan so we see some index-based plans, +-- then switch back to the TSQL dialect +-- +reset babelfishpg_tsql.sql_dialect; +set enable_seqscan = false; +set babelfishpg_tsql.sql_dialect = "tsql"; +-- test that like is case-insenstive +select c1 from testing4 where c1 LIKE 'jones'; -- this gets converted to '=' + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +explain (costs false) select c1 from testing4 where c1 LIKE 'jones'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text = 'jones'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 LIKE 'Jon%'; + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +explain (costs false) select c1 from testing4 where c1 LIKE 'Jon%'; + QUERY PLAN +----------------------------------------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: (((c1)::text ~~* 'Jon%'::text) AND ((c1)::text >= 'Jon'::text) AND ((c1)::text < 'Jon￿'::text)) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 LIKE 'jone_'; + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +explain (costs false) select c1 from testing4 where c1 LIKE 'jone_'; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: (((c1)::text ~~* 'jone_'::text) AND ((c1)::text >= 'jone'::text) AND ((c1)::text < 'jone￿'::text)) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 LIKE '_one_'; + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +explain (costs false) select c1 from testing4 where c1 LIKE '_one_'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text ~~* '_one_'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 LIKE '%on%s'; + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +explain (costs false) select c1 from testing4 where c1 LIKE '%on%s'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text ~~* '%on%s'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +-- test that like is accent-senstive for CI_AS collation +select c1 from testing4 where c1 LIKE 'ab%'; + c1 +------ + abcD +(1 row) + +select c1 from testing4 where c1 LIKE 'äb%'; + c1 +------ + äbĆD +(1 row) + +select c1 from testing4 where c1 LIKE 'äḃĆ_'; + c1 +---- +(0 rows) + +-- test not like +select c1 from testing4 where c1 NOT LIKE 'jones'; + c1 +------ + abcD + äbĆD +(2 rows) + +explain (costs false) select c1 from testing4 where c1 NOT LIKE 'jones'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text <> 'jones'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 NOT LIKE 'jone%'; + c1 +------ + abcD + äbĆD +(2 rows) + +explain (costs false) select c1 from testing4 where c1 NOT LIKE 'jone%'; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: (((c1)::text !~~* 'jone%'::text) OR ((c1)::text < 'jone'::text) OR ((c1)::text >= 'jone￿'::text)) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 NOT LIKE 'ä%'; + c1 +------- + JONES + JoneS + jOnes + abcD +(4 rows) + +explain (costs false) select c1 from testing4 where c1 NOT LIKE 'ä%'; + QUERY PLAN +---------------------------------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: (((c1)::text !~~* 'ä%'::text) OR ((c1)::text < 'ä'::text) OR ((c1)::text >= 'ä￿'::text)) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +-- test escape function and wildcard literal +select c1 from testing4 where c1 LIKE E'\_ones'; + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +explain (costs false) select c1 from testing4 where c1 LIKE E'\_ones'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text ~~* '_ones'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 LIKE E'\%ones'; + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +explain (costs false) select c1 from testing4 where c1 LIKE E'\%ones'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text ~~* '%ones'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +-- wild card literals are transformed to equal +select c1 from testing4 where c1 LIKE '\%ones'; + c1 +---- +(0 rows) + +explain(costs false) select c1 from testing4 where c1 LIKE '\%ones'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text = '%ones'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +select c1 from testing4 where c1 LIKE '\_ones'; + c1 +---- +(0 rows) + +explain(costs false) select c1 from testing4 where c1 LIKE '\_ones'; + QUERY PLAN +--------------------------------------------------------------------------- + Bitmap Heap Scan on testing4 + Filter: ((c1)::text = '_ones'::text) + -> Bitmap Index Scan on c1_idxtesting49a168d73f3ba5aacdfd495b931b8d187 +(3 rows) + +-- test combining with other string functions +select c1 from testing4 where c1 LIKE lower('_ones'); + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +select c1 from testing4 where c1 LIKE upper('_ones'); + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +select c1 from testing4 where c1 LIKE concat('_on','_s'); + c1 +------- + JONES + JoneS + jOnes +(3 rows) + +select c1 from testing4 where c1 LIKE concat('a','%d'); + c1 +------ + abcD +(1 row) + +select c1 from testing4 where c1 NOT LIKE lower('%s'); + c1 +------ + abcD + äbĆD +(2 rows) + +-- test sub-queries +Select * from testing4 where c1 LIKE (select c1 from testing4 where c1 LIKE 'AbcD'); + c1 | c2 | c3 +------+----------------------+------ + abcD | AbcD | ABCd +(1 row) + +Select * from testing4 where c2 NOT LIKE (select c2 from testing4 where c2 NOT LIKE 'jo%' AND c2 NOT LIKE 'ä%'); + c1 | c2 | c3 +-------+----------------------+------- + JONES | JONES | JONES + JoneS | JoneS | JoneS + jOnes | jOnes | jOnes + äbĆD | äḃcD | äƀCd +(4 rows) + +Select * from testing4 where c3 LIKE (select c3 from testing4 where c3 NOT LIKE'jo%' AND c3 NOT LIKE 'ä%'); + c1 | c2 | c3 +------+----------------------+------ + abcD | AbcD | ABCd +(1 row) + +with p1 as (select c1 from testing4 where c1 LIKE '__Ć_'), +p2 as (select c3 from testing4 where c3 LIKE 'äƀ__') +select * from p1 union all select * from p2; + c1 +------ + äbĆD + äƀCd +(2 rows) + +-- test case expression +select c1,(case c1 LIKE 'j%' when true then 1 when false then 2 end) from testing4; + c1 | case +-------+------ + JONES | 1 + JoneS | 1 + jOnes | 1 + abcD | 2 + äbĆD | 2 +(5 rows) + +select c2,(case when c2 LIKE '_bc%' then 1 when c2 LIKE 'jon%' then 2 when c3 LIKE 'ä%' then 3 end) from testing4; + c2 | case +----------------------+------ + JONES | 2 + JoneS | 2 + jOnes | 2 + AbcD | 1 + äḃcD | 3 +(5 rows) + +-- test that LIKE transformation is applied only for CI_AS column +create table testing5(c1 varchar(20) COLLATE SQL_Latin1_General_CP1_CS_AS); +insert into testing5 values ('JONES'); +insert into testing5 values ('JoneS'); +insert into testing5 values ('abcD'); +insert into testing5 values ('äbĆD'); +select * from testing5 where c1 LIKE 'jo%'; -- does not use the transformation + c1 +---- +(0 rows) + +explain(costs false) select * from testing5 where c1 LIKE 'jo%'; + QUERY PLAN +--------------------------------------- + Seq Scan on testing5 + Filter: ((c1)::text ~~ 'jo%'::text) +(2 rows) + +select * from testing5 where c1 NOT LIKE 'j%'; + c1 +------- + JONES + JoneS + abcD + äbĆD +(4 rows) + +select * from testing5 where c1 LIKE 'AB%'; + c1 +---- +(0 rows) + +-- test explicitly specify collation as CI_AS, like transformation is also applied. +SELECT 'JONES' like 'jo%'; + ?column? +---------- + f +(1 row) + +SELECT 'JONES' COLLATE SQL_Latin1_General_CP1_CI_AS like 'jo%' ; + ?column? +---------- + t +(1 row) + +-- test when pattern is empty string or NULL +SELECT 'JONES' like ''; + ?column? +---------- + f +(1 row) + +SELECT 'JONES' like NULL; + ?column? +---------- + +(1 row) + +SELECT * from testing5 where c1 like ''; + c1 +---- +(0 rows) + +explain (costs false) SELECT * from testing5 where c1 like ''; + QUERY PLAN +------------------------------------ + Seq Scan on testing5 + Filter: ((c1)::text ~~ ''::text) +(2 rows) + +SELECT * from testing5 where c1 like NULL; + c1 +---- +(0 rows) + +explain (costs false) SELECT * from testing5 where c1 like NULL; + QUERY PLAN +-------------------------- + Result + One-Time Filter: false +(2 rows) + +SELECT * FROM testing5 where c1 COLLATE French_CI_AS like 'jo%' ; + c1 +------- + JONES + JoneS +(2 rows) + +explain (costs false) SELECT * FROM testing5 where c1 COLLATE French_CI_AS like 'jo%' ; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Seq Scan on testing5 + Filter: (((c1)::text ~~* 'jo%'::text) AND ((c1)::text >= 'jo'::text) AND ((c1)::text < 'jo￿'::text)) +(2 rows) + +SELECT * FROM testing5 where c1 COLLATE Chinese_PRC_CI_AS like 'jo%' ; + c1 +------- + JONES + JoneS +(2 rows) + +explain (costs false) SELECT * FROM testing5 where c1 COLLATE Chinese_PRC_CI_AS like 'jo%' ; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Seq Scan on testing5 + Filter: (((c1)::text ~~* 'jo%'::text) AND ((c1)::text >= 'jo'::text) AND ((c1)::text < 'jo￿'::text)) +(2 rows) + +-- tsql collations +alter table testing1 alter column col nvarchar(60) collate Arabic_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Chinese_PRC_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Cyrillic_General_CS_AS; +alter table testing1 alter column col nvarchar(60) collate French_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Korean_Wansung_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Traditional_Spanish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Modern_Spanish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate SQL_Latin1_General_CP1_CS_AS; +alter table testing1 alter column col nvarchar(60) collate SQL_Latin1_General_CP1_CI_AS; +alter table testing1 alter column col nvarchar(60) collate Traditional_Spanish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Thai_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Turkish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Ukrainian_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Vietnamese_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Finnish_Swedish_CS_AS; +-- expect different result order from previous select +select * from testing1 order by col; + col +------------ + Magic + Muffler + MX Systems + Mülle +(4 rows) + +-- test expression level collate, expect the same result order +select * from testing1 order by col collate Finnish_Swedish_CS_AS; + col +------------ + Magic + Muffler + MX Systems + Mülle +(4 rows) + +-- test catalog +select * from sys.fn_helpcollations(); + name | description +-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------- + arabic_cs_as | Arabic, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + arabic_ci_ai | Arabic, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + arabic_ci_as | Arabic, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_bin2 | Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1250_ci_ai | Default locale, code page 1250, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1250_ci_as | Default locale, code page 1250, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1250_cs_ai | Default locale, code page 1250, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1250_cs_as | Default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1250_cs_as | Default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1251_ci_ai | Default locale, code page 1251, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1251_ci_as | Default locale, code page 1251, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1251_cs_ai | Default locale, code page 1251, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1251_cs_as | Default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1251_cs_as | Default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1253_ci_ai | Default locale, code page 1253, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1253_ci_as | Default locale, code page 1253, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1253_cs_ai | Default locale, code page 1253, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1253_cs_as | Default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1253_cs_as | Default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1254_ci_ai | Default locale, code page 1254, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1254_ci_as | Default locale, code page 1254, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1254_cs_ai | Default locale, code page 1254, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1254_cs_as | Default locale, code page 1254, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1254_cs_as | Default locale, code page 1254, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1255_ci_ai | Default locale, code page 1255, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1255_ci_as | Default locale, code page 1255, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1255_cs_ai | Default locale, code page 1255, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1255_cs_as | Default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1255_cs_as | Default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1256_ci_ai | Default locale, code page 1256, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1256_ci_as | Default locale, code page 1256, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1256_cs_ai | Default locale, code page 1256, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1256_cs_as | Default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1256_cs_as | Default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1257_ci_ai | Default locale, code page 1257, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1257_ci_as | Default locale, code page 1257, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1257_cs_ai | Default locale, code page 1257, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1257_cs_as | Default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1257_cs_as | Default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1258_ci_ai | Default locale, code page 1258, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1258_ci_as | Default locale, code page 1258, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1258_cs_ai | Default locale, code page 1258, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1258_cs_as | Default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1258_cs_as | Default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp1_ci_ai | Default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1_ci_as | Default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1_cs_ai | Default locale, code page 1252, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp1_cs_as | Default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp1_cs_as | Default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_cp847_ci_ai | Default locale, code page 847, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp847_ci_as | Default locale, code page 847, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp847_cs_ai | Default locale, code page 847, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_cp847_cs_as | Default locale, code page 847, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_pref_cp847_cs_as | Default locale, code page 847, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + bbf_unicode_general_ci_ai | Default locale, default code page, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_general_ci_as | Default locale, default code page, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_general_cs_ai | Default locale, default code page, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + bbf_unicode_general_cs_as | Default locale, default code page, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + bbf_unicode_general_pref_cs_as | Default locale, default code page, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + chinese_prc_cs_as | Chinese-PRC, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + chinese_prc_ci_ai | Chinese-PRC, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + chinese_prc_ci_as | Chinese-PRC, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + cyrillic_general_cs_as | Cyrillic-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + cyrillic_general_ci_ai | Cyrillic-General, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + cyrillic_general_ci_as | Cyrillic-General, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + finnish_swedish_cs_as | Finnish-Swedish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + finnish_swedish_ci_as | Finnish-Swedish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + finnish_swedish_ci_ai | Finnish-Swedish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + french_cs_as | French, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + french_ci_as | French, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + french_ci_ai | French, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + korean_wansung_cs_as | Korean-Wansung, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + korean_wansung_ci_as | Korean-Wansung, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + korean_wansung_ci_ai | Korean-Wansung, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + latin1_general_bin2 | Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + latin1_general_90_bin2 | Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + latin1_general_100_bin2 | Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + latin1_general_140_bin2 | Virtual, Unicode-General, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + latin1_general_ci_ai | Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + latin1_general_ci_as | Virtual, default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + latin1_general_cs_ai | Virtual, default locale, code page 1252, case-sensitive, accent-insensitive, kanatype-insensitive, width-insensitive + latin1_general_cs_as | Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + modern_spanish_cs_as | Traditional-Spanish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + modern_spanish_ci_as | Traditional-Spanish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + modern_spanish_ci_ai | Traditional-Spanish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + polish_cs_as | Polish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + polish_ci_as | Polish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + polish_ci_ai | Polish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1250_ci_as | Virtual, default locale, code page 1250, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1250_cs_as | Virtual, default locale, code page 1250, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1251_ci_as | Virtual, default locale, code page 1251, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1251_cs_as | Virtual, default locale, code page 1251, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1_ci_ai | Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1_ci_as | Virtual, default locale, code page 1252, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1_ci_ai | Virtual, default locale, code page 1252, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1_cs_as | Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_pref_cp1_cs_as | Virtual, default locale, code page 1252, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive, uppercase-first + sql_latin1_general_cp1253_ci_as | Virtual, default locale, code page 1253, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1253_cs_as | Virtual, default locale, code page 1253, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1254_ci_as | Virtual, default locale, code page 1254, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1254_cs_as | Virtual, default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1255_ci_as | Virtual, default locale, code page 1255, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1255_cs_as | Virtual, default locale, code page 1255, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1256_ci_as | Virtual, default locale, code page 1256, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1256_cs_as | Virtual, default locale, code page 1256, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1257_ci_as | Virtual, default locale, code page 1257, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1257_cs_as | Virtual, default locale, code page 1257, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1258_ci_as | Virtual, default locale, code page 1258, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + sql_latin1_general_cp1258_cs_as | Virtual, default locale, code page 1258, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + thai_cs_as | Thai, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + thai_ci_as | Thai, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + thai_ci_ai | Thai, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + traditional_spanish_cs_as | Traditional-Spanish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + traditional_spanish_ci_as | Traditional-Spanish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + traditional_spanish_ci_ai | Traditional-Spanish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + turkish_cs_as | Turkish, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + turkish_ci_as | Turkish, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + turkish_ci_ai | Turkish, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + ukrainian_cs_as | Ukrainian, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + ukrainian_ci_as | Ukrainian, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + ukrainian_ci_ai | Ukrainian, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive + vietnamese_cs_as | Vietnamese, case-sensitive, accent-sensitive, kanatype-insensitive, width-insensitive + vietnamese_ci_as | Vietnamese, case-insensitive, accent-sensitive, kanatype-insensitive, width-insensitive + vietnamese_ci_ai | Vietnamese, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive +(124 rows) + +-- test the TYPE keyword is only required in postgres dialect, but not in tsql dialect +alter table testing1 alter column col varchar(60) collate Finnish_Swedish_CS_AS; +alter table testing1 alter column col TYPE varchar(60) collate Finnish_Swedish_CS_AS; +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); + set_config +------------ + postgres +(1 row) + +alter table testing1 alter column col varchar(60) collate sys.Finnish_Swedish_CS_AS; +ERROR: syntax error at or near "varchar" +LINE 1: alter table testing1 alter column col varchar(60) collate sy... + ^ +alter table testing1 alter column col TYPE varchar(60) collate sys.Finnish_Swedish_CS_AS; +SELECT set_config('babelfishpg_tsql.sql_dialect', 'tsql', false); + set_config +------------ + tsql +(1 row) + +-- test collation list sys table +SELECT collation_name, l1_priority, l2_priority, l3_priority, l4_priority, l5_priority FROM sys.babelfish_collation_list() order by collation_name; + collation_name | l1_priority | l2_priority | l3_priority | l4_priority | l5_priority +--------------------------------+-------------+-------------+-------------+-------------+------------- + arabic_ci_ai | 1025 | 0 | 196608 | 0 | 15 + arabic_ci_as | 1025 | 0 | 196608 | 0 | 13 + arabic_cs_as | 1025 | 0 | 196608 | 0 | 12 + bbf_unicode_bin2 | 1033 | 0 | 196608 | 54 | 544 + bbf_unicode_cp1250_ci_ai | 1045 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1250_ci_as | 1045 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1250_cs_ai | 1045 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1250_cs_as | 1045 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1251_ci_ai | 1049 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1251_ci_as | 1049 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1251_cs_ai | 1049 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1251_cs_as | 1049 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1253_ci_ai | 1032 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1253_ci_as | 1032 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1253_cs_ai | 1032 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1253_cs_as | 1032 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1254_ci_ai | 1055 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1254_ci_as | 1055 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1254_cs_ai | 1055 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1254_cs_as | 1055 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1255_ci_ai | 1037 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1255_ci_as | 1037 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1255_cs_ai | 1037 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1255_cs_as | 1037 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1256_ci_ai | 1025 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1256_ci_as | 1025 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1256_cs_ai | 1025 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1256_cs_as | 1025 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1257_ci_ai | 1061 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1257_ci_as | 1061 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1257_cs_ai | 1061 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1257_cs_as | 1061 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1258_ci_ai | 1066 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1258_ci_as | 1066 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1258_cs_ai | 1066 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1258_cs_as | 1066 | 0 | 196608 | 51 | 12 + bbf_unicode_cp1_ci_ai | 1033 | 0 | 196608 | 54 | 15 + bbf_unicode_cp1_ci_as | 1033 | 0 | 196608 | 52 | 13 + bbf_unicode_cp1_cs_ai | 1033 | 0 | 196608 | 51 | 14 + bbf_unicode_cp1_cs_as | 1033 | 0 | 196608 | 51 | 12 + bbf_unicode_cp874_ci_ai | 1054 | 0 | 196608 | 54 | 15 + bbf_unicode_cp874_ci_as | 1054 | 0 | 196608 | 52 | 13 + bbf_unicode_cp874_cs_ai | 1054 | 0 | 196608 | 51 | 14 + bbf_unicode_cp874_cs_as | 1054 | 0 | 196608 | 51 | 12 + bbf_unicode_general_ci_ai | 1033 | 0 | 196608 | 54 | 15 + bbf_unicode_general_ci_as | 1033 | 0 | 196608 | 52 | 13 + bbf_unicode_general_cs_ai | 1033 | 0 | 196608 | 51 | 14 + bbf_unicode_general_cs_as | 1033 | 0 | 196608 | 51 | 12 + bbf_unicode_general_pref_cs_as | 1033 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1250_cs_as | 1045 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1251_cs_as | 1049 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1253_cs_as | 1032 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1254_cs_as | 1055 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1255_cs_as | 1037 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1256_cs_as | 1025 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1257_cs_as | 1061 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1258_cs_as | 1066 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp1_cs_as | 1033 | 0 | 196608 | 51 | 12 + bbf_unicode_pref_cp874_cs_as | 1054 | 0 | 196608 | 51 | 12 + chinese_prc_ci_ai | 2052 | 0 | 196608 | 0 | 15 + chinese_prc_ci_as | 2052 | 0 | 196608 | 0 | 13 + chinese_prc_cs_as | 2052 | 0 | 196608 | 0 | 12 + cyrillic_general_ci_ai | 1049 | 0 | 196608 | 0 | 15 + cyrillic_general_ci_as | 1049 | 0 | 196608 | 0 | 13 + cyrillic_general_cs_as | 1049 | 0 | 196608 | 0 | 12 + estonian_ci_ai | 1061 | 0 | 196608 | 0 | 15 + estonian_ci_as | 1061 | 0 | 196608 | 0 | 13 + estonian_cs_as | 1061 | 0 | 196608 | 0 | 12 + finnish_swedish_ci_ai | 1035 | 0 | 196608 | 0 | 15 + finnish_swedish_ci_as | 1035 | 0 | 196608 | 0 | 13 + finnish_swedish_cs_as | 1035 | 0 | 196608 | 0 | 12 + french_ci_ai | 1036 | 0 | 196608 | 0 | 15 + french_ci_as | 1036 | 0 | 196608 | 0 | 13 + french_cs_as | 1036 | 0 | 196608 | 0 | 12 + greek_ci_ai | 1032 | 0 | 196608 | 0 | 15 + greek_ci_as | 1032 | 0 | 196608 | 0 | 13 + greek_cs_as | 1032 | 0 | 196608 | 0 | 12 + hebrew_ci_ai | 1037 | 0 | 196608 | 0 | 15 + hebrew_ci_as | 1037 | 0 | 196608 | 0 | 13 + hebrew_cs_as | 1037 | 0 | 196608 | 0 | 12 + korean_wansung_ci_ai | 1042 | 0 | 196608 | 0 | 15 + korean_wansung_ci_as | 1042 | 0 | 196608 | 0 | 13 + korean_wansung_cs_as | 1042 | 0 | 196608 | 0 | 12 + modern_spanish_ci_ai | 3082 | 0 | 196608 | 0 | 15 + modern_spanish_ci_as | 3082 | 0 | 196608 | 0 | 13 + modern_spanish_cs_as | 3082 | 0 | 196608 | 0 | 12 + mongolian_ci_ai | 1104 | 0 | 196608 | 0 | 15 + mongolian_ci_as | 1104 | 0 | 196608 | 52 | 13 + mongolian_cs_as | 1104 | 0 | 196608 | 51 | 12 + polish_ci_ai | 1045 | 0 | 196608 | 0 | 15 + polish_ci_as | 1045 | 0 | 196608 | 0 | 13 + polish_cs_as | 1045 | 0 | 196608 | 0 | 12 + thai_ci_ai | 1054 | 0 | 196608 | 0 | 15 + thai_ci_as | 1054 | 0 | 196608 | 0 | 13 + thai_cs_as | 1054 | 0 | 196608 | 0 | 12 + traditional_spanish_ci_ai | 1034 | 0 | 196608 | 0 | 15 + traditional_spanish_ci_as | 1034 | 0 | 196608 | 0 | 13 + traditional_spanish_cs_as | 1034 | 0 | 196608 | 0 | 12 + turkish_ci_ai | 1055 | 0 | 196608 | 0 | 15 + turkish_ci_as | 1055 | 0 | 196608 | 0 | 13 + turkish_cs_as | 1055 | 0 | 196608 | 0 | 12 + ukrainian_ci_ai | 1058 | 0 | 196608 | 0 | 15 + ukrainian_ci_as | 1058 | 0 | 196608 | 0 | 13 + ukrainian_cs_as | 1058 | 0 | 196608 | 0 | 12 + vietnamese_ci_ai | 1066 | 0 | 196608 | 0 | 15 + vietnamese_ci_as | 1066 | 0 | 196608 | 0 | 13 + vietnamese_cs_as | 1066 | 0 | 196608 | 0 | 12 +(107 rows) + +-- clean up +drop table testing1; +drop table testing2; +drop table testing3; +drop table testing4; +drop table testing5; diff --git a/contrib/babelfishpg_tsql/expected/test/babel_datatype.out b/contrib/babelfishpg_tsql/expected/test/babel_datatype.out new file mode 100644 index 00000000000..89c536e65b3 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_datatype.out @@ -0,0 +1,1439 @@ +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql" CASCADE; +NOTICE: extension "babelfishpg_tsql" already exists, skipping +-- The default scale is 2 in PG. +select CAST('$100,123.4567' AS money); + money +------------- + $100,123.46 +(1 row) + +-- Currency symbol followed by number without being quoted is not recognized +-- as Money in postgres dialect. +select CAST($100123.4567 AS money); +ERROR: syntax error at or near ".4567" +LINE 1: select CAST($100123.4567 AS money); + ^ +-- Scale changes to the sql server default 4 in tsql dialect +-- Currency symbol followed by number without being quoted is recognized +-- as Money type in tsql dialect. +set babelfishpg_tsql.sql_dialect = "tsql"; +select CAST($100123.4567 AS money); + money +------------- + 100123.4567 +(1 row) + +select CAST($100123. AS money); + money +------------- + 100123.0000 +(1 row) + +select CAST($.4567 AS money); + money +-------- + 0.4567 +(1 row) + +select CAST('$100,123.4567' AS money); + money +------------- + 100123.4567 +(1 row) + +-- Test numeric types with brackets +create table testing1 (a [tinyint]); +drop table testing1; +create table testing1 (a [smallint]); +drop table testing1; +create table testing1 (a [int]); +drop table testing1; +create table testing1 (a [bigint]); +drop table testing1; +create table testing1 (a [real]); +drop table testing1; +create table testing1 (a [float]); +drop table testing1; +-- Comma separated format without quote is not allowed in sql server +select CAST($100,123.4567 AS money); +ERROR: syntax error at or near "," +LINE 1: select CAST($100,123.4567 AS money); + ^ +-- Smallmoney in tsql dialect +select CAST($100123.4567 AS smallmoney); + smallmoney +------------- + 100123.4567 +(1 row) + +select CAST('$100,123.4567' AS smallmoney); + smallmoney +------------- + 100123.4567 +(1 row) + +-- Comma separated format without quote is not allowed in sql server +select CAST($100,123.4567 AS smallmoney); +ERROR: syntax error at or near "," +LINE 1: select CAST($100,123.4567 AS smallmoney); + ^ +create table testing1(mon money, smon smallmoney); +insert into testing1 (mon, smon) values ('$100,123.4567', '$123.9999'); +insert into testing1 (mon, smon) values ($100123.4567, $123.9999); +select * from testing1; + mon | smon +-------------+---------- + 100123.4567 | 123.9999 + 100123.4567 | 123.9999 +(2 rows) + +select avg(CAST(mon AS numeric(38,4))), avg(CAST(smon AS numeric(38,4))) from testing1; + avg | avg +---------------------+---------------------- + 100123.456700000000 | 123.9999000000000000 +(1 row) + +select mon+smon as total from testing1; + total +------------- + 100247.4566 + 100247.4566 +(2 rows) + +-- Comma separated format without quote is not allowed in sql server +insert into testing1 (mon, smon) values ($100,123.4567, $123.9999); +ERROR: INSERT has more expressions than target columns +LINE 1: ... into testing1 (mon, smon) values ($100,123.4567, $123.9999)... + ^ +-- Test other allowed currency symbols with/without quote +-- TODO: fix BABEL-2636 "Money datatype doesn't support any currency symbol other than Dollar" +select CAST(€100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(€100.123 AS money); + ^ +select CAST('€100.123' AS money); + money +---------- + 100.1230 +(1 row) + +select CAST(¢100.123 AS money); +ERROR: invalid input syntax for type numeric: "�100.123" +LINE 1: select CAST(¢100.123 AS money); + ^ +select CAST(£100.123 AS money); +ERROR: invalid input syntax for type numeric: "�100.123" +LINE 1: select CAST(£100.123 AS money); + ^ +select CAST('£100.123' AS money); + money +---------- + 100.1230 +(1 row) + +select CAST(¤100.123 AS money); +ERROR: invalid input syntax for type numeric: "�100.123" +LINE 1: select CAST(¤100.123 AS money); + ^ +select CAST(¥100.123 AS money); +ERROR: invalid input syntax for type numeric: "�100.123" +LINE 1: select CAST(¥100.123 AS money); + ^ +select CAST(৲100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(৲100.123 AS money); + ^ +select CAST(৳100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(৳100.123 AS money); + ^ +select CAST(฿100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(฿100.123 AS money); + ^ +select CAST(៛100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(៛100.123 AS money); + ^ +select CAST(₠100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₠100.123 AS money); + ^ +select CAST(₡100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₡100.123 AS money); + ^ +select CAST(₢100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₢100.123 AS money); + ^ +select CAST(₣100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₣100.123 AS money); + ^ +select CAST(₤100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₤100.123 AS money); + ^ +select CAST(₥100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₥100.123 AS money); + ^ +select CAST(₦100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₦100.123 AS money); + ^ +select CAST(₧100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₧100.123 AS money); + ^ +select CAST(₨100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₨100.123 AS money); + ^ +select CAST(₩100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₩100.123 AS money); + ^ +select CAST(₪100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₪100.123 AS money); + ^ +select CAST(₫100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₫100.123 AS money); + ^ +select CAST(₭100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₭100.123 AS money); + ^ +select CAST(₮100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₮100.123 AS money); + ^ +select CAST(₯100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₯100.123 AS money); + ^ +select CAST(₰100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₰100.123 AS money); + ^ +select CAST(₱100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₱100.123 AS money); + ^ +select CAST(﷼100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(﷼100.123 AS money); + ^ +select CAST(﹩100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(﹩100.123 AS money); + ^ +select CAST($100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST($100.123 AS money); + ^ +select CAST(¢100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(¢100.123 AS money); + ^ +select CAST(£100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(£100.123 AS money); + ^ +select CAST(¥100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(¥100.123 AS money); + ^ +select CAST('¥100.123' AS money); + money +---------- + 100.1230 +(1 row) + +select CAST(₩100.123 AS money); +ERROR: invalid input syntax for type numeric: "��100.123" +LINE 1: select CAST(₩100.123 AS money); + ^ +-- Test unsupoorted currency symbol +select CAST(←100.123 AS money); +ERROR: syntax error at or near ".123" +LINE 1: select CAST(←100.123 AS money); + ^ +select CAST('←100.123' AS money); + money +---------- + 100.1230 +(1 row) + +-- Test that space is allowed between currency symbol and number, this is +-- a TSQL behavior +select CAST($ 123.5 AS money); + money +---------- + 123.5000 +(1 row) + +select CAST('$ 123.5' AS money); + money +---------- + 123.5000 +(1 row) + +-- Test inexact result mutliply/divide money with money, to match +-- SQL Server behavior +select CAST(100 AS money)/CAST(339 AS money)*CAST(10000 AS money); + ?column? +----------- + 2949.0000 +(1 row) + +-- Test postgres dialect +-- Test currency symbol without quote is not allowed in postgres dialect +reset babelfishpg_tsql.sql_dialect; +select CAST(€100.123 AS money); +ERROR: syntax error at or near ".123" +LINE 1: select CAST(€100.123 AS money); + ^ +-- Test exact result multiply/divide money with money in postgres dialect +select CAST(100 AS money)/CAST(339 AS money)*CAST(10000 AS money); + ?column? +----------- + $2,949.85 +(1 row) + +-- Clean up +drop table testing1; +-- BABEL-109 test no more not unique operator error caused by fixeddeciaml +select CAST(2 AS numeric) > 1; + ?column? +---------- + t +(1 row) + +select CAST(2 AS decimal) > 1; + ?column? +---------- + t +(1 row) + +-- Test that numeric > int and fixeddecimal > int is different +select CAST(2.00001 AS numeric) > 2; + ?column? +---------- + t +(1 row) + +select CAST(2.00001 AS sys.fixeddecimal) > 2; + ?column? +---------- + f +(1 row) + +-- test TSQL Money (based on fixeddecimal) cross datatype operators +set babelfishpg_tsql.sql_dialect = "tsql"; +select CAST(2 AS money) > 1; + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) > CAST(1 AS int); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) > CAST(1 AS int2); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) > CAST(1 AS int4); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) > CAST(1 AS numeric); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) > CAST(1 AS decimal); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) >= 1; + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) >= CAST(1 AS int); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) >= CAST(1 AS int2); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) >= CAST(1 AS int4); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) >= CAST(1 AS numeric); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) >= CAST(1 AS decimal); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) < 1; + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) < CAST(1 AS int); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) < CAST(1 AS int2); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) < CAST(1 AS int4); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) < CAST(1 AS numeric); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) < CAST(1 AS decimal); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) <= 1; + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) <= CAST(1 AS int); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) <= CAST(1 AS int2); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) <= CAST(1 AS int4); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) <= CAST(1 AS numeric); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) <= CAST(1 AS decimal); + ?column? +---------- + f +(1 row) + +select CAST(2 AS money) <> 1; + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) <> CAST(1 AS int); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) <> CAST(1 AS int2); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) <> CAST(1 AS int4); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) <> CAST(1 AS numeric); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) <> CAST(1 AS decimal); + ?column? +---------- + t +(1 row) + +select CAST(2 AS money) + 1; + ?column? +---------- + 3.0000 +(1 row) + +select CAST(2 AS money) + CAST(1 AS int); + ?column? +---------- + 3.0000 +(1 row) + +select CAST(2 AS money) + CAST(1 AS int2); + ?column? +---------- + 3.0000 +(1 row) + +select CAST(2 AS money) + CAST(1 AS int4); + ?column? +---------- + 3.0000 +(1 row) + +select CAST(2 AS money) + CAST(1 AS numeric); + ?column? +---------- + 3.0000 +(1 row) + +select CAST(2 AS money) + CAST(1 AS decimal); + ?column? +---------- + 3.0000 +(1 row) + +select CAST(2 AS money) - 1; + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) - CAST(1 AS int); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) - CAST(1 AS int2); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) - CAST(1 AS int4); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) - CAST(1 AS numeric); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) - CAST(1 AS decimal); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) * 2; + ?column? +---------- + 4.0000 +(1 row) + +select CAST(2 AS money) * CAST(2 AS int); + ?column? +---------- + 4.0000 +(1 row) + +select CAST(2 AS money) * CAST(2 AS int2); + ?column? +---------- + 4.0000 +(1 row) + +select CAST(2 AS money) * CAST(2 AS int4); + ?column? +---------- + 4.0000 +(1 row) + +select CAST(2 AS money) * CAST(2 AS numeric); + ?column? +---------- + 4.0000 +(1 row) + +select CAST(2 AS money) * CAST(2 AS decimal); + ?column? +---------- + 4.0000 +(1 row) + +select CAST(2 AS money) / 0.5; + ?column? +-------------------- + 4.0000000000000000 +(1 row) + +select CAST(2 AS money) / CAST(2 AS int); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) / CAST(2 AS int2); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) / CAST(2 AS int4); + ?column? +---------- + 1.0000 +(1 row) + +select CAST(2 AS money) / CAST(0.5 AS numeric(4,2)); + ?column? +-------------------- + 4.0000000000000000 +(1 row) + +select CAST(2 AS money) / CAST(0.5 AS decimal(4,2)); + ?column? +-------------------- + 4.0000000000000000 +(1 row) + +reset babelfishpg_tsql.sql_dialect; +-- Test DATE, DATETIME, DATETIMEOFFSET, DATETIME2 +set babelfishpg_tsql.sql_dialect = "tsql"; +-- DATE DATETIME, DATETIMEOFFSET, DATETIME2 and SMALLDATETIME are defined in tsql dialect +select CAST('2020-03-15' AS date); + date +------------ + 03-15-2020 +(1 row) + +select CAST('2020-03-15 09:00:00+8' AS datetimeoffset); + datetimeoffset +--------------------------------- + Sun Mar 15 09:00:00 2020 +08:00 +(1 row) + +select CAST('2020-03-15 09:00:00' AS datetime2); + datetime2 +-------------------------- + Sun Mar 15 09:00:00 2020 +(1 row) + +select CAST('2020-03-15 09:00:00' AS smalldatetime); + smalldatetime +-------------------------- + Sun Mar 15 09:00:00 2020 +(1 row) + +-- test the range of date +select CAST('0001-01-01' AS date); + date +------------ + 01-01-0001 +(1 row) + +select CAST('9999-12-31' AS date); + date +------------ + 12-31-9999 +(1 row) + +-- test the range of datetime2 +select CAST('0001-01-01 12:00:00.12345' AS datetime2); + datetime2 +-------------------------------- + Mon Jan 01 12:00:00.12345 0001 +(1 row) + +select CAST('9999-12-31 12:00:00.12345' AS datetime2); + datetime2 +-------------------------------- + Fri Dec 31 12:00:00.12345 9999 +(1 row) + +-- precision +select CAST('2020-03-15 09:00:00+8' AS datetimeoffset(7)) ; +WARNING: TIMESTAMP(7) WITH TIME ZONE precision reduced to maximum allowed, 6 +LINE 1: select CAST('2020-03-15 09:00:00+8' AS datetimeoffset(7)) ; + ^ + datetimeoffset +--------------------------------- + Sun Mar 15 09:00:00 2020 +08:00 +(1 row) + +create table testing1(ts DATETIME, tstz DATETIMEOFFSET(7)); +WARNING: TIMESTAMP(7) WITH TIME ZONE precision reduced to maximum allowed, 6 +LINE 1: create table testing1(ts DATETIME, tstz DATETIMEOFFSET(7)); + ^ +WARNING: TIMESTAMP(7) WITH TIME ZONE precision reduced to maximum allowed, 6 +insert into testing1 (ts, tstz) values ('2020-03-15 09:00:00', '2020-03-15 09:00:00+8'); +select * from testing1; + ts | tstz +--------------------------+--------------------------------- + Sun Mar 15 09:00:00 2020 | Sun Mar 15 09:00:00 2020 +08:00 +(1 row) + +drop table testing1; +select CAST('2020-03-15 09:00:00' AS datetime2(7)); +WARNING: TIMESTAMP(7) precision reduced to maximum allowed, 6 +LINE 1: select CAST('2020-03-15 09:00:00' AS datetime2(7)); + ^ + datetime2 +-------------------------- + Sun Mar 15 09:00:00 2020 +(1 row) + +select CAST('2020-03-15 09:00:00.123456' AS datetime2(3)); + datetime2 +------------------------------ + Sun Mar 15 09:00:00.123 2020 +(1 row) + +select CAST('2020-03-15 09:00:00.123456' AS datetime2(0)); + datetime2 +-------------------------- + Sun Mar 15 09:00:00 2020 +(1 row) + +select CAST('2020-03-15 09:00:00.123456' AS datetime2(-1)); +ERROR: TIMESTAMP(-1) precision must not be negative +LINE 1: select CAST('2020-03-15 09:00:00.123456' AS datetime2(-1)); + ^ +create table testing1(ts DATETIME, tstz DATETIME2(7)); +WARNING: TIMESTAMP(7) precision reduced to maximum allowed, 6 +LINE 1: create table testing1(ts DATETIME, tstz DATETIME2(7)); + ^ +WARNING: TIMESTAMP(7) precision reduced to maximum allowed, 6 +insert into testing1 (ts, tstz) values ('2020-03-15 09:00:00', '2020-03-15 09:00:00'); +select * from testing1; + ts | tstz +--------------------------+-------------------------- + Sun Mar 15 09:00:00 2020 | Sun Mar 15 09:00:00 2020 +(1 row) + +drop table testing1; +-- DATETIME, DATETIMEOFFSET, DATETIME2 and SMALLDATETIME are not defined in +-- postgres dialect +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); + set_config +------------ + postgres +(1 row) + +select CAST('2020-03-15 09:00:00+8' AS datetimeoffset); +ERROR: type "datetimeoffset" does not exist +LINE 1: select CAST('2020-03-15 09:00:00+8' AS datetimeoffset); + ^ +create table testing1(ts DATETIME); +ERROR: type "datetime" does not exist +LINE 1: create table testing1(ts DATETIME); + ^ +create table testing1(tstz DATETIMEOFFSET); +ERROR: type "datetimeoffset" does not exist +LINE 1: create table testing1(tstz DATETIMEOFFSET); + ^ +select CAST('2020-03-15 09:00:00' AS datetime2); +ERROR: type "datetime2" does not exist +LINE 1: select CAST('2020-03-15 09:00:00' AS datetime2); + ^ +create table testing1(ts SMALLDATETIME); +ERROR: type "smalldatetime" does not exist +LINE 1: create table testing1(ts SMALLDATETIME); + ^ +create table testing1(tstz DATETIME2); +ERROR: type "datetime2" does not exist +LINE 1: create table testing1(tstz DATETIME2); + ^ +-- Test DATETIME, DATETIMEOFFSET, DATETIME2 and SMALLDATETIME can be used as identifier +create table testing1(DATETIME int); +insert into testing1 (DATETIME) values (1); +select * from testing1; + datetime +---------- + 1 +(1 row) + +drop table testing1; +create table testing1(DATETIMEOFFSET int); +insert into testing1 (DATETIMEOFFSET) values (1); +select * from testing1; + datetimeoffset +---------------- + 1 +(1 row) + +drop table testing1; +create table testing1(DATETIME2 int); +insert into testing1 (DATETIME2) values (1); +select * from testing1; + datetime2 +----------- + 1 +(1 row) + +drop table testing1; +create table testing1(SMALLDATETIME int); +insert into testing1 (SMALLDATETIME) values (1); +select * from testing1; + smalldatetime +--------------- + 1 +(1 row) + +set babelfishpg_tsql.sql_dialect = 'tsql'; +insert into testing1 (SMALLDATETIME) values (2); +select * from testing1; + smalldatetime +--------------- + 1 + 2 +(2 rows) + +-- Test conversion between DATE and other date/time types +select CAST(CAST('2020-03-15' AS date) AS datetime); + datetime +-------------------------- + Sun Mar 15 00:00:00 2020 +(1 row) + +select CAST(CAST('2020-03-15' AS date) AS smalldatetime); + smalldatetime +-------------------------- + Sun Mar 15 00:00:00 2020 +(1 row) + +select CAST(CAST('2020-03-15' AS date) AS datetimeoffset(3)); + datetimeoffset +--------------------------------- + Sun Mar 15 00:00:00 2020 +00:00 +(1 row) + +select CAST(CAST('2020-03-15' AS date) AS datetime2(3)); + datetime2 +-------------------------- + Sun Mar 15 00:00:00 2020 +(1 row) + +-- Clean up +reset babelfishpg_tsql.sql_dialect; +drop table testing1; +-- Test SYS.NCHAR, SYS.NVARCHAR and SYS.VARCHAR +-- nchar is already available in postgres dialect +select CAST('£' AS nchar(1)); + bpchar +-------- + £ +(1 row) + +-- nvarchar is not available in postgres dialect +select CAST('£' AS nvarchar); +ERROR: type "nvarchar" does not exist +LINE 1: select CAST('£' AS nvarchar); + ^ +-- both are available in tsql dialect +set babelfishpg_tsql.sql_dialect = 'tsql'; +select CAST('£' AS nchar(2)); + nchar +------- + £ +(1 row) + +select CAST('£' AS nvarchar(2)); + nvarchar +---------- + £ +(1 row) + +-- multi-byte character doesn't fit in nchar(1) in tsql if it +-- would require a UTF16-surrogate-pair on output +select CAST('£' AS char(1)); -- allowed + bpchar +-------- + £ +(1 row) + +select CAST('£' AS sys.nchar(1)); -- allowed + nchar +------- + £ +(1 row) + +select CAST('£' AS sys.nvarchar(1)); -- allowed + nvarchar +---------- + £ +(1 row) + +select CAST('£' AS sys.varchar(1)); -- allowed + varchar +--------- + £ +(1 row) + +select CAST('😀' AS char(1)); -- not allowed +ERROR: value too long for type character(1) as UTF16 output +select CAST('😀' AS sys.nchar(1)); -- not allowed +ERROR: value too long for type character(1) as UTF16 output +select CAST('😀' AS sys.nvarchar(1)); -- not allowed +ERROR: value too long for type character varying(1) as UTF16 output +select CAST('😀' AS sys.varchar(1)); -- not allowed +ERROR: value too long for type character varying(1) as UTF16 output +-- Check that things work the same in postgres dialect +reset babelfishpg_tsql.sql_dialect; +select CAST('£' AS char(1)); + bpchar +-------- + £ +(1 row) + +select CAST('£' AS sys.nchar(1)); + nchar +------- + £ +(1 row) + +select CAST('£' AS sys.nvarchar(1)); + nvarchar +---------- + £ +(1 row) + +select CAST('£' AS sys.varchar(1)); + varchar +--------- + £ +(1 row) + +select CAST('😀' AS char(1)); + bpchar +-------- + 😀 +(1 row) + +select CAST('😀' AS sys.nchar(1)); -- this should not be allowed as nchar is T-SQL type +ERROR: value too long for type character(1) as UTF16 output +select CAST('😀' AS sys.nvarchar(1)); -- this should not be allowed as nvarchar is T-SQL type +ERROR: value too long for type character varying(1) as UTF16 output +select CAST('😀' AS sys.varchar(1)); -- this should not be allowed as sys.varchar is T-SQL type +ERROR: value too long for type character varying(1) as UTF16 output +set babelfishpg_tsql.sql_dialect = 'tsql'; +-- truncate input on explicit cast +select CAST('ab' AS char(1)); + bpchar +-------- + a +(1 row) + +select CAST('ab' AS nchar(1)); + nchar +------- + a +(1 row) + +select CAST('ab' AS nvarchar(1)); + nvarchar +---------- + a +(1 row) + +select CAST('ab' AS sys.varchar(1)); + varchar +--------- + a +(1 row) + +-- But still don't allow surrogate pairs to exceed max length +select CAST('😀b' AS char(1)); +ERROR: value too long for type character(1) as UTF16 output +select CAST('😀b' AS nchar(1)); +ERROR: value too long for type character(1) as UTF16 output +select CAST('😀b' AS nvarchar(1)); +ERROR: value too long for type character varying(1) as UTF16 output +select CAST('😀b' AS sys.varchar(1)); +ERROR: value too long for type character varying(1) as UTF16 output +-- default length of nchar/char is 1 in tsql (and pg) +create table testing1(col nchar); +\d testing1; + Table "public.testing1" + Column | Type | Collation | Nullable | Default +--------+------------+-----------------------+----------+--------- + col | "nchar"(1) | bbf_unicode_cp1_ci_as | | + +-- check length at insert +insert into testing1 (col) select 'a'; +insert into testing1 (col) select '£'; +insert into testing1 (col) select '😀'; +ERROR: value too long for type character(1) as UTF16 output +insert into testing1 (col) select 'ab'; +ERROR: value too long for type character(1) +-- space is automatically truncated +insert into testing1 (col) select 'c '; +select * from testing1; + col +----- + a + £ + c +(3 rows) + +-- default length of nvarchar in tsql is 1 +create table testing2(col nvarchar); +insert into testing2 (col) select 'a'; +insert into testing2 (col) select '£'; +insert into testing2 (col) select '😀'; +ERROR: value too long for type character varying(1) as UTF16 output +insert into testing2 (col) select 'ab'; +ERROR: value too long for type character varying(1) +-- space is automatically truncated +insert into testing2 (col) select 'c '; +select * from testing2; + col +----- + a + £ + c +(3 rows) + +-- default length of varchar in tsql is 1 +create table testing4(col sys.varchar); +insert into testing4 (col) select 'a'; +insert into testing4 (col) select '£'; +insert into testing4 (col) select '😀'; +ERROR: value too long for type character varying(1) as UTF16 output +insert into testing4 (col) select 'ab'; +ERROR: value too long for type character varying(1) +-- space is automatically truncated +insert into testing4 (col) select 'c '; +insert into testing2 (col) select '£ '; +insert into testing2 (col) select '😀 '; +ERROR: value too long for type character varying(1) as UTF16 output +select * from testing4; + col +----- + a + £ + c +(3 rows) + +-- test sys.varchar(max) and sys.nvarchar(max) syntax is allowed in tsql dialect +select CAST('abcdefghijklmn' AS sys.varchar(max)); + varchar +---------------- + abcdefghijklmn +(1 row) + +select CAST('abcdefghijklmn' AS varchar(max)); + varchar +---------------- + abcdefghijklmn +(1 row) + +select CAST('abcdefghijklmn' AS sys.nvarchar(max)); + nvarchar +---------------- + abcdefghijklmn +(1 row) + +select CAST('abcdefghijklmn' AS nvarchar(max)); + nvarchar +---------------- + abcdefghijklmn +(1 row) + +-- test char(max), nchar(max) is invalid syntax in tsql dialect +select cast('abc' as char(max)); +ERROR: Incorrect syntax near the keyword 'bpchar'. +select cast('abc' as nchar(max)); +ERROR: Incorrect syntax near the keyword 'nchar'. +-- test max can still be used as an identifier +create table max (max int); +insert into max (max) select 100; +select * from max; + max +----- + 100 +(1 row) + +drop table max; +-- test sys.varchar(max) and nvarchar(max) syntax is not allowed in postgres dialect +reset babelfishpg_tsql.sql_dialect; +select CAST('abcdefghijklmn' AS sys.varchar(max)); +ERROR: invalid input syntax for type integer: "max" +LINE 1: select CAST('abcdefghijklmn' AS sys.varchar(max)); + ^ +select CAST('abcdefghijklmn' AS varchar(max)); +ERROR: syntax error at or near "max" +LINE 1: select CAST('abcdefghijklmn' AS varchar(max)); + ^ +select CAST('abcdefghijklmn' AS sys.nvarchar(max)); +ERROR: invalid input syntax for type integer: "max" +LINE 1: select CAST('abcdefghijklmn' AS sys.nvarchar(max)); + ^ +select CAST('abcdefghijklmn' AS nvarchar(max)); +ERROR: type "nvarchar" does not exist +LINE 1: select CAST('abcdefghijklmn' AS nvarchar(max)); + ^ +-- test max max character length is (10 * 1024 * 1024) = 10485760 +select CAST('abc' AS varchar(10485761)); +ERROR: length for type varchar cannot exceed 10485760 +LINE 1: select CAST('abc' AS varchar(10485761)); + ^ +select CAST('abc' AS varchar(10485760)); + varchar +--------- + abc +(1 row) + +-- test column type nvarchar(max) +set babelfishpg_tsql.sql_dialect = 'tsql'; +create table testing5(col nvarchar(max)); +\d testing5 + Table "public.testing5" + Column | Type | Collation | Nullable | Default +--------+----------+-----------------------+----------+--------- + col | nvarchar | bbf_unicode_cp1_ci_as | | + +insert into testing5 (col) select 'ab'; +insert into testing5 (col) select 'abcdefghijklmn'; +select * from testing5; + col +---------------- + ab + abcdefghijklmn +(2 rows) + +--test COPY command works with sys.nvarchar +COPY public.testing5 (col) FROM stdin; +select * from testing5; + col +---------------- + ab + abcdefghijklmn + c + ab + abcdefghijk +(5 rows) + +-- [BABEL-220] test varchar(max) as a column +drop table testing5; +create table testing5(col varchar(max)); +\d testing5 + Table "public.testing5" + Column | Type | Collation | Nullable | Default +--------+-----------+-----------------------+----------+--------- + col | "varchar" | bbf_unicode_cp1_ci_as | | + +insert into testing5 (col) select 'ab'; +insert into testing5 (col) select 'abcdefghijklmn'; +select * from testing5; + col +---------------- + ab + abcdefghijklmn +(2 rows) + +-- test type modifer persist if babelfishpg_tsql.sql_dialect changes +create table testing3(col nvarchar(2)); +insert into testing3 (col) select 'ab'; +insert into testing3 (col) select 'a£'; +insert into testing3 (col) select 'a😀'; +ERROR: value too long for type character varying(2) as UTF16 output +insert into testing3 (col) select 'abc'; +ERROR: value too long for type character varying(2) +reset babelfishpg_tsql.sql_dialect; +insert into testing3 (col) select 'ab'; +insert into testing3 (col) select 'a£'; +insert into testing3 (col) select 'a😀'; +ERROR: value too long for type character varying(2) as UTF16 output +insert into testing3 (col) select 'abc'; +ERROR: value too long for type character varying(2) +set babelfishpg_tsql.sql_dialect = 'tsql'; +insert into testing3 (col) select 'ab'; +insert into testing3 (col) select 'a£'; +insert into testing3 (col) select 'a😀'; +ERROR: value too long for type character varying(2) as UTF16 output +insert into testing3 (col) select 'abc'; +ERROR: value too long for type character varying(2) +-- test normal create domain works when apg_enable_domain_typmod is enabled +set apg_enable_domain_typmod true; +create domain varchar3 as varchar(3); +select CAST('abc' AS varchar3); + varchar3 +---------- + abc +(1 row) + +select CAST('ab£' AS varchar3); + varchar3 +---------- + ab£ +(1 row) + +select CAST('ab😀' AS varchar3); +ERROR: value too long for type character varying(3) as UTF16 output +select CAST('abcd' AS varchar3); + varchar3 +---------- + abc +(1 row) + +reset apg_enable_domain_typmod; +ERROR: unrecognized configuration parameter "apg_enable_domain_typmod" +-- [BABEL-191] test typmod of sys.varchar/nvarchar engages when the input +-- is casted multiple times +select CAST(CAST('abc' AS text) AS sys.varchar(3)); + varchar +--------- + abc +(1 row) + +select CAST(CAST('abc' AS pg_catalog.varchar(3)) AS sys.varchar(3)); + varchar +--------- + abc +(1 row) + +select CAST(CAST('abc' AS text) AS sys.nvarchar(3)); + nvarchar +---------- + abc +(1 row) + +select CAST(CAST('abc' AS text) AS sys.nchar(3)); + nchar +------- + abc +(1 row) + +select CAST(CAST(CAST(CAST('abc' AS text) AS sys.varchar(3)) AS sys.nvarchar(3)) AS sys.nchar(3)); + nchar +------- + abc +(1 row) + +-- test truncation on explicit cast through multiple levels +select CAST(CAST(CAST(CAST('abcde' AS text) AS sys.varchar(5)) AS sys.nvarchar(4)) AS sys.nchar(3)); + nchar +------- + abc +(1 row) + +select CAST(CAST(CAST(CAST('abcde' AS text) AS sys.varchar(3)) AS sys.nvarchar(4)) AS sys.nchar(5)); + nchar +------- + abc +(1 row) + +-- test sys.ntext is available +select CAST('abc£' AS sys.ntext); + ntext +------- + abc£ +(1 row) + +-- pg_catalog.text +select CAST('abc£' AS text); + text +------ + abc£ +(1 row) + +-- [BABEL-218] test varchar defaults to sys.varchar in tsql dialect +-- test default length of sys.varchar is 30 in CAST/CONVERT +-- expect the last 'e' to be truncated +select cast('abcdefghijklmnopqrstuvwxyzabcde' as varchar); + varchar +-------------------------------- + abcdefghijklmnopqrstuvwxyzabcd +(1 row) + +select cast('abcdefghijklmnopqrstuvwxyzabcde' as sys.varchar); + varchar +-------------------------------- + abcdefghijklmnopqrstuvwxyzabcd +(1 row) + +select convert(varchar, 'abcdefghijklmnopqrstuvwxyzabcde'); + babelfish_conv_helper_to_varchar +---------------------------------- + abcdefghijklmnopqrstuvwxyzabcd +(1 row) + +select convert(sys.varchar, 'abcdefghijklmnopqrstuvwxyzabcde'); + varchar +-------------------------------- + abcdefghijklmnopqrstuvwxyzabcd +(1 row) + +-- default length of pg_catalog.varchar is unlimited, no truncation in output +select cast('abcdefghijklmnopqrstuvwxyzabcde' as pg_catalog.varchar); + varchar +--------------------------------- + abcdefghijklmnopqrstuvwxyzabcde +(1 row) + +-- varchar defaults to pg_catalog.varchar in PG dialect +reset babelfishpg_tsql.sql_dialect; +select cast('abcdefghijklmnopqrstuvwxyzabcde' as pg_catalog.varchar); -- default length of pg_catalog.varchar is unlimited, no truncation + varchar +--------------------------------- + abcdefghijklmnopqrstuvwxyzabcde +(1 row) + +set babelfishpg_tsql.sql_dialect = 'tsql'; +-- [BABEL-255] test nchar defaults to sys.nchar in tsql dialect +create table test_nchar (col1 nchar); +\d test_nchar + Table "public.test_nchar" + Column | Type | Collation | Nullable | Default +--------+------------+-----------------------+----------+--------- + col1 | "nchar"(1) | bbf_unicode_cp1_ci_as | | + +drop table test_nchar; +-- test nchar defaults to bpchar in pg dialect +reset babelfishpg_tsql.sql_dialect; +create table test_nchar (col1 nchar); +\d test_nchar + Table "public.test_nchar" + Column | Type | Collation | Nullable | Default +--------+--------------+-----------+----------+--------- + col1 | character(1) | | | + +drop table test_nchar; +set babelfishpg_tsql.sql_dialect = 'tsql'; +-- [BABEL-257] test varchar defaults to sys.varchar in new +-- database and new schema +SELECT current_database(); + current_database +-------------------- + contrib_regression +(1 row) + +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); + set_config +------------ + postgres +(1 row) + +CREATE DATABASE demo; +\c demo +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql" CASCADE; +NOTICE: installing required extension "uuid-ossp" +NOTICE: installing required extension "babelfishpg_common" +-- Reconnect to make sure CLUSTER_COLLATION_OID is initialized +\c postgres +\c demo +set babelfishpg_tsql.sql_dialect = 'tsql'; +-- Test varchar is mapped to sys.varchar +-- Expect truncated output because sys.varchar defaults to sys.varchar(30) in CAST function +select cast('abcdefghijklmnopqrstuvwxyzabcde' as varchar); + varchar +-------------------------------- + abcdefghijklmnopqrstuvwxyzabcd +(1 row) + +-- Expect non-truncated output because pg_catalog.varchar has unlimited length +select cast('abcdefghijklmnopqrstuvwxyzabcde' as pg_catalog.varchar); + varchar +--------------------------------- + abcdefghijklmnopqrstuvwxyzabcde +(1 row) + +-- Test bit is mapped to sys.bit +-- sys.bit allows numeric input +select CAST(1.5 AS bit); + bit +----- + 1 +(1 row) + +-- pg_catalog.bit doesn't allow numeric input +select CAST(1.5 AS pg_catalog.bit); +ERROR: cannot cast type numeric to bit +LINE 1: select CAST(1.5 AS pg_catalog.bit); + ^ +-- Test varchar is mapped to sys.varchar in a new schema and a new table +CREATE SCHEMA s1; +create table s1.test1 (col varchar); +-- Test sys.varchar is created for test1.col, expect an error +-- because sys.varchar defaults to sys.varchar(1) +insert into s1.test1 values('abc'); +ERROR: value too long for type character varying(1) +insert into s1.test1 values('a'); +select * from s1.test1; + col +----- + a +(1 row) + +drop schema s1 cascade; +NOTICE: drop cascades to table s1.test1 +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); + set_config +------------ + postgres +(1 row) + +\c regression +\connect: FATAL: database "regression" does not exist diff --git a/contrib/babelfishpg_tsql/expected/test/babel_ddl.out b/contrib/babelfishpg_tsql/expected/test/babel_ddl.out new file mode 100644 index 00000000000..a9aa45564bf --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_ddl.out @@ -0,0 +1,323 @@ +-- CLUSTERED INDEX / NONCLUSTERED IDNEX +create table t1 ( a int, b int); +create nonclustered index t1_idx1 on t1 (a); +ERROR: syntax error at or near "nonclustered" +LINE 1: create nonclustered index t1_idx1 on t1 (a); + ^ +create clustered index t1_idx2 on t1(a); +ERROR: syntax error at or near "clustered" +LINE 1: create clustered index t1_idx2 on t1(a); + ^ +create table t2 ( a int, b int, primary key nonclustered (a)); +ERROR: syntax error at or near "nonclustered" +LINE 1: create table t2 ( a int, b int, primary key nonclustered (a)... + ^ +create table t3 ( a int, b int, primary key clustered (a)); +ERROR: syntax error at or near "clustered" +LINE 1: create table t3 ( a int, b int, primary key clustered (a)); + ^ +create table t4 ( a int, b int, unique nonclustered (a)); +ERROR: syntax error at or near "nonclustered" +LINE 1: create table t4 ( a int, b int, unique nonclustered (a)); + ^ +create table t5 ( a int, b int, unique clustered (a)); +ERROR: syntax error at or near "clustered" +LINE 1: create table t5 ( a int, b int, unique clustered (a)); + ^ +create table t6 ( a int primary key nonclustered, b int); +ERROR: syntax error at or near "nonclustered" +LINE 1: create table t6 ( a int primary key nonclustered, b int); + ^ +create table t7 ( a int primary key clustered, b int); +ERROR: syntax error at or near "clustered" +LINE 1: create table t7 ( a int primary key clustered, b int); + ^ +create table t8 ( a int unique nonclustered, b int); +ERROR: syntax error at or near "nonclustered" +LINE 1: create table t8 ( a int unique nonclustered, b int); + ^ +create table t9 ( a int unique clustered, b int); +ERROR: syntax error at or near "clustered" +LINE 1: create table t9 ( a int unique clustered, b int); + ^ +set babelfishpg_tsql.sql_dialect = "tsql"; +create index t1_idx1 on t1 (a); +create index t1_idx2 on t1(a); +create table t2 ( a int, b int, primary key (a)); +create table t3 ( a int, b int, primary key (a)); +create table t4 ( a int, b int, unique (a)); +create table t5 ( a int, b int, unique (a)); +create table t6 ( a int primary key, b int); +create table t7 ( a int primary key, b int); +create table t8 ( a int unique not null, b int); +create table t9 ( a int unique not null, b int); +-- CREATE INDEX ... ON syntax +create index t1_idx3 on t1 (a) on [primary]; +create index t1_idx4 on t1 (a) on "default"; +-- CREATE TABLE WITH ( [,...n]) syntax +create table t10 (a int) +with (fillfactor = 90, FILETABLE_COLLATE_FILENAME = database_default); +create table t11 (a int) +with (data_compression = row on partitions (2, 4, 6 to 8)); +create table t12 (a int) +with (system_versioning = on (history_table = aaa.bbb, data_consistency_check = off)); +create table t13 (a int) +with (remote_data_archive = on (filter_predicate = null, migration_state = outbound)); +create table t14 (a int) +with (data_deletion = on (filter_column = a, retention_period = 14 day)); +-- CREATE INDEX WHERE... WITH ( [,...n]) syntax +create index t1_idx5 on t1(a) where a is not null +with (pad_index = off, fillfactor = 90, maxdop = 1, sort_in_tempdb = off, max_duration = 2 minutes); +create index t1_idx6 on t1(a) +with (data_compression = page on partitions (2, 4, 6 to 8)); +-- CREATE COLUMNSTORE INDEX +create columnstore index t1_idx7 on t1 (a) with (drop_existing = on); +NOTICE: The COLUMNSTORE option is currently ignored +create clustered columnstore index t1_idx8 on t1 (a) on [primary]; +NOTICE: The COLUMNSTORE option is currently ignored +-- CREATE TABLE... WITH FILLFACTOR = num +create table t15 (a int primary key with fillfactor=50); +-- ALTER TABLE... WITH FILLFACTOR = num +create table t16 (a int not null); +alter table t16 add primary key (a) with fillfactor=50; +-- check property of the index +select indexname, indexdef from pg_indexes where tablename like 't_' order by indexname; + indexname | indexdef +-------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + t1_idx1t18e881e6977bd6b8cbb78725b3a8ac988 | CREATE INDEX t1_idx1t18e881e6977bd6b8cbb78725b3a8ac988 ON public.t1 USING btree (a) + t1_idx2t117dbbb74ced1fe936cdf7cd7baeff266 | CREATE INDEX t1_idx2t117dbbb74ced1fe936cdf7cd7baeff266 ON public.t1 USING btree (a) + t1_idx3t19eceb46c036c3c1bd6895a34ec3c93f1 | CREATE INDEX t1_idx3t19eceb46c036c3c1bd6895a34ec3c93f1 ON public.t1 USING btree (a) + t1_idx4t1fb4b953a652720bfa47919dff09b172e | CREATE INDEX t1_idx4t1fb4b953a652720bfa47919dff09b172e ON public.t1 USING btree (a) + t1_idx5t1b35d191ff61a4ba407b80329c7ac459a | CREATE INDEX t1_idx5t1b35d191ff61a4ba407b80329c7ac459a ON public.t1 USING btree (a) WITH (pad_index=off, fillfactor='90', maxdop='1', sort_in_tempdb=off, max_duration='2') WHERE (a IS NOT NULL) + t1_idx6t144818325f74bdb1fd5bca880a0aef84c | CREATE INDEX t1_idx6t144818325f74bdb1fd5bca880a0aef84c ON public.t1 USING btree (a) WITH (data_compression=page) + t1_idx7t1a053e704a0d67d6d079dd35cca63a489 | CREATE INDEX t1_idx7t1a053e704a0d67d6d079dd35cca63a489 ON public.t1 USING btree (a) WITH (drop_existing='on') + t1_idx8t171284af2ea6a5a7032b931c5725d1fc4 | CREATE INDEX t1_idx8t171284af2ea6a5a7032b931c5725d1fc4 ON public.t1 USING btree (a) + t2_pkey | CREATE UNIQUE INDEX t2_pkey ON public.t2 USING btree (a) + t3_pkey | CREATE UNIQUE INDEX t3_pkey ON public.t3 USING btree (a) + t4_a_key | CREATE UNIQUE INDEX t4_a_key ON public.t4 USING btree (a) + t5_a_key | CREATE UNIQUE INDEX t5_a_key ON public.t5 USING btree (a) + t6_pkey | CREATE UNIQUE INDEX t6_pkey ON public.t6 USING btree (a) + t7_pkey | CREATE UNIQUE INDEX t7_pkey ON public.t7 USING btree (a) + t8_a_key | CREATE UNIQUE INDEX t8_a_key ON public.t8 USING btree (a) + t9_a_key | CREATE UNIQUE INDEX t9_a_key ON public.t9 USING btree (a) +(16 rows) + +-- CREATE TABLE(..., { PRIMARY KEY | UNIQUE } ... +-- ON { partition_scheme | filegroup | "default" }) syntax +-- ^ +create table t17(a int, primary key clustered (a) on [PRIMARY]); +create table t18(a int, primary key clustered (a) on [PRIMARY]); +create table t19(a int, unique clustered (a) on [PRIMARY]); +create table t20(a int, unique clustered (a) on [PRIMARY]); +-- ALTER TABLE ... ADD [CONSTRAINT ...] DEFAULT ... FOR ... +create table t21 (a int, b int); +alter table t21 add default 99 for a; +NOTICE: DEFAULT added. To drop the default, use ALTER TABLE...ALTER COLUMN...DROP DEFAULT; it cannot be dropped by name +insert into t21(b) values (10); +select * from t21; + a | b +----+---- + 99 | 10 +(1 row) + +alter table t21 alter a drop default; +alter table t21 add constraint dflt11 default 11 for a; +NOTICE: DEFAULT added. To drop the default, use ALTER TABLE...ALTER COLUMN...DROP DEFAULT; it cannot be dropped by name +insert into t21(b) values (20); +select * from t21; + a | b +----+---- + 99 | 10 + 11 | 20 +(2 rows) + +-- Invalid default value +alter table t21 add default 'test' for a; +NOTICE: DEFAULT added. To drop the default, use ALTER TABLE...ALTER COLUMN...DROP DEFAULT; it cannot be dropped by name +ERROR: invalid input syntax for type integer: "test" +-- Invalid column +alter table t21 add default 99 for c; +NOTICE: DEFAULT added. To drop the default, use ALTER TABLE...ALTER COLUMN...DROP DEFAULT; it cannot be dropped by name +ERROR: column "c" of relation "t21" does not exist +-- Invalid table +alter table t_invalid add default 99 for a; +NOTICE: DEFAULT added. To drop the default, use ALTER TABLE...ALTER COLUMN...DROP DEFAULT; it cannot be dropped by name +ERROR: relation "t_invalid" does not exist +-- ALTER TABLE ... WITH [NO]CHECK ADD CONSTRAINT ... +alter table t21 with check add constraint chk1 check (a > 0); -- add chk1 and enable it +NOTICE: The WITH CHECK/NOCHECK option is currently ignored +alter table t21 with nocheck add constraint chk2 check (b > 0); -- add chk2 and disable it +NOTICE: The WITH CHECK/NOCHECK option is currently ignored +insert into t21 values (1, 1); +-- error, not fulfilling constraint chk1 +insert into t21 values (0, 1); +ERROR: new row for relation "t21" violates check constraint "chk1t21848ea8bb1121ee393ad72ae0d412d8d2" +DETAIL: Failing row contains (0, 1). +-- should pass after CHECK/NOCHECK is fully supported +insert into t21 values (1, 0); +ERROR: new row for relation "t21" violates check constraint "chk2t21e79a26fb83f057113598e77ab0b1983d" +DETAIL: Failing row contains (1, 0). +select * from t21; + a | b +----+---- + 99 | 10 + 11 | 20 + 1 | 1 +(3 rows) + +-- ALTER TABLE ... [NO]CHECK CONSTRAINT ... +-- should pass after CHECK/NOCHECK is fully supported +alter table t21 nocheck constraint chk1; -- disable chk1 +NOTICE: The CHECK/NOCHECK option is currently ignored +alter table t21 check constraint chk2; -- enable chk2 +NOTICE: The CHECK/NOCHECK option is currently ignored +-- CREATE TABLE ... ( a int identity(...) NOT FOR REPLICATION) +create table t22 (a int identity(1,1) NOT FOR REPLICATION); +create table t23 (a int identity(1,1) NOT FOR REPLICATION NOT NULL); +-- ROWGUIDCOL syntax support +create table t24 (a uniqueidentifier ROWGUIDCOL); +create table t25 (a int); +alter table t25 add b uniqueidentifier ROWGUIDCOL; +-- computed columns +-- CREATE TABLE(..., AS +-- ^ [ PERSISTED ] ) +create table computed_column_t1 (a nvarchar(10), b AS substring(a,1,3) UNIQUE NOT NULL); +insert into computed_column_t1 values('abcd'); +select * from computed_column_t1; + a | b +------+----- + abcd | abc +(1 row) + +-- test whether other constraints are working with computed columns +insert into computed_column_t1 values('abcd'); -- throws error +ERROR: duplicate key value violates unique constraint "computed_column_t1_b_key" +DETAIL: Key (b)=(abc) already exists. +-- check PERSISTED keyword +-- should be able to use columns from left and right in the expression +create table computed_column_t2 (a int, b AS (a + c) / 4 PERSISTED, c int); +insert into computed_column_t2 (a,c) values (12, 12); +select * from computed_column_t2; + a | b | c +----+---+---- + 12 | 6 | 12 +(1 row) + +-- should throw error - order matters +create table computed_column_error (a int, b AS a/4 NOT NULL PERSISTED); +ERROR: syntax error at or near "PERSISTED" +LINE 1: ... computed_column_error (a int, b AS a/4 NOT NULL PERSISTED)... + ^ +-- should throw error if postgres syntax is used in TSQL dialect +create table computed_column_error (a int, b numeric generated always as (a/4) stored); +ERROR: This syntax is only valid when babelfishpg_tsql.sql_dialect is postgres +LINE 1: ...computed_column_error (a int, b numeric generated always as ... + ^ +-- should throw error if there is any error in computed column expression +create table computed_column_error (a nvarchar(10), b AS non_existant_function(a,1,3) UNIQUE NOT NULL); +ERROR: function non_existant_function(nvarchar, integer, integer) does not exist +LINE 1: ...able computed_column_error (a nvarchar(10), b AS non_exista... + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- should throw error in case of nested computed columns +create table computed_column_error (a int, b as c, c as a); +ERROR: computed column "c" in table "computed_column_error" is not allowed to be used in another computed-column definition +LINE 1: create table computed_column_error (a int, b as c, c as a); + ^ +create table computed_column_error (a int, b as b + 1); +ERROR: computed column "b" in table "computed_column_error" is not allowed to be used in another computed-column definition +LINE 1: create table computed_column_error (a int, b as b + 1); + ^ +-- in case of multiple computed column, the entire statement should be rolled +-- back even when the last one throws error +create table computed_column_error (a int, b as a, c as b); +ERROR: computed column "b" in table "computed_column_error" is not allowed to be used in another computed-column definition +LINE 1: create table computed_column_error (a int, b as a, c as b); + ^ +select * from computed_column_error; +ERROR: relation "computed_column_error" does not exist +LINE 1: select * from computed_column_error; + ^ +-- ALTER TABLE... ADD AS +-- ^ [ PERSISTED ] ) +alter table computed_column_t1 add c int; +alter table computed_column_t1 add d as c / 4; +insert into computed_column_t1(a, c) VALUES ('efgh', 12); +select * from computed_column_t1; + a | b | c | d +------+-----+----+--- + abcd | abc | | + efgh | efg | 12 | 3 +(2 rows) + +--should thow error in case of nested computed columns + alter table computed_column_t1 add e as d; +ERROR: cannot use generated column "d" in column generation expression +DETAIL: A generated column cannot reference another generated column. + alter table computed_column_t1 add e as e + 1; +ERROR: computed column "e" in table "computed_column_t1" is not allowed to be used in another computed-column definition +-- should throw error if any of the dependant columns is modified or dropped. +alter table computed_column_t1 drop column a; +ERROR: cannot drop a column used by a generated column +DETAIL: Column "a" is used by generated column "b". +alter table computed_column_t1 alter column a varchar; +ERROR: cannot alter type of a column used by a generated column +DETAIL: Column "a" is used by generated column "b". +-- should throw error as rand is non-deterministic +alter table computed_column_t1 add e as rand() persisted; +ERROR: generation expression is not immutable +-- but rand[seed] should succeed +alter table computed_column_t1 add e as rand(1) persisted; +-- should throw error in postgres dialect +select set_config('babelfishpg_tsql.sql_dialect', 'postgres', null); + set_config +------------ + postgres +(1 row) + +create table computed_column_error (a int, b AS (a/4) PERSISTED NOT NULL); +ERROR: syntax error at or near "AS" +LINE 1: create table computed_column_error (a int, b AS (a/4) PERSI... + ^ +-- since we're in postgres dialect, also check the table definition whether +-- the computed column got resolved to correct datatype +\d computed_column_t1 + Table "public.computed_column_t1" + Column | Type | Collation | Nullable | Default +--------+------------------+-----------------------+----------+------------------------------------------------------- + a | sys.nvarchar(10) | bbf_unicode_cp1_ci_as | | + b | sys.nvarchar | bbf_unicode_cp1_ci_as | not null | generated always as (sys."substring"(a, 1, 3)) stored + c | integer | | | + d | integer | | | generated always as (c / 4) stored + e | double precision | | | generated always as (sys.rand(1)) stored +Indexes: + "computed_column_t1_b_key" UNIQUE CONSTRAINT, btree (b) + +set babelfishpg_tsql.sql_dialect = "tsql"; +drop table t1; +drop table t2; +drop table t3; +drop table t4; +drop table t5; +drop table t6; +drop table t7; +drop table t8; +drop table t9; +drop table t10; +drop table t11; +drop table t12; +drop table t13; +drop table t14; +drop table t15; +drop table t16; +drop table t17; +drop table t18; +drop table t19; +drop table t20; +drop table t21; +drop table t22; +drop table t23; +drop table t24; +drop table t25; +drop table computed_column_t1; +drop table computed_column_t2; diff --git a/contrib/babelfishpg_tsql/expected/test/babel_delete.out b/contrib/babelfishpg_tsql/expected/test/babel_delete.out new file mode 100644 index 00000000000..0f37c683f67 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_delete.out @@ -0,0 +1,294 @@ +-- +-- Tests for DELETE clause +-- +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql"; +NOTICE: extension "babelfishpg_tsql" already exists, skipping +-- Negative cases when using postgres dialect +RESET babelfishpg_tsql.sql_dialect; +SHOW babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + postgres +(1 row) + +CREATE TABLE delete_test_tbl ( + age int, + fname char(10), + lname char(10), + city nchar(20) +); +INSERT INTO delete_test_tbl(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (35, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname4', 'new york'), + (26, 'fname5', 'lname5', 'los angeles'), + (74, 'fname6', 'lname6', 'tokyo'), + (44, 'fname7', 'lname7', 'oslo'), + (19, 'fname8', 'lname8', 'hong kong'), + (61, 'fname9', 'lname9', 'shanghai'), + (29, 'fname10', 'lname10', 'mumbai'); +SELECT * FROM delete_test_tbl; + age | fname | lname | city +-----+------------+------------+---------------------- + 50 | fname1 | lname1 | london + 34 | fname2 | lname2 | paris + 35 | fname3 | lname3 | brussels + 90 | fname4 | lname4 | new york + 26 | fname5 | lname5 | los angeles + 74 | fname6 | lname6 | tokyo + 44 | fname7 | lname7 | oslo + 19 | fname8 | lname8 | hong kong + 61 | fname9 | lname9 | shanghai + 29 | fname10 | lname10 | mumbai +(10 rows) + +\set ON_ERROR_STOP 0 +DELETE delete_test_tbl; +ERROR: syntax error at or near "delete_test_tbl" +LINE 1: DELETE delete_test_tbl; + ^ +-- Positive cases when using tsql dialect +SET babelfishpg_tsql.sql_dialect = "tsql"; +SHOW babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + tsql +(1 row) + +\set ON_ERROR_STOP 1 +-- Prove that a user may delete rows from a table without using the FROM clause +SELECT * FROM delete_test_tbl; + age | fname | lname | city +-----+------------+------------+---------------------- + 50 | fname1 | lname1 | london + 34 | fname2 | lname2 | paris + 35 | fname3 | lname3 | brussels + 90 | fname4 | lname4 | new york + 26 | fname5 | lname5 | los angeles + 74 | fname6 | lname6 | tokyo + 44 | fname7 | lname7 | oslo + 19 | fname8 | lname8 | hong kong + 61 | fname9 | lname9 | shanghai + 29 | fname10 | lname10 | mumbai +(10 rows) + +-- Test that that WHERE clause can be used without FROM +DELETE delete_test_tbl WHERE city='hong kong'; +SELECT * FROM delete_test_tbl; + age | fname | lname | city +-----+------------+------------+---------------------- + 50 | fname1 | lname1 | london + 34 | fname2 | lname2 | paris + 35 | fname3 | lname3 | brussels + 90 | fname4 | lname4 | new york + 26 | fname5 | lname5 | los angeles + 74 | fname6 | lname6 | tokyo + 44 | fname7 | lname7 | oslo + 61 | fname9 | lname9 | shanghai + 29 | fname10 | lname10 | mumbai +(9 rows) + +DELETE delete_test_tbl WHERE age > 50; +SELECT * FROM delete_test_tbl; + age | fname | lname | city +-----+------------+------------+---------------------- + 50 | fname1 | lname1 | london + 34 | fname2 | lname2 | paris + 35 | fname3 | lname3 | brussels + 26 | fname5 | lname5 | los angeles + 44 | fname7 | lname7 | oslo + 29 | fname10 | lname10 | mumbai +(6 rows) + +DELETE delete_test_tbl WHERE fname IN ('fname1', 'fname2'); +SELECT * FROM delete_test_tbl; + age | fname | lname | city +-----+------------+------------+---------------------- + 35 | fname3 | lname3 | brussels + 26 | fname5 | lname5 | los angeles + 44 | fname7 | lname7 | oslo + 29 | fname10 | lname10 | mumbai +(4 rows) + +-- Test that DELETE works without any other clauses +DELETE delete_test_tbl; +SELECT * FROM delete_test_tbl; + age | fname | lname | city +-----+-------+-------+------ +(0 rows) + +-- Test delete for joined table +CREATE TABLE delete_test_tbl2 ( + age int, + fname char(10), + lname char(10), + city nchar(20) +); +INSERT INTO delete_test_tbl2(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (50, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname4', 'new york'), + (26, 'fname5', 'lname5', 'los angeles'), + (74, 'fname6', 'lname6', 'tokyo'), + (44, 'fname7', 'lname7', 'oslo'), + (19, 'fname8', 'lname8', 'hong kong'), + (61, 'fname9', 'lname9', 'shanghai'), + (29, 'fname10', 'lname10', 'mumbai'); +CREATE TABLE delete_test_tbl3 ( + year int, + lname char(10), +); +INSERT INTO delete_test_tbl3(year, lname) +VALUES (51, 'lname1'), + (34, 'lname3'), + (25, 'lname8'), + (95, 'lname9'), + (36, 'lname10'); +CREATE TABLE delete_test_tbl4 ( + lname char(10), + city char(10), +); +INSERT INTO delete_test_tbl4(lname, city) +VALUES ('lname8','london'), + ('lname9','tokyo'), + ('lname10','mumbai'); +SELECT * FROM delete_test_tbl2 ORDER BY lname; + age | fname | lname | city +-----+------------+------------+---------------------- + 50 | fname1 | lname1 | london + 29 | fname10 | lname10 | mumbai + 34 | fname2 | lname2 | paris + 50 | fname3 | lname3 | brussels + 90 | fname4 | lname4 | new york + 26 | fname5 | lname5 | los angeles + 74 | fname6 | lname6 | tokyo + 44 | fname7 | lname7 | oslo + 19 | fname8 | lname8 | hong kong + 61 | fname9 | lname9 | shanghai +(10 rows) + +SELECT * FROM delete_test_tbl3 ORDER BY lname; + year | lname +------+------------ + 51 | lname1 + 36 | lname10 + 34 | lname3 + 25 | lname8 + 95 | lname9 +(5 rows) + +SELECT * FROM delete_test_tbl4 ORDER BY lname; + lname | city +------------+------------ + lname10 | mumbai + lname8 | london + lname9 | tokyo +(3 rows) + +DELETE delete_test_tbl2 +FROM delete_test_tbl2 t2 +INNER JOIN delete_test_tbl3 t3 +ON t2.lname = t3.lname +WHERE year > 50; +SELECT * FROM delete_test_tbl2 ORDER BY lname; + age | fname | lname | city +-----+------------+------------+---------------------- + 29 | fname10 | lname10 | mumbai + 34 | fname2 | lname2 | paris + 50 | fname3 | lname3 | brussels + 90 | fname4 | lname4 | new york + 26 | fname5 | lname5 | los angeles + 74 | fname6 | lname6 | tokyo + 44 | fname7 | lname7 | oslo + 19 | fname8 | lname8 | hong kong +(8 rows) + +DELETE delete_test_tbl2 +FROM delete_test_tbl3 t3 +LEFT JOIN delete_test_tbl2 t2 +ON t2.lname = t3.lname +WHERE t3.year < 30 AND t2.age > 40; +SELECT * FROM delete_test_tbl2 ORDER BY lname; + age | fname | lname | city +-----+------------+------------+---------------------- + 29 | fname10 | lname10 | mumbai + 34 | fname2 | lname2 | paris + 50 | fname3 | lname3 | brussels + 90 | fname4 | lname4 | new york + 26 | fname5 | lname5 | los angeles + 74 | fname6 | lname6 | tokyo + 44 | fname7 | lname7 | oslo + 19 | fname8 | lname8 | hong kong +(8 rows) + +-- delete with outer join on multiple tables +DELETE delete_test_tbl2 +FROM delete_test_tbl4 t4 +LEFT JOIN delete_test_tbl2 t2 +ON t4.city = t2.city +LEFT JOIN delete_test_tbl3 t3 +ON t2.lname = t3.lname +WHERE t4.city = 'mumbai'; +SELECT * FROM delete_test_tbl2 ORDER BY lname; + age | fname | lname | city +-----+------------+------------+---------------------- + 34 | fname2 | lname2 | paris + 50 | fname3 | lname3 | brussels + 90 | fname4 | lname4 | new york + 26 | fname5 | lname5 | los angeles + 74 | fname6 | lname6 | tokyo + 44 | fname7 | lname7 | oslo + 19 | fname8 | lname8 | hong kong +(7 rows) + +-- delete when target table not shown in JoinExpr +DELETE delete_test_tbl2 +FROM delete_test_tbl4 t4 +LEFT JOIN delete_test_tbl3 t3 +ON t3.lname = t4.lname +WHERE t4.city = 'mumbai'; +SELECT * FROM delete_test_tbl2 ORDER BY lname; + age | fname | lname | city +-----+-------+-------+------ +(0 rows) + +-- delete with self join +DELETE delete_test_tbl3 +FROM delete_test_tbl3 t1 +INNER JOIN delete_test_tbl3 t2 +on t1.lname = t2.lname; +SELECT * FROM delete_test_tbl3 ORDER BY lname; + year | lname +------+------- +(0 rows) + +DELETE delete_test_tbl2 +FROM delete_test_tbl2 c +JOIN +(SELECT lname, fname, age from delete_test_tbl2) b +on b.lname = c.lname +JOIN +(SELECT lname, city, age from delete_test_tbl2) a +on a.city = c.city; +SELECT * FROM delete_test_tbl2 ORDER BY lname; + age | fname | lname | city +-----+-------+-------+------ +(0 rows) + +DELETE delete_test_tbl4 +FROM +(SELECT lname, city from delete_test_tbl4) b +JOIN +(SELECT lname from delete_test_tbl4) a +on a.lname = b.lname; +SELECT * FROM delete_test_tbl4 ORDER BY lname; + lname | city +-------+------ +(0 rows) + +DROP TABLE delete_test_tbl; +DROP TABLE delete_test_tbl2; +DROP TABLE delete_test_tbl3; +DROP TABLE delete_test_tbl4; diff --git a/contrib/babelfishpg_tsql/expected/test/babel_function.out b/contrib/babelfishpg_tsql/expected/test/babel_function.out new file mode 100644 index 00000000000..5f41232c14a --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_function.out @@ -0,0 +1,3044 @@ +-- tsql stype create function/procedure is not supported in postgres dialect +CREATE FUNCTION hi_func("@message" varchar(20)) RETURNS VOID AS BEGIN PRINT @message END; +ERROR: syntax error at or near "BEGIN" +LINE 1: ...N hi_func("@message" varchar(20)) RETURNS VOID AS BEGIN PRIN... + ^ +CREATE PROCEDURE hi_proc("@message" varchar(20)) AS BEGIN PRINT @message END; +ERROR: syntax error at or near "BEGIN" +LINE 1: ...EATE PROCEDURE hi_proc("@message" varchar(20)) AS BEGIN PRIN... + ^ +set babelfishpg_tsql.sql_dialect = "tsql"; +-- it's supported in tsql dialect +CREATE FUNCTION hi_func("@message" varchar(20)) RETURNS VOID AS BEGIN PRINT @message END; +CREATE PROCEDURE hi_proc("@message" varchar(20)) AS BEGIN PRINT @message END; +-- PROC is also supported in tsql dialect +create proc proc_1 as print 'Hello World from Babel'; +-- BABEL-219 typmod/length of sys.varchar works correctly in procudure parameter +call hi_proc('Hello World'); +INFO: Hello World +call proc_1(); +INFO: Hello World from Babel +-- clean up +drop function hi_func; +drop procedure hi_proc; +drop proc proc_1; +-- test executing pltsql function in postgres dialect +reset babelfishpg_tsql.sql_dialect; +CREATE OR REPLACE FUNCTION test_func() RETURNS int AS $$ +BEGIN + DECLARE @a int = 1; + RETURN @a +END; +$$ LANGUAGE pltsql; +-- should be able execute a pltsql function in postgres dialect +show babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + postgres +(1 row) + +select test_func(); + test_func +----------- + 1 +(1 row) + +show babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + postgres +(1 row) + +-- test executing pltsql trigger in postgres dialect +CREATE TABLE employees( + id SERIAL PRIMARY KEY, + first_name VARCHAR(40) NOT NULL, + last_name VARCHAR(40) NOT NULL +); +CREATE TABLE employee_audits ( + id SERIAL PRIMARY KEY, + employee_id INT NOT NULL, + last_name VARCHAR(40) NOT NULL +); +CREATE OR REPLACE FUNCTION log_last_name_changes() RETURNS trigger AS $$ +BEGIN + IF NEW.last_name <> OLD.last_name THEN + INSERT INTO employee_audits(employee_id,last_name) + VALUES(OLD.id,OLD.last_name); + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; +CREATE TRIGGER last_name_changes +BEFORE UPDATE +ON employees +FOR EACH ROW +EXECUTE PROCEDURE log_last_name_changes(); +INSERT INTO employees (first_name, last_name) VALUES ('A', 'B'); +INSERT INTO employees (first_name, last_name) VALUES ('C', 'D'); +SELECT * FROM employees; + id | first_name | last_name +----+------------+----------- + 1 | A | B + 2 | C | D +(2 rows) + +show babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + postgres +(1 row) + +UPDATE employees SET last_name = 'E' WHERE ID = 2; +show babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + postgres +(1 row) + +SELECT * FROM employees; + id | first_name | last_name +----+------------+----------- + 1 | A | B + 2 | C | E +(2 rows) + +SELECT * FROM employee_audits; + id | employee_id | last_name +----+-------------+----------- + 1 | 2 | D +(1 row) + +-- test executing a plpgsql function in tsql dialect +CREATE OR REPLACE FUNCTION test_increment(i integer) RETURNS integer AS $$ +BEGIN + RETURN i + "1"; +END; +$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION test_increment1(i integer) RETURNS integer AS $$ +BEGIN + RETURN i + CAST(n'1' AS varchar); +END; +$$ LANGUAGE plpgsql; +-- test that sql_dialect is restored even when the function has error in it +set babelfishpg_tsql.sql_dialect = "tsql"; +show babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + tsql +(1 row) + +select test_increment(1); +ERROR: column "1" does not exist +LINE 1: SELECT i + "1" + ^ +QUERY: SELECT i + "1" +CONTEXT: PL/pgSQL function test_increment(integer) line 3 at RETURN +show babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + tsql +(1 row) + +select test_increment1(1); +ERROR: operator does not exist: integer + character varying +LINE 1: SELECT i + CAST(n'1' AS varchar) + ^ +HINT: No operator matches the given name and argument types. You might need to add explicit type casts. +QUERY: SELECT i + CAST(n'1' AS varchar) +CONTEXT: PL/pgSQL function test_increment1(integer) line 3 at RETURN +show babelfishpg_tsql.sql_dialect; + babelfishpg_tsql.sql_dialect +------------------------------ + tsql +(1 row) + +-- test OBJECT_NAME function +select OBJECT_NAME('sys.columns'::regclass::Oid::int); + object_name +------------- + columns +(1 row) + +select OBJECT_NAME('boolin'::regproc::Oid::int); + object_name +------------- + boolin +(1 row) + +select OBJECT_NAME('int4'::regtype::Oid::int); + object_name +------------- + int4 +(1 row) + +select OBJECT_NAME(1); + object_name +------------- + +(1 row) + +-- test OBJECT_ID function +select (OBJECT_NAME(OBJECT_ID('sys.columns')) = 'columns'); + ?column? +---------- + t +(1 row) + +select (OBJECT_NAME(OBJECT_ID('[columns]')) = 'columns'); + ?column? +---------- + t +(1 row) + +select (OBJECT_NAME(OBJECT_ID('contrib_regression.sys.columns')) = 'columns'); + ?column? +---------- + t +(1 row) + +select (OBJECT_NAME(OBJECT_ID('[sys].[columns]')) = 'columns'); + ?column? +---------- + t +(1 row) + +select (OBJECT_NAME(OBJECT_ID(N'[contrib_regression].sys.[columns]')) = 'columns'); + ?column? +---------- + t +(1 row) + +select (OBJECT_ID('db.sys.[tb].[col]') IS NULL); + ?column? +---------- + t +(1 row) + +select (OBJECT_ID('sys.babelfish_sysmail_mailitems') IS NULL); + ?column? +---------- + t +(1 row) + +select (OBJECT_NAME(OBJECT_ID('sys.columns', 'U')) = 'columns'); + ?column? +---------- + t +(1 row) + +select (OBJECT_NAME(OBJECT_ID('pg_catalog.boolin', 'FN')) = 'boolin'); + ?column? +---------- + t +(1 row) + +select (OBJECT_ID('sysmail_mailitems', 'P') IS NULL); + ?column? +---------- + t +(1 row) + +select (OBJECT_ID('boolin', 'C') IS NULL); + ?column? +---------- + t +(1 row) + +create table #tt(a int); +select (OBJECT_ID('#tt') IS NOT NULL); + ?column? +---------- + t +(1 row) + +select (OBJECT_ID('tempdb..#tt') IS NOT NULL); + ?column? +---------- + t +(1 row) + +select (OBJECT_ID('tempdb..#tt2') IS NULL); + ?column? +---------- + t +(1 row) + +drop table #tt; +-- test SYSDATETIME function +-- Returns of type datetime2 +select pg_typeof(SYSDATETIME()); + pg_typeof +----------- + datetime2 +(1 row) + +-- test GETDATE function +-- Returns of type datetime +select pg_typeof(GETDATE()); + pg_typeof +----------- + datetime +(1 row) + +-- test current_timestamp function +select pg_typeof(current_timestamp); + pg_typeof +----------- + datetime +(1 row) + +-- test calling with parenthesis, should fail +select current_timestamp(); +ERROR: syntax error at or near ")" +LINE 1: select current_timestamp(); + ^ +-- test CONVERT function +-- Conversion between varchar and date/time/datetime +select CONVERT(varchar(30), CAST('2017-08-25' AS date), 102); + babelfish_conv_helper_to_varchar +---------------------------------- + 2017.08.25 +(1 row) + +select CONVERT(varchar(30), CAST('13:01:59' AS time), 8); + babelfish_conv_helper_to_varchar +---------------------------------- + 13:01:59 +(1 row) + +select CONVERT(varchar(30), CAST('13:01:59' AS time), 22); + babelfish_conv_helper_to_varchar +---------------------------------- + 1:01:59 PM +(1 row) + +select CONVERT(varchar(30), CAST('13:01:59' AS time), 22); + babelfish_conv_helper_to_varchar +---------------------------------- + 1:01:59 PM +(1 row) + +select CONVERT(varchar(30), CAST('2017-08-25 13:01:59' AS datetime), 100); + babelfish_conv_helper_to_varchar +---------------------------------- + Aug 25 2017 1:01PM +(1 row) + +select CONVERT(varchar(30), CAST('2017-08-25 13:01:59' AS datetime), 109); + babelfish_conv_helper_to_varchar +---------------------------------- + Aug 25 2017 1:01:59:000PM +(1 row) + +select CONVERT(date, '08/25/2017', 101); + babelfish_conv_helper_to_date +------------------------------- + 08-25-2017 +(1 row) + +select CONVERT(time, '12:01:59', 101); + babelfish_conv_helper_to_time +------------------------------- + 12:01:59 +(1 row) + +select CONVERT(datetime, '2017-08-25 01:01:59PM', 120); + babelfish_conv_helper_to_datetime +----------------------------------- + Fri Aug 25 13:01:59 2017 +(1 row) + +select CONVERT(varchar, CONVERT(datetime2(7), '9999-12-31 23:59:59.9999999')); +WARNING: TIMESTAMP(7) precision reduced to maximum allowed, 6 +WARNING: TIMESTAMP(7) precision reduced to maximum allowed, 6 +LINE 1: select CONVERT(varchar, CONVERT(datetime2(7), '9999-12-31 23... + ^ + babelfish_conv_helper_to_varchar +---------------------------------- + Fri Dec 31 23:59:59.999999 999 +(1 row) + +-- Conversion from float to varchar +select CONVERT(varchar(30), 11234561231231.234::float, 0); + babelfish_conv_helper_to_varchar +---------------------------------- + 1.12346e+13 +(1 row) + +select CONVERT(varchar(30), 11234561231231.234::float, 1); + babelfish_conv_helper_to_varchar +---------------------------------- + 1.1234561e+13 +(1 row) + +select CONVERT(varchar(30), 11234561231231.234::float, 2); + babelfish_conv_helper_to_varchar +---------------------------------- + 1.123456123123123e+13 +(1 row) + +select CONVERT(varchar(30), 11234561231231.234::float, 3); + babelfish_conv_helper_to_varchar +---------------------------------- + 1.1234561231231234e+13 +(1 row) + +-- Conversion from money to varchar +select CONVERT(varchar(10), CAST(4936.56 AS MONEY), 0); + babelfish_conv_helper_to_varchar +---------------------------------- + 4936.56 +(1 row) + +select CONVERT(varchar(10), CAST(4936.56 AS MONEY), 1); + babelfish_conv_helper_to_varchar +---------------------------------- + 4,936.56 +(1 row) + +select CONVERT(varchar(10), CAST(4936.56 AS MONEY), 2); + babelfish_conv_helper_to_varchar +---------------------------------- + 4936.5600 +(1 row) + +select CONVERT(varchar(10), CAST(-4936.56 AS MONEY), 0); + babelfish_conv_helper_to_varchar +---------------------------------- + -4936.56 +(1 row) + +-- Floor conversion to smallint, int, bigint +SELECT CONVERT(int, 99.9); + int4 +------ + 99 +(1 row) + +SELECT CONVERT(smallint, 99.9); + int2 +------ + 99 +(1 row) + +SELECT CONVERT(bigint, 99.9); + int8 +------ + 99 +(1 row) + +SELECT CONVERT(int, -99.9); + int4 +------ + -99 +(1 row) + +SELECT CONVERT(int, '99'); + int4 +------ + 99 +(1 row) + +SELECT CONVERT(int, CAST(99.9 AS double precision)); + int4 +------ + 99 +(1 row) + +SELECT CONVERT(int, CAST(99.9 AS real)); + int4 +------ + 99 +(1 row) + +-- test TRY_CONVERT function +-- Conversion between different types and varchar +select TRY_CONVERT(varchar(30), CAST('2017-08-25' AS date), 102); + babelfish_conv_helper_to_varchar +---------------------------------- + 2017.08.25 +(1 row) + +select TRY_CONVERT(varchar(30), CAST('13:01:59' AS time), 8); + babelfish_conv_helper_to_varchar +---------------------------------- + 13:01:59 +(1 row) + +select TRY_CONVERT(varchar(30), CAST('13:01:59' AS time), 22); + babelfish_conv_helper_to_varchar +---------------------------------- + 1:01:59 PM +(1 row) + +select TRY_CONVERT(varchar(30), CAST('2017-08-25 13:01:59' AS datetime), 109); + babelfish_conv_helper_to_varchar +---------------------------------- + Aug 25 2017 1:01:59:000PM +(1 row) + +select TRY_CONVERT(varchar(30), 11234561231231.234::float, 0); + babelfish_conv_helper_to_varchar +---------------------------------- + 1.12346e+13 +(1 row) + +select TRY_CONVERT(varchar(30), 11234561231231.234::float, 1); + babelfish_conv_helper_to_varchar +---------------------------------- + 1.1234561e+13 +(1 row) + +select TRY_CONVERT(varchar(10), CAST(4936.56 AS MONEY), 0); + babelfish_conv_helper_to_varchar +---------------------------------- + 4936.56 +(1 row) + +-- Wrong conversions that return NULL +select TRY_CONVERT(date, 123); + babelfish_conv_helper_to_date +------------------------------- + +(1 row) + +select TRY_CONVERT(time, 123); + babelfish_conv_helper_to_time +------------------------------- + +(1 row) + +select TRY_CONVERT(datetime, 123); + babelfish_conv_helper_to_datetime +----------------------------------- + +(1 row) + +select TRY_CONVERT(money, 'asdf'); + babelfish_try_cast_to_any +--------------------------- + +(1 row) + +-- test PARSE function +-- Conversion from string to date/time/datetime +select PARSE('2017-08-25' AS date); + babelfish_parse_helper_to_date +-------------------------------- + 08-25-2017 +(1 row) + +select PARSE('2017-08-25' AS date USING 'Cs-CZ'); + babelfish_parse_helper_to_date +-------------------------------- + 08-25-2017 +(1 row) + +select PARSE('08/25/2017' AS date USING 'en-US'); + babelfish_parse_helper_to_date +-------------------------------- + 08-25-2017 +(1 row) + +select PARSE('25/08/2017' AS date USING 'de-DE'); + babelfish_parse_helper_to_date +-------------------------------- + 08-25-2017 +(1 row) + +select PARSE('13:01:59' AS time); + babelfish_parse_helper_to_time +-------------------------------- + 13:01:59 +(1 row) + +select PARSE('13:01:59' AS time USING 'en-US'); + babelfish_parse_helper_to_time +-------------------------------- + 13:01:59 +(1 row) + +select PARSE('13:01:59' AS time USING 'zh-CN'); + babelfish_parse_helper_to_time +-------------------------------- + 13:01:59 +(1 row) + +select PARSE('2017-08-25 13:01:59' AS datetime); + babelfish_parse_helper_to_datetime +------------------------------------ + Fri Aug 25 13:01:59 2017 +(1 row) + +select PARSE('2017-08-25 13:01:59' AS datetime USING 'zh-CN'); + babelfish_parse_helper_to_datetime +------------------------------------ + Fri Aug 25 13:01:59 2017 +(1 row) + +select PARSE('12:01:59' AS time); + babelfish_parse_helper_to_time +-------------------------------- + 12:01:59 +(1 row) + +select PARSE('2017-08-25 01:01:59PM' AS datetime); + babelfish_parse_helper_to_datetime +------------------------------------ + Fri Aug 25 13:01:59 2017 +(1 row) + +-- Test if unnecessary culture arg given +select PARSE('123' AS int USING 'de-DE'); + int4 +------ + 123 +(1 row) + +-- test TRY_PARSE function +-- Expect null return on error +-- Conversion from string to date/time/datetime +select TRY_PARSE('2017-08-25' AS date); + babelfish_parse_helper_to_date +-------------------------------- + 08-25-2017 +(1 row) + +select TRY_PARSE('2017-08-25' AS date USING 'Cs-CZ'); + babelfish_parse_helper_to_date +-------------------------------- + 08-25-2017 +(1 row) + +select TRY_PARSE('789' AS date USING 'en-US'); + babelfish_parse_helper_to_date +-------------------------------- + +(1 row) + +select TRY_PARSE('asdf' AS date USING 'de-DE'); + babelfish_parse_helper_to_date +-------------------------------- + +(1 row) + +select TRY_PARSE('13:01:59' AS time); + babelfish_parse_helper_to_time +-------------------------------- + 13:01:59 +(1 row) + +select TRY_PARSE('asdf' AS time USING 'en-US'); + babelfish_parse_helper_to_time +-------------------------------- + +(1 row) + +select TRY_PARSE('13-12-21' AS time USING 'zh-CN'); + babelfish_parse_helper_to_time +-------------------------------- + 00:00:00 +(1 row) + +select TRY_PARSE('2017-08-25 13:01:59' AS datetime); + babelfish_parse_helper_to_datetime +------------------------------------ + Fri Aug 25 13:01:59 2017 +(1 row) + +select TRY_PARSE('20asdf17' AS datetime USING 'de-DE'); + babelfish_parse_helper_to_datetime +------------------------------------ + +(1 row) + +-- Wrong conversions that return NULL +select TRY_PARSE('asdf' AS numeric(3,2)); + babelfish_try_cast_to_any +--------------------------- + +(1 row) + +select TRY_PARSE('123' AS datetime2); + babelfish_try_cast_to_any +--------------------------- + +(1 row) + +select TRY_PARSE('asdf' AS MONEY); + babelfish_try_cast_to_any +--------------------------- + +(1 row) + +select TRY_PARSE('asdf' AS int USING 'de-DE'); + babelfish_try_cast_floor_int +------------------------------ + +(1 row) + +-- test serverproperty() function +-- invalid property name, should reutnr NULL +select serverproperty(n'invalid property'); + serverproperty +---------------- + +(1 row) + +-- valid supported properties +select serverproperty(n'collation'); + serverproperty +------------------------------ + sql_latin1_general_cp1_ci_as +(1 row) + +select serverproperty(n'collationId'); + serverproperty +---------------- + 0 +(1 row) + +select serverproperty(n'IsSingleUser'); + serverproperty +---------------- + 0 +(1 row) + +select serverproperty(n'ServerName'); + serverproperty +---------------- + BABELFISH +(1 row) + +-- test has_dbaccess() function +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); + set_config +------------ + postgres +(1 row) + +create role test_role; +set role 'test_role'; +show role; + role +----------- + test_role +(1 row) + +set babelfishpg_tsql.sql_dialect = 'tsql'; +-- test access to current database, should return 1 +select has_dbaccess(CAST(current_database() as text)); + has_dbaccess +-------------- + +(1 row) + +-- test access to an invalid database, should return NULL +select has_dbaccess(n'invalid database'); + has_dbaccess +-------------- + +(1 row) + +reset role; +drop role test_role; +-- test ISDATE function +-- test valid argument +SELECT ISDATE('12/26/2016'); + isdate +-------- + 1 +(1 row) + +SELECT ISDATE('12-26-2016'); + isdate +-------- + 1 +(1 row) + +SELECT ISDATE('12.26.2016'); + isdate +-------- + 1 +(1 row) + +SELECT ISDATE('2016-12-26 23:30:05.523456'); + isdate +-------- + 1 +(1 row) + +-- test invalid argument +SELECT ISDATE('02/30/2016'); + isdate +-------- + 0 +(1 row) + +SELECT ISDATE('12/32/2016'); + isdate +-------- + 0 +(1 row) + +SELECT ISDATE('1995-10-1a'); + isdate +-------- + 0 +(1 row) + +SELECT ISDATE(NULL); + isdate +-------- + 0 +(1 row) + +-- test DATEFROMPARTS function +-- test valid arguments +select datefromparts(2020,12,31); + datefromparts +--------------- + 12-31-2020 +(1 row) + +-- test invalid arguments, should fail +select datefromparts(2020, 2, 30); +ERROR: date field value out of range: 2020-02-30 +CONTEXT: SQL function "datefromparts" statement 1 +select datefromparts(2020, 13, 1); +ERROR: date field value out of range: 2020-13-01 +CONTEXT: SQL function "datefromparts" statement 1 +select datefromparts(-4, 3, 150); +ERROR: date field value out of range: -3-03-150 +CONTEXT: SQL function "datefromparts" statement 1 +select datefromparts(10, 55, 10.1); +ERROR: date field value out of range: 10-55-10 +select datefromparts('2020', 55, 100.1); +ERROR: date field value out of range: 2020-55-100 +-- test DATETIMEFROMPARTS function +-- test valid arguments +select datetimefromparts(2016, 12, 26, 23, 30, 5, 32); + datetimefromparts +------------------------------ + Mon Dec 26 23:30:05.033 2016 +(1 row) + +select datetimefromparts(2016.0, 12, 26, 23, 30, 5, 32); + datetimefromparts +------------------------------ + Mon Dec 26 23:30:05.033 2016 +(1 row) + +select datetimefromparts(2016.1, 12, 26, 23, 30, 5, 32); + datetimefromparts +------------------------------ + Mon Dec 26 23:30:05.033 2016 +(1 row) + +select datetimefromparts(2016, 12, 26.99, 23, 30, 5, 32); + datetimefromparts +------------------------------ + Mon Dec 26 23:30:05.033 2016 +(1 row) + +select datetimefromparts(2016, 12.90, 26, 23, 30, 5, 32); + datetimefromparts +------------------------------ + Mon Dec 26 23:30:05.033 2016 +(1 row) + +-- test invalid arguments +select datetimefromparts(2016, 2, 30, 23, 30, 5, 32); +ERROR: date field value out of range: 2016-02-30 +CONTEXT: PL/pgSQL function sys.datetimefromparts(numeric,numeric,numeric,numeric,numeric,numeric,numeric) line 29 at assignment +select datetimefromparts(2016, 12, 26, 23, 30, 5); +ERROR: The datetimefromparts function requires 7 arguments +LINE 1: select datetimefromparts(2016, 12, 26, 23, 30, 5); + ^ +select datetimefromparts(2016, 12, 26, 23, 30, 5, NULL); + datetimefromparts +------------------- + +(1 row) + +-- test DATEPART function +-- test all valid datepart arguments +select datepart(year, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 2016 +(1 row) + +select datepart(yyyy, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 2016 +(1 row) + +select datepart(yy, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 2016 +(1 row) + +select datepart(quarter, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 4 +(1 row) + +select datepart(qq, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 4 +(1 row) + +select datepart(q, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 4 +(1 row) + +select datepart(month, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 12 +(1 row) + +select datepart(mm, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 12 +(1 row) + +select datepart(m, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 12 +(1 row) + +select datepart(dayofyear, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 361 +(1 row) + +select datepart(dy, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 361 +(1 row) + +select datepart(day, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 26 +(1 row) + +select datepart(dd, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 26 +(1 row) + +select datepart(d, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 26 +(1 row) + +select datepart(week, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 53 +(1 row) + +select datepart(wk, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 53 +(1 row) + +select datepart(ww, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 53 +(1 row) + +select datepart(weekday, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 2 +(1 row) + +select datepart(dw, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 2 +(1 row) + +select datepart(hour, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 15 +(1 row) + +select datepart(hh, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 15 +(1 row) + +select datepart(minute, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 30 +(1 row) + +select datepart(n, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 30 +(1 row) + +select datepart(second, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 5 +(1 row) + +select datepart(ss, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 5 +(1 row) + +select datepart(s, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 5 +(1 row) + +select datepart(millisecond, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 456 +(1 row) + +select datepart(ms, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 456 +(1 row) + +select datepart(microsecond, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 523456 +(1 row) + +select datepart(mcs, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 523456 +(1 row) + +select datepart(nanosecond, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +----------- + 523456000 +(1 row) + +select datepart(ns, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +----------- + 523456000 +(1 row) + +select datepart(tzoffset, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 480 +(1 row) + +select datepart(tz, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 480 +(1 row) + +select datepart(iso_week, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 52 +(1 row) + +select datepart(isowk, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 52 +(1 row) + +select datepart(isoww, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datepart +---------- + 52 +(1 row) + +-- test different types of date/time arguments +select datepart(month, '2016-12-26 23:30:05.523'::sys.datetime); + datepart +---------- + 12 +(1 row) + +select datepart(quarter, '2016-12-26 23:30:05.523456'::datetime2); + datepart +---------- + 4 +(1 row) + +select datepart(hour, '2016-12-26 23:30:05'::smalldatetime); + datepart +---------- + 23 +(1 row) + +select datepart(dayofyear, '2016-12-26'::date); + datepart +---------- + 361 +(1 row) + +select datepart(second, '04:12:34.876543'::time); + datepart +---------- + 34 +(1 row) + +-- test edge cases: try to get datepart that does not exist in the argument +select datepart(year, cast('12:10:30.123' as time)); + datepart +---------- + 1900 +(1 row) + +select datepart(yyyy, cast('12:10:30.123' as time)); + datepart +---------- + 1900 +(1 row) + +select datepart(yy, cast('12:10:30.123' as time)); + datepart +---------- + 1900 +(1 row) + +select datepart(quarter, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(qq, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(q, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(month, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(mm, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(m, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(dayofyear, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(dy, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(y, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(day, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(dd, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(d, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(week, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(wk, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(ww, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(weekday, cast('12:10:30.123' as time)); + datepart +---------- + 2 +(1 row) + +select datepart(dw, cast('12:10:30.123' as time)); + datepart +---------- + 2 +(1 row) + +select datepart(tzoffset, cast('12:10:30.123' as time)); + datepart +---------- + 0 +(1 row) + +select datepart(tz, cast('12:10:30.123' as time)); + datepart +---------- + 0 +(1 row) + +select datepart(iso_week, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(isowk, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(isoww, cast('12:10:30.123' as time)); + datepart +---------- + 1 +(1 row) + +select datepart(hour, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(hh, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(minute, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(n, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(second, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(ss, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(s, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(millisecond, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(ms, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(microsecond, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(mcs, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(nanosecond, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +select datepart(ns, cast('2016-12-26' as date)); + datepart +---------- + 0 +(1 row) + +-- test invalid interval, expect error +select datepart(invalid_interval, cast('2016-12-26 23:30:05.523456' as date)); +ERROR: 'invalid_interval' is not a recognized datepart option +CONTEXT: PL/pgSQL function sys.datepart_internal(text,anyelement,integer) line 66 at RAISE +PL/pgSQL function sys.datepart(text,anyelement) line 7 at RETURN +select datepart(invalidinterval, cast('12:10:30.123' as time)); +ERROR: 'invalidinterval' is not a recognized datepart option +CONTEXT: PL/pgSQL function sys.datepart_internal(text,anyelement,integer) line 66 at RAISE +PL/pgSQL function sys.datepart(text,anyelement) line 7 at RETURN +-- test DATENAME function +select datename(year, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + 2016 +(1 row) + +select datename(dd, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + 26 +(1 row) + +select datename(weekday, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + Monday +(1 row) + +select datename(dw, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + Monday +(1 row) + +select datename(month, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + December +(1 row) + +select datename(mm, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + December +(1 row) + +select datename(m, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + December +(1 row) + +select datename(isowk, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + datename +---------- + 52 +(1 row) + +-- test invalid argument, expect error +select datename(invalid_interval, cast('2016-12-26 23:30:05.523456' as date)); +ERROR: 'invalid_interval' is not a recognized datepart option +CONTEXT: PL/pgSQL function sys.datepart_internal(text,anyelement,integer) line 66 at RAISE +PL/pgSQL function sys.datepart(text,anyelement) line 7 at RETURN +SQL function "datename" statement 1 +-- test DATEFIRST option, together DATEPART function +-- This shows the return value for the week and weekday datepart for '2007-04-21' for each SET DATEFIRST argument. +-- January 1, 2007 falls on a Monday. April 21, 2007 falls on a Saturday. +-- DATEFIRST week weekday +-- 1 16 6 +-- 2 17 5 +-- 3 17 4 +-- 4 17 3 +-- 5 17 2 +-- 6 17 1 +-- 7 16 7 +select @@datefirst; + datefirst +----------- + 7 +(1 row) + +set datefirst 1; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); + datepart | datepart +----------+---------- + 16 | 6 +(1 row) + +set datefirst 2; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); + datepart | datepart +----------+---------- + 17 | 5 +(1 row) + +set datefirst 3; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); + datepart | datepart +----------+---------- + 17 | 4 +(1 row) + +set datefirst 4; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); + datepart | datepart +----------+---------- + 17 | 3 +(1 row) + +set datefirst 5; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); + datepart | datepart +----------+---------- + 17 | 2 +(1 row) + +set datefirst 6; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); + datepart | datepart +----------+---------- + 17 | 1 +(1 row) + +set datefirst 7; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); + datepart | datepart +----------+---------- + 16 | 7 +(1 row) + +-- test edge case: date within the week of Jan. 1st +select datepart(week, '2007-01-01'::date), datepart(weekday, '2007-01-01'::date); + datepart | datepart +----------+---------- + 1 | 2 +(1 row) + +select datepart(week, '2007-01-02'::date), datepart(weekday, '2007-01-02'::date); + datepart | datepart +----------+---------- + 1 | 3 +(1 row) + +select datepart(week, '2007-01-03'::date), datepart(weekday, '2007-01-03'::date); + datepart | datepart +----------+---------- + 1 | 4 +(1 row) + +select datepart(week, '2007-01-04'::date), datepart(weekday, '2007-01-04'::date); + datepart | datepart +----------+---------- + 1 | 5 +(1 row) + +select datepart(week, '2007-01-05'::date), datepart(weekday, '2007-01-05'::date); + datepart | datepart +----------+---------- + 1 | 6 +(1 row) + +select datepart(week, '2007-01-06'::date), datepart(weekday, '2007-01-06'::date); + datepart | datepart +----------+---------- + 1 | 7 +(1 row) + +-- test edge case: date just outside the week of Jan. 1st +select datepart(week, '2007-01-07'::date), datepart(weekday, '2007-01-07'::date); + datepart | datepart +----------+---------- + 2 | 1 +(1 row) + +-- test DATEDIFF function +select datediff(year, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -1 +(1 row) + +select datediff(quarter, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -4 +(1 row) + +select datediff(month, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -13 +(1 row) + +select datediff(dayofyear, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -367 +(1 row) + +select datediff(day, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -367 +(1 row) + +select datediff(week, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -52 +(1 row) + +select datediff(hour, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -8808 +(1 row) + +select datediff(minute, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +---------- + -528480 +(1 row) + +select datediff(second, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); + datediff +----------- + -31708800 +(1 row) + +select datediff(millisecond, '2036-02-28 01:23:45.234'::sys.datetime, '2036-02-28 01:23:45.123'::sys.datetime); + datediff +---------- + -111 +(1 row) + +select datediff(microsecond, '2036-02-28 01:23:45.234'::sys.datetime, '2036-02-28 01:23:45.123'::sys.datetime); + datediff +---------- + -111000 +(1 row) + +select datediff(nanosecond, '2036-02-28 01:23:45.234'::sys.datetime, '2036-02-28 01:23:45.123'::sys.datetime); + datediff +------------ + -111000000 +(1 row) + +-- test different types of date/time arguments +select datediff(minute, '2016-12-26 23:30:05.523456+8'::datetimeoffset, '2016-12-31 23:30:05.523456+8'::datetimeoffset); + datediff +---------- + 7200 +(1 row) + +select datediff(quarter, '2016-12-26 23:30:05.523456'::datetime2, '2018-08-31 23:30:05.523456'::datetime2); + datediff +---------- + 6 +(1 row) + +select datediff(hour, '2016-12-26 23:30:05'::smalldatetime, '2016-12-28 21:29:05'::smalldatetime); + datediff +---------- + 45 +(1 row) + +select datediff(year, '2037-03-01'::date, '2036-02-28'::date); + datediff +---------- + -1 +(1 row) + +-- test DATEADD function +select dateadd(year, 2, '20060830'::datetime); + dateadd +-------------------------- + Sat Aug 30 00:00:00 2008 +(1 row) + +select dateadd(quarter, 2, '20060830'::datetime); + dateadd +-------------------------- + Wed Feb 28 00:00:00 2007 +(1 row) + +select dateadd(month, 1, '20060831'::datetime); + dateadd +-------------------------- + Sat Sep 30 00:00:00 2006 +(1 row) + +select dateadd(dayofyear, 2, '20060830'::datetime); + dateadd +-------------------------- + Fri Sep 01 00:00:00 2006 +(1 row) + +select dateadd(day, 2, '20060830'::datetime); + dateadd +-------------------------- + Fri Sep 01 00:00:00 2006 +(1 row) + +select dateadd(week, 2, '20060830'::datetime); + dateadd +-------------------------- + Wed Sep 13 00:00:00 2006 +(1 row) + +select dateadd(weekday, 2, '20060830'::datetime); + dateadd +-------------------------- + Fri Sep 01 00:00:00 2006 +(1 row) + +select dateadd(hour, 2, '20060830'::datetime); + dateadd +-------------------------- + Wed Aug 30 02:00:00 2006 +(1 row) + +select dateadd(minute, 2, '20060830'::datetime); + dateadd +-------------------------- + Wed Aug 30 00:02:00 2006 +(1 row) + +select dateadd(second, 2, '20060830'::datetime); + dateadd +-------------------------- + Wed Aug 30 00:00:02 2006 +(1 row) + +select dateadd(millisecond, 123, '20060830'::datetime); + dateadd +------------------------------ + Wed Aug 30 00:00:00.123 2006 +(1 row) + +select dateadd(microsecond, 123456, '20060830'::datetime); + dateadd +------------------------------ + Wed Aug 30 00:00:00.123 2006 +(1 row) + +select dateadd(nanosecond, 123456, '20060830'::datetime); + dateadd +-------------------------- + Wed Aug 30 00:00:00 2006 +(1 row) + +-- test different types of date/time arguments +select dateadd(hour, 2, '23:12:34.876543'::time); + dateadd +----------------- + 01:12:34.876543 +(1 row) + +select dateadd(quarter, 3, '2037-03-01'::date); + dateadd +------------ + 12-01-2037 +(1 row) + +select dateadd(minute, 70, '2016-12-26 23:30:05.523456+8'::datetimeoffset); + dateadd +---------------------------------------- + Mon Dec 26 16:40:05.523456 2016 +08:00 +(1 row) + +select dateadd(month, 2, '2016-12-26 23:30:05.523456'::datetime2); + dateadd +--------------------------------- + Sun Feb 26 23:30:05.523456 2017 +(1 row) + +select dateadd(second, 56, '2016-12-26 23:30:05'::smalldatetime); + dateadd +-------------------------- + Mon Dec 26 23:31:00 2016 +(1 row) + +-- test negative argument +select dateadd(year, -2, '20060830'::datetime); + dateadd +-------------------------- + Mon Aug 30 00:00:00 2004 +(1 row) + +select dateadd(month, -20, '2016-12-26 23:30:05.523456'::datetime2); + dateadd +--------------------------------- + Sun Apr 26 23:30:05.523456 2015 +(1 row) + +select dateadd(hour, -2, '01:12:34.876543'::time); + dateadd +----------------- + 23:12:34.876543 +(1 row) + +select dateadd(minute, -70, '2016-12-26 00:30:05.523456+8'::datetimeoffset); + dateadd +---------------------------------------- + Sun Dec 25 15:20:05.523456 2016 +08:00 +(1 row) + +select dateadd(second, -56, '2016-12-26 00:00:55'::smalldatetime); + dateadd +-------------------------- + Mon Dec 26 00:00:00 2016 +(1 row) + +-- test return type +select pg_typeof(dateadd(hour, -2, '01:12:34.876543'::time)); + pg_typeof +------------------------ + time without time zone +(1 row) + +select pg_typeof(dateadd(second, -56, '2016-12-26 00:00:55'::smalldatetime)); + pg_typeof +--------------- + smalldatetime +(1 row) + +select pg_typeof(dateadd(year, -2, '20060830'::datetime)); + pg_typeof +----------- + datetime +(1 row) + +select pg_typeof(dateadd(month, -20, '2016-12-26 23:30:05.523456'::datetime2)); + pg_typeof +----------- + datetime2 +(1 row) + +select pg_typeof(dateadd(minute, -70, '2016-12-26 00:30:05.523456+8'::datetimeoffset)); + pg_typeof +---------------- + datetimeoffset +(1 row) + +-- test illegal usage +select dateadd(minute, 2, '2037-03-01'::date); +ERROR: The datepart minute is not supported by date function dateadd for data type date. +CONTEXT: PL/pgSQL function sys.dateadd_internal(text,integer,anyelement) line 5 at RAISE +PL/pgSQL function sys.dateadd(text,integer,anyelement) line 7 at RETURN +select dateadd(day, 4, '04:12:34.876543'::time); +ERROR: The datepart day is not supported by date function dateadd for data type time. +CONTEXT: PL/pgSQL function sys.dateadd_internal(text,integer,anyelement) line 9 at RAISE +PL/pgSQL function sys.dateadd(text,integer,anyelement) line 7 at RETURN +-- test using variables, instead of constants, for the second parameter +create table dateadd_table(a int, b datetime); +insert into dateadd_table values(1, '2020-10-29'::datetime); +select * from dateadd_table; + a | b +---+-------------------------- + 1 | Thu Oct 29 00:00:00 2020 +(1 row) + +update dateadd_table set b = dateadd(dd, a, '2020-10-30'::datetime); +select * from dateadd_table; + a | b +---+-------------------------- + 1 | Sat Oct 31 00:00:00 2020 +(1 row) + +create procedure dateadd_procedure as +begin + declare @d int = 1 + update dateadd_table set b = dateadd(dd, @d, CAST('2020-10-31' AS datetime)) +end; +call dateadd_procedure(); +select * from dateadd_table; + a | b +---+-------------------------- + 1 | Sun Nov 01 00:00:00 2020 +(1 row) + +-- test CHARINDEX function +select CHARINDEX('hello', 'hello world'); + charindex +----------- + 1 +(1 row) + +select CHARINDEX('hello ', 'hello world'); + charindex +----------- + 0 +(1 row) + +select CHARINDEX('hello world', 'hello'); + charindex +----------- + 0 +(1 row) + +-- test NULL input +select CHARINDEX(NULL, NULL); + charindex +----------- + +(1 row) + +select CHARINDEX(NULL, 'string'); + charindex +----------- + +(1 row) + +select CHARINDEX('pattern', NULL); + charindex +----------- + +(1 row) + +select CHARINDEX('pattern', 'string', NULL); + charindex +----------- + +(1 row) + +-- test start_location parameter +select CHARINDEX('hello', 'hello world', -1); + charindex +----------- + 1 +(1 row) + +select CHARINDEX('hello', 'hello world', 0); + charindex +----------- + 1 +(1 row) + +select CHARINDEX('hello', 'hello world', 1); + charindex +----------- + 1 +(1 row) + +select CHARINDEX('hello', 'hello world', 2); + charindex +----------- + 0 +(1 row) + +select CHARINDEX('world', 'hello world', 6); + charindex +----------- + 7 +(1 row) + +select CHARINDEX('world', 'hello world', 7); + charindex +----------- + 7 +(1 row) + +select CHARINDEX('world', 'hello world', 8); + charindex +----------- + 0 +(1 row) + +select CHARINDEX('is', 'This is a string'); + charindex +----------- + 3 +(1 row) + +select CHARINDEX('is', 'This is a string', 4); + charindex +----------- + 6 +(1 row) + +-- test STUFF function +select STUFF(n'abcdef', 2, 3, n'ijklmn'); + stuff +----------- + aijklmnef +(1 row) + +select STUFF(N' abcdef', 2, 3, N'ijklmn '); + stuff +------------- + ijklmn def +(1 row) + +select STUFF(N'abcdef', 2, 3, N' ijklmn '); + stuff +------------- + a ijklmn ef +(1 row) + +select STUFF(N'abcdef', 2, 3, N'ijklmn '); + stuff +------------- + aijklmn ef +(1 row) + +-- test corner cases +-- when start is negative or zero or longer than expr, return NULL +select STUFF(n'abcdef', -1, 3, n'ijklmn'); + stuff +------- + +(1 row) + +select STUFF(n'abcdef', 0, 3, n'ijklmn'); + stuff +------- + +(1 row) + +select STUFF(n'abcdef', 7, 3, n'ijklmn'); + stuff +------- + +(1 row) + +-- when length is negative, return NULL +select STUFF(n'abcdef', 2, -3, n'ijklmn'); + stuff +------- + +(1 row) + +-- when length is zero, just insert without deleting +select STUFF(n'abcdef', 2, 0, n'ijklmn'); + stuff +-------------- + aijklmnbcdef +(1 row) + +-- when length is longer than expr, delete up to the last character in expr +select STUFF(n'abcdef', 2, 7, n'ijklmn'); + stuff +--------- + aijklmn +(1 row) + +-- when replace_expr is NULL, just delete without inserting +select STUFF(n'abcdef', 2, 3, NULL); + stuff +------- + aef +(1 row) + +-- when argument are type unknown +select STUFF('abcdef', 2, 3, 'ijklmn'); + stuff +----------- + aijklmnef +(1 row) + +select STUFF('abcdef', 2, 3, n'ijklmn'); + stuff +----------- + aijklmnef +(1 row) + +select STUFF(n'abcdef', 2, 3, 'ijklmn'); + stuff +----------- + aijklmnef +(1 row) + +-- when argument are type text +SELECT STUFF(CAST('abcdef' as text), 2, 3, CAST('ijklmn' as text)); + stuff +----------- + aijklmnef +(1 row) + +SELECT STUFF(CAST('abcdef' as text), 2, 3, 'ijklmn'); + stuff +----------- + aijklmnef +(1 row) + +SELECT STUFF('abcdef', 2, 3, CAST('ijklmn' as text)); + stuff +----------- + aijklmnef +(1 row) + +-- when argument are type sys.varchar +SELECT STUFF(CAST('abcdef' as sys.varchar), 2, 3, CAST('ijklmn' as sys.varchar)); + stuff +----------- + aijklmnef +(1 row) + +SELECT STUFF('abcdef', 2, 3, CAST('ijklmn' as sys.varchar)); + stuff +----------- + aijklmnef +(1 row) + +SELECT STUFF(CAST('abcdef' as sys.varchar), 2, 3, 'ijklmn'); + stuff +----------- + aijklmnef +(1 row) + +-- test ROUND function +-- test rounding to the left of decimal point +select ROUND(748.58, -1); + round +------- + 750 +(1 row) + +select ROUND(748.58, -2); + round +------- + 700 +(1 row) + +select ROUND(748.58, -3); +ERROR: value overflows for numeric format +select ROUND(748.58, -4); + round +------- + 0 +(1 row) + +select ROUND(-648.1234, -2); + round +------- + -600 +(1 row) + +select ROUND(-648.1234, -3); +ERROR: value overflows for numeric format +select ROUND(-1548.1234, -3); + round +------- + -2000 +(1 row) + +select ROUND(-1548.1234, -4); +ERROR: value overflows for numeric format +-- test NULL input +select ROUND(NULL, -3); + round +------- + +(1 row) + +select ROUND(748.58, NULL); + round +------- + +(1 row) + +-- test rounding +SELECT ROUND(123.9994, 3); + round +--------- + 123.999 +(1 row) + +SELECT ROUND(123.9995, 3); + round +--------- + 124.000 +(1 row) + +SELECT ROUND(123.4545, 2); + round +-------- + 123.45 +(1 row) + +SELECT ROUND(123.45, -2); + round +------- + 100 +(1 row) + +-- test function parameter, i.e. truncation when not NULL or 0 +SELECT ROUND(150.75, 0); + round +------- + 151 +(1 row) + +SELECT ROUND(150.75, 0, 0); + round +------- + 151 +(1 row) + +SELECT ROUND(150.75, 0, NULL); + round +------- + 151 +(1 row) + +SELECT ROUND(150.75, 0, 1); + round +------- + 150 +(1 row) + +-- test negative numbers +SELECT ROUND(-150.49, 0); + round +------- + -150 +(1 row) + +SELECT ROUND(-150.75, 0); + round +------- + -151 +(1 row) + +SELECT ROUND(-150.49, 0, 1); + round +------- + -150 +(1 row) + +SELECT ROUND(-150.75, 0, 1); + round +------- + -150 +(1 row) + +-- test SELECT ROUND(col, ) +create table t1 (col numeric(4,2)); +insert into t1 values (64.24); +insert into t1 values (79.65); +insert into t1 values (NULL); +select ROUND(col, 3) from t1; + round +-------- + 64.240 + 79.650 + +(3 rows) + +select ROUND(col, 2) from t1; + round +------- + 64.24 + 79.65 + +(3 rows) + +select ROUND(col, 1) from t1; + round +------- + 64.2 + 79.7 + +(3 rows) + +select ROUND(col, 0) from t1; + round +------- + 64 + 80 + +(3 rows) + +select ROUND(col, -1) from t1; + round +------- + 60 + 80 + +(3 rows) + +select ROUND(col, -2) from t1; +ERROR: value overflows for numeric format +select ROUND(col, -3) from t1; + round +------- + 0 + 0 + +(3 rows) + +select ROUND(col, 1, 1) from t1; + round +------- + 64.2 + 79.6 + +(3 rows) + +drop table t1; +-- test DAY function +select DAY(CAST('2016-12-26 23:30:05.523456+8' AS datetimeoffset)); + day +----- + 26 +(1 row) + +select DAY(CAST('2016-12-26 23:30:05.523456' AS datetime2)); + day +----- + 26 +(1 row) + +select DAY(CAST('2016-12-26 23:30:05' AS smalldatetime)); + day +----- + 26 +(1 row) + +select DAY(CAST('04:12:34.876543' AS time)); + day +----- + 1 +(1 row) + +select DAY(CAST('2037-03-01' AS date)); + day +----- + 1 +(1 row) + +select DAY(CAST('2037-03-01 23:30:05.523' AS sys.datetime)); + day +----- + 1 +(1 row) + +-- test MONTH function +select MONTH('2016-12-26 23:30:05.523456+8'::datetimeoffset); + month +------- + 12 +(1 row) + +select MONTH('2016-12-26 23:30:05.523456'::datetime2); + month +------- + 12 +(1 row) + +select MONTH('2016-12-26 23:30:05'::smalldatetime); + month +------- + 12 +(1 row) + +select MONTH('04:12:34.876543'::time); + month +------- + 1 +(1 row) + +select MONTH('2037-03-01'::date); + month +------- + 3 +(1 row) + +select MONTH('2037-03-01 23:30:05.523'::sys.datetime); + month +------- + 3 +(1 row) + +-- test YEAR function +select YEAR('2016-12-26 23:30:05.523456+8'::datetimeoffset); + year +------ + 2016 +(1 row) + +select YEAR('2016-12-26 23:30:05.523456'::datetime2); + year +------ + 2016 +(1 row) + +select YEAR('2016-12-26 23:30:05'::smalldatetime); + year +------ + 2016 +(1 row) + +select YEAR('04:12:34.876543'::time); + year +------ + 1900 +(1 row) + +select YEAR('2037-03-01'::date); + year +------ + 2037 +(1 row) + +select YEAR('2037-03-01 23:30:05.523'::sys.datetime); + year +------ + 2037 +(1 row) + +-- test SPACE function +select SPACE(NULL); + space +------- + +(1 row) + +select SPACE(2); + space +------- + +(1 row) + +select LEN(SPACE(5)); + len +----- + 0 +(1 row) + +select DATALENGTH(SPACE(5)); + datalength +------------ + 5 +(1 row) + +-- test COUNT and COUNT_BIG aggregate function +CREATE TABLE t2(a int, b int); +INSERT INTO t2 VALUES(1, 100); +INSERT INTO t2 VALUES(2, 200); +INSERT INTO t2 VALUES(NULL, 300); +INSERT INTO t2 VALUES(2, 400); +CREATE TABLE t3(a varchar(255), b varchar(255),c int); +INSERT INTO t3 VALUES('xyz', 'a',1); +INSERT INTO t3 VALUES('xyz', 'b',1); +INSERT INTO t3 VALUES('abc', 'a',2); +INSERT INTO t3 VALUES('abc', 'b',2); +INSERT INTO t3 VALUES('efg', 'a',3); +INSERT INTO t3 VALUES('efg', 'b',3); +INSERT INTO t3 VALUES(NULL, NULL, 1); +-- Aggregation Function Syntax +-- COUNT[_BIG] ( { [ [ ALL | DISTINCT ] expression ] | * } ) +-- should return all rows - 4 +SELECT COUNT(*) from t2; + count +------- + 4 +(1 row) + +SELECT pg_typeof(COUNT(*)) from t2; + pg_typeof +----------- + integer +(1 row) + +SELECT COUNT_BIG(*) from t2; + count_big +----------- + 4 +(1 row) + +SELECT pg_typeof(COUNT_BIG(*)) from t2; + pg_typeof +----------- + bigint +(1 row) + +-- should return all rows where a is not NULL - 3 +SELECT COUNT(a) from t2; + count +------- + 3 +(1 row) + +SELECT pg_typeof(COUNT(a)) from t2; + pg_typeof +----------- + integer +(1 row) + +SELECT COUNT_BIG(a) from t2; + count_big +----------- + 3 +(1 row) + +SELECT pg_typeof(COUNT_BIG(a)) from t2; + pg_typeof +----------- + bigint +(1 row) + +-- should return all rows where a is not NULL - 3 +SELECT COUNT(ALL a) from t2; + count +------- + 3 +(1 row) + +SELECT pg_typeof(COUNT(ALL a)) from t2; + pg_typeof +----------- + integer +(1 row) + +SELECT COUNT_BIG(ALL a) from t2; + count_big +----------- + 3 +(1 row) + +SELECT pg_typeof(COUNT_BIG(ALL a)) from t2; + pg_typeof +----------- + bigint +(1 row) + +-- should return all rows where a is distinct - 2 +SELECT COUNT(DISTINCT a) from t2; + count +------- + 2 +(1 row) + +SELECT pg_typeof(COUNT(DISTINCT a)) from t2; + pg_typeof +----------- + integer +(1 row) + +SELECT COUNT_BIG(DISTINCT a) from t2; + count_big +----------- + 2 +(1 row) + +SELECT pg_typeof(COUNT_BIG(DISTINCT a)) from t2; + pg_typeof +----------- + bigint +(1 row) + +-- Analytic Function Syntax +-- COUNT[_BIG] ( [ ALL ] { expression | * } ) OVER ( [ ] ) +SELECT pg_typeof(COUNT(*) OVER (PARTITION BY a)) from t2; + pg_typeof +----------- + integer + integer + integer + integer +(4 rows) + +SELECT pg_typeof(COUNT_BIG(*) OVER (PARTITION BY a)) from t2; + pg_typeof +----------- + bigint + bigint + bigint + bigint +(4 rows) + +SELECT pg_typeof(COUNT(a) OVER (PARTITION BY a)) from t2; + pg_typeof +----------- + integer + integer + integer + integer +(4 rows) + +SELECT pg_typeof(COUNT_BIG(a) OVER (PARTITION BY a)) from t2; + pg_typeof +----------- + bigint + bigint + bigint + bigint +(4 rows) + +SELECT pg_typeof(COUNT(ALL a) OVER (PARTITION BY a)) from t2; + pg_typeof +----------- + integer + integer + integer + integer +(4 rows) + +SELECT pg_typeof(COUNT_BIG(ALL a) OVER (PARTITION BY a)) from t2; + pg_typeof +----------- + bigint + bigint + bigint + bigint +(4 rows) + +SELECT COUNT(*) from t3; + count +------- + 7 +(1 row) + +SELECT a, b, COUNT(*) OVER () from t3; + a | b | count +-----+---+------- + xyz | a | 7 + xyz | b | 7 + abc | a | 7 + abc | b | 7 + efg | a | 7 + efg | b | 7 + | | 7 +(7 rows) + +-- The result for order by is different in sql server because we have +-- an ordering issue for null type (JIRA: BABEL-788) +SELECT a, b, COUNT(*) OVER (ORDER BY a) from t3; + a | b | count +-----+---+------- + abc | b | 2 + abc | a | 2 + efg | a | 4 + efg | b | 4 + xyz | a | 6 + xyz | b | 6 + | | 7 +(7 rows) + +SELECT a, b, COUNT(*) OVER (ORDER BY a DESC) from t3; + a | b | count +-----+---+------- + | | 1 + xyz | b | 3 + xyz | a | 3 + efg | b | 5 + efg | a | 5 + abc | b | 7 + abc | a | 7 +(7 rows) + +SELECT a, b, COUNT(*) OVER(PARTITION BY a) from t3; + a | b | count +-----+---+------- + abc | b | 2 + abc | a | 2 + efg | a | 2 + efg | b | 2 + xyz | a | 2 + xyz | b | 2 + | | 1 +(7 rows) + +SELECT a, b, COUNT(*) OVER(PARTITION BY a ORDER BY b) from t3; + a | b | count +-----+---+------- + abc | a | 1 + abc | b | 2 + efg | a | 1 + efg | b | 2 + xyz | a | 1 + xyz | b | 2 + | | 1 +(7 rows) + +SELECT a, b, COUNT(*) OVER(PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) from t3; + a | b | count +-----+---+------- + abc | a | 2 + abc | b | 1 + efg | a | 2 + efg | b | 1 + xyz | a | 2 + xyz | b | 1 + | | 1 +(7 rows) + +SELECT COUNT_BIG(*) from t3; + count_big +----------- + 7 +(1 row) + +SELECT a, b, COUNT_BIG(*) OVER () from t3; + a | b | count_big +-----+---+----------- + xyz | a | 7 + xyz | b | 7 + abc | a | 7 + abc | b | 7 + efg | a | 7 + efg | b | 7 + | | 7 +(7 rows) + +SELECT a, b, COUNT_BIG(*) OVER (ORDER BY a) from t3; + a | b | count_big +-----+---+----------- + abc | b | 2 + abc | a | 2 + efg | a | 4 + efg | b | 4 + xyz | a | 6 + xyz | b | 6 + | | 7 +(7 rows) + +SELECT a, b, COUNT_BIG(*) OVER (ORDER BY a DESC) from t3; + a | b | count_big +-----+---+----------- + | | 1 + xyz | b | 3 + xyz | a | 3 + efg | b | 5 + efg | a | 5 + abc | b | 7 + abc | a | 7 +(7 rows) + +SELECT a, b, COUNT_BIG(*) OVER(PARTITION BY a) from t3; + a | b | count_big +-----+---+----------- + abc | b | 2 + abc | a | 2 + efg | a | 2 + efg | b | 2 + xyz | a | 2 + xyz | b | 2 + | | 1 +(7 rows) + +SELECT a, b, COUNT_BIG(*) OVER(PARTITION BY a ORDER BY b) from t3; + a | b | count_big +-----+---+----------- + abc | a | 1 + abc | b | 2 + efg | a | 1 + efg | b | 2 + xyz | a | 1 + xyz | b | 2 + | | 1 +(7 rows) + +SELECT a, b, COUNT_BIG(*) OVER(PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) from t3; + a | b | count_big +-----+---+----------- + abc | a | 2 + abc | b | 1 + efg | a | 2 + efg | b | 1 + xyz | a | 2 + xyz | b | 1 + | | 1 +(7 rows) + +-- COUNT(*) takes no parameters and does not support the use of DISTINC, expect error +SELECT COUNT(DISTINCT *) from t3; +ERROR: syntax error at or near "*" +LINE 1: SELECT COUNT(DISTINCT *) from t3; + ^ +SELECT COUNT(ALL *) from t3; +ERROR: syntax error at or near "*" +LINE 1: SELECT COUNT(ALL *) from t3; + ^ +DROP TABLE t2; +DROP TABLE t3; +-- clean up +drop function test_func; +drop table employees; +drop table employee_audits; +drop function log_last_name_changes; +drop function test_increment; +drop function test_increment1; +drop table dateadd_table; +drop procedure dateadd_procedure; +-- test inline table-valued functions +-- simple case +create function itvf1 (@number int) returns table as return (select 1 as a, 2 as b); +select * from itvf1(5); + a | b +---+--- + 1 | 2 +(1 row) + +-- should fail because column names are not specified +create function itvf2 (@number int) returns table as return (select 1, 2); +ERROR: CREATE FUNCTION failed because a column name is not specified for column 1 +-- select from a table +create table example_table(name text, age int); +insert into example_table values('hello', 3); +-- should have 'a' and 'b' as result column names +create function itvf3 (@number int) returns table as return (select name as a, age as b from example_table); +select * from itvf3(5); + a | b +-------+--- + hello | 3 +(1 row) + +-- test returning multiple rows +insert into example_table values('hello1', 4); +insert into example_table values('hello2', 5); +insert into example_table values('hello3', 6); +select * from itvf3(5); + a | b +--------+--- + hello | 3 + hello1 | 4 + hello2 | 5 + hello3 | 6 +(4 rows) + +-- invoke a function +create function itvf4 (@number int) returns table as +return (select sys.serverproperty(N'collation') as property1, sys.serverproperty(N'IsSingleUser') as property2); +select * from itvf4(5); + property1 | property2 +------------------------------+----------- + sql_latin1_general_cp1_ci_as | 0 +(1 row) + +-- case where the return table has only one column - Postgres considers these as +-- scalar functions +create or replace function itvf5 (@number int) returns table as return (select 1 as a); +select * from itvf5(5); + a +--- + 1 +(1 row) + +create or replace function itvf6 (@number int) returns table as +return (select sys.serverproperty(N'collation') as property); +select * from itvf6(5); + property +------------------------------ + sql_latin1_general_cp1_ci_as +(1 row) + +-- complex queries with use of function parameter +create table id_name(id int, name text); +insert into id_name values(1001, 'adam'); +insert into id_name values(1002, 'bob'); +insert into id_name values(1003, 'chaz'); +insert into id_name values(1004, 'dave'); +insert into id_name values(1005, 'ed'); +create table id_score(id int, score int); +insert into id_score values(1001, 90); +insert into id_score values(1001, 70); +insert into id_score values(1002, 90); +insert into id_score values(1002, 80); +insert into id_score values(1003, 80); +insert into id_score values(1003, 70); +insert into id_score values(1004, 80); +insert into id_score values(1004, 60); +insert into id_score values(1005, 80); +insert into id_score values(1005, 100); +create function itvf7 (@number int) returns table as return ( +select n.id, n.name as first_name, sum(s.score) as total_score +from id_name as n +join id_score as s +on n.id = s.id +where s.id <= @number +group by n.id, n.name +order by n.id +); +select * from itvf7(1004); + id | first_name | total_score +------+------------+------------- + 1001 | adam | 160 + 1002 | bob | 170 + 1003 | chaz | 150 + 1004 | dave | 140 +(4 rows) + +-- test inline table-valued function with table-valued parameter +create type tableType as table( + a text not null, + b int primary key, + c int); +create function itvf8 (@number int, @tableVar tableType READONLY) returns table as return ( +select n.id, n.name as first_name, sum(s.score) as total_score +from id_name as n +join id_score as s +on n.id = s.id +where s.id <= @number and s.id in (select c from @tableVar) +group by n.id, n.name +order by n.id +); +create procedure itvf8_proc as +begin + declare @tableVariable tableType + insert into @tableVariable values('hello1', 1, 1001) + insert into @tableVariable values('hello2', 2, 1002) + select * from itvf8(1004, @tableVariable) +end; +call itvf8_proc(); + id | first_name | total_score +------+------------+------------- + 1001 | adam | 160 + 1002 | bob | 170 +(2 rows) + +-- test using parameter in projection list +create function itvf9(@number int) returns table as return ( +select @number as a from id_name +); +select * from itvf9(1); + a +--- + 1 + 1 + 1 + 1 + 1 +(5 rows) + +-- test invalid ITVFs +-- function does not have RETURN QUERY +create function itvf10(@number int) returns table as BEGIN select * from id_name END; +ERROR: syntax error near 'BEGIN' at line 1 and character position 0 +CONTEXT: compilation of PL/tsql function "itvf10" near line 1 +-- function has more than one RETURN QUERY +create function itvf11(@number int) returns table as +BEGIN + return select * from id_name + return select id from id_name +END; +ERROR: syntax error near 'BEGIN' at line 1 and character position 0 +CONTEXT: compilation of PL/tsql function "itvf11" near line 1 +-- test creating ITVF in a transaction and rollback - should still work as +-- normal despite the function validator's modification of the pg_proc entry +begin transaction; +create function itvf12(@number int) returns table as return ( +select @number as a from id_name +); +rollback; +select * from itvf12(1); +ERROR: function itvf12(integer) does not exist +LINE 1: select * from itvf12(1); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- "AS" keyword is optional in TSQL function +\tsql on +create function babel651_f() returns int +begin + return 1 +end +go +create table babel651_t(a int); +go +create function babel651_itvf() returns table + return (select * from babel651_t) +go +create function babel651_mstvf(@i int) returns @tableVar table +( + a text not null +) +begin + insert into @tableVar values('hello1'); +end; +go +select babel651_f(); +go + babel651_f +------------ + 1 +(1 row) + +select * from babel651_itvf(); +go + a +--- +(0 rows) + +select * from babel651_mstvf(1); +go + a +-------- + hello1 +(1 row) + +\tsql off +-- clean up +drop function itvf1; +drop table example_table; +drop function itvf3; +drop function itvf4; +drop function itvf5; +drop function itvf6; +drop table id_name; +drop table id_score; +drop function itvf7; +drop procedure itvf8_proc; +drop function itvf8; +drop type tableType; +drop function itvf9; +drop table babel651_t; +drop function babel651_f; +drop function babel651_itvf; +drop function babel651_mstvf; +-- test RETURN not followed by a semicolon +\tsql on +create function test_return1(@stringToSplit VARCHAR(MAX)) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + RETURN +END +GO +select * from test_return1('test'); +GO + name +------ +(0 rows) + +drop function test_return1; +GO +create function test_return2(@stringToSplit VARCHAR(MAX)) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + RETURN; +END +GO +select * from test_return2('test'); +GO + name +------ +(0 rows) + +drop function test_return2; +GO +create function test_return3(@a int) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + IF @a = 1 + RETURN + SELECT @a = 2 + INSERT into @returnList values('abc') + RETURN +END +GO +select * from test_return3(1); +GO + name +------ +(0 rows) + +select * from test_return3(2); +GO + name +------ + abc +(1 row) + +drop function test_return3; +GO +create function test_return4(@a int) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + IF @a = 1 + RETURN + ELSE + SELECT @a = 2 + INSERT into @returnList values('abc') + RETURN +END +GO +select * from test_return4(1); +GO + name +------ +(0 rows) + +select * from test_return4(2); +GO + name +------ + abc +(1 row) + +drop function test_return4; +GO +\tsql off diff --git a/contrib/babelfishpg_tsql/expected/test/babel_init.out b/contrib/babelfishpg_tsql/expected/test/babel_init.out new file mode 100644 index 00000000000..33fc6df4d9b --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_init.out @@ -0,0 +1,14 @@ +SET client_min_messages TO WARNING; +CREATE EXTENSION "babelfishpg_tsql" cascade; +RESET client_min_messages; +SET babelfishpg_tsql.sql_dialect = 'tsql'; +reset babelfishpg_tsql.sql_dialect; +--BABEL-978: make sure error stack is not exceeded in/after extension creation. +CREATE PROCEDURE myproc() LANGUAGE plpgsql AS $$ +BEGIN +CREATE invalid_syntax; +END; +$$; +ERROR: syntax error at or near "invalid_syntax" +LINE 3: CREATE invalid_syntax; + ^ diff --git a/contrib/babelfishpg_tsql/expected/test/babel_like.out b/contrib/babelfishpg_tsql/expected/test/babel_like.out new file mode 100644 index 00000000000..310a0d6da16 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_like.out @@ -0,0 +1,98 @@ +set babelfishpg_tsql.sql_dialect = 'tsql'; +select relname from pg_class where relname like '['; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like ']'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like '[]'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like NULL; + relname +--------- +(0 rows) + +select relname from pg_class where relname like ''; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg[1:9]class'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg\[1:9\]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg\[1:9 ]class'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg [1:9\]class'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg*[1:9*]class' escape '*'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg [1:9*]class' escape '*'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg*[1:9 ]class' escape '*'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +set babelfishpg_tsql.sql_dialect = 'postgres'; +select relname from pg_class where relname like '['; + relname +--------- +(0 rows) + +select relname from pg_class where relname like ']'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like '[]'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like NULL; + relname +--------- +(0 rows) + +select relname from pg_class where relname like ''; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg[1:9]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg\[1:9\]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg\[1:9 ]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg [1:9\]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg*[1:9*]class' escape '*'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg [1:9*]class' escape '*'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg*[1:9 ]class' escape '*'; + relname +--------- +(0 rows) + diff --git a/contrib/babelfishpg_tsql/expected/test/babel_procedures.out b/contrib/babelfishpg_tsql/expected/test/babel_procedures.out new file mode 100644 index 00000000000..2fcc5bb11b0 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_procedures.out @@ -0,0 +1,79 @@ +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql"; +NOTICE: extension "babelfishpg_tsql" already exists, skipping +-- test sp_unprepare +create table t1 ( a int ); +insert into t1 values (1); +insert into t1 values (2); +prepare s_100 (int) as select * from t1 where a = $1; +execute s_100(1); + a +--- + 1 +(1 row) + +set babelfishpg_tsql.sql_dialect = "tsql"; +call sp_unprepare(null); +ERROR: expect handle as integer +call sp_unprepare(10); +call sp_unprepare(100); +execute s_100(1); +ERROR: procedure s_100(integer) does not exist +LINE 1: execute s_100(1); + ^ +HINT: No procedure matches the given name and argument types. You might need to add explicit type casts. +\tsql on +-- Test 'with' in procedure language that shouldn't be recognized as CTE +create or replace procedure prc1 as begin +select count(*) from t1 with (tablockx) +end +go +call prc1(); +go + count +------- + 2 +(1 row) + +create or replace procedure prc2 as begin +insert into t1 select * from t1 with (tablockx); +end +go +call prc2(); +go +-- Test C-style comment with a following '@' +create procedure prc3 +/* comment */ +@p int +as +select 123; +go +-- BABEL-831 +CREATE PROCEDURE babel_831_proc +AS +BEGIN + CREATE TABLE #t(id INT) + DECLARE Babel831Cursor CURSOR FOR SELECT id FROM #t + OPEN Babel831Cursor + CLOSE Babel831Cursor +END +GO +exec babel_831_proc +GO +--BABEL-1099 +CREATE TABLE babel_1099_t1 (a int); +CREATE TABLE babel_1099_t2 (a int); +CREATE PROCEDURE babel_1099_proc AS + TRUNCATE TABLE babel_1099_t1 + TRUNCATE TABLE babel_1099_t2 +GO +EXEC babel_1099_proc +GO +\tsql off +drop table t1; +drop table babel_1099_t1; +drop table babel_1099_t2; +drop procedure prc1; +drop procedure prc2; +drop procedure prc3; +drop procedure babel_831_proc; +drop procedure babel_1099_proc; diff --git a/contrib/babelfishpg_tsql/expected/test/babel_set_command.out b/contrib/babelfishpg_tsql/expected/test/babel_set_command.out new file mode 100644 index 00000000000..813df197459 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_set_command.out @@ -0,0 +1,91 @@ +-- Simple SET +SET lc_messages ='fr_FR.utf8'; +show lc_messages; + lc_messages +------------- + fr_FR.utf8 +(1 row) + +reset lc_messages; +-- Inside transaction with commit +BEGIN; + SET lc_messages = 'fr_FR.utf8'; +COMMIT; +show lc_messages; + lc_messages +------------- + fr_FR.utf8 +(1 row) + +reset lc_messages; +-- Inside transaction with rollback +BEGIN; + SET lc_messages = 'fr_FR.utf8'; +ROLLBACK; +show lc_messages; + lc_messages +------------- + C +(1 row) + +reset lc_messages; +-- Inside transaction with rollback to savepoint +BEGIN; + SET lc_messages = 'en_GB.utf8'; + SAVEPOINT SP1; + SET lc_messages = 'fr_FR.utf8'; + show lc_messages; + lc_messages +------------- + fr_FR.utf8 +(1 row) + + ROLLBACK TO SAVEPOINT SP1; + show lc_messages; + lc_messages +------------- + en_GB.utf8 +(1 row) + +ROLLBACK; +show lc_messages; + lc_messages +------------- + C +(1 row) + +reset lc_messages; +-- Inside procedure +CREATE PROCEDURE lc_proc() +AS $$ +begin + SET lc_messages ='fr_FR.utf8'; + commit; +end; +$$ LANGUAGE plpgsql; +CALL lc_proc(); +show lc_messages; + lc_messages +------------- + fr_FR.utf8 +(1 row) + +drop procedure lc_proc(); +reset lc_messages; +CREATE PROCEDURE lc_proc() +AS $$ +begin + SET lc_messages ='fr_FR.utf8'; + rollback; +end; +$$ LANGUAGE plpgsql; +CALL lc_proc(); +show lc_messages; + lc_messages +------------- + C +(1 row) + +-- Cleanup +drop procedure lc_proc(); +reset lc_messages; diff --git a/contrib/babelfishpg_tsql/expected/test/babel_table_type.out b/contrib/babelfishpg_tsql/expected/test/babel_table_type.out new file mode 100644 index 00000000000..42b6913c448 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_table_type.out @@ -0,0 +1,729 @@ +CREATE USER my_test_user; +GRANT CREATE ON DATABASE contrib_regression TO my_test_user; +SET SESSION AUTHORIZATION my_test_user; +-- table type is only supported in tsql dialect +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int); +ERROR: syntax error at or near "table" +LINE 1: CREATE TYPE tableType AS table( + ^ +set babelfishpg_tsql.sql_dialect = 'tsql'; +\tsql ON +-- table type supports all CREATE TABLE element lists +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int); +GO +-- a table with the same name is created +select * from tableType; +GO + a | b | c +---+---+--- +(0 rows) + +-- create type with same name, should fail +CREATE TYPE tableType as (d int, e int); +GO +ERROR: type "tabletype" already exists +-- create table with same name, should succeed +-- TODO: BABEL-689: Postgres doesn't support this yet, because CREATE TABLE will automatically +-- create a composite type as well, which will cause name collision +CREATE TABLE tableType(d int, e int); +GO +ERROR: relation "tabletype" already exists +-- dropping the table should fail, as it depends on the table type +DROP TABLE tableType; +GO +ERROR: cannot drop table tabletype because type tabletype requires it +HINT: You can drop type tabletype instead. +-- dropping the table type should drop the table as well +DROP TYPE tableType; +GO +SELECT * FROM tableType; +GO +ERROR: relation "tabletype" does not exist +LINE 1: SELECT * FROM tableType; + ^ +-- creating index (other than primary and unique keys) during table type creation is not +-- yet supported +-- TODO: BABEL-688: fully support TSQL CREATE TABLE syntax +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int, + d int index idx1 nonclustered, + index idx2(c), + index idx3(e), + e varchar); +GO +ERROR: syntax error at or near "index" +LINE 5: d int index idx1 nonclustered, + ^ +-- test dotted prefixes of the table type name +-- allowed to have one dotted prefix +CREATE TYPE public.tableType AS table(a int, b int); +GO +DROP TYPE public.tableType; +GO +-- not allowed to have more than one dotted prefix +CREATE TYPE postgres.public.tableType AS table(a int, b int); +GO +ERROR: The type name 'postgres.public.tabletype' contains more than the maximum number of prefixes. The maximum is 1. +LINE 1: CREATE TYPE postgres.public.tableType AS table(a int, b int)... + ^ +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int); +GO +-- test declaring variables with table type +create procedure table_var_procedure as +begin + declare @a int; + declare @b tableType; + insert into @b values('hello', 4, 100); + select count(*) from @b; +end; +GO +CALL table_var_procedure(); +GO + count +------- + 1 +(1 row) + +DROP PROCEDURE table_var_procedure; +GO +-- test declaring table variable without table type, and doing DMLs +create procedure table_var_procedure as +begin + declare @tableVar table (a int, b int); + insert into @tableVar values(1, 100); + insert into @tableVar values(2, 200); + update @tableVar set b = 1000 where a = 1; + delete from @tableVar where a = 2; + select * from @tableVar; +end; +GO +CALL table_var_procedure(); +GO + a | b +---+------ + 1 | 1000 +(1 row) + +DROP PROCEDURE table_var_procedure; +GO +-- test declaring table variable with whitespace before column definition +create procedure table_var_procedure as +begin + declare @tableVar1 table + (a int, b int); + insert into @tableVar1 values(1, 100); + declare @tableVar2 table (c int, d varchar); + insert into @tableVar2 values(1, 'a'); + select * from @tableVar1 t1 join @tableVar2 t2 on t1.a = t2.c; +end; +GO +CALL table_var_procedure(); +GO + a | b | c | d +---+-----+---+--- + 1 | 100 | 1 | a +(1 row) + +DROP PROCEDURE table_var_procedure; +GO +-- test MERGE on table variables +-- TODO: BABEL-877 Support MERGE +/* +create procedure merge_proc as +begin + declare @tv1 table(a int); + insert into @tv1 values (200); + + declare @tv2 table(b int); + insert into @tv2 values (100); + insert into @tv2 values (200); + + merge into @tv1 using @tv2 on a=b + when not matched then insert (a) values(b) + when matched then update set a = a + b; + + select * from @tv1; +end; +GO +CALL merge_proc(); +GO +-- result should have two rows, 400 and 100. +DROP PROCEDURE merge_proc; +GO +*/ +-- test declaring a variable whose name is already used - should throw error +create procedure dup_var_name_procedure as +begin + declare @a int; + declare @a tableType; +end; +GO +ERROR: duplicate declaration +CONTEXT: compilation of PL/tsql function "dup_var_name_procedure" near line 0 +-- test declaring a variable whose name is already used as table name - should work +create table @test_table (d int); +GO +create procedure dup_var_name_procedure as +begin + declare @test_table tableType; + insert into @test_table values('hello1', 1, 100); + select * from @test_table; +end; +GO +call dup_var_name_procedure(); +GO + a | b | c +--------+---+----- + hello1 | 1 | 100 +(1 row) + +drop procedure dup_var_name_procedure; +GO +drop table @test_table; +GO +-- test assigning to table variables, should not be allowed +create table test_table(a int, b int); +GO +insert into test_table values(1, 10); +GO +create procedure assign_proc as +begin + declare @tableVar table (a int, b int); + set @tableVar = test_table; +end; +GO +ERROR: unrecognized dtype: 3 +LINE 3: set @tableVar = test_table; + ^ +QUERY: begin + declare @tableVar table (a int, b int); + set @tableVar = test_table; +end; + +-- test selecting into table variables, should not be allowed +create procedure select_into_proc as +begin + declare @tableVar table (a int, b int); + select * into @tableVar from test_table; +end; +GO +ERROR: syntax error near '@tableVar' at line 3 and character position 15 +LINE 3: select * into @tableVar from test_table; + ^ +QUERY: begin + declare @tableVar table (a int, b int); + select * into @tableVar from test_table; +end; + +-- test truncating table variables, should not be allowed +create procedure truncate_proc as +begin + declare @tableVar table (a int, b int); + insert into @tableVar values(1, 2); + truncate table @tableVar; + select * from @tableVar; +end; +GO +ERROR: syntax error near '@tableVar' at line 4 and character position 16 +LINE 4: truncate table @tableVar; + ^ +QUERY: begin + declare @tableVar table (a int, b int); + insert into @tableVar values(1, 2); + truncate table @tableVar; + select * from @tableVar; +end; + +-- test JOIN on table variables, on both sides +create procedure join_proc1 as +begin + declare @tableVar table (a int, b int, c int); + insert into @tableVar values(1, 2, 3); + select * from test_table t inner join @tableVar tv on t.a = tv.a; +end; +GO +CALL join_proc1(); +GO + a | b | a | b | c +---+----+---+---+--- + 1 | 10 | 1 | 2 | 3 +(1 row) + +DROP PROCEDURE join_proc1; +create procedure join_proc2 as +begin + declare @tableVar table (a int, b int, c int); + insert into @tableVar values(1, 2, 3); + select * from @tableVar tv inner join test_table t on tv.a = t.a; +end; +GO +CALL join_proc2(); +GO + a | b | c | a | b +---+---+---+---+---- + 1 | 2 | 3 | 1 | 10 +(1 row) + +DROP PROCEDURE join_proc2; +GO +-- test altering table variables, should not be allowed +create procedure alter_proc as +begin + declare @tableVar table (a int); + alter table @tableVar add b int; + select * from @tableVar; +end; +GO +ERROR: syntax error near '@tableVar' at line 3 and character position 13 +LINE 3: alter table @tableVar add b int; + ^ +QUERY: begin + declare @tableVar table (a int); + alter table @tableVar add b int; + select * from @tableVar; +end; + +-- test using the same variables as source and target +create procedure source_target_proc as +begin + declare @tv table (a int); + insert into @tv values (1); + insert into @tv select a+1 from @tv; + insert into @tv select a+2 from @tv; + insert into @tv select a+4 from @tv; + select * from @tv; +end; +GO +CALL source_target_proc(); +GO + a +--- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 +(8 rows) + +DROP PROCEDURE source_target_proc; +GO +-- test multiple '@' characters in table variable name +-- TODO: BABEL-476 Support variable name with multiple '@' characters +/* +create procedure nameing_proc as +begin + declare @@@tv@1@@@ as table(a int); + insert @@@tv@1@@@ values(1); + select * from @@@tv@1@@@; +end; +GO +CALL naming_proc(); +GO +DROP PROCEDURE naming_proc; +GO +*/ +-- test nested functions using table variables with the same name, each should +-- have its own variable +create function inner_func() returns int as +begin + declare @result int; + declare @tableVar table (a int); + insert into @tableVar values(1); + select @result = count(*) from @tableVar; -- should be 1 + return @result; +end; +GO +create function outer_func() returns int as +begin + declare @result int; + declare @tableVar table(b int); + select @result = count(*) from @tableVar; -- should be 0 + select @result = @result + inner_func(); -- should be 0 + 1 = 1 + -- the temp table in inner_func() should have been dropped by now, so the + -- next call to inner_func() should still return 1 + select @result = @result + inner_func(); -- should be 1 + 1 = 2 + return @result; +end; +GO +select outer_func(); +GO + outer_func +------------ + 2 +(1 row) + +DROP FUNCTION outer_func; +GO +-- test calling a function with table variables in a loop, each should have its +-- own variable +create procedure loop_func_proc as +begin + declare @result int; + declare @counter int; + select @result = 0; + set @counter = 1; + while (@counter < 6) + begin + select @result = @result + inner_func(); -- each call to inner_func should return 1 + set @counter = @counter + 1; + end + select @result; +end; +GO +call loop_func_proc(); +GO + @result +--------- + 5 +(1 row) + +DROP PROCEDURE loop_func_proc; +GO +DROP FUNCTION inner_func; +GO +-- test declaring the same variable in a loop - should not have any error, and +-- should all refer to the same underlying table +create procedure loop_proc as +begin + declare @result int; + declare @curr int; + declare @counter int; + select @result = 0; + set @counter = 1; + while (@counter < 6) + begin + declare @a tableType; + insert into @a values('hello', @counter, 100); + select @curr = count(*) from @a; -- @curr in each loop should be 1,2,3,4,5 + select @result = @result + @curr; + set @counter = @counter + 1; + end + select @result; +end; +GO +call loop_proc() +GO + @result +--------- + 15 +(1 row) + +DROP PROCEDURE loop_proc; +GO +-- test using table variables in CTE, both in with clause and in main query +create procedure cte_proc as +begin + declare @tablevar1 tableType; + insert into @tablevar1 values('hello1', 1, 100); + declare @tablevar2 tableType; + insert into @tablevar2 values('hello1', 1, 100); + insert into @tablevar2 values('hello2', 2, 200); + WITH t1 (a) AS (SELECT a FROM @tablevar1) SELECT * FROM @tablevar2 t2 JOIN t1 ON t2.a = t1.a; +end; +GO +call cte_proc() +GO + a | b | c | a +--------+---+-----+-------- + hello1 | 1 | 100 | hello1 +(1 row) + +DROP PROCEDURE cte_proc; +GO +-- BABEL-894: test PLtsql_expr->tsql_tablevars is initialized to NIL so that it +-- won't cause seg faults when looked up during execution. One place missed +-- earlier is when parsing the SET command. +create procedure pl_set_proc as +begin + set datefirst 7; +end; +GO +call pl_set_proc() +GO +DROP PROCEDURE pl_set_proc; +GO +-- test select from multiple table variables +create procedure select_multi_tablevars as +begin + declare @tablevar1 tableType; + insert into @tablevar1 values('hello1', 1, 100); + declare @tablevar2 tableType; + insert into @tablevar2 values('hello1', 1, 100); + insert into @tablevar2 values('hello2', 2, 200); + select * from @tablevar1, @tablevar2; +end; +GO +call select_multi_tablevars() +GO + a | b | c | a | b | c +--------+---+-----+--------+---+----- + hello1 | 1 | 100 | hello1 | 1 | 100 + hello1 | 1 | 100 | hello2 | 2 | 200 +(2 rows) + +DROP PROCEDURE select_multi_tablevars; +GO +-- test select from table and table variable +create procedure select_table_tablevar as +begin + declare @tablevar tableType; + insert into @tablevar values('hello1', 1, 100); + select * from test_table, @tablevar; +end; +GO +call select_table_tablevar() +GO + a | b | a | b | c +---+----+--------+---+----- + 1 | 10 | hello1 | 1 | 100 +(1 row) + +DROP PROCEDURE select_table_tablevar; +GO +-- test table-valued parameters +-- if no READONLY behind table-valued param: report error +create function error_func(@tableVar tableType) returns int as +begin + return 1; +end +GO +ERROR: The table-valued parameter "@tablevar" must be declared with the READONLY option. +-- if READONLY on other param type: report error +create function error_func(@a int, @b int READONLY) returns int as +begin + return 1; +end +GO +ERROR: The parameter "@b" can not be declared READONLY since it is not a table-valued parameter. +-- correct syntax +create function tvp_func(@tableVar tableType READONLY) returns int as +begin + declare @result int; + select @result = count(*) from @tableVar; + return @result; +end +GO +-- test passing in a table variable whose type is different from what the function wants +-- TODO: BABEL-899: error message should be "Operand type clash: table is incompatible with tableType" +create procedure error_proc as +begin + declare @tableVar as table (a text, b int, c int); + insert into @tableVar values('hello1', 1, 100); + select tvp_func(@tableVar); +end; +GO +call error_proc() +GO +ERROR: The function tvp_func is found but cannot be used. Possibly due to datatype mismatch and implicit casting is not allowed. +LINE 1: select tvp_func("@tableVar") + ^ +QUERY: select tvp_func("@tableVar") +CONTEXT: PL/tsql function error_proc() line 4 at SQL statement +DROP PROCEDURE error_proc; +GO +create procedure tvp_proc as +begin + declare @tableVar tableType; + insert into @tablevar values('hello1', 1, 100); + select tvp_func(@tableVar); +end; +GO +call tvp_proc() +GO + tvp_func +---------- + 1 +(1 row) + +DROP PROCEDURE tvp_proc; +GO +DROP FUNCTION tvp_func; +GO +-- test multiple table-valued parameters +CREATE TYPE tableType1 AS table(d int, e int); +GO +create function multi_tvp_func(@tableVar tableType READONLY, + @tableVar1 tableType1 READONLY) returns int as +begin + declare @result int; + select @result = count(*) from @tableVar tv inner join @tableVar1 tv1 on tv.b = tv1.d; + return @result; +end +GO +create procedure multi_tvp_proc as +begin + declare @v1 tableType; + declare @v2 tableType1; + insert into @v1 values('hello1', 1, 100); + insert into @v2 values(1, 100); + insert into @v2 values(2, 200); + select multi_tvp_func(@v1, @v2); +end; +GO +call multi_tvp_proc() +GO + multi_tvp_func +---------------- + 1 +(1 row) + +DROP PROCEDURE multi_tvp_proc; +GO +DROP FUNCTION multi_tvp_func; +GO +DROP TYPE tableType1; +GO +-- test multi-statement table-valued functions +create function mstvf(@i int) returns @tableVar table +( + a text not null, + b int primary key, + c int +) +as +begin + insert into @tableVar values('hello1', 1, 100); + insert into @tableVar values('hello2', 2, 200); +end; +GO +select * from mstvf(1); +GO + a | b | c +--------+---+----- + hello1 | 1 | 100 + hello2 | 2 | 200 +(2 rows) + +DROP FUNCTION mstvf; +GO +-- test mstvf whose return table has only one column +create function mstvf_one_col(@i int) returns @tableVar table +( + a text not null +) +as +begin + insert into @tableVar values('hello1'); +end; +GO +select * from mstvf_one_col(1); +GO + a +-------- + hello1 +(1 row) + +DROP FUNCTION mstvf_one_col; +GO +-- test mstvf whose return table has only one column +create function mstvf_return(@i int) returns @tableVar table +( + a text not null +) +as +begin + insert into @tableVar values('hello2'); + return; +end; +GO +select * from mstvf_return(1); +GO + a +-------- + hello2 +(1 row) + +DROP FUNCTION mstvf_return; +GO +-- test mstvf's with same names in different schemas +create function mstvf_schema(@i int) returns @resultTable table +( + name varchar(128) not null +) +as +begin + insert into @resultTable (name) select 'test_name'; + RETURN; +end; +GO +create schema test_schema; +GO +create function test_schema.mstvf_schema(@i int) returns @resultTable table +( + name1 varchar(128) not null +) +as +begin + insert into @resultTable (name1) select 'test_name1'; + RETURN; +end; +GO +select * from mstvf_schema(1); +GO + name +----------- + test_name +(1 row) + +select * from test_schema.mstvf_schema(1); +GO + name1 +------------ + test_name1 +(1 row) + +drop function mstvf_schema; +GO +drop function test_schema.mstvf_schema; +GO +drop schema test_schema; +GO +-- test mstvf with constraints in result table +create function mstvf_constraints(@i int) returns @resultTable table +( + name varchar(128) not null, + unique (name), + id int, + primary key clustered (id) +) +as +begin + insert into @resultTable (name, id) select 'test_name', @i; + RETURN; +end; +GO +select * from mstvf_constraints(1); +GO + name | id +-----------+---- + test_name | 1 +(1 row) + +drop function mstvf_constraints; +GO +-- cleanup +DROP TYPE tableType; +GO +DROP TABLE test_table; +GO +\tsql OFF +reset babelfishpg_tsql.sql_dialect; +RESET SESSION AUTHORIZATION; +-- if we are able to drop the user, then it means that all the underlying tables +-- of table variables have been dropped because they depend on the user. +REVOKE CREATE ON DATABASE contrib_regression FROM my_test_user; +DROP USER my_test_user; diff --git a/contrib/babelfishpg_tsql/expected/test/babel_transaction.out b/contrib/babelfishpg_tsql/expected/test/babel_transaction.out new file mode 100644 index 00000000000..2c3031b83d4 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_transaction.out @@ -0,0 +1,509 @@ +SET babelfishpg_tsql.sql_dialect = 'tsql'; +-- setup +drop table TxnTable; +ERROR: table "txntable" does not exist +create table TxnTable(c1 int); +-- Begin transaction -> commit transaction +begin transaction; +select @@trancount; + trancount +----------- + 1 +(1 row) + +begin transaction; +select @@trancount; + trancount +----------- + 2 +(1 row) + +set transaction isolation level read committed; +show transaction_isolation; + transaction_isolation +----------------------- + read committed +(1 row) + +show default_transaction_isolation; + default_transaction_isolation +------------------------------- + read committed +(1 row) + +insert into TxnTable values(1); +commit transaction; +select @@trancount; + trancount +----------- + 1 +(1 row) + +commit transaction; +select @@trancount; + trancount +----------- + 0 +(1 row) + +select c1 from TxnTable; + c1 +---- + 1 +(1 row) + +-- Begin transaction -> rollback transaction +begin transaction; +insert into TxnTable values(2); +rollback transaction; +select c1 from TxnTable; + c1 +---- + 1 +(1 row) + +-- Begin tran -> commit tran +begin tran; +insert into TxnTable values(2); +commit tran; +select c1 from TxnTable; + c1 +---- + 1 + 2 +(2 rows) + +-- Begin tran -> rollback tran +begin tran; +select @@trancount; + trancount +----------- + 1 +(1 row) + +begin tran; +set transaction isolation level read uncommitted; +show transaction_isolation; + transaction_isolation +----------------------- + read committed +(1 row) + +show default_transaction_isolation; + default_transaction_isolation +------------------------------- + read uncommitted +(1 row) + +insert into TxnTable values(3); +select @@trancount; + trancount +----------- + 2 +(1 row) + +rollback tran; +select @@trancount; + trancount +----------- + 0 +(1 row) + +select c1 from TxnTable; + c1 +---- + 1 + 2 +(2 rows) + +set transaction isolation level repeatable read; +ERROR: REPEATABLE READ isolation level is not supported +LINE 1: set transaction isolation level repeatable read; + ^ +show transaction_isolation; + transaction_isolation +----------------------- + read committed +(1 row) + +show default_transaction_isolation; + default_transaction_isolation +------------------------------- + read committed +(1 row) + +-- Begin transaction -> commit +begin transaction; +insert into TxnTable values(4); +commit; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 +(3 rows) + +-- Begin transaction -> commit work +begin transaction; +insert into TxnTable values(5); +commit work; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 + 5 +(4 rows) + +-- Begin transaction -> rollback +begin transaction; +insert into TxnTable values(6); +rollback; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 + 5 +(4 rows) + +-- Begin transaction -> rollback work +begin transaction; +insert into TxnTable values(7); +rollback work; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 + 5 +(4 rows) + +-- Begin transaction name -> commit transaction name +begin transaction txn1; +insert into TxnTable values(8); +commit transaction txn1; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 + 5 + 8 +(5 rows) + +-- Begin transaction name -> rollback transaction name +begin transaction txn1; +insert into TxnTable values(9); +rollback transaction txn1; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 + 5 + 8 +(5 rows) + +-- Begin tran name -> commit tran name +begin tran txn1; +insert into TxnTable values(10); +commit tran txn1; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 + 5 + 8 + 10 +(6 rows) + +-- Begin tran name -> rollback tran name +begin tran txn1; +insert into TxnTable values(10); +rollback tran txn1; +select c1 from TxnTable; + c1 +---- + 1 + 2 + 4 + 5 + 8 + 10 +(6 rows) + +truncate table TxnTable; +-- save tran name -> rollback tran name +set transaction isolation level snapshot; +show transaction_isolation; + transaction_isolation +----------------------- + repeatable read +(1 row) + +show default_transaction_isolation; + default_transaction_isolation +------------------------------- + repeatable read +(1 row) + +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +select @@trancount; + trancount +----------- + 1 +(1 row) + +insert into TxnTable values(2); +save tran sp2; +insert into TxnTable values(3); +save tran sp2; +select @@trancount; + trancount +----------- + 1 +(1 row) + +insert into TxnTable values(4); +select c1 from TxnTable; + c1 +---- + 1 + 2 + 3 + 4 +(4 rows) + +rollback tran sp2; +select @@trancount; + trancount +----------- + 1 +(1 row) + +select c1 from TxnTable; + c1 +---- + 1 + 2 + 3 +(3 rows) + +rollback tran sp2; +select @@trancount; + trancount +----------- + 1 +(1 row) + +select c1 from TxnTable; + c1 +---- + 1 + 2 +(2 rows) + +rollback tran sp1; +select @@trancount; + trancount +----------- + 1 +(1 row) + +select c1 from TxnTable; + c1 +---- + 1 +(1 row) + +rollback tran txn1; +select @@trancount; + trancount +----------- + 0 +(1 row) + +select c1 from TxnTable; + c1 +---- +(0 rows) + +-- begin transaction name -> save transaction name -> rollback to first +-- savepoint +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +insert into TxnTable values(2); +save transaction sp2; +insert into TxnTable values(3); +save transaction sp3; +insert into TxnTable values(4); +rollback tran sp1; +rollback tran sp1; +ERROR: savepoint "sp1" does not exist +rollback tran; +select c1 from TxnTable; + c1 +---- +(0 rows) + +-- begin transaction name -> save transaction name -> rollback tran name +-- Rollback whole transaction +set transaction isolation level serializable; +ERROR: SERIALIZABLE isolation level is not supported +LINE 1: set transaction isolation level serializable; + ^ +show transaction_isolation; + transaction_isolation +----------------------- + repeatable read +(1 row) + +show default_transaction_isolation; + default_transaction_isolation +------------------------------- + repeatable read +(1 row) + +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +begin transaction txn1; +insert into TxnTable values(2); +save transaction sp1; +insert into TxnTable values(3); +select @@trancount; + trancount +----------- + 2 +(1 row) + +rollback tran txn1; +select @@trancount; + trancount +----------- + 0 +(1 row) + +select c1 from TxnTable; + c1 +---- +(0 rows) + +-- begin transaction -> save transaction name -> rollback to savepoint +-- commit transaction +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +insert into TxnTable values(2); +select c1 from TxnTable; + c1 +---- + 1 + 2 +(2 rows) + +rollback tran sp1; +commit transaction; +select c1 from TxnTable; + c1 +---- + 1 +(1 row) + +-- begin transaction -> save transaction name -> rollback to savepoint +-- save transaction name -> commit transaction +begin transaction txn1; +insert into TxnTable values(3); +save transaction sp1; +insert into TxnTable values(4); +select c1 from TxnTable; + c1 +---- + 1 + 3 + 4 +(3 rows) + +rollback tran sp1; +save transaction sp2; +insert into TxnTable values(5); +commit transaction; +select c1 from TxnTable; + c1 +---- + 1 + 3 + 5 +(3 rows) + +-- begin transaction -> save transaction name -> error -> rollback to savepoint +-- commit transaction +begin transaction txn1; +insert into TxnTable values(6); +save transaction sp1; +insert into TxnTable values(7); +select c1 frm TxnTable; +ERROR: syntax error at or near "TxnTable" +LINE 1: select c1 frm TxnTable; + ^ +rollback tran sp1; +commit transaction; +select c1 from TxnTable; + c1 +---- + 1 + 3 + 5 + 6 +(4 rows) + +-- create and execute procedure with transaction commands +-- \tsql on +-- create procedure txnproc as +-- begin tran; +-- insert into TxnTable values(8); +-- select c1 from TxnTable; +-- save tran sp1; +-- commit; +-- rollback tran; +-- go +-- execute txnproc; +-- go +-- \tsql off +-- drop procedure txnproc; +-- transaction syntax error +begin; +ERROR: syntax error at or near ";" +LINE 1: begin; + ^ +begin txn1; +ERROR: syntax error at or near "txn1" +LINE 1: begin txn1; + ^ +commit txn1; +ERROR: syntax error at or near "txn1" +LINE 1: commit txn1; + ^ +rollback txx1; +ERROR: syntax error at or near "txx1" +LINE 1: rollback txx1; + ^ +-- invalid transaction name +begin transaction txn1; +rollback transaction txn2; +ERROR: savepoint "txn2" does not exist +rollback; +drop table TxnTable; +reset babelfish_pg_tsql.sql_dialect; diff --git a/contrib/babelfishpg_tsql/expected/test/babel_typecode.out b/contrib/babelfishpg_tsql/expected/test/babel_typecode.out new file mode 100644 index 00000000000..4802f931041 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_typecode.out @@ -0,0 +1,40 @@ +SET babelfishpg_tsql.sql_dialect = 'tsql'; +-- test typecode list sys table +SELECT pg_namespace, pg_typname, tsql_typname, type_family_priority, priority, sql_variant_hdr_size FROM sys.babelfish_typecode_list(); + pg_namespace | pg_typname | tsql_typname | type_family_priority | priority | sql_variant_hdr_size +--------------+------------------+------------------+----------------------+----------+---------------------- + sys | sql_variant | sql_variant | 1 | 1 | 1 + sys | datetimeoffset | datetimeoffset | 2 | 2 | 2 + sys | datetime2 | datetime2 | 2 | 3 | 2 + sys | datetime | datetime | 2 | 4 | 1 + sys | smalldatetime | smalldatetime | 2 | 5 | 1 + pg_catalog | date | date | 2 | 6 | 1 + pg_catalog | time | time | 2 | 7 | 2 + pg_catalog | float8 | float | 3 | 8 | 1 + pg_catalog | float4 | real | 3 | 9 | 1 + pg_catalog | numeric | numeric | 4 | 10 | 3 + sys | money | money | 4 | 11 | 1 + sys | smallmoney | smallmoney | 4 | 12 | 1 + pg_catalog | int8 | bigint | 4 | 13 | 1 + pg_catalog | int4 | int | 4 | 14 | 1 + pg_catalog | int2 | smallint | 4 | 15 | 1 + sys | tinyint | tinyint | 4 | 16 | 1 + sys | bit | bit | 4 | 17 | 1 + sys | nvarchar | nvarchar | 5 | 18 | 5 + sys | nchar | nchar | 5 | 19 | 5 + sys | varchar | varchar | 5 | 20 | 5 + sys | bpchar | char | 5 | 21 | 5 + sys | varbinary | varbinary | 6 | 22 | 3 + sys | binary | binary | 6 | 23 | 3 + sys | uniqueidentifier | uniqueidentifier | 7 | 24 | 1 + pg_catalog | text | text | 5 | 25 | 5 + sys | ntext | ntext | 5 | 26 | 5 + sys | image | image | 5 | 27 | 5 + pg_catalog | xml | xml | 5 | 28 | 5 + pg_catalog | bpchar | char | 5 | 29 | 5 + sys | decimal | decimal | 5 | 30 | 5 + sys | sysname | sysname | 5 | 31 | 5 + sys | rowversion | timestamp | 8 | 32 | 3 + sys | timestamp | timestamp | 8 | 33 | 3 +(33 rows) + diff --git a/contrib/babelfishpg_tsql/expected/test/babel_uniqueidentifier.out b/contrib/babelfishpg_tsql/expected/test/babel_uniqueidentifier.out new file mode 100644 index 00000000000..3ee92a29dc2 --- /dev/null +++ b/contrib/babelfishpg_tsql/expected/test/babel_uniqueidentifier.out @@ -0,0 +1,175 @@ +SET babelfishpg_tsql.sql_dialect = 'tsql'; +create table t1 (a uniqueidentifier, b uniqueidentifier, c uniqueidentifier, primary key(a)); +insert into t1(a) values ('6F9619FF-8B86-D011-B42D-00C04FC964FF'); +insert into t1(a) values ('6F9619FF-8B86-D011-B42D-00C04FC964FF'); -- trigger error +ERROR: duplicate key value violates unique constraint "t1_pkey" +DETAIL: Key (a)=(6F9619FF-8B86-D011-B42D-00C04FC964FF) already exists. +select * from t1; + a | b | c +--------------------------------------+---+--- + 6F9619FF-8B86-D011-B42D-00C04FC964FF | | +(1 row) + +insert into t1 values ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', newid(), newid()); +explain (costs off) select * from t1 where a = '6F9619FF-8B86-D011-B42D-00C04FC964FF'; -- test PK + QUERY PLAN +------------------------------------------------------------------------------ + Index Scan using t1_pkey on t1 + Index Cond: (a = '6F9619FF-8B86-D011-B42D-00C04FC964FF'::uniqueidentifier) +(2 rows) + +select count(*) from t1 where a = '6F9619FF-8B86-D011-B42D-00C04FC964FF'; + count +------- + 1 +(1 row) + +select count(*) from t1 where a > '6F9619FF-8B86-D011-B42D-00C04FC964FF'; + count +------- + 1 +(1 row) + +select count(*) from t1 where a >= '6F9619FF-8B86-D011-B42D-00C04FC964FF'; + count +------- + 2 +(1 row) + +select count(*) from t1 where a < 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; + count +------- + 1 +(1 row) + +select count(*) from t1 where a <= 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; + count +------- + 2 +(1 row) + +select count(*) from t1 where a <> 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; + count +------- + 1 +(1 row) + +-- newid's value could not be verified +insert into t1 values (newid(), newid(), newid()); +insert into t1 values (newid(), newid(), newid()); +insert into t1 values (newid(), newid(), newid()); +select count(a) from t1; + count +------- + 5 +(1 row) + +create table t2 (like t1); +insert into t2 select * from t1 order by a; +select count(distinct a) from t2; + count +------- + 5 +(1 row) + +-- test index (need more data) +create table t3 ( a uniqueidentifier, b uniqueidentifier); +-- create inital distinct values +insert into t3 values (newid(), newid()); +insert into t3 values (newid(), newid()); +insert into t3 values (newid(), newid()); +insert into t3 values (newid(), newid()); +create index t3_a on t3 using btree (a); +create index t3_b on t3 using hash (b); +-- test truncate feature of uniqueidentifier_in +create table t4 ( a uniqueidentifier); +insert into t4 values ('6F9619FF-8B86-D011-B42D-00C04FC964FF'); +insert into t4 values ('6F9619FF-8B86-D011-B42D-00C04FC964FFwrong'); -- characters exceeding are truncated +insert into t4 values ('{6F9619FF-8B86-D011-B42D-00C04FC964FF}'); -- with braces +insert into t4 values ('{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong'); -- error due to no matching brace +ERROR: invalid input syntax for type uuid: "{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong" +LINE 1: insert into t4 values ('{6F9619FF-8B86-D011-B42D-00C04FC964F... + ^ +insert into t4 values ('6F9619FF-8B86-D011-B42D-00C04FC964FF}'); -- single brace at the end are truncated +select * from t4; + a +-------------------------------------- + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF +(4 rows) + +reset babelfishpg_tsql.sql_dialect; +SET ENABLE_SEQSCAN = OFF; +SET ENABLE_BITMAPSCAN = OFF; +SET SEARCH_PATH = sys, public; +select name, setting from pg_settings where name in ('enable_seqscan', 'enable_bitmapscan'); + name | setting +-------------------+--------- + enable_bitmapscan | off + enable_seqscan | off +(2 rows) + +explain (costs off) select * from t3 where a = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; -- test btree index + QUERY PLAN +------------------------------------------------------------------------------ + Index Scan using t3_at386baf2ea94a757b06124ac7a4c87f41f on t3 + Index Cond: (a = 'A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'::uniqueidentifier) +(2 rows) + +explain (costs off) select * from t3 where b = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; -- test hash index + QUERY PLAN +------------------------------------------------------------------------------ + Index Scan using t3_bt3e585137b3a7e6e454e6dffa0e011ffe7 on t3 + Index Cond: (b = 'A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11'::uniqueidentifier) +(2 rows) + +-- assignment cast, should have same behavior as normal insert +set babelfishpg_tsql.sql_dialect = "tsql"; +create table t5 ( a uniqueidentifier); +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as varchar(50))); +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as varchar(50))); -- characters exceeding are truncated +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FF}' as varchar(50))); -- with braces +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as varchar(50))); -- error due to no matching brace +ERROR: invalid input syntax for type uuid: "{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong" +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF}' as varchar(50))); -- single brace at the end are truncated +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as nvarchar(50))); +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as nvarchar(50))); -- characters exceeding are truncated +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FF}' as nvarchar(50))); -- with braces +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as nvarchar(50))); -- error due to no matching brace +ERROR: invalid input syntax for type uuid: "{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong" +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF}' as nvarchar(50))); -- single brace at the end are truncated +-- error cases, implicit cast not supported +select * from t5 where a = cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as varchar(50)); + a +-------------------------------------- + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF +(8 rows) + +select * from t5 where a = cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as nvarchar(50)); + a +-------------------------------------- + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF + 6F9619FF-8B86-D011-B42D-00C04FC964FF +(8 rows) + +reset babelfishpg_tsql.sql_dialect; +drop table t1; +drop table t2; +drop table t3; +drop table t4; +drop table t5; diff --git a/contrib/babelfishpg_tsql/results/test/babel_219.out b/contrib/babelfishpg_tsql/results/test/babel_219.out new file mode 100644 index 00000000000..cdf9b9b5a1c --- /dev/null +++ b/contrib/babelfishpg_tsql/results/test/babel_219.out @@ -0,0 +1,84 @@ +-- This test should be run without installing the babelfishpg_tsql extension +-- BABEL-219 test a domain named varchar in schema other than sys +-- is not affected by the fix of BABEL-219 +create domain public.varchar as pg_catalog.varchar(2) check (char_length(value) < 1); +select cast('a' as public.varchar); -- throw error +ERROR: value for domain public."varchar" violates check constraint "varchar_check" +select cast('' as public.varchar); + varchar +--------- + +(1 row) + +select cast('a' as varchar); -- pg_catalog.varchar should work + varchar +--------- + a +(1 row) + +show search_path; + search_path +----------------- + "$user", public +(1 row) + +-- Explicitly add pg_catalog to tail of search_path, +-- to force varchar default to public.varchar +select set_config('search_path', current_setting('search_path') || ', pg_catalog', false); + set_config +----------------------------- + "$user", public, pg_catalog +(1 row) + +-- Set tsql dialet so the fix for BABEL-219 can kick in +SET babelfishpg_tsql.sql_dialect = 'tsql'; +select cast('a' as varchar); -- varchar default to public.varchar. should fail exactly the same way as explicitly specifying public.varchar +ERROR: value for domain "varchar" violates check constraint "varchar_check" +select cast('' as varchar); -- varchar default to public.varchar. should pass + varchar +--------- + +(1 row) + +create table t1(col varchar); +insert into t1 (col) select 'a'; -- fail +ERROR: value for domain "varchar" violates check constraint "varchar_check" +insert into t1 (col) select ''; -- pass +select * from t1; + col +----- + +(1 row) + +-- verify behavior of public.varchar is unchanged in tsql dialect +select cast('a' as public.varchar); -- fail +ERROR: value for domain "varchar" violates check constraint "varchar_check" +select cast('' as public.varchar); -- pass + varchar +--------- + +(1 row) + +create table t2(col public.varchar); +insert into t1 (col) select 'a'; -- fail +ERROR: value for domain "varchar" violates check constraint "varchar_check" +insert into t1 (col) select ''; -- pass +select * from t1; + col +----- + + +(2 rows) + +-- Clean up +drop table t1; +drop table t2; +set babelfishpg_tsql.sql_dialect = 'postgres'; +-- Reset search_path +set search_path to "$user", public; +show search_path; + search_path +----------------- + "$user", public +(1 row) + diff --git a/contrib/babelfishpg_tsql/results/test/babel_init.out b/contrib/babelfishpg_tsql/results/test/babel_init.out new file mode 100644 index 00000000000..33fc6df4d9b --- /dev/null +++ b/contrib/babelfishpg_tsql/results/test/babel_init.out @@ -0,0 +1,14 @@ +SET client_min_messages TO WARNING; +CREATE EXTENSION "babelfishpg_tsql" cascade; +RESET client_min_messages; +SET babelfishpg_tsql.sql_dialect = 'tsql'; +reset babelfishpg_tsql.sql_dialect; +--BABEL-978: make sure error stack is not exceeded in/after extension creation. +CREATE PROCEDURE myproc() LANGUAGE plpgsql AS $$ +BEGIN +CREATE invalid_syntax; +END; +$$; +ERROR: syntax error at or near "invalid_syntax" +LINE 3: CREATE invalid_syntax; + ^ diff --git a/contrib/babelfishpg_tsql/results/test/babel_like.out b/contrib/babelfishpg_tsql/results/test/babel_like.out new file mode 100644 index 00000000000..310a0d6da16 --- /dev/null +++ b/contrib/babelfishpg_tsql/results/test/babel_like.out @@ -0,0 +1,98 @@ +set babelfishpg_tsql.sql_dialect = 'tsql'; +select relname from pg_class where relname like '['; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like ']'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like '[]'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like NULL; + relname +--------- +(0 rows) + +select relname from pg_class where relname like ''; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg[1:9]class'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg\[1:9\]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg\[1:9 ]class'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg [1:9\]class'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg*[1:9*]class' escape '*'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg [1:9*]class' escape '*'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +select relname from pg_class where relname like 'pg*[1:9 ]class' escape '*'; +ERROR: pattern matching operators '[' and ']' are not supported for LIKE +set babelfishpg_tsql.sql_dialect = 'postgres'; +select relname from pg_class where relname like '['; + relname +--------- +(0 rows) + +select relname from pg_class where relname like ']'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like '[]'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like NULL; + relname +--------- +(0 rows) + +select relname from pg_class where relname like ''; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg[1:9]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg\[1:9\]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg\[1:9 ]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg [1:9\]class'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg*[1:9*]class' escape '*'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg [1:9*]class' escape '*'; + relname +--------- +(0 rows) + +select relname from pg_class where relname like 'pg*[1:9 ]class' escape '*'; + relname +--------- +(0 rows) + 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..ae5505ded2b --- /dev/null +++ b/contrib/babelfishpg_tsql/runtime/functions.c @@ -0,0 +1,1007 @@ +#include "postgres.h" +#include "port.h" +#include "funcapi.h" +#include "pgstat.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 "common/md5.h" +#include "miscadmin.h" +#include "parser/scansup.h" +#include "tsearch/ts_locale.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 "utils/xid8.h" +#include + +#include "../src/babelfish_version.h" +#include "../src/datatype_info.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" +#include "../src/rolecmds.h" + +#define TSQL_STAT_GET_ACTIVITY_COLS 24 +#define SP_DATATYPE_INFO_HELPER_COLS 23 + +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(servicename); +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); +PG_FUNCTION_INFO_V1(host_os); +PG_FUNCTION_INFO_V1(tsql_stat_get_activity); +PG_FUNCTION_INFO_V1(get_current_full_xact_id); +PG_FUNCTION_INFO_V1(checksum); +PG_FUNCTION_INFO_V1(has_dbaccess); +PG_FUNCTION_INFO_V1(sp_datatype_info_helper); + +/* Not supported -- only syntax support */ +PG_FUNCTION_INFO_V1(procid); + +void* get_servername_internal(void); +void* get_servicename_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; +extern bool pltsql_case_insensitive_identifiers; + +char *bbf_servername = "BABELFISH"; +const char *bbf_servicename = "MSSQLSERVER"; +#define MD5_HASH_LEN 32 + +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* string_to_tsql_varchar(const char *input_str) +{ + StringInfoData temp; + void* info; + + initStringInfo(&temp); + appendStringInfoString(&temp, input_str); + + info = tsql_varchar_input(temp.data, temp.len, -1); + pfree(temp.data); + return info; +} + +void* get_servername_internal() +{ + return string_to_tsql_varchar(bbf_servername); +} + +void* get_servicename_internal() +{ + return string_to_tsql_varchar(bbf_servicename); +} + +/* + * This function will return the servername. + */ +Datum +servername(PG_FUNCTION_ARGS) +{ + PG_RETURN_VARCHAR_P(get_servername_internal()); +} + +/* + * This function will return the servicename. + */ +Datum +servicename(PG_FUNCTION_ARGS) +{ + PG_RETURN_VARCHAR_P(get_servicename_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; + + if (pltsql_case_insensitive_identifiers) + name = downcase_identifier(name, strlen(name), false, false); /* no truncation here. truncation will be handled inside get_physical_schema_name() */ + physical_name = get_physical_schema_name(get_cur_db_name(), name); + + /* + * If physical schema name is empty or NULL for any reason then return NULL. + */ + if (physical_name == NULL || strlen(physical_name) == 0) + PG_RETURN_NULL(); + + 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); +} + +Datum +host_os(PG_FUNCTION_ARGS) +{ + char *host_os_res, *pg_version, host_str[256]; + void *info; + + /* filter out host info */ + pg_version = pstrdup(PG_VERSION_STR); + sscanf(pg_version, "PostgreSQL %*s on %s, compiled by %*s", host_str); + + if (strstr(host_str, "w64") || strstr(host_str, "w32") || strstr(host_str, "mingw") || strstr(host_str, "visual studio")) + { + host_os_res = pstrdup("Windows"); + } + else if (strstr(host_str, "linux")) + { + host_os_res = pstrdup("Linux"); + } + else if (strstr(host_str, "mac")) + { + host_os_res = pstrdup("Mac"); + } + else + host_os_res = pstrdup("UNKNOWN"); + + info = tsql_varchar_input(host_os_res, strlen(host_os_res), -1); + if (pg_version) + pfree(pg_version); + if (host_os_res) + pfree(host_os_res); + PG_RETURN_VARCHAR_P(info); +} + +/* + * Returns activity of TDS backends. + */ +Datum +tsql_stat_get_activity(PG_FUNCTION_ARGS) +{ + int num_backends = pgstat_fetch_stat_numbackends(); + int curr_backend; + char* view_name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + int pid = -1; + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + /* For sys.dm_exec_sessions view: + * - If user is sysadmin, we show info of all the sessions + * - If user is not sysadmin, we only show info of current session + * For sys.dm_exec_connections view: + * - If user is sysadmin, we show info of all the connections + * - If user is not sysadmin, we throw an error since user does not + * have the required permissions to query this view + */ + if (strcmp(view_name, "sessions") == 0) + { + if (role_is_sa(GetSessionUserId())) + pid = -1; + else + pid = MyProcPid; + } + else if (strcmp(view_name, "connections") == 0) + { + if (role_is_sa(GetSessionUserId())) + pid = -1; + else + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("The user does not have permission to perform this action"))); + } + + /* 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"))); + + /* Build tupdesc for result tuples. */ + tupdesc = CreateTemplateTupleDesc(TSQL_STAT_GET_ACTIVITY_COLS); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "procid", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "client_version", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "library_name", VARCHAROID, 32, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "language", VARCHAROID, 128, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "quoted_identifier", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "arithabort", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "ansi_null_dflt_on", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, "ansi_defaults", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 9, "ansi_warnings", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 10, "ansi_padding", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 11, "ansi_nulls", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 12, "concat_null_yields_null", BOOLOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 13, "textsize", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 14, "datefirst", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 15, "lock_timeout", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 16, "transaction_isolation", INT2OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 17, "client_pid", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 18, "row_count", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 19, "prev_error", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 20, "trancount", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 21, "protocol_version", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 22, "packet_size", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 23, "encrypt_option", VARCHAROID, 40, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 24, "database_id", INT2OID, -1, 0); + tupdesc = BlessTupleDesc(tupdesc); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + /* 1-based index */ + for (curr_backend = 1; curr_backend <= num_backends; curr_backend++) + { + /* for each row */ + Datum values[TSQL_STAT_GET_ACTIVITY_COLS]; + bool nulls[TSQL_STAT_GET_ACTIVITY_COLS]; + + if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->get_stat_values && + (*pltsql_protocol_plugin_ptr)->get_stat_values(values, nulls, TSQL_STAT_GET_ACTIVITY_COLS, pid, curr_backend)) + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + else continue; + + /* If only a single backend was requested, and we found it, break. */ + if (pid != -1) + break; + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->invalidate_stat_view) + (*pltsql_protocol_plugin_ptr)->invalidate_stat_view(); + + return (Datum) 0; +} + +Datum +get_current_full_xact_id(PG_FUNCTION_ARGS) +{ + PreventCommandDuringRecovery("get_current_full_xact_id()"); + + PG_RETURN_FULLTRANSACTIONID(GetCurrentFullTransactionId()); +} + +Datum +checksum(PG_FUNCTION_ARGS) +{ + int32 result = 0; + int nargs = PG_NARGS(); + StringInfoData buf; + char md5[MD5_HASH_LEN + 1]; + char *name; + + initStringInfo(&buf); + if (nargs > 0) + { + ArrayType *arr; + Datum *values; + bool *nulls; + int nelems; + int i; + arr = PG_GETARG_ARRAYTYPE_P(0); + deconstruct_array(arr, TEXTOID, -1, false, TYPALIGN_INT, &values, &nulls, &nelems); + for (i=0; i 0 && isspace((unsigned char) lowercase_db_name[i - 1])) + lowercase_db_name[--i] = '\0'; + const char *user = NULL; + const char *login; + + int16 db_id = get_db_id(lowercase_db_name); + + if (!DbidIsValid(db_id)) + PG_RETURN_NULL(); + + login = GetUserNameFromId(GetSessionUserId(), false); + user = get_authid_user_ext_physical_name(lowercase_db_name, login); + + /* Special cases: + Database Owner should always have access + If this DB has guest roles, the guests should always have access + */ + if (!user) + { + Oid datdba; + + datdba = get_role_oid("sysadmin", false); + if (is_member_of_role(GetSessionUserId(), datdba)) + user = get_dbo_role_name(lowercase_db_name); + else + user = get_guest_role_name(lowercase_db_name); + } + + if (!user) + PG_RETURN_INT32(0); + else + PG_RETURN_INT32(1); +} + +Datum +sp_datatype_info_helper(PG_FUNCTION_ARGS) +{ + + int16 odbcVer = PG_GETARG_INT16(0); + bool is_100 = PG_GETARG_BOOL(1); + + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + + int i; + + /* 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"))); + + /* Build tupdesc for result tuples. */ + tupdesc = CreateTemplateTupleDesc(SP_DATATYPE_INFO_HELPER_COLS); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "TYPE_NAME", VARCHAROID, 20, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "DATA_TYPE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "PRECISION", INT8OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "LITERAL_PREFIX", VARCHAROID, 20, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "LITERAL_SUFFIX", VARCHAROID, 20, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 6, "CREATE_PARAMS", VARCHAROID, 20, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 7, "NULLABLE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 8, "CASE_SENSITIVE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 9, "SEARCHABLE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 10, "UNSIGNED_ATTRIBUTE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 11, "MONEY", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 12, "AUTO_INCREMENT", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 13, "LOCAL_TYPE_NAME", VARCHAROID, 20, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 14, "MINIMUM_SCALE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 15, "MAXIMUM_SCALE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 16, "SQL_DATA_TYPE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 17, "SQL_DATETIME_SUB", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 18, "NUM_PREC_RADIX", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 19, "INTERVAL_PRECISION", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 20, "USERTYPE", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 21, "LENGTH", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 22, "SS_DATA_TYPE", INT2OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 23, "PG_TYPE_NAME", VARCHAROID, 20, 0); + tupdesc = BlessTupleDesc(tupdesc); + + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + for (i = 0; i < DATATYPE_INFO_TABLE_ROWS; i++) + { + /* for each row */ + Datum values[SP_DATATYPE_INFO_HELPER_COLS]; + bool nulls[SP_DATATYPE_INFO_HELPER_COLS]; + + DatatypeInfo datatype_info_element = datatype_info_table[i]; + + MemSet(nulls, false, SP_DATATYPE_INFO_HELPER_COLS); + + values[0] = CStringGetTextDatum(datatype_info_element.type_name); + + if (odbcVer == 3) + { + if (is_100) + values[1] = Int32GetDatum(datatype_info_element.data_type_3_100); + else + values[1] = Int32GetDatum(datatype_info_element.data_type_3); + } + else + { + if (is_100) + values[1] = Int32GetDatum(datatype_info_element.data_type_2_100); + else + values[1] = Int32GetDatum(datatype_info_element.data_type_2); + } + + values[2] = Int64GetDatum(datatype_info_element.precision); + + if (strcmp(datatype_info_element.literal_prefix, NULLVAL_STR) == 0) + nulls[3] = true; + else + values[3] = CStringGetTextDatum(datatype_info_element.literal_prefix); + + if (strcmp(datatype_info_element.literal_suffix, NULLVAL_STR) == 0) + nulls[4] = true; + else + values[4] = CStringGetTextDatum(datatype_info_element.literal_suffix); + + if (strcmp(datatype_info_element.create_params, NULLVAL_STR) == 0) + nulls[5] = true; + else + values[5] = CStringGetTextDatum(datatype_info_element.create_params); + + values[6] = Int32GetDatum(datatype_info_element.nullable); + values[7] = Int32GetDatum(datatype_info_element.case_sensitive); + values[8] = Int32GetDatum(datatype_info_element.searchable); + + if (datatype_info_element.unsigned_attribute == NULLVAL) + nulls[9] = true; + else + values[9] = Int32GetDatum(datatype_info_element.unsigned_attribute); + + values[10] = Int32GetDatum(datatype_info_element.money); + + if (datatype_info_element.auto_increment == NULLVAL) + nulls[11] = true; + else + values[11] = Int32GetDatum(datatype_info_element.auto_increment); + + values[12] = CStringGetTextDatum(datatype_info_element.local_type_name); + + if (datatype_info_element.minimum_scale == NULLVAL) + nulls[13] = true; + else + values[13] = Int32GetDatum(datatype_info_element.minimum_scale); + + if (datatype_info_element.maximum_scale == NULLVAL) + nulls[14] = true; + else + values[14] = Int32GetDatum(datatype_info_element.maximum_scale); + + values[15] = Int32GetDatum(datatype_info_element.sql_data_type); + + if (datatype_info_element.sql_datetime_sub == NULLVAL) + nulls[16] = true; + else + values[16] = Int32GetDatum(datatype_info_element.sql_datetime_sub); + + if (datatype_info_element.num_prec_radix == NULLVAL) + nulls[17] = true; + else + values[17] = Int32GetDatum(datatype_info_element.num_prec_radix); + + if (datatype_info_element.interval_precision == NULLVAL) + nulls[18] = true; + else + values[18] = Int32GetDatum(datatype_info_element.interval_precision); + + values[19] = Int32GetDatum(datatype_info_element.usertype); + values[20] = Int32GetDatum(datatype_info_element.length); + values[21] = UInt8GetDatum(datatype_info_element.ss_data_type); + + if (strcmp(datatype_info_element.pg_type_name, NULLVAL_STR) == 0) + nulls[22] = true; + else + values[22] = CStringGetTextDatum(datatype_info_element.pg_type_name); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} 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..5a6f2e1f907 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.in @@ -0,0 +1,30 @@ +/* + * All objects created by the included files will be created in sys + */ + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +#include "collation.sql" +#include "datatype.sql" +#include "datatype_string_operators.sql" +#include "sys.sql" +#include "sys_languages.sql" +#include "sys_babelfish_configurations.sql" +#include "sys_function_helpers.sql" +#include "sys_functions.sql" +#include "sys_cast.sql" +#include "coerce.sql" +#include "ownership.sql" +#include "information_schema_tsql.sql" +#include "sys_views.sql" +#include "sys_procedures.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..04a1adcf2e6 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/babelfishpg_tsql.sql @@ -0,0 +1,2276 @@ +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; + +CREATE FUNCTION fulltextserviceproperty (TEXT) + RETURNS sys.int AS 'babelfishpg_tsql', 'fulltextserviceproperty' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION COLUMNS_UPDATED () + RETURNS sys.VARBINARY AS 'babelfishpg_tsql', 'columnsupdated' LANGUAGE C; + +CREATE OR REPLACE FUNCTION UPDATE (TEXT) + RETURNS BOOLEAN AS 'babelfishpg_tsql', 'updated' LANGUAGE C; + +CREATE OR REPLACE PROCEDURE xp_qv(IN nvarchar(256), IN nvarchar(256)) + AS 'babelfishpg_tsql', 'xp_qv_internal' LANGUAGE C; + +-- +-- 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'); + +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::CHAR(20), 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.sp_datatype_info_helper(@odbcver, false) where @data_type = 0 or data_type = @data_type + order by DATA_TYPE, AUTO_INCREMENT, MONEY, USERTYPE; +END; +$$ +LANGUAGE 'pltsql'; + +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::CHAR(20), 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.sp_datatype_info_helper(@odbcver, true) where @data_type = 0 or data_type = @data_type + order by DATA_TYPE, AUTO_INCREMENT, MONEY, USERTYPE; +END; +$$ +LANGUAGE 'pltsql'; + +CREATE OR REPLACE FUNCTION sys.tsql_type_radix_for_sp_columns_helper(IN type TEXT) +RETURNS SMALLINT +AS $$ +DECLARE + radix SMALLINT; +BEGIN + CASE type + WHEN 'tinyint' THEN radix = 10; + WHEN 'money' THEN radix = 10; + WHEN 'smallmoney' THEN radix = 10; + WHEN 'sql_variant' THEN radix = 10; + ELSE + radix = NULL; + END CASE; + RETURN radix; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.tsql_type_length_for_sp_columns_helper(IN type TEXT, IN typelen INT, IN typemod INT) +RETURNS INT +AS $$ +DECLARE + length INT; + precision INT; +BEGIN + -- unknown tsql type + IF type IS NULL THEN + RETURN typelen::INT; + END IF; + + IF typemod = -1 AND (type = 'varchar' OR type = 'nvarchar' OR type = 'varbinary') THEN + length = 0; + RETURN length; + END IF; + + IF typelen != -1 THEN + CASE type + WHEN 'tinyint' THEN length = 1; + WHEN 'date' THEN length = 6; + WHEN 'smalldatetime' THEN length = 16; + WHEN 'smallmoney' THEN length = 12; + WHEN 'money' THEN length = 21; + WHEN 'datetime' THEN length = 16; + WHEN 'datetime2' THEN length = 16; + WHEN 'datetimeoffset' THEN length = 20; + WHEN 'time' THEN length = 12; + WHEN 'timestamp' THEN length = 8; + ELSE length = typelen; + END CASE; + RETURN length; + END IF; + + CASE + WHEN type in ('char', 'bpchar', 'varchar', 'binary', 'varbinary') THEN length = typemod - 4; + WHEN type in ('nchar', 'nvarchar') THEN length = (typemod - 4) * 2; + WHEN type in ('text', 'image') THEN length = 2147483647; + WHEN type = 'ntext' THEN length = 2147483646; + WHEN type = 'xml' THEN length = 0; + WHEN type = 'sql_variant' THEN length = 8000; + WHEN type = 'money' THEN length = 21; + WHEN type = 'sysname' THEN length = (typemod - 4) * 2; + WHEN type in ('numeric', 'decimal') THEN + precision = ((typemod - 4) >> 16) & 65535; + length = precision + 2; + ELSE + length = typemod; + END CASE; + RETURN length; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +-- BABEL-1784: support for sp_columns/sp_columns_100 +CREATE OR REPLACE VIEW sys.sp_columns_100_view AS + SELECT + CAST(t4."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, + CAST(t4."TABLE_SCHEMA" AS sys.sysname) AS TABLE_OWNER, + CAST(t4."TABLE_NAME" 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(coalesce(tsql_type_name, t.typname) AS sys.sysname) AS TYPE_NAME, + + CASE WHEN t4."CHARACTER_MAXIMUM_LENGTH" = -1 THEN 0::INT + WHEN a.atttypmod != -1 + THEN + CAST(coalesce(t4."NUMERIC_PRECISION", t4."CHARACTER_MAXIMUM_LENGTH", sys.tsql_type_precision_helper(t4."DATA_TYPE", a.atttypmod)) AS INT) + WHEN tsql_type_name = 'timestamp' + THEN 8 + ELSE + CAST(coalesce(t4."NUMERIC_PRECISION", t4."CHARACTER_MAXIMUM_LENGTH", sys.tsql_type_precision_helper(t4."DATA_TYPE", t.typtypmod)) AS INT) + END AS PRECISION, + + CASE WHEN a.atttypmod != -1 + THEN + CAST(sys.tsql_type_length_for_sp_columns_helper(t4."DATA_TYPE", a.attlen, a.atttypmod) AS int) + ELSE + CAST(sys.tsql_type_length_for_sp_columns_helper(t4."DATA_TYPE", a.attlen, t.typtypmod) AS int) + END AS LENGTH, + + + CASE WHEN a.atttypmod != -1 + THEN + CAST(coalesce(t4."NUMERIC_SCALE", sys.tsql_type_scale_helper(t4."DATA_TYPE", a.atttypmod, true)) AS smallint) + ELSE + CAST(coalesce(t4."NUMERIC_SCALE", sys.tsql_type_scale_helper(t4."DATA_TYPE", t.typtypmod, true)) AS smallint) + END AS SCALE, + + + CAST(coalesce(t4."NUMERIC_PRECISION_RADIX", sys.tsql_type_radix_for_sp_columns_helper(t4."DATA_TYPE")) 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, + + CASE WHEN t4."DATA_TYPE" = 'xml' THEN 0::INT + WHEN t4."DATA_TYPE" = 'sql_variant' THEN 8000::INT + WHEN t4."CHARACTER_MAXIMUM_LENGTH" = -1 THEN 0::INT + ELSE CAST(t4."CHARACTER_OCTET_LENGTH" AS int) + END AS CHAR_OCTET_LENGTH, + + CAST(t4."ORDINAL_POSITION" 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, + CAST(t6.is_computed as smallint) AS SS_IS_COMPUTED, + CAST(t6.is_identity as smallint) 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 + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on t2.nspname = ext.nspname + JOIN information_schema_tsql.columns t4 ON (t1.relname = t4."TABLE_NAME" AND ext.orig_name = t4."TABLE_SCHEMA") + LEFT JOIN pg_attribute a on a.attrelid = t1.oid AND a.attname = t4."COLUMN_NAME" + LEFT JOIN pg_type t ON t.oid = a.atttypid + LEFT JOIN sys.columns t6 ON + ( + t1.oid = t6.object_id AND + t4."ORDINAL_POSITION" = t6.column_id + ) + , sys.translate_pg_type_to_tsql(a.atttypid) AS tsql_type_name + , sys.spt_datatype_info_table AS t5 + WHERE (t4."DATA_TYPE" = t5.TYPE_NAME) + AND ext.dbid = cast(sys.db_id() as oid); + +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 lower(table_name) similar to lower(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, ordinal_position; + 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, ordinal_position; + 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(sys.babelfish_truncate_identifier(@table_name), + sys.babelfish_truncate_identifier(@table_owner), + sys.babelfish_truncate_identifier(@table_qualifier), + sys.babelfish_truncate_identifier(@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(sys.babelfish_truncate_identifier(@table_name), + sys.babelfish_truncate_identifier(@table_owner), + sys.babelfish_truncate_identifier(@table_qualifier), + sys.babelfish_truncate_identifier(@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'; + +-- TODO: Remove information_schema references +CREATE OR REPLACE VIEW sys.spt_columns_view_managed AS +SELECT + o.object_id AS OBJECT_ID, + isc."TABLE_CATALOG"::information_schema.sql_identifier AS TABLE_CATALOG, + isc."TABLE_SCHEMA"::information_schema.sql_identifier AS TABLE_SCHEMA, + o.name AS TABLE_NAME, + c.name AS COLUMN_NAME, + isc."ORDINAL_POSITION"::information_schema.cardinal_number AS ORDINAL_POSITION, + isc."COLUMN_DEFAULT"::information_schema.character_data AS COLUMN_DEFAULT, + isc."IS_NULLABLE"::information_schema.yes_or_no AS IS_NULLABLE, + isc."DATA_TYPE"::information_schema.character_data AS DATA_TYPE, + + CAST (CASE WHEN isc."CHARACTER_MAXIMUM_LENGTH" < 0 THEN 0 ELSE isc."CHARACTER_MAXIMUM_LENGTH" END + AS information_schema.cardinal_number) AS CHARACTER_MAXIMUM_LENGTH, + + CAST (CASE WHEN isc."CHARACTER_OCTET_LENGTH" < 0 THEN 0 ELSE isc."CHARACTER_OCTET_LENGTH" END + AS information_schema.cardinal_number) AS CHARACTER_OCTET_LENGTH, + + CAST (CASE WHEN isc."NUMERIC_PRECISION" < 0 THEN 0 ELSE isc."NUMERIC_PRECISION" END + AS information_schema.cardinal_number) AS NUMERIC_PRECISION, + + CAST (CASE WHEN isc."NUMERIC_PRECISION_RADIX" < 0 THEN 0 ELSE isc."NUMERIC_PRECISION_RADIX" END + AS information_schema.cardinal_number) AS NUMERIC_PRECISION_RADIX, + + CAST (CASE WHEN isc."NUMERIC_SCALE" < 0 THEN 0 ELSE isc."NUMERIC_SCALE" END + AS information_schema.cardinal_number) AS NUMERIC_SCALE, + + CAST (CASE WHEN isc."DATETIME_PRECISION" < 0 THEN 0 ELSE isc."DATETIME_PRECISION" END + AS information_schema.cardinal_number) AS DATETIME_PRECISION, + + isc."CHARACTER_SET_CATALOG"::information_schema.sql_identifier AS CHARACTER_SET_CATALOG, + isc."CHARACTER_SET_SCHEMA"::information_schema.sql_identifier AS CHARACTER_SET_SCHEMA, + isc."CHARACTER_SET_NAME"::information_schema.sql_identifier AS CHARACTER_SET_NAME, + isc."COLLATION_CATALOG"::information_schema.sql_identifier AS COLLATION_CATALOG, + isc."COLLATION_SCHEMA"::information_schema.sql_identifier AS COLLATION_SCHEMA, + c.collation_name AS COLLATION_NAME, + isc."DOMAIN_CATALOG"::information_schema.sql_identifier AS DOMAIN_CATALOG, + isc."DOMAIN_SCHEMA"::information_schema.sql_identifier AS DOMAIN_SCHEMA, + isc."DOMAIN_NAME"::information_schema.sql_identifier 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_tsql.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'); +GRANT SELECT ON sys.spt_columns_view_managed TO PUBLIC; + +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 LOWER(in_catalog)) AND + (in_owner IS NULL OR s_cv.TABLE_SCHEMA LIKE LOWER(in_owner)) AND + (in_table IS NULL OR s_cv.TABLE_NAME LIKE LOWER(in_table)) AND + (in_column IS NULL OR s_cv.COLUMN_NAME LIKE LOWER(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 sys.nvarchar(4000), + params sys.nvarchar(4000) = 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" sys.nvarchar(4000), + "@params" sys.nvarchar(4000) = 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 OR REPLACE VIEW sys.sp_tables_view AS +SELECT +t2.dbname AS TABLE_QUALIFIER, +CAST(t3.name AS name) 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, sys.schemas AS t3 +WHERE t1.relnamespace = t3.schema_id AND t1.relnamespace = t2.oid AND t1.relkind IN ('r','v','m') +AND has_schema_privilege(t1.relnamespace, 'USAGE') +AND has_table_privilege(t1.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +GRANT SELECT ON sys.sp_tables_view TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sp_tables_internal( + in_table_name sys.nvarchar(384) = '', + in_table_owner sys.nvarchar(384) = '', + in_table_qualifier sys.sysname = '', + in_table_type sys.varchar(100) = '', + in_fusepattern sys.bit = '1') + RETURNS TABLE ( + out_table_qualifier sys.sysname, + out_table_owner sys.sysname, + out_table_name sys.sysname, + out_table_type sys.varchar(32), + out_remarks sys.varchar(254) + ) + AS $$ + DECLARE opt_table sys.varchar(16) = ''; + DECLARE opt_view sys.varchar(16) = ''; + BEGIN + + IF (SELECT count(*) FROM unnest(string_to_array(in_table_type, ',')) WHERE upper(trim(unnest)) = 'TABLE' OR upper(trim(unnest)) = '''TABLE''') >= 1 THEN + opt_table = 'TABLE'; + END IF; + IF (SELECT count(*) from unnest(string_to_array(in_table_type, ',')) WHERE upper(trim(unnest)) = 'VIEW' OR upper(trim(unnest)) = '''VIEW''') >= 1 THEN + opt_view = 'VIEW'; + END IF; + IF in_fusepattern = 1 THEN + RETURN query + SELECT + CAST(table_qualifier AS sys.sysname) AS TABLE_QUALIFIER, + CAST(table_owner AS sys.sysname) AS TABLE_OWNER, + CAST(table_name AS sys.sysname) AS TABLE_NAME, + CAST(table_type AS sys.varchar(32)) AS TABLE_TYPE, + CAST(remarks AS sys.varchar(254)) AS REMARKS + FROM sys.sp_tables_view + WHERE ((SELECT coalesce(in_table_name,'')) = '' OR lower(table_name) LIKE lower(in_table_name)) + AND ((SELECT coalesce(in_table_owner,'')) = '' OR lower(table_owner) LIKE lower(in_table_owner)) + AND ((SELECT coalesce(in_table_qualifier,'')) = '' OR lower(table_qualifier) LIKE lower(in_table_qualifier)) + AND ((SELECT coalesce(in_table_type,'')) = '' OR table_type = opt_table OR table_type = opt_view) + ORDER BY table_qualifier, table_owner, table_name; + ELSE + RETURN query + SELECT + CAST(table_qualifier AS sys.sysname) AS TABLE_QUALIFIER, + CAST(table_owner AS sys.sysname) AS TABLE_OWNER, + CAST(table_name AS sys.sysname) AS TABLE_NAME, + CAST(table_type AS sys.varchar(32)) AS TABLE_TYPE, + CAST(remarks AS sys.varchar(254)) AS REMARKS + FROM sys.sp_tables_view + WHERE ((SELECT coalesce(in_table_name,'')) = '' OR lower(table_name) = lower(in_table_name)) + AND ((SELECT coalesce(in_table_owner,'')) = '' OR lower(table_owner) = lower(in_table_owner)) + AND ((SELECT coalesce(in_table_qualifier,'')) = '' OR lower(table_qualifier) = lower(in_table_qualifier)) + AND ((SELECT coalesce(in_table_type,'')) = '' OR table_type = opt_table OR table_type = opt_view) + ORDER BY table_qualifier, table_owner, table_name; + END IF; + END; +$$ +LANGUAGE plpgsql; + + +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 $$ + DECLARE @opt_table sys.varchar(16) = ''; + DECLARE @opt_view sys.varchar(16) = ''; +BEGIN + IF (@table_qualifier != '') AND (LOWER(@table_qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + SELECT + CAST(out_table_qualifier AS sys.sysname) AS TABLE_QUALIFIER, + CAST(out_table_owner AS sys.sysname) AS TABLE_OWNER, + CAST(out_table_name AS sys.sysname) AS TABLE_NAME, + CAST(out_table_type AS sys.varchar(32)) AS TABLE_TYPE, + CAST(out_remarks AS sys.varchar(254)) AS REMARKS + FROM sys.sp_tables_internal(@table_name, @table_owner, @table_qualifier, CAST(@table_type AS varchar(100)), @fusepattern); +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE 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 OR REPLACE VIEW sys.sp_pkeys_view AS +SELECT +CAST(t4."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, +CAST(t4."TABLE_SCHEMA" 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 + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on t2.nspname = ext.nspname + JOIN information_schema_tsql.columns t4 ON (t1.relname = t4."TABLE_NAME" AND ext.orig_name = t4."TABLE_SCHEMA") + 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."ORDINAL_POSITION" AS smallint) = ANY (t5.conkey) + AND CAST(t4."ORDINAL_POSITION" AS smallint) = t5.conkey[seq] + AND ext.dbid = cast(sys.db_id() as oid); + +GRANT SELECT on sys.sp_pkeys_view TO PUBLIC; + +-- internal function in order to workaround BABEL-1597 +create or replace 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 table_owner = coalesce(in_table_owner,'dbo') + 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) = 'dbo', + "@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(t3."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, +CAST(t3."TABLE_SCHEMA" AS sys.sysname) AS TABLE_OWNER, +CAST(t3."TABLE_NAME" AS sys.sysname) AS TABLE_NAME, +CAST(NULL AS smallint) AS NON_UNIQUE, +CAST(NULL AS sys.sysname) AS INDEX_QUALIFIER, +CAST(NULL AS sys.sysname) AS INDEX_NAME, +CAST(0 AS smallint) AS TYPE, +CAST(NULL AS smallint) AS SEQ_IN_INDEX, +CAST(NULL AS sys.sysname) AS COLUMN_NAME, +CAST(NULL AS sys.varchar(1)) AS COLLATION, +CAST(t1.reltuples AS int) AS CARDINALITY, +CAST(t1.relpages AS int) AS PAGES, +CAST(NULL AS sys.varchar(128)) AS FILTER_CONDITION +FROM pg_catalog.pg_class t1 + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN information_schema_tsql.columns t3 ON (t1.relname = t3."TABLE_NAME" AND s1.name = t3."TABLE_SCHEMA") + , generate_series(0,31) seq -- SQL server has max 32 columns per index +UNION +SELECT +CAST(t4."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, +CAST(t4."TABLE_SCHEMA" AS sys.sysname) AS TABLE_OWNER, +CAST(t4."TABLE_NAME" 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.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN pg_catalog.pg_roles t3 ON t1.relowner = t3.oid + JOIN information_schema_tsql.columns t4 ON (t1.relname = t4."TABLE_NAME" AND s1.name = t4."TABLE_SCHEMA") + 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."ORDINAL_POSITION" AS smallint) = ANY (t5.indkey) + AND CAST(t4."ORDINAL_POSITION" 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 = 'Y' and (non_unique IS NULL or non_unique = 0)) or (in_is_unique = 'N')) + 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 sys.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; + +CREATE OR REPLACE VIEW sys.dm_os_host_info AS +SELECT + -- get_host_os() depends on a Postgres function created separately. + cast( sys.get_host_os() as sys.nvarchar(256) ) as host_platform + -- Hardcoded at the moment. Should likely be GUC with default '' (empty string). + , cast( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.host_distribution') as sys.nvarchar(256) ) as host_distribution + -- documentation on one hand states this is empty string on linux, but otoh shows an example with "ubuntu 16.04" + , cast( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.host_release') as sys.nvarchar(256) ) as host_release + -- empty string on linux. + , cast( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.host_service_pack_level') as sys.nvarchar(256) ) + as host_service_pack_level + -- windows stock keeping unit. null on linux. + , cast( null as int ) as host_sku + -- lcid + , cast( sys.collationproperty( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.server_collation_name') , 'lcid') as int ) + as "os_language_version"; +GRANT SELECT ON sys.dm_os_host_info TO PUBLIC; + +-- For some cases, T-SQL throws an error in DML-time even though it can be detected in DDL-time. +-- This function can be used in DDL-time to postpone errors without impacting general DML performance. +CREATE OR REPLACE FUNCTION sys.babelfish_runtime_error(msg ANYCOMPATIBLE) +RETURNS ANYCOMPATIBLE AS +$$ +BEGIN + RAISE EXCEPTION '%', msg; +END; +$$ +LANGUAGE PLPGSQL; +GRANT ALL on FUNCTION sys.babelfish_runtime_error TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sp_column_privileges_view AS +SELECT +CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS TABLE_OWNER, +CAST(t1.relname AS sys.sysname) AS TABLE_NAME, +CAST(COALESCE(SPLIT_PART(t6.attoptions[1], '=', 2), t5.column_name) AS sys.sysname) AS COLUMN_NAME, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t5.grantor) AS sys.sysname) AS GRANTOR, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t5.grantee) AS sys.sysname) AS GRANTEE, +CAST(t5.privilege_type AS sys.varchar(32)) AS PRIVILEGE, +CAST(t5.is_grantable AS sys.varchar(3)) AS IS_GRANTABLE +FROM pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN information_schema.column_privileges t5 ON t1.relname = t5.table_name AND t2.nspname = t5.table_schema + JOIN pg_attribute t6 ON t6.attrelid = t1.oid AND t6.attname = t5.column_name +WHERE t5.privilege_type NOT IN ('TRIGGER', 'TRUNCATE'); +GRANT SELECT ON sys.sp_column_privileges_view TO PUBLIC; + + +CREATE OR REPLACE PROCEDURE sys.sp_column_privileges( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@table_qualifier" sys.sysname = '', + "@column_name" sys.nvarchar(384) = '' +) +AS $$ +BEGIN + IF (@table_qualifier != '') AND (LOWER(@table_qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + IF (COALESCE(@table_owner, '') = '') + BEGIN + + IF EXISTS ( + SELECT * FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) and LOWER(SCHEMA_NAME()) = LOWER(table_qualifier) + ) + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + COLUMN_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE + FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND (LOWER(SCHEMA_NAME()) = LOWER(table_owner)) + AND ((SELECT COALESCE(@table_qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@table_qualifier)) + AND ((SELECT COALESCE(@column_name,'')) = '' OR LOWER(column_name) LIKE LOWER(@column_name)) + ORDER BY table_qualifier, table_owner, table_name, column_name, privilege; + END + ELSE + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + COLUMN_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE + FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND (LOWER('dbo')= LOWER(table_owner)) + AND ((SELECT COALESCE(@table_qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@table_qualifier)) + AND ((SELECT COALESCE(@column_name,'')) = '' OR LOWER(column_name) LIKE LOWER(@column_name)) + ORDER BY table_qualifier, table_owner, table_name, column_name, privilege; + END + END + ELSE + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + COLUMN_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE + FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT COALESCE(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT COALESCE(@table_qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@table_qualifier)) + AND ((SELECT COALESCE(@column_name,'')) = '' OR LOWER(column_name) LIKE LOWER(@column_name)) + ORDER BY table_qualifier, table_owner, table_name, column_name, privilege; + END +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE ON PROCEDURE sys.sp_column_privileges TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sp_table_privileges_view AS +SELECT DISTINCT +CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS TABLE_OWNER, +CAST(t1.relname AS sys.sysname) AS TABLE_NAME, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t4.grantor) AS sys.sysname) AS GRANTOR, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t4.grantee) AS sys.sysname) AS GRANTEE, +CAST(t4.privilege_type AS sys.sysname) AS PRIVILEGE, +CAST(t4.is_grantable AS sys.sysname) AS IS_GRANTABLE +FROM pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN information_schema.table_privileges t4 ON t1.relname = t4.table_name +WHERE t4.privilege_type NOT IN ('TRIGGER', 'TRUNCATE'); +GRANT SELECT on sys.sp_table_privileges_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_table_privileges( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = '', + "@table_qualifier" sys.sysname = '', + "@fusepattern" sys.bit = 1 +) +AS $$ +BEGIN + + IF (@table_qualifier != '') AND (LOWER(@table_qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + IF @fusepattern = 1 + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE FROM sys.sp_table_privileges_view + WHERE LOWER(TABLE_NAME) LIKE LOWER(@table_name) + AND ((SELECT COALESCE(@table_owner,'')) = '' OR LOWER(TABLE_OWNER) LIKE LOWER(@table_owner)) + ORDER BY table_qualifier, table_owner, table_name, privilege; + END + ELSE + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE FROM sys.sp_table_privileges_view + WHERE LOWER(TABLE_NAME) = LOWER(@table_name) + AND ((SELECT COALESCE(@table_owner,'')) = '' OR LOWER(TABLE_OWNER) = LOWER(@table_owner)) + ORDER BY table_qualifier, table_owner, table_name, privilege; + END + +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE ON PROCEDURE sys.sp_table_privileges TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sp_special_columns_precision_helper(IN type TEXT, IN sp_columns_precision INT, IN sp_columns_max_length SMALLINT, IN sp_datatype_info_precision BIGINT) RETURNS INT +AS $$ +SELECT + CASE + WHEN type in ('real','float') THEN sp_columns_max_length * 2 - 1 + WHEN type in ('char','varchar','binary','varbinary') THEN sp_columns_max_length + WHEN type in ('nchar','nvarchar') THEN sp_columns_max_length / 2 + WHEN type in ('sysname','uniqueidentifier') THEN sp_datatype_info_precision + ELSE sp_columns_precision + END; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.sp_special_columns_length_helper(IN type TEXT, IN sp_columns_precision INT, IN sp_columns_max_length SMALLINT, IN sp_datatype_info_precision BIGINT) RETURNS INT +AS $$ +SELECT + CASE + WHEN type in ('decimal','numeric','money','smallmoney') THEN sp_columns_precision + 2 + WHEN type in ('time','date','datetime2','datetimeoffset') THEN sp_columns_precision * 2 + WHEN type in ('smalldatetime') THEN sp_columns_precision + WHEN type in ('datetime') THEN sp_columns_max_length * 2 + WHEN type in ('sql_variant') THEN sp_datatype_info_precision + ELSE sp_columns_max_length + END; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.sp_special_columns_scale_helper(IN type TEXT, IN sp_columns_scale INT) RETURNS INT +AS $$ +SELECT + CASE + WHEN type in ('bit','real','float','char','varchar','nchar','nvarchar','time','date','datetime2','datetimeoffset','varbinary','binary','sql_variant','sysname','uniqueidentifier') THEN NULL + ELSE sp_columns_scale + END; +$$ LANGUAGE SQL IMMUTABLE; + +-- TODO: BABEL-2838 +CREATE OR REPLACE VIEW sys.sp_special_columns_view AS +SELECT DISTINCT +CAST(1 as smallint) AS SCOPE, +CAST(coalesce (split_part(pa.attoptions[1], '=', 2) ,c1.name) AS sys.sysname) AS COLUMN_NAME, -- get original column name if exists +CAST(t6.data_type AS smallint) AS DATA_TYPE, + +CASE -- cases for when they are of type identity. + WHEN c1.is_identity = 1 AND (t8.name = 'decimal' or t8.name = 'numeric') + THEN CAST(CONCAT(t8.name, '() identity') AS sys.sysname) + WHEN c1.is_identity = 1 AND (t8.name != 'decimal' AND t8.name != 'numeric') + THEN CAST(CONCAT(t8.name, ' identity') AS sys.sysname) + ELSE CAST(t8.name AS sys.sysname) +END AS TYPE_NAME, + +CAST(sys.sp_special_columns_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), c1.precision, c1.max_length, t6."PRECISION") AS int) AS PRECISION, +CAST(sys.sp_special_columns_length_helper(coalesce(tsql_type_name, tsql_base_type_name), c1.precision, c1.max_length, t6."PRECISION") AS int) AS LENGTH, +CAST(sys.sp_special_columns_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), c1.scale) AS smallint) AS SCALE, +CAST(1 AS smallint) AS PSEUDO_COLUMN, +CAST(c1.is_nullable AS int) AS IS_NULLABLE, +CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS TABLE_OWNER, +CAST(t1.relname AS sys.sysname) AS TABLE_NAME, + +CASE + WHEN idx.is_unique = 1 AND (idx.is_unique_constraint !=1 AND idx.is_primary_key != 1) + THEN CAST('u' AS sys.sysname) -- if it is a unique index, then we should cast it as 'u' for filtering purposes + ELSE CAST(t5.contype AS sys.sysname) +END AS CONSTRAINT_TYPE + +FROM pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + LEFT JOIN pg_constraint t5 ON t1.oid = t5.conrelid + LEFT JOIN sys.indexes idx ON idx.object_id = t1.oid + JOIN sys.columns c1 ON t1.oid = c1.object_id + + JOIN pg_catalog.pg_type AS t7 ON t7.oid = c1.system_type_id + JOIN sys.types as t8 ON c1.user_type_id = t8.user_type_id + LEFT JOIN sys.sp_datatype_info_helper(2::smallint, false) AS t6 ON t7.typname = t6.pg_type_name OR t7.typname = t6.type_name --need in order to get accurate DATA_TYPE value + LEFT JOIN pg_catalog.pg_attribute AS pa ON t1.oid = pa.attrelid AND c1.name = pa.attname + , sys.translate_pg_type_to_tsql(t8.user_type_id) AS tsql_type_name + , sys.translate_pg_type_to_tsql(t8.system_type_id) AS tsql_base_type_name + WHERE (t5.contype = 'p' OR t5.contype = 'u' + OR ((idx.is_unique = 1) AND (idx.is_primary_key !=1 AND idx.is_unique_constraint !=1))) -- Only looking for unique indexes + AND (CAST(c1.column_id AS smallint) = ANY (t5.conkey) OR ((idx.is_unique = 1) AND (idx.is_primary_key !=1 AND idx.is_unique_constraint !=1))) + AND has_schema_privilege(s1.schema_id, 'USAGE'); + +GRANT SELECT ON sys.sp_special_columns_view TO PUBLIC; + + +CREATE OR REPLACE PROCEDURE sys.sp_special_columns( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@qualifier" sys.sysname = '', + "@col_type" char(1) = 'R', + "@scope" char(1) = 'T', + "@nullable" char(1) = 'U', + "@odbcver" int = 2 +) +AS $$ +DECLARE @special_col_type sys.sysname; +BEGIN + IF (@qualifier != '') AND (LOWER(@qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + + END + + IF (LOWER(@col_type) = LOWER('V')) + BEGIN + THROW 33557097, N'TIMESTAMP datatype is not currently supported in Babelfish', 1; + END + + IF (LOWER(@nullable) = LOWER('O')) + BEGIN + SELECT TOP 1 @special_col_type=constraint_type FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) + ORDER BY constraint_type, column_name; + + IF @special_col_type='u' + BEGIN + IF @scope='C' + BEGIN + SELECT TOP 1 + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + ORDER BY scope, column_name; + + END + ELSE + BEGIN + SELECT TOP 1 + SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + END + + END + + ELSE + BEGIN + IF @scope='C' + BEGIN + SELECT + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + ELSE + BEGIN + SELECT SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + END + END + + ELSE + BEGIN + SELECT TOP 1 @special_col_type=constraint_type FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) + ORDER BY constraint_type, column_name; + + IF @special_col_type='u' + BEGIN + IF @scope='C' + BEGIN + SELECT TOP 1 + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + ORDER BY scope, column_name; + END + + ELSE + BEGIN + SELECT TOP 1 SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + ORDER BY scope, column_name; + END + + END + ELSE + BEGIN + IF @scope='C' + BEGIN + SELECT + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + + ELSE + BEGIN + SELECT SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + + END + END + +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_special_columns TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_special_columns_100( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@qualifier" sys.sysname = '', + "@col_type" char(1) = 'R', + "@scope" char(1) = 'T', + "@nullable" char(1) = 'U', + "@odbcver" int = 2 +) +AS $$ +BEGIN + EXEC sp_special_columns @table_name, @table_owner, @qualifier, @col_type, @scope, @nullable, @odbcver +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_special_columns_100 TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sp_fkeys_view AS +SELECT +-- primary key info +CAST(t2.dbname AS sys.sysname) AS PKTABLE_QUALIFIER, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = ref.table_schema) AS sys.sysname) AS PKTABLE_OWNER, +CAST(ref.table_name AS sys.sysname) AS PKTABLE_NAME, +CAST(coalesce(split_part(pkname_table.attoptions[1], '=', 2), ref.column_name) AS sys.sysname) AS PKCOLUMN_NAME, + +-- foreign key info +CAST(t2.dbname AS sys.sysname) AS FKTABLE_QUALIFIER, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = fk.table_schema) AS sys.sysname) AS FKTABLE_OWNER, +CAST(fk.table_name AS sys.sysname) AS FKTABLE_NAME, +CAST(coalesce(split_part(fkname_table.attoptions[1], '=', 2), fk.column_name) AS sys.sysname) AS FKCOLUMN_NAME, + +CAST(seq AS smallint) AS KEY_SEQ, +CASE + WHEN map.update_rule = 'NO ACTION' THEN CAST(1 AS smallint) + WHEN map.update_rule = 'SET NULL' THEN CAST(2 AS smallint) + WHEN map.update_rule = 'SET DEFAULT' THEN CAST(3 AS smallint) + ELSE CAST(0 AS smallint) +END AS UPDATE_RULE, + +CASE + WHEN map.delete_rule = 'NO ACTION' THEN CAST(1 AS smallint) + WHEN map.delete_rule = 'SET NULL' THEN CAST(2 AS smallint) + WHEN map.delete_rule = 'SET DEFAULT' THEN CAST(3 AS smallint) + ELSE CAST(0 AS smallint) +END AS DELETE_RULE, +CAST(fk.constraint_name AS sys.sysname) AS FK_NAME, +CAST(ref.constraint_name AS sys.sysname) AS PK_NAME + +FROM information_schema.referential_constraints AS map + +-- join unique constraints (e.g. PKs constraints) to ref columns info +INNER JOIN information_schema.key_column_usage AS ref + JOIN pg_catalog.pg_class p1 -- Need to join this in order to get oid for pkey's original bbf name + JOIN sys.pg_namespace_ext p2 ON p1.relnamespace = p2.oid + JOIN information_schema.columns p4 ON p1.relname = p4.table_name AND p1.relnamespace::regnamespace::text = p4.table_schema + JOIN pg_constraint p5 ON p1.oid = p5.conrelid + ON (p1.relname=ref.table_name AND p4.column_name=ref.column_name AND ref.table_schema = p2.nspname AND ref.table_schema = p4.table_schema) + + ON ref.constraint_catalog = map.unique_constraint_catalog + AND ref.constraint_schema = map.unique_constraint_schema + AND ref.constraint_name = map.unique_constraint_name + +-- join fk columns to the correct ref columns using ordinal positions +INNER JOIN information_schema.key_column_usage AS fk + ON fk.constraint_catalog = map.constraint_catalog + AND fk.constraint_schema = map.constraint_schema + AND fk.constraint_name = map.constraint_name + AND fk.position_in_unique_constraint = ref.ordinal_position + +INNER JOIN pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN information_schema.columns t4 ON t1.relname = t4.table_name AND t1.relnamespace::regnamespace::text = t4.table_schema + JOIN pg_constraint t5 ON t1.oid = t5.conrelid + ON (t1.relname=fk.table_name AND t4.column_name=fk.column_name AND fk.table_schema = t2.nspname AND fk.table_schema = t4.table_schema) + +-- get foreign key's original bbf name +JOIN pg_catalog.pg_attribute fkname_table + ON (t1.oid = fkname_table.attrelid) AND (fk.column_name = fkname_table.attname) + +-- get primary key's original bbf name +JOIN pg_catalog.pg_attribute pkname_table + ON (p1.oid = pkname_table.attrelid) AND (ref.column_name = pkname_table.attname) + + , generate_series(1,16) seq -- BBF has max 16 columns per primary key +WHERE t5.contype = 'f' +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_fkeys_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_fkeys( + "@pktable_name" sys.sysname = '', + "@pktable_owner" sys.sysname = '', + "@pktable_qualifier" sys.sysname = '', + "@fktable_name" sys.sysname = '', + "@fktable_owner" sys.sysname = '', + "@fktable_qualifier" sys.sysname = '' +) +AS $$ +BEGIN + + IF coalesce(@pktable_name,'') = '' AND coalesce(@fktable_name,'') = '' + BEGIN + THROW 33557097, N'Primary or foreign key table name must be given.', 1; + END + + IF (@pktable_qualifier != '' AND (SELECT sys.db_name()) != @pktable_qualifier) OR + (@fktable_qualifier != '' AND (SELECT sys.db_name()) != @fktable_qualifier) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + SELECT + PKTABLE_QUALIFIER, + PKTABLE_OWNER, + PKTABLE_NAME, + PKCOLUMN_NAME, + FKTABLE_QUALIFIER, + FKTABLE_OWNER, + FKTABLE_NAME, + FKCOLUMN_NAME, + KEY_SEQ, + UPDATE_RULE, + DELETE_RULE, + FK_NAME, + PK_NAME + FROM sys.sp_fkeys_view + WHERE ((SELECT coalesce(@pktable_name,'')) = '' OR LOWER(pktable_name) = LOWER(@pktable_name)) + AND ((SELECT coalesce(@fktable_name,'')) = '' OR LOWER(fktable_name) = LOWER(@fktable_name)) + AND ((SELECT coalesce(@pktable_owner,'')) = '' OR LOWER(pktable_owner) = LOWER(@pktable_owner)) + AND ((SELECT coalesce(@pktable_qualifier,'')) = '' OR LOWER(pktable_qualifier) = LOWER(@pktable_qualifier)) + AND ((SELECT coalesce(@fktable_owner,'')) = '' OR LOWER(fktable_owner) = LOWER(@fktable_owner)) + AND ((SELECT coalesce(@fktable_qualifier,'')) = '' OR LOWER(fktable_qualifier) = LOWER(@fktable_qualifier)) + ORDER BY fktable_qualifier, fktable_owner, fktable_name, key_seq; + +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE ON PROCEDURE sys.sp_fkeys TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sp_stored_procedures_view AS +SELECT +CAST(d.name AS sys.sysname) AS PROCEDURE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS PROCEDURE_OWNER, + +CASE + WHEN p.prokind = 'p' THEN CAST(concat(p.proname, ';1') AS sys.nvarchar(134)) + ELSE CAST(concat(p.proname, ';0') AS sys.nvarchar(134)) +END AS PROCEDURE_NAME, + +-1 AS NUM_INPUT_PARAMS, +-1 AS NUM_OUTPUT_PARAMS, +-1 AS NUM_RESULT_SETS, +CAST(NULL AS varchar(254)) AS REMARKS, +cast(2 AS smallint) AS PROCEDURE_TYPE + +FROM pg_catalog.pg_proc p + +INNER JOIN sys.schemas s1 ON p.pronamespace = s1.schema_id +INNER JOIN sys.databases d ON d.database_id = sys.db_id() +WHERE has_schema_privilege(s1.schema_id, 'USAGE') + +UNION + +SELECT CAST((SELECT sys.db_name()) AS sys.sysname) AS PROCEDURE_QUALIFIER, +CAST(nspname AS sys.sysname) AS PROCEDURE_OWNER, + +CASE + WHEN prokind = 'p' THEN cast(concat(proname, ';1') AS sys.nvarchar(134)) + ELSE cast(concat(proname, ';0') AS sys.nvarchar(134)) +END AS PROCEDURE_NAME, + +-1 AS NUM_INPUT_PARAMS, +-1 AS NUM_OUTPUT_PARAMS, +-1 AS NUM_RESULT_SETS, +CAST(NULL AS varchar(254)) AS REMARKS, +cast(2 AS smallint) AS PROCEDURE_TYPE + +FROM pg_catalog.pg_namespace n +JOIN pg_catalog.pg_proc p +ON pronamespace = n.oid +WHERE nspname = 'sys' AND (proname LIKE 'sp\_%' OR proname LIKE 'xp\_%' OR proname LIKE 'dm\_%' OR proname LIKE 'fn\_%'); + +GRANT SELECT ON sys.sp_stored_procedures_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_stored_procedures( + "@sp_name" sys.nvarchar(390) = '', + "@sp_owner" sys.nvarchar(384) = '', + "@sp_qualifier" sys.sysname = '', + "@fusepattern" sys.bit = '1' +) +AS $$ +BEGIN + IF (@sp_qualifier != '') AND LOWER(sys.db_name()) != LOWER(@sp_qualifier) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + -- If @sp_name or @sp_owner = '%', it gets converted to NULL or '' regardless of @fusepattern + IF @sp_name = '%' + BEGIN + SELECT @sp_name = '' + END + + IF @sp_owner = '%' + BEGIN + SELECT @sp_owner = '' + END + + -- Changes fusepattern to 0 if no wildcards are used. NOTE: Need to add [] wildcard pattern when it is implemented. Wait for BABEL-2452 + IF @fusepattern = 1 + BEGIN + IF (CHARINDEX('%', @sp_name) != 0 AND CHARINDEX('_', @sp_name) != 0 AND CHARINDEX('%', @sp_owner) != 0 AND CHARINDEX('_', @sp_owner) != 0 ) + BEGIN + SELECT @fusepattern = 0; + END + END + + -- Condition for when sp_name argument is not given or is null, or is just a wildcard (same order) + IF COALESCE(@sp_name, '') = '' + BEGIN + IF @fusepattern=1 + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE ((SELECT COALESCE(@sp_owner,'')) = '' OR LOWER(procedure_owner) LIKE LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE ((SELECT COALESCE(@sp_owner,'')) = '' OR LOWER(procedure_owner) LIKE LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + END + -- When @sp_name is not null + ELSE + BEGIN + -- When sp_owner is null and fusepattern = 0 + IF (@fusepattern = 0 AND COALESCE(@sp_owner,'') = '') + BEGIN + IF EXISTS ( -- Search in the sys schema + SELECT * FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = 'sys')) + BEGIN + SELECT PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = 'sys') + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE IF EXISTS ( + SELECT * FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = LOWER(SCHEMA_NAME())) + ) + BEGIN + SELECT PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = LOWER(SCHEMA_NAME())) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE -- Search in the dbo schema (if nothing exists it should just return nothing). + BEGIN + SELECT PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = 'dbo') + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + + END + ELSE IF (@fusepattern = 0 AND COALESCE(@sp_owner,'') != '') + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE -- fusepattern = 1 + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE ((SELECT COALESCE(@sp_name,'')) = '' OR LOWER(LEFT(procedure_name, -2)) LIKE LOWER(@sp_name)) + AND ((SELECT COALESCE(@sp_owner,'')) = '' OR LOWER(procedure_owner) LIKE LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + END +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_stored_procedures TO PUBLIC; + +CREATE OR REPLACE FUNCTION is_srvrolemember(role sys.SYSNAME, login sys.SYSNAME DEFAULT suser_name()) +RETURNS INTEGER AS +$$ +DECLARE has_role BOOLEAN; +DECLARE login_valid BOOLEAN; +BEGIN + role := TRIM(trailing from LOWER(role)); + login := TRIM(trailing from LOWER(login)); + + login_valid = (login = suser_name()) OR + (EXISTS (SELECT name + FROM sys.server_principals + WHERE + LOWER(name) = login + AND type = 'S')); + + IF NOT login_valid THEN + RETURN NULL; + + ELSIF role = 'public' THEN + RETURN 1; + + ELSIF role = 'sysadmin' THEN + has_role = pg_has_role(login::TEXT, role::TEXT, 'MEMBER'); + IF has_role THEN + RETURN 1; + ELSE + RETURN 0; + END IF; + + ELSIF role IN ( + 'serveradmin', + 'securityadmin', + 'setupadmin', + 'securityadmin', + 'processadmin', + 'dbcreator', + 'diskadmin', + 'bulkadmin') THEN + RETURN 0; + + ELSE + RETURN NULL; + END IF; + + EXCEPTION WHEN OTHERS THEN + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE PROCEDURE sys.sp_helpuser("@name_in_db" sys.SYSNAME = NULL) AS +$$ +BEGIN + IF @name_in_db IS NULL + BEGIN + SELECT CAST(Ext.orig_username AS SYS.SYSNAME) AS 'UserName', + CAST(CASE WHEN Ext.orig_username = 'dbo' THEN 'db_owner' ELSE 'PUBLIC' END AS SYS.SYSNAME) AS 'RoleName', + CAST(Ext.login_name AS SYS.SYSNAME) AS 'LoginName', + CAST(LogExt.default_database_name AS SYS.SYSNAME) AS 'DefDBName', + CAST(Ext.default_schema_name AS SYS.SYSNAME) AS 'DefSchemaName', + CAST(Base.oid AS INT) AS 'UserID', + CAST(CAST(Base.oid AS INT) AS SYS.VARBINARY(85)) AS 'SID' + FROM pg_catalog.pg_roles AS Base INNER JOIN sys.babelfish_authid_user_ext AS Ext + ON Base.rolname = Ext.rolname + LEFT OUTER JOIN sys.babelfish_authid_login_ext As LogExt + ON LogExt.rolname = Ext.orig_username + WHERE Ext.database_name = DB_NAME() + END + ELSE IF @name_in_db = 'db_owner' + BEGIN + -- simplification of role case, since no user defined roles exist yet + SELECT CAST('db_owner' AS SYS.SYSNAME) AS 'Role_name', + ROLE_ID('db_owner') AS 'Role_id', + CAST('dbo' AS SYS.SYSNAME) AS Users_in_role, + USER_ID('dbo') AS 'Userid'; + END + ELSE IF EXISTS (SELECT 1 + FROM sys.babelfish_authid_user_ext + WHERE (orig_username = @name_in_db + OR lower(orig_username) = lower(@name_in_db)) + AND database_name = DB_NAME()) + BEGIN + SELECT CAST(Ext.orig_username AS SYS.SYSNAME) AS 'UserName', + CAST(CASE WHEN Ext.orig_username = 'dbo' THEN 'db_owner' ELSE 'PUBLIC' END AS SYS.SYSNAME) AS 'RoleName', + CAST(Ext.login_name AS SYS.SYSNAME) AS 'LoginName', + CAST(LogExt.default_database_name AS SYS.SYSNAME) AS 'DefDBName', + CAST(Ext.default_schema_name AS SYS.SYSNAME) AS 'DefSchemaName', + CAST(Base.oid AS INT) AS 'UserID', + CAST(CAST(Base.oid AS INT) AS SYS.VARBINARY(85)) AS 'SID' + FROM pg_catalog.pg_roles AS Base INNER JOIN sys.babelfish_authid_user_ext AS Ext + ON Base.rolname = Ext.rolname + LEFT OUTER JOIN sys.babelfish_authid_login_ext As LogExt + ON LogExt.rolname = Ext.orig_username + WHERE Ext.database_name = DB_NAME() + AND (orig_username = @name_in_db OR lower(orig_username) = lower(@name_in_db)); + END + ELSE + RAISERROR ( 'The name supplied (%s) is not a user, role, or aliased login.', 16, 1, @name_in_db); +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_helpuser 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..91a9e662100 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/datatype.sql @@ -0,0 +1,10 @@ +-- Types with different default typmod behavior +SET enable_domain_typmod = TRUE; + +CREATE DOMAIN sys.CURSOR AS REFCURSOR; + +RESET enable_domain_typmod; + +-- At this point, the hooks are loaded, so sys.name will pick up the correct +-- collation. +CREATE DOMAIN sys._ci_sysname AS sys.sysname; 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/information_schema_tsql.sql b/contrib/babelfishpg_tsql/sql/information_schema_tsql.sql new file mode 100644 index 00000000000..ee37ed69722 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/information_schema_tsql.sql @@ -0,0 +1,404 @@ + +/* + * TSQL Information Schema + * + * Copyright (c) 2003-2020, PostgreSQL Global Development Group + * + * contrib/babelfishpg_tsql/sql/information_schema_tsql.sql + * + */ + +/* + * INFORMATION_SCHEMA_TSQL schema + */ + +CREATE SCHEMA information_schema_tsql; +GRANT USAGE ON SCHEMA information_schema_tsql TO PUBLIC; +SET search_path TO information_schema_tsql; + + +/* + * Introducing information_schema_tsql Utility functions; + * Re-using most of the original functions provided by Postgres. + */ + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_truetypid(nt pg_namespace, at pg_attribute, tp pg_type) RETURNS oid + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT CASE WHEN nt.nspname = 'pg_catalog' OR nt.nspname = 'sys' THEN at.atttypid ELSE tp.typbasetype END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_truetypmod(nt pg_namespace, at pg_attribute, tp pg_type) RETURNS int4 + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT CASE WHEN nt.nspname = 'pg_catalog' OR nt.nspname = 'sys' THEN at.atttypmod ELSE tp.typtypmod END$$; + +-- these functions encapsulate knowledge about the encoding of typmod: + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_char_max_length(type text, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN type IN ('char', 'nchar', 'varchar', 'nvarchar', 'binary', 'varbinary') + THEN CASE WHEN typmod = -1 + THEN -1 + ELSE typmod - 4 + END + WHEN type IN ('text', 'image') + THEN 2147483647 + WHEN type = 'ntext' + THEN 1073741823 + WHEN type = 'sysname' + THEN 128 + WHEN type = 'xml' + THEN -1 + WHEN type = 'sql_variant' + THEN 0 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_char_octet_length(type text, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN type IN ('char', 'varchar', 'binary', 'varbinary') + THEN CASE WHEN typmod = -1 /* default typmod */ + THEN -1 + ELSE typmod - 4 + END + WHEN type IN ('nchar', 'nvarchar') + THEN CASE WHEN typmod = -1 /* default typmod */ + THEN -1 + ELSE (typmod - 4) * 2 + END + WHEN type IN ('text', 'image') + THEN 2147483647 /* 2^30 + 1 */ + WHEN type = 'ntext' + THEN 2147483646 /* 2^30 */ + WHEN type = 'sysname' + THEN 256 + WHEN type = 'sql_variant' + THEN 0 + WHEN type = 'xml' + THEN -1 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_numeric_precision(type text, typid oid, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE typid + WHEN 21 /*int2*/ THEN 5 + WHEN 23 /*int4*/ THEN 10 + WHEN 20 /*int8*/ THEN 19 + WHEN 1700 /*numeric*/ THEN + CASE WHEN typmod = -1 + THEN null + ELSE ((typmod - 4) >> 16) & 65535 + END + WHEN 700 /*float4*/ THEN 24 + WHEN 701 /*float8*/ THEN 53 + ELSE + CASE WHEN type = 'tinyint' THEN 3 + WHEN type = 'money' THEN 19 + WHEN type = 'smallmoney' THEN 10 + ELSE null + END + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_numeric_precision_radix(type text, typid oid, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN typid IN (700, 701) THEN 2 + WHEN typid IN (20, 21, 23, 1700) THEN 10 + WHEN type IN ('tinyint', 'money', 'smallmoney') THEN 10 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_numeric_scale(type text, typid oid, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN typid IN (21, 23, 20) THEN 0 + WHEN typid IN (1700) THEN + CASE WHEN typmod = -1 + THEN null + ELSE (typmod - 4) & 65535 + END + WHEN type = 'tinyint' THEN 0 + WHEN type IN ('money', 'smallmoney') THEN 4 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_datetime_precision(type text, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN type = 'date' + THEN 0 + WHEN type = 'datetime' + THEN 3 + WHEN type IN ('time', 'datetime2', 'smalldatetime', 'datetimeoffset') + THEN CASE WHEN typmod < 0 THEN 6 ELSE typmod END + ELSE null + END$$; + + +/* + * COLUMNS view + */ + +CREATE OR REPLACE VIEW information_schema_tsql.columns AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "TABLE_CATALOG", + CAST(ext.orig_name AS sys.nvarchar(128)) AS "TABLE_SCHEMA", + CAST(c.relname AS sys.nvarchar(128)) AS "TABLE_NAME", + CAST(a.attname AS sys.nvarchar(128)) AS "COLUMN_NAME", + CAST(a.attnum AS int) AS "ORDINAL_POSITION", + CAST(CASE WHEN a.attgenerated = '' THEN pg_get_expr(ad.adbin, ad.adrelid) END AS sys.nvarchar(4000)) AS "COLUMN_DEFAULT", + CAST(CASE WHEN a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) THEN 'NO' ELSE 'YES' END + AS varchar(3)) + AS "IS_NULLABLE", + + CAST( + CASE WHEN tsql_type_name = 'sysname' THEN sys.translate_pg_type_to_tsql(t.typbasetype) + ELSE tsql_type_name END + AS sys.nvarchar(128)) + AS "DATA_TYPE", + + CAST( + information_schema_tsql._pgtsql_char_max_length(tsql_type_name, true_typmod) + AS int) + AS "CHARACTER_MAXIMUM_LENGTH", + + CAST( + information_schema_tsql._pgtsql_char_octet_length(tsql_type_name, true_typmod) + AS int) + AS "CHARACTER_OCTET_LENGTH", + + CAST( + /* Handle Tinyint separately */ + information_schema_tsql._pgtsql_numeric_precision(tsql_type_name, true_typid, true_typmod) + AS sys.tinyint) + AS "NUMERIC_PRECISION", + + CAST( + information_schema_tsql._pgtsql_numeric_precision_radix(tsql_type_name, true_typid, true_typmod) + AS smallint) + AS "NUMERIC_PRECISION_RADIX", + + CAST( + information_schema_tsql._pgtsql_numeric_scale(tsql_type_name, true_typid, true_typmod) + AS int) + AS "NUMERIC_SCALE", + + CAST( + information_schema_tsql._pgtsql_datetime_precision(tsql_type_name, true_typmod) + AS smallint) + AS "DATETIME_PRECISION", + + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_CATALOG", + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_SCHEMA", + /* + * TODO: We need to first create mapping of collation name to char-set name; + * Until then return null. + */ + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_NAME", + + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_CATALOG", + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_SCHEMA", + + /* Returns Babelfish specific collation name. */ + CAST(co.collname AS sys.nvarchar(128)) AS "COLLATION_NAME", + + CAST(CASE WHEN t.typtype = 'd' AND nt.nspname <> 'pg_catalog' AND nt.nspname <> 'sys' + THEN nc.dbname ELSE null END + AS sys.nvarchar(128)) AS "DOMAIN_CATALOG", + CAST(CASE WHEN t.typtype = 'd' AND nt.nspname <> 'pg_catalog' AND nt.nspname <> 'sys' + THEN ext.orig_name ELSE null END + AS sys.nvarchar(128)) AS "DOMAIN_SCHEMA", + CAST(CASE WHEN t.typtype = 'd' AND nt.nspname <> 'pg_catalog' AND nt.nspname <> 'sys' + THEN t.typname ELSE null END + AS sys.nvarchar(128)) AS "DOMAIN_NAME" + + FROM (pg_attribute a LEFT JOIN pg_attrdef ad ON attrelid = adrelid AND attnum = adnum) + JOIN (pg_class c JOIN sys.pg_namespace_ext nc ON (c.relnamespace = nc.oid)) ON a.attrelid = c.oid + JOIN (pg_type t JOIN pg_namespace nt ON (t.typnamespace = nt.oid)) ON a.atttypid = t.oid + LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON (bt.typnamespace = nbt.oid)) + ON (t.typtype = 'd' AND t.typbasetype = bt.oid) + LEFT JOIN pg_collation co on co.oid = a.attcollation + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on nc.nspname = ext.nspname, + information_schema_tsql._pgtsql_truetypid(nt, a, t) AS true_typid, + information_schema_tsql._pgtsql_truetypmod(nt, a, t) AS true_typmod, + sys.translate_pg_type_to_tsql(true_typid) AS tsql_type_name + + WHERE (NOT pg_is_other_temp_schema(nc.oid)) + AND a.attnum > 0 AND NOT a.attisdropped + AND c.relkind IN ('r', 'v', 'p') + AND (pg_has_role(c.relowner, 'USAGE') + OR has_column_privilege(c.oid, a.attnum, + 'SELECT, INSERT, UPDATE, REFERENCES')) + AND ext.dbid = cast(sys.db_id() as oid); + +GRANT SELECT ON information_schema_tsql.columns TO PUBLIC; + +/* + * DOMAINS view + */ + +CREATE OR REPLACE VIEW information_schema_tsql.domains AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "DOMAIN_CATALOG", + CAST(ext.orig_name AS sys.nvarchar(128)) AS "DOMAIN_SCHEMA", + CAST(t.typname AS sys.sysname) AS "DOMAIN_NAME", + CAST(case when is_tbl_type THEN 'table type' ELSE tsql_type_name END AS sys.sysname) AS "DATA_TYPE", + + CAST(information_schema_tsql._pgtsql_char_max_length(tsql_type_name, t.typtypmod) + AS int) + AS "CHARACTER_MAXIMUM_LENGTH", + + CAST(information_schema_tsql._pgtsql_char_octet_length(tsql_type_name, t.typtypmod) + AS int) + AS "CHARACTER_OCTET_LENGTH", + + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_CATALOG", + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_SCHEMA", + + /* Returns Babelfish specific collation name. */ + CAST( + CASE co.collname + WHEN 'default' THEN current_setting('babelfishpg_tsql.server_collation_name') + ELSE co.collname + END + AS sys.nvarchar(128)) AS "COLLATION_NAME", + + CAST(null AS sys.varchar(6)) AS "CHARACTER_SET_CATALOG", + CAST(null AS sys.varchar(3)) AS "CHARACTER_SET_SCHEMA", + /* + * TODO: We need to first create mapping of collation name to char-set name; + * Until then return null. + */ + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_NAME", + + CAST(information_schema_tsql._pgtsql_numeric_precision(tsql_type_name, t.typbasetype, t.typtypmod) + AS sys.tinyint) + AS "NUMERIC_PRECISION", + + CAST(information_schema_tsql._pgtsql_numeric_precision_radix(tsql_type_name, t.typbasetype, t.typtypmod) + AS smallint) + AS "NUMERIC_PRECISION_RADIX", + + CAST(information_schema_tsql._pgtsql_numeric_scale(tsql_type_name, t.typbasetype, t.typtypmod) + AS int) + AS "NUMERIC_SCALE", + + CAST(information_schema_tsql._pgtsql_datetime_precision(tsql_type_name, t.typtypmod) + AS smallint) + AS "DATETIME_PRECISION", + + CAST(case when is_tbl_type THEN NULL ELSE t.typdefault END AS sys.nvarchar(4000)) AS "DOMAIN_DEFAULT" + + FROM (pg_type t JOIN sys.pg_namespace_ext nc ON t.typnamespace = nc.oid) + LEFT JOIN pg_collation co ON t.typcollation = co.oid + LEFT JOIN sys.babelfish_namespace_ext ext on nc.nspname = ext.nspname, + sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_type_name, + sys.is_table_type(t.typrelid) as is_tbl_type + + WHERE (pg_has_role(t.typowner, 'USAGE') + OR has_type_privilege(t.oid, 'USAGE')) + AND (t.typtype = 'd' OR is_tbl_type) + AND ext.dbid = cast(sys.db_id() as oid); + +GRANT SELECT ON information_schema_tsql.domains TO PUBLIC; + +/* + * TABLES view + */ + +CREATE VIEW information_schema_tsql.tables AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "TABLE_CATALOG", + CAST(ext.orig_name AS sys.nvarchar(128)) AS "TABLE_SCHEMA", + CAST( + CASE WHEN c.reloptions[1] LIKE 'bbf_original_rel_name%' THEN substring(c.reloptions[1], 23) + ELSE c.relname END + AS sys._ci_sysname) AS "TABLE_NAME", + + CAST( + CASE WHEN c.relkind IN ('r', 'p') THEN 'BASE TABLE' + WHEN c.relkind = 'v' THEN 'VIEW' + ELSE null END + AS varchar(10)) AS "TABLE_TYPE" + + FROM sys.pg_namespace_ext nc JOIN pg_class c ON (nc.oid = c.relnamespace) + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on nc.nspname = ext.nspname + + WHERE c.relkind IN ('r', 'v', 'p') + AND (NOT pg_is_other_temp_schema(nc.oid)) + AND (pg_has_role(c.relowner, 'USAGE') + OR has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') + OR has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES') ) + AND ext.dbid = cast(sys.db_id() as oid); + +GRANT SELECT ON information_schema_tsql.tables TO PUBLIC; + +/* + * TABLE_CONSTRAINTS view + */ + +CREATE VIEW information_schema_tsql.table_constraints AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "CONSTRAINT_CATALOG", + CAST(extc.orig_name AS sys.nvarchar(128)) AS "CONSTRAINT_SCHEMA", + CAST(c.conname AS sys.sysname) AS "CONSTRAINT_NAME", + CAST(nr.dbname AS sys.nvarchar(128)) AS "TABLE_CATALOG", + CAST(extr.orig_name AS sys.nvarchar(128)) AS "TABLE_SCHEMA", + CAST(r.relname AS sys.sysname) AS "TABLE_NAME", + CAST( + CASE c.contype WHEN 'c' THEN 'CHECK' + WHEN 'f' THEN 'FOREIGN KEY' + WHEN 'p' THEN 'PRIMARY KEY' + WHEN 'u' THEN 'UNIQUE' END + AS sys.varchar(11)) AS "CONSTRAINT_TYPE", + CAST('NO' AS sys.varchar(2)) AS "IS_DEFERRABLE", + CAST('NO' AS sys.varchar(2)) AS "INITIALLY_DEFERRED" + + FROM sys.pg_namespace_ext nc LEFT OUTER JOIN sys.babelfish_namespace_ext extc ON nc.nspname = extc.nspname, + sys.pg_namespace_ext nr LEFT OUTER JOIN sys.babelfish_namespace_ext extr ON nr.nspname = extr.nspname, + pg_constraint c, + pg_class r + + WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace + AND c.conrelid = r.oid + AND c.contype NOT IN ('t', 'x') + AND r.relkind IN ('r', 'p') + AND (NOT pg_is_other_temp_schema(nr.oid)) + AND (pg_has_role(r.relowner, 'USAGE') + OR has_table_privilege(r.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') + OR has_any_column_privilege(r.oid, 'SELECT, INSERT, UPDATE, REFERENCES') ) + AND extc.dbid = cast(sys.db_id() as oid); + +GRANT SELECT ON information_schema_tsql.table_constraints TO PUBLIC; + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); diff --git a/contrib/babelfishpg_tsql/sql/ownership.sql b/contrib/babelfishpg_tsql/sql/ownership.sql new file mode 100644 index 00000000000..853c3506a7b --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/ownership.sql @@ -0,0 +1,391 @@ +-- 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 sys.babel_drop_all_users() +LANGUAGE C +AS 'babelfishpg_tsql', 'drop_all_users'; + +-- The items in initialize_babel_extras procedure need to be initialized or created +-- during babelfish initialization. They depend on the core babelfish to be initialized first. +CREATE OR REPLACE PROCEDURE initialize_babel_extras() +LANGUAGE plpgsql +AS $$ +BEGIN + CREATE OR REPLACE PROCEDURE sys.create_xp_qv_in_master_dbo() + LANGUAGE C + AS 'babelfishpg_tsql', 'create_xp_qv_in_master_dbo_internal'; + + CALL sys.create_xp_qv_in_master_dbo(); + ALTER PROCEDURE master_dbo.xp_qv OWNER TO sysadmin; + DROP PROCEDURE sys.create_xp_qv_in_master_dbo; +END +$$; + +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); + CALL sys.initialize_babel_extras(); +END +$$; + +CREATE OR REPLACE PROCEDURE remove_babelfish () +LANGUAGE plpgsql +AS $$ +BEGIN + CALL sys.babel_drop_all_dbs(); + CALL sys.babel_drop_all_users(); + 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', ''); +SELECT pg_catalog.pg_extension_config_dump('sys.babelfish_configurations', ''); + +-- 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; + +-- USER extension +CREATE TABLE sys.babelfish_authid_user_ext ( +rolname NAME NOT NULL, +login_name NAME NOT NULL, +type CHAR(1) NOT NULL DEFAULT 'S', +owning_principal_id INT, +is_fixed_role INT NOT NULL DEFAULT 0, +authentication_type INT, +default_language_lcid INT, +allow_encrypted_value_modifications INT NOT NULL DEFAULT 0, +create_date timestamptz NOT NULL, +modify_date timestamptz NOT NULL, +orig_username SYS.NVARCHAR(128) NOT NULL, +database_name SYS.NVARCHAR(128) NOT NULL, +default_schema_name SYS.NVARCHAR(128) NOT NULL, +default_language_name SYS.NVARCHAR(128), +authentication_type_desc SYS.NVARCHAR(60), +PRIMARY KEY (rolname)); + +CREATE INDEX babelfish_authid_user_ext_login_db_idx ON sys.babelfish_authid_user_ext (login_name, database_name); + +GRANT SELECT ON sys.babelfish_authid_user_ext TO PUBLIC; + +-- DATABASE_PRINCIPALS +CREATE VIEW sys.database_principals AS SELECT +Ext.orig_username AS name, +CAST(Base.OID AS INT) AS principal_id, +Ext.type, +CAST(CASE WHEN Ext.type = 'S' THEN 'SQL_USER' ELSE NULL END AS SYS.NVARCHAR(60)) AS type_desc, +Ext.default_schema_name, +Ext.create_date, +Ext.modify_date, +Ext.owning_principal_id, +CAST(CAST(Base2.oid AS INT) AS SYS.VARBINARY(85)) AS SID, +CAST(Ext.is_fixed_role AS SYS.BIT) AS is_fixed_role, +Ext.authentication_type, +Ext.authentication_type_desc, +Ext.default_language_name, +Ext.default_language_lcid, +CAST(Ext.allow_encrypted_value_modifications AS SYS.BIT) AS allow_encrypted_value_modifications +FROM pg_catalog.pg_authid AS Base INNER JOIN sys.babelfish_authid_user_ext AS Ext +ON Base.rolname = Ext.rolname +LEFT OUTER JOIN pg_catalog.pg_roles Base2 +ON Ext.login_name = Base2.rolname +WHERE Ext.database_name = DB_NAME(); + +GRANT SELECT ON sys.database_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(return_consistency boolean default false) +RETURNS table ( + object_type varchar(32), + schema_name varchar(128), + object_name varchar(128), + detail jsonb +) AS 'babelfishpg_tsql', 'babelfish_inconsistent_metadata' LANGUAGE C; + + +CREATE OR REPLACE FUNCTION sys.role_id(role_name SYS.SYSNAME) +RETURNS INT +AS 'babelfishpg_tsql', 'role_id' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.role_id TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/sys.sql b/contrib/babelfishpg_tsql/sql/sys.sql new file mode 100644 index 00000000000..5120edae96b --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys.sql @@ -0,0 +1,143 @@ +/* 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()::pg_catalog.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; + +CREATE TABLE sys.babelfish_configurations ( + configuration_id INT, + name sys.nvarchar(35), + value sys.sql_variant, + minimum sys.sql_variant, + maximum sys.sql_variant, + value_in_use sys.sql_variant, + description sys.nvarchar(255), + is_dynamic sys.BIT, + is_advanced sys.BIT, + comment_syscurconfigs sys.nvarchar(255), + comment_sysconfigures sys.nvarchar(255) +) WITH (OIDS = FALSE); diff --git a/contrib/babelfishpg_tsql/sql/sys_babelfish_configurations.sql b/contrib/babelfishpg_tsql/sql/sys_babelfish_configurations.sql new file mode 100644 index 00000000000..f1545a9ceeb --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_babelfish_configurations.sql @@ -0,0 +1,14 @@ +-- The value and value_in_use is set to 1 because SSMS-Babelfish connectivity requires it. +INSERT INTO sys.babelfish_configurations + VALUES (16387, + 'SMO and DMO XPs', + 1, + 0, + 1, + 1, + 'Enable or disable SMO and DMO XPs', + sys.bitin('1'), + sys.bitin('1'), + 'Enable or disable SMO and DMO XPs', + 'Enable or disable SMO and DMO XPs' + ); 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..b3dee3b7311 --- /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'; \ No newline at end of file diff --git a/contrib/babelfishpg_tsql/sql/sys_functions.sql b/contrib/babelfishpg_tsql/sql/sys_functions.sql new file mode 100644 index 00000000000..bc45821b575 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_functions.sql @@ -0,0 +1,2002 @@ +-- 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; + +-- Since SIDs are currently not supported in Babelfish, this essentially behaves the same as suser_name but +-- with a different input data type +CREATE OR REPLACE FUNCTION sys.suser_sname(IN server_user_sid SYS.VARBINARY(85) DEFAULT NULL) +RETURNS SYS.NVARCHAR(128) +AS $$ + SELECT sys.suser_name(CAST(server_user_sid AS INT)); +$$ +LANGUAGE SQL 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; + +-- Since SIDs are currently not supported in Babelfish, this essentially behaves the same as suser_id but +-- with different input/output data types. The second argument will be ignored as its functionality is not supported +CREATE OR REPLACE FUNCTION sys.suser_sid(IN login SYS.SYSNAME DEFAULT NULL, IN Param2 INT DEFAULT NULL) +RETURNS SYS.VARBINARY(85) +AS $$ + SELECT CAST(CAST(sys.suser_id(login) AS INT) AS SYS.VARBINARY(85)); +$$ +LANGUAGE SQL 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(VARIADIC arr TEXT[]) +RETURNS INTEGER +AS 'babelfishpg_tsql', 'checksum' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +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 SYSNAME) RETURNS INTEGER AS +'babelfishpg_tsql', 'has_dbaccess' +LANGUAGE C STRICT; + +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; + +-- Added for BABEL-1544 +CREATE OR REPLACE FUNCTION sys.len(expr sys.BBF_VARBINARY) RETURNS INTEGER AS +'babelfishpg_common', 'varbinary_length' +STRICT +LANGUAGE c IMMUTABLE PARALLEL SAFE; + +-- 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; + +CREATE OR REPLACE FUNCTION sys.servicename() + 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.get_current_full_xact_id() + RETURNS XID8 AS 'babelfishpg_tsql' LANGUAGE C STABLE; + +CREATE OR REPLACE FUNCTION sys.DBTS() +RETURNS sys.ROWVERSION AS +$$ +DECLARE + eh_setting text; +BEGIN + eh_setting = (select s.setting FROM pg_catalog.pg_settings s where name = 'babelfishpg_tsql.escape_hatch_rowversion'); + IF eh_setting = 'strict' THEN + RAISE EXCEPTION 'DBTS is not currently supported in Babelfish. please use babelfishpg_tsql.escape_hatch_rowversion to ignore'; + ELSE + RETURN pg_snapshot_xmin(pg_current_snapshot())::sys.ROWVERSION; + END IF; +END; +$$ +STRICT +LANGUAGE plpgsql; + +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 = 'babelfishpg_tsql.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.schema_id() +RETURNS INT +LANGUAGE plpgsql +STRICT +AS $$ +BEGIN + RETURN (select oid from sys.pg_namespace_ext where nspname = (select current_schema()))::INT; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.schema_id() 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; + +-- For getting host os from PG_VERSION_STR +CREATE OR REPLACE FUNCTION sys.get_host_os() +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'host_os' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.tsql_stat_get_activity( + IN view_name text, + OUT procid int, + OUT client_version int, + OUT library_name VARCHAR(32), + OUT language VARCHAR(128), + OUT quoted_identifier bool, + OUT arithabort bool, + OUT ansi_null_dflt_on bool, + OUT ansi_defaults bool, + OUT ansi_warnings bool, + OUT ansi_padding bool, + OUT ansi_nulls bool, + OUT concat_null_yields_null bool, + OUT textsize int, + OUT datefirst int, + OUT lock_timeout int, + OUT transaction_isolation int2, + OUT client_pid int, + OUT row_count bigint, + OUT error int, + OUT trancount int, + OUT protocol_version int, + OUT packet_size int, + OUT encrypyt_option VARCHAR(40), + OUT database_id int2) +AS 'babelfishpg_tsql', 'tsql_stat_get_activity' +LANGUAGE C VOLATILE STRICT; + +CREATE OR REPLACE FUNCTION sys.is_table_type(object_id oid) RETURNS bool AS +$BODY$ +SELECT + EXISTS( + SELECT 1 + FROM pg_catalog.pg_type pt + INNER JOIN pg_catalog.pg_depend dep + ON pt.typrelid = dep.objid + join sys.schemas sch on pt.typnamespace = sch.schema_id + JOIN pg_catalog.pg_class pc ON pc.oid = dep.objid + WHERE pt.typtype = 'c' AND dep.deptype = 'i' AND pt.typrelid = object_id AND pc.relkind = 'r'); +$BODY$ +LANGUAGE SQL VOLATILE STRICT; + +-- JSON Functions +CREATE OR REPLACE FUNCTION sys.isjson(json_string text) +RETURNS INTEGER +AS 'babelfishpg_tsql', 'tsql_isjson' LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.json_value(json_string text, path text) +RETURNS sys.NVARCHAR(4000) +AS 'babelfishpg_tsql', 'tsql_json_value' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.json_query(json_string text, path text default '$') +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'tsql_json_query' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.sp_datatype_info_helper( + IN odbcVer smallint, + IN is_100 bool, + OUT TYPE_NAME VARCHAR(20), + OUT DATA_TYPE INT, + OUT "PRECISION" BIGINT, + OUT LITERAL_PREFIX VARCHAR(20), + OUT LITERAL_SUFFIX VARCHAR(20), + OUT CREATE_PARAMS VARCHAR(20), + OUT NULLABLE INT, + OUT CASE_SENSITIVE INT, + OUT SEARCHABLE INT, + OUT UNSIGNED_ATTRIBUTE INT, + OUT MONEY INT, + OUT AUTO_INCREMENT INT, + OUT LOCAL_TYPE_NAME VARCHAR(20), + OUT MINIMUM_SCALE INT, + OUT MAXIMUM_SCALE INT, + OUT SQL_DATA_TYPE INT, + OUT SQL_DATETIME_SUB INT, + OUT NUM_PREC_RADIX INT, + OUT INTERVAL_PRECISION INT, + OUT USERTYPE INT, + OUT LENGTH INT, + OUT SS_DATA_TYPE smallint, +-- below column is added in order to join PG's information_schema.columns for sys.sp_columns_100_view + OUT PG_TYPE_NAME VARCHAR(20) +) +AS 'babelfishpg_tsql', 'sp_datatype_info_helper' +LANGUAGE C IMMUTABLE STRICT; 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..2e034a88405 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_procedures.sql @@ -0,0 +1,166 @@ +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); + default_value text; + 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; + + -- Each setting has a boot_val which is the wired-in default value + -- Assuming that escape hatches cannot be modified using ALTER SYTEM/config file + -- we are setting the boot_val as the default value for the escape hatches + SELECT boot_val INTO default_value FROM pg_catalog.pg_settings WHERE name = eh_name; + IF lower("@option_value") = 'default' THEN + PERFORM pg_catalog.set_config(eh_name, default_value, 'false'); + ELSE + PERFORM pg_catalog.set_config(eh_name, "@option_value", 'false'); + END IF; + IF server THEN + SELECT current_user INTO prev_user; + PERFORM sys.babelfish_set_role(session_user); + IF lower("@option_value") = 'default' THEN + EXECUTE format('ALTER DATABASE %s SET %s = %s', CURRENT_DATABASE(), eh_name, default_value); + ELSE + -- 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"); + END IF; + 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..11639eab9bb --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/sys_views.sql @@ -0,0 +1,1820 @@ +/* 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 + , sch.schema_id 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 sys.schemas sch on t.relnamespace = sch.schema_id +where t.relpersistence in ('p', 'u', 't') +and t.relkind = 'r' +and not sys.is_table_type(t.oid) +and has_schema_privilege(sch.schema_id, 'USAGE') +and has_table_privilege(t.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +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 + , sch.schema_id 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 sys.schemas sch on t.relnamespace = sch.schema_id +where t.relkind = 'v' +and has_schema_privilege(sch.schema_id, 'USAGE') +and has_table_privilege(t.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +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 +and (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +-- 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_schema_privilege(s.oid, 'USAGE') +and has_column_privilege(quote_ident(s.nspname) ||'.'||quote_ident(c.relname), a.attname, 'SELECT,INSERT,UPDATE,REFERENCES') +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 (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +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 FUNCTION sys.tsql_type_scale_helper(IN type TEXT, IN typemod INT, IN return_null_for_rest bool) RETURNS sys.TINYINT +AS $$ +DECLARE + scale INT; +BEGIN + IF type IS NULL THEN + RETURN -1; + END IF; + + IF typemod = -1 THEN + CASE type + WHEN 'date' THEN scale = 0; + WHEN 'datetime' THEN scale = 3; + WHEN 'smalldatetime' THEN scale = 0; + WHEN 'datetime2' THEN scale = 6; + WHEN 'datetimeoffset' THEN scale = 6; + WHEN 'decimal' THEN scale = 38; + WHEN 'numeric' THEN scale = 38; + WHEN 'money' THEN scale = 4; + WHEN 'smallmoney' THEN scale = 4; + WHEN 'time' THEN scale = 6; + WHEN 'tinyint' THEN scale = 0; + ELSE + IF return_null_for_rest + THEN scale = NULL; + ELSE scale = 0; + END IF; + END CASE; + RETURN scale; + END IF; + + CASE type + WHEN 'decimal' THEN scale = (typemod - 4) & 65535; + WHEN 'numeric' THEN scale = (typemod - 4) & 65535; + WHEN 'smalldatetime' THEN scale = 0; + WHEN 'datetime2' THEN + CASE typemod + WHEN 0 THEN scale = 0; + WHEN 1 THEN scale = 1; + WHEN 2 THEN scale = 2; + WHEN 3 THEN scale = 3; + WHEN 4 THEN scale = 4; + WHEN 5 THEN scale = 5; + WHEN 6 THEN scale = 6; + -- typemod = 7 is not possible for datetime2 in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN scale = 7; + END CASE; + WHEN 'datetimeoffset' THEN + CASE typemod + WHEN 0 THEN scale = 0; + WHEN 1 THEN scale = 1; + WHEN 2 THEN scale = 2; + WHEN 3 THEN scale = 3; + WHEN 4 THEN scale = 4; + WHEN 5 THEN scale = 5; + WHEN 6 THEN scale = 6; + -- typemod = 7 is not possible for datetimeoffset in Babelfish + -- but adding the case just in case we support it in future + WHEN 7 THEN scale = 7; + END CASE; + WHEN 'time' THEN + CASE typemod + WHEN 0 THEN scale = 0; + WHEN 1 THEN scale = 1; + WHEN 2 THEN scale = 2; + WHEN 3 THEN scale = 3; + WHEN 4 THEN scale = 4; + WHEN 5 THEN scale = 5; + WHEN 6 THEN scale = 6; + -- typemod = 7 is not possible for time in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN scale = 7; + END CASE; + ELSE + IF return_null_for_rest + THEN scale = NULL; + ELSE scale = 0; + END IF; + END CASE; + RETURN scale; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.tsql_type_precision_helper(IN type TEXT, IN typemod INT) RETURNS sys.TINYINT +AS $$ +DECLARE + precision INT; +BEGIN + IF type IS NULL THEN + RETURN -1; + END IF; + + IF typemod = -1 THEN + CASE type + WHEN 'bigint' THEN precision = 19; + WHEN 'bit' THEN precision = 1; + WHEN 'date' THEN precision = 10; + WHEN 'datetime' THEN precision = 23; + WHEN 'datetime2' THEN precision = 26; + WHEN 'datetimeoffset' THEN precision = 33; + WHEN 'decimal' THEN precision = 38; + WHEN 'numeric' THEN precision = 38; + WHEN 'float' THEN precision = 53; + WHEN 'int' THEN precision = 10; + WHEN 'money' THEN precision = 19; + WHEN 'real' THEN precision = 24; + WHEN 'smalldatetime' THEN precision = 16; + WHEN 'smallint' THEN precision = 5; + WHEN 'smallmoney' THEN precision = 10; + WHEN 'time' THEN precision = 15; + WHEN 'tinyint' THEN precision = 3; + ELSE precision = 0; + END CASE; + RETURN precision; + END IF; + + CASE type + WHEN 'numeric' THEN precision = ((typemod - 4) >> 16) & 65535; + WHEN 'decimal' THEN precision = ((typemod - 4) >> 16) & 65535; + WHEN 'smalldatetime' THEN precision = 16; + WHEN 'datetime2' THEN + CASE typemod + WHEN 0 THEN precision = 19; + WHEN 1 THEN precision = 21; + WHEN 2 THEN precision = 22; + WHEN 3 THEN precision = 23; + WHEN 4 THEN precision = 24; + WHEN 5 THEN precision = 25; + WHEN 6 THEN precision = 26; + -- typemod = 7 is not possible for datetime2 in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN precision = 27; + END CASE; + WHEN 'datetimeoffset' THEN + CASE typemod + WHEN 0 THEN precision = 26; + WHEN 1 THEN precision = 28; + WHEN 2 THEN precision = 29; + WHEN 3 THEN precision = 30; + WHEN 4 THEN precision = 31; + WHEN 5 THEN precision = 32; + WHEN 6 THEN precision = 33; + -- typemod = 7 is not possible for datetimeoffset in Babelfish + -- but adding the case just in case we support it in future + WHEN 7 THEN precision = 34; + END CASE; + WHEN 'time' THEN + CASE typemod + WHEN 0 THEN precision = 8; + WHEN 1 THEN precision = 10; + WHEN 2 THEN precision = 11; + WHEN 3 THEN precision = 12; + WHEN 4 THEN precision = 13; + WHEN 5 THEN precision = 14; + WHEN 6 THEN precision = 15; + -- typemod = 7 is not possible for time in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN precision = 16; + END CASE; + ELSE precision = 0; + END CASE; + RETURN precision; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + + +CREATE OR REPLACE FUNCTION sys.tsql_type_max_length_helper(IN type TEXT, IN typelen INT, IN typemod INT, IN for_sys_types boolean DEFAULT false) +RETURNS SMALLINT +AS $$ +DECLARE + max_length SMALLINT; + precision INT; +BEGIN + -- unknown tsql type + IF type IS NULL THEN + RETURN CAST(typelen as SMALLINT); + END IF; + + IF typelen != -1 THEN + CASE type + WHEN 'tinyint' THEN max_length = 1; + WHEN 'date' THEN max_length = 3; + WHEN 'smalldatetime' THEN max_length = 4; + WHEN 'smallmoney' THEN max_length = 4; + WHEN 'datetime2' THEN + IF typemod = -1 THEN max_length = 8; + ELSIF typemod <= 2 THEN max_length = 6; + ELSIF typemod <= 4 THEN max_length = 7; + ELSEIF typemod <= 7 THEN max_length = 8; + -- typemod = 7 is not possible for datetime2 in Babel + END IF; + WHEN 'datetimeoffset' THEN + IF typemod = -1 THEN max_length = 10; + ELSIF typemod <= 2 THEN max_length = 8; + ELSIF typemod <= 4 THEN max_length = 9; + ELSIF typemod <= 7 THEN max_length = 10; + -- typemod = 7 is not possible for datetimeoffset in Babel + END IF; + WHEN 'time' THEN + IF typemod = -1 THEN max_length = 5; + ELSIF typemod <= 2 THEN max_length = 3; + ELSIF typemod <= 4 THEN max_length = 4; + ELSIF typemod <= 7 THEN max_length = 5; + END IF; + WHEN 'timestamp' THEN max_length = 8; + ELSE max_length = typelen; + END CASE; + RETURN max_length; + END IF; + + IF typemod = -1 THEN + CASE + WHEN type in ('image', 'text', 'ntext') THEN max_length = 16; + WHEN type = 'sql_variant' THEN max_length = 8016; + WHEN type in ('varbinary', 'varchar', 'nvarchar') THEN + IF for_sys_types THEN max_length = 8000; + ELSE max_length = -1; + END IF; + WHEN type in ('binary', 'char', 'bpchar', 'nchar') THEN max_length = 8000; + WHEN type in ('decimal', 'numeric') THEN max_length = 17; + ELSE max_length = typemod; + END CASE; + RETURN max_length; + END IF; + + CASE + WHEN type in ('char', 'bpchar', 'varchar', 'binary', 'varbinary') THEN max_length = typemod - 4; + WHEN type in ('nchar', 'nvarchar') THEN max_length = (typemod - 4) * 2; + WHEN type = 'sysname' THEN max_length = (typemod - 4) * 2; + WHEN type in ('numeric', 'decimal') THEN + precision = ((typemod - 4) >> 16) & 65535; + IF precision >= 1 and precision <= 9 THEN max_length = 5; + ELSIF precision <= 19 THEN max_length = 9; + ELSIF precision <= 28 THEN max_length = 13; + ELSIF precision <= 38 THEN max_length = 17; + ELSE max_length = typelen; + END IF; + ELSE + max_length = typemod; + END CASE; + RETURN max_length; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +-- internal function in order to workaround BABEL-1597 +CREATE OR REPLACE 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), + CASE + WHEN tsql_type_name IS NOT NULL OR t.typbasetype = 0 THEN + -- either tsql or PG base type + CAST(a.atttypid AS int) + ELSE + CAST(t.typbasetype AS int) + END, + CAST(a.atttypid AS int), + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, a.atttypmod) + ELSE + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod) + ELSE + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod, false) + ELSE + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod, false) + END, + 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 sys.schemas sch on c.relnamespace = sch.schema_id + INNER JOIN sys.pg_namespace_ext ext on sch.schema_id = ext.oid + INNER JOIN information_schema.columns isc ON c.relname = isc.table_name AND ext.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 + , sys.translate_pg_type_to_tsql(a.atttypid) AS tsql_type_name + , sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_base_type_name + 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 has_schema_privilege(sch.schema_id, 'USAGE') + AND has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES') + union all + -- system tables information + SELECT CAST(c.oid AS int), + CAST(a.attname AS sys.sysname), + CAST(a.attnum AS int), + CASE + WHEN tsql_type_name IS NOT NULL OR t.typbasetype = 0 THEN + -- either tsql or PG base type + CAST(a.atttypid AS int) + ELSE + CAST(t.typbasetype AS int) + END, + CAST(a.atttypid AS int), + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, a.atttypmod) + ELSE + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod) + ELSE + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod, false) + ELSE + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod, false) + END, + 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 nsp ON (nsp.oid = c.relnamespace and nsp.nspname = 'sys') + INNER JOIN information_schema.columns isc ON c.relname = isc.table_name AND nsp.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 + , sys.translate_pg_type_to_tsql(a.atttypid) AS tsql_type_name + , sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_base_type_name + WHERE NOT a.attisdropped + AND a.attnum > 0 + AND c.relkind = 'r' + AND has_schema_privilege(nsp.oid, 'USAGE') + AND has_column_privilege(a.attrelid, 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' +and (c.connamespace in (select schema_id from sys.schemas)) +and has_schema_privilege(c.connamespace, 'USAGE'); +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 + , sch.schema_id 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 sys.schemas sch on sch.schema_id = c.connamespace +where has_schema_privilege(sch.schema_id, 'USAGE') +and c.contype = 'f'; +GRANT SELECT ON sys.foreign_keys TO PUBLIC; + +create or replace view sys.identity_columns AS +select out_object_id::bigint 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 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::character varying(60) as generated_always_type_desc + , out_encryption_type::integer as encryption_type + , out_encryption_type_desc::character varying(64) as encryption_type_desc + , out_encryption_algorithm_name::character varying as encryption_algorithm_name + , out_column_encryption_key_id::integer as column_encryption_key_id + , out_column_encryption_key_database_name::character varying as column_encryption_key_database_name + , out_is_hidden::integer as is_hidden + , out_is_masked::integer as is_masked + , sys.ident_seed(OBJECT_NAME(sc.out_object_id))::bigint as seed_value + , sys.ident_incr(OBJECT_NAME(sc.out_object_id))::bigint as increment_value + , sys.babelfish_get_sequence_value(pg_get_serial_sequence(quote_ident(ext.nspname)||'.'||quote_ident(c.relname), a.attname)) as last_value +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_class c on c.oid = a.attrelid +inner join sys.pg_namespace_ext ext on ext.oid = c.relnamespace +where not a.attisdropped +and sc.out_is_identity::integer = 1 +and pg_get_serial_sequence(quote_ident(ext.nspname)||'.'||quote_ident(c.relname), a.attname) is not null +and has_sequence_privilege(pg_get_serial_sequence(quote_ident(ext.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 sys.schemas sch on c.relnamespace = sch.schema_id +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 has_schema_privilege(sch.schema_id, 'USAGE'); +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 + , sch.schema_id 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 sys.schemas sch on sch.schema_id = c.connamespace +where has_schema_privilege(sch.schema_id, 'USAGE') +and c.contype in ('p', 'u'); +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 + , sch.schema_id as schema_id + , cast (case when tr.tgrelid is not null + then tr.tgrelid + else 0 end as int) + as parent_object_id + , case p.prokind + when 'p' then 'P'::varchar(2) + when 'a' then 'AF'::varchar(2) + else + case format_type(p.prorettype, null) when 'trigger' + then 'TR'::varchar(2) + else 'FN'::varchar(2) + end + end as type + , case p.prokind + when 'p' then 'SQL_STORED_PROCEDURE'::varchar(60) + when 'a' then 'AGGREGATE_FUNCTION'::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 sys.schemas sch on sch.schema_id = p.pronamespace +left join pg_trigger tr on tr.tgfoid = p.oid +where has_schema_privilege(sch.schema_id, 'USAGE') +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 sys.schemas s on s.schema_id = p.pronamespace +inner join pg_type t on t.oid = p.prorettype +left join pg_collation c on c.oid = t.typcollation +where has_schema_privilege(s.schema_id, 'USAGE') +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_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 (c.connamespace in (select schema_id from sys.schemas)) +and has_schema_privilege(c.connamespace, 'USAGE'); +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 + , coalesce(t.database_id, 0)::oid 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 sys.tsql_stat_get_activity('sessions') as t on a.pid = t.procid +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 + where a.datname = current_database(); /* current physical database will always be babelfish database */ +GRANT SELECT ON sys.sysprocesses TO PUBLIC; + +create or replace view sys.types As +-- For System types +select tsql_type_name as name + , t.oid as system_type_id + , t.oid as user_type_id + , s.oid as schema_id + , cast(NULL as INT) as principal_id + , sys.tsql_type_max_length_helper(tsql_type_name, t.typlen, t.typtypmod, true) as max_length + , cast(sys.tsql_type_precision_helper(tsql_type_name, t.typtypmod) as int) as precision + , cast(sys.tsql_type_scale_helper(tsql_type_name, t.typtypmod, false) as int) as scale + , CASE c.collname + WHEN 'default' THEN cast(current_setting('babelfishpg_tsql.server_collation_name') as name) + ELSE c.collname + END as collation_name + , case when typnotnull then 0 else 1 end as is_nullable + , 0 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 +, sys.translate_pg_type_to_tsql(t.oid) AS tsql_type_name +where tsql_type_name IS NOT NULL +and pg_type_is_visible(t.oid) +and (s.nspname = 'pg_catalog' OR s.nspname = 'sys') +union all +-- For User Defined Types +select cast(t.typname as text) as name + , t.typbasetype as system_type_id + , t.oid as user_type_id + , s.oid as schema_id + , null::integer as principal_id + , case when is_tbl_type then -1::smallint else sys.tsql_type_max_length_helper(tsql_base_type_name, t.typlen, t.typtypmod) end as max_length + , case when is_tbl_type then 0::smallint else cast(sys.tsql_type_precision_helper(tsql_base_type_name, t.typtypmod) as int) end as precision + , case when is_tbl_type then 0::smallint else cast(sys.tsql_type_scale_helper(tsql_base_type_name, t.typtypmod, false) as int) end as scale + , CASE c.collname + WHEN 'default' THEN cast(current_setting('babelfishpg_tsql.server_collation_name') as name) + ELSE c.collname + END as collation_name + , case when is_tbl_type then 0 + else case when typnotnull then 0 else 1 end + end + as is_nullable + -- CREATE TYPE ... FROM is implemented as CREATE DOMAIN in babel + , 1 as is_user_defined + , 0 as is_assembly_type + , 0 as default_object_id + , 0 as rule_object_id + , case when is_tbl_type then 1 else 0 end as is_table_type +from pg_type t +inner join pg_namespace s on s.oid = t.typnamespace +join sys.schemas sch on t.typnamespace = sch.schema_id +left join pg_collation c on c.oid = t.typcollation +, sys.translate_pg_type_to_tsql(t.oid) AS tsql_type_name +, sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_base_type_name +, sys.is_table_type(t.typrelid) as is_tbl_type +-- we want to show details of user defined datatypes created under babelfish database +where tsql_type_name IS NULL +and + ( + -- show all user defined datatypes created under babelfish database except table types + t.typtype = 'd' + or + -- only for table types + sys.is_table_type(t.typrelid) + ); +GRANT SELECT ON sys.types TO PUBLIC; + +create or replace view sys.table_types as +select st.* + , pt.typrelid::int as type_table_object_id + , 0::sys.bit as is_memory_optimized -- return 0 until we support in-memory tables +from sys.types st +inner join pg_catalog.pg_type pt on st.user_type_id = pt.oid +where is_table_type = 1; +GRANT SELECT ON sys.table_types TO PUBLIC; + +create or replace view sys.default_constraints +AS +select CAST(('DF_' || tab.name || '_' || d.oid) as sys.sysname) as name + , d.oid as object_id + , null::int as principal_id + , tab.schema_id 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_attribute a on a.attrelid = d.adrelid and d.adnum = a.attnum +inner join sys.tables tab on d.adrelid = tab.object_id +WHERE a.atthasdef = 't' and a.attgenerated = '' +AND has_schema_privilege(tab.schema_id, 'USAGE') +AND has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +GRANT SELECT ON sys.default_constraints TO PUBLIC; + +CREATE or replace VIEW sys.check_constraints AS +SELECT CAST(c.conname as sys.sysname) as name + , oid::integer as object_id + , NULL::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 +INNER JOIN sys.schemas s on c.connamespace = s.schema_id +WHERE has_schema_privilege(s.schema_id, 'USAGE') +AND c.contype = 'c' and c.conrelid != 0; +GRANT SELECT ON sys.check_constraints TO PUBLIC; + +create or replace 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 +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 +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 +where p.type = 'PK' +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 + def.name::name + , def.object_id + , def.principal_id + , def.schema_id + , def.parent_object_id + , def.type + , def.type_desc + , def.create_date + , def.modified_date as modify_date + , def.is_ms_shipped::int + , def.is_published::int + , def.is_schema_published::int + from sys.default_constraints def +union all +select + chk.name::name + , chk.object_id + , chk.principal_id + , chk.schema_id + , chk.parent_object_id + , chk.type + , chk.type_desc + , chk.create_date + , chk.modify_date + , chk.is_ms_shipped::int + , chk.is_published::int + , chk.is_schema_published::int + from sys.check_constraints chk +union all +select + p.relname as name + ,p.oid as object_id + , null::integer as principal_id + , s.schema_id 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 sys.schemas s on s.schema_id = p.relnamespace +and p.relkind = 'S' +and has_schema_privilege(s.schema_id, 'USAGE') +union all +select + ('TT_' || tt.name || '_' || tt.type_table_object_id)::name as name + , tt.type_table_object_id as object_id + , tt.principal_id as principal_id + , tt.schema_id as schema_id + , 0 as parent_object_id + , 'TT'::varchar(2) as type + , 'TABLE_TYPE'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 1 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from sys.table_types tt; +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 or replace view sys.all_objects as +-- details of user defined and system tables +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' as type + , 'USER_TABLE' 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 t inner join pg_namespace s on s.oid = t.relnamespace +where t.relpersistence in ('p', 'u', 't') +and t.relkind = 'r' +and (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and has_table_privilege(t.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER') +union all +-- details of user defined and system views +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 +-- details of user defined and system foreign key constraints +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' as type + , 'FOREIGN_KEY_CONSTRAINT' + , 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_constraint c +inner join pg_namespace s on s.oid = c.connamespace +where (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and c.contype = 'f' +union all +-- details of user defined and system primary key constraints +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 + , 'PK' as type + , 'PRIMARY_KEY_CONSTRAINT' 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_constraint c +inner join pg_namespace s on s.oid = c.connamespace +where (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and c.contype = 'p' +union all +-- details of user defined and system defined procedures +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 p.prokind + when 'p' then 'P'::varchar(2) + when 'a' then 'AF'::varchar(2) + else + case format_type(p.prorettype, null) when 'trigger' + then 'TR'::varchar(2) + else 'FN'::varchar(2) + end + end as type + , case p.prokind + when 'p' then 'SQL_STORED_PROCEDURE'::varchar(60) + when 'a' then 'AGGREGATE_FUNCTION'::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.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and has_function_privilege(p.oid, 'EXECUTE') +union all +-- details of all default constraints +select + ('DF_' || o.relname || '_' || d.oid)::name 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 modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_catalog.pg_attrdef d +inner join pg_attribute a on a.attrelid = d.adrelid and d.adnum = a.attnum +inner join pg_class o on d.adrelid = o.oid +inner join pg_namespace s on s.oid = o.relnamespace +where a.atthasdef = 't' and a.attgenerated = '' +and (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES') +union all +-- details of all check constraints +select + c.conname::name + , c.oid::integer as object_id + , NULL::integer as principal_id + , c.connamespace::integer as schema_id + , c.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 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_catalog.pg_constraint as c +inner join pg_namespace s on s.oid = c.connamespace +where (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and c.contype = 'c' and c.conrelid != 0 +union all +-- details of user defined and system defined sequence objects +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 (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +union all +-- details of user defined table types +select + ('TT_' || tt.name || '_' || tt.type_table_object_id)::name as name + , tt.type_table_object_id as object_id + , tt.principal_id as principal_id + , tt.schema_id as schema_id + , 0 as parent_object_id + , 'TT'::varchar(2) as type + , 'TABLE_TYPE'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 1 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from sys.table_types tt; +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 = 'sys'; +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_' || tab.name || '_' || d.oid) as sys.sysname) as name + , d.oid as object_id + , null::int as principal_id + , tab.schema_id 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_attribute a on a.attrelid = d.adrelid and d.adnum = a.attnum +inner join sys.tables tab on d.adrelid = tab.object_id +WHERE a.atthasdef = 't' and a.attgenerated = '' +AND has_schema_privilege(tab.schema_id, 'USAGE') +AND has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +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.endpoints +AS +SELECT CAST('TSQL Default TCP' AS sys.sysname) AS name + , CAST(4 AS int) AS endpoint_id + , CAST(1 AS int) AS principal_id + , CAST(2 AS sys.tinyint) AS protocol + , CAST('TCP' AS sys.nvarchar(60)) AS protocol_desc + , CAST(2 AS sys.tinyint) AS type + , CAST('TSQL' AS sys.nvarchar(60)) AS type_desc + , CAST(0 AS tinyint) AS state + , CAST('STARTED' AS sys.nvarchar(60)) AS state_desc + , CAST(0 AS sys.bit) AS is_admin_endpoint; +GRANT SELECT ON sys.endpoints 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 +inner join pg_class c on i.indrelid = c.oid +inner join sys.schemas sch on sch.schema_id = c.relnamespace +where has_schema_privilege(sch.schema_id, 'USAGE') +and has_table_privilege(c.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +GRANT SELECT ON sys.index_columns 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 OR REPLACE 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) +left join sys.schemas sch on sch.schema_id = pgproc.pronamespace +where has_schema_privilege(sch.schema_id, 'USAGE'); +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; + +create or replace view sys.dm_exec_sessions + as + select a.pid as session_id + , a.backend_start::sys.datetime as login_time + , a.client_hostname::sys.nvarchar(128) as host_name + , a.application_name::sys.nvarchar(128) as program_name + , d.client_pid as host_process_id + , d.client_version as client_version + , d.library_name::sys.nvarchar(32) as client_interface_name + , null::sys.varbinary(85) as security_id + , a.usename::sys.nvarchar(128) as login_name + , (select sys.default_domain())::sys.nvarchar(128) as nt_domain + , null::sys.nvarchar(128) as nt_user_name + , a.state::sys.nvarchar(30) as status + , null::sys.nvarchar(128) as context_info + , null::integer as cpu_time + , null::integer as memory_usage + , null::integer as total_scheduled_time + , null::integer as total_elapsed_time + , a.client_port as endpoint_id + , a.query_start::sys.datetime as last_request_start_time + , a.state_change::sys.datetime as last_request_end_time + , null::bigint as "reads" + , null::bigint as "writes" + , null::bigint as logical_reads + , case when a.client_port > 0 then 1::sys.bit else 0::sys.bit end as is_user_process + , d.textsize as text_size + , d.language::sys.nvarchar(128) as language + , 'ymd'::sys.nvarchar(3) as date_format-- Bld 173 lacks support for SET DATEFORMAT and always expects ymd + , d.datefirst::smallint as date_first -- Bld 173 lacks support for SET DATEFIRST and always returns 7 + , CAST(CAST(d.quoted_identifier as integer) as sys.bit) as quoted_identifier + , CAST(CAST(d.arithabort as integer) as sys.bit) as arithabort + , CAST(CAST(d.ansi_null_dflt_on as integer) as sys.bit) as ansi_null_dflt_on + , CAST(CAST(d.ansi_defaults as integer) as sys.bit) as ansi_defaults + , CAST(CAST(d.ansi_warnings as integer) as sys.bit) as ansi_warnings + , CAST(CAST(d.ansi_padding as integer) as sys.bit) as ansi_padding + , CAST(CAST(d.ansi_nulls as integer) as sys.bit) as ansi_nulls + , CAST(CAST(d.concat_null_yields_null as integer) as sys.bit) as concat_null_yields_null + , d.transaction_isolation::smallint as transaction_isolation_level + , d.lock_timeout as lock_timeout + , 0 as deadlock_priority + , d.row_count as row_count + , d.error as prev_error + , null::sys.varbinary(85) as original_security_id + , a.usename::sys.nvarchar(128) as original_login_name + , null::sys.datetime as last_successful_logon + , null::sys.datetime as last_unsuccessful_logon + , null::bigint as unsuccessful_logons + , null::int as group_id + , d.database_id::smallint as database_id + , 0 as authenticating_database_id + , d.trancount as open_transaction_count + from pg_catalog.pg_stat_activity AS a + RIGHT JOIN sys.tsql_stat_get_activity('sessions') AS d ON (a.pid = d.procid); + GRANT SELECT ON sys.dm_exec_sessions TO PUBLIC; + +create or replace view sys.dm_exec_connections + as + select a.pid as session_id + , a.pid as most_recent_session_id + , a.backend_start::sys.datetime as connect_time + , 'TCP'::sys.nvarchar(40) as net_transport + , 'TSQL'::sys.nvarchar(40) as protocol_type + , d.protocol_version as protocol_version + , 4 as endpoint_id + , d.encrypyt_option::sys.nvarchar(40) as encrypt_option + , null::sys.nvarchar(40) as auth_scheme + , null::smallint as node_affinity + , null::int as num_reads + , null::int as num_writes + , null::sys.datetime as last_read + , null::sys.datetime as last_write + , d.packet_size as net_packet_size + , a.client_addr::varchar(48) as client_net_address + , a.client_port as client_tcp_port + , null::varchar(48) as local_net_address + , null::int as local_tcp_port + , null::sys.uniqueidentifier as connection_id + , null::sys.uniqueidentifier as parent_connection_id + , a.pid::sys.varbinary(64) as most_recent_sql_handle + from pg_catalog.pg_stat_activity AS a + RIGHT JOIN sys.tsql_stat_get_activity('connections') AS d ON (a.pid = d.procid); + GRANT SELECT ON sys.dm_exec_connections TO PUBLIC; + +CREATE OR REPLACE VIEW sys.configurations +AS +SELECT configuration_id, + name, + value, + minimum, + maximum, + value_in_use, + description, + is_dynamic, + is_advanced +FROM sys.babelfish_configurations; +GRANT SELECT ON sys.configurations TO PUBLIC; + +CREATE OR REPLACE VIEW sys.syscurconfigs +AS +SELECT value, + configuration_id AS config, + comment_syscurconfigs AS comment, + CASE + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 0 THEN CAST(0 as smallint) + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 1 THEN CAST(1 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 0 THEN CAST(2 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 1 THEN CAST(3 as smallint) + END AS status +FROM sys.babelfish_configurations; +GRANT SELECT ON sys.syscurconfigs TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sysconfigures +AS +SELECT value_in_use AS value, + configuration_id AS config, + comment_sysconfigures AS comment, + CASE + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 0 THEN CAST(0 as smallint) + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 1 THEN CAST(1 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 0 THEN CAST(2 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 1 THEN CAST(3 as smallint) + END AS status +FROM sys.babelfish_configurations; +GRANT SELECT ON sys.sysconfigures TO PUBLIC; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_219.sql b/contrib/babelfishpg_tsql/sql/test/babel_219.sql new file mode 100644 index 00000000000..77dc7ca3e54 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_219.sql @@ -0,0 +1,35 @@ +-- This test should be run without installing the babelfishpg_tsql extension +-- BABEL-219 test a domain named varchar in schema other than sys +-- is not affected by the fix of BABEL-219 +create domain public.varchar as pg_catalog.varchar(2) check (char_length(value) < 1); +select cast('a' as public.varchar); -- throw error +select cast('' as public.varchar); +select cast('a' as varchar); -- pg_catalog.varchar should work + +show search_path; +-- Explicitly add pg_catalog to tail of search_path, +-- to force varchar default to public.varchar +select set_config('search_path', current_setting('search_path') || ', pg_catalog', false); +-- Set tsql dialet so the fix for BABEL-219 can kick in +SET babelfishpg_tsql.sql_dialect = 'tsql'; +select cast('a' as varchar); -- varchar default to public.varchar. should fail exactly the same way as explicitly specifying public.varchar +select cast('' as varchar); -- varchar default to public.varchar. should pass +create table t1(col varchar); +insert into t1 (col) select 'a'; -- fail +insert into t1 (col) select ''; -- pass +select * from t1; +-- verify behavior of public.varchar is unchanged in tsql dialect +select cast('a' as public.varchar); -- fail +select cast('' as public.varchar); -- pass +create table t2(col public.varchar); +insert into t1 (col) select 'a'; -- fail +insert into t1 (col) select ''; -- pass +select * from t1; + +-- Clean up +drop table t1; +drop table t2; +set babelfishpg_tsql.sql_dialect = 'postgres'; +-- Reset search_path +set search_path to "$user", public; +show search_path; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_collation.sql b/contrib/babelfishpg_tsql/sql/test/babel_collation.sql new file mode 100644 index 00000000000..61b072a5122 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_collation.sql @@ -0,0 +1,175 @@ +-- nvarchar is not supported in PG +create table testing1(col nvarchar(60)); -- expect this to fail in the Postgres dialect + +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql" CASCADE; +set babelfishpg_tsql.sql_dialect = "tsql"; + +-- nvarchar is supported in tsql dialect +create table testing1(col nvarchar(60)); +insert into testing1 (col) select N'Muffler'; +insert into testing1 (col) select N'Mülle'; +insert into testing1 (col) select N'MX Systems'; +insert into testing1 (col) select N'Magic'; +select * from testing1 order by col; + +-- test case insensitive collation +create table testing2 (col varchar(20) collate SQL_Latin1_General_CP1_CI_AS); + +insert into testing2 values ('JONES'); +insert into testing2 values ('jones'); +insert into testing2 values ('Jones'); +insert into testing2 values ('JoNes'); +insert into testing2 values ('JoNés'); + +select * from testing2 where col collate BBF_Unicode_General_CS_AS = 'JoNes'; +select * from testing2 where col collate BBF_Unicode_General_CI_AS = 'JoNes'; +select * from testing2 where col collate BBF_Unicode_General_CI_AI = 'JoNes'; +select * from testing2 where col collate BBF_Unicode_General_CS_AI = 'JoNes'; + +-- test case insensitivity for default collation +create table testing3 (c1 varchar(20), c2 char(20), c3 nvarchar(20)); +\d testing3 +insert into testing3 values ('JONES','JONES','JONES'); +insert into testing3 values ('JoneS','JoneS','JoneS'); +insert into testing3 values ('jOnes','jOnes','jOnes'); + +select c1 from testing3 where c1='jones'; +select c2 from testing3 where c2='jones'; +select c3 from testing3 where c3='jones'; + +-- test LIKE to ILIKE transformation +create table testing4 (c1 varchar(20), c2 char(20), c3 nvarchar(20)); +create index c1_idx on testing4 (c1); + +insert into testing4 values ('JONES','JONES','JONES'); +insert into testing4 values ('JoneS','JoneS','JoneS'); +insert into testing4 values ('jOnes','jOnes','jOnes'); +insert into testing4 values ('abcD','AbcD','ABCd'); +insert into testing4 values ('äbĆD','äḃcD','äƀCd'); + +-- set enable_seqscan doesn't work from the TSQL dialect, so switch +-- dialects, disable sequential scan so we see some index-based plans, +-- then switch back to the TSQL dialect +-- +reset babelfishpg_tsql.sql_dialect; +set enable_seqscan = false; +set babelfishpg_tsql.sql_dialect = "tsql"; + +-- test that like is case-insenstive +select c1 from testing4 where c1 LIKE 'jones'; -- this gets converted to '=' +explain (costs false) select c1 from testing4 where c1 LIKE 'jones'; +select c1 from testing4 where c1 LIKE 'Jon%'; +explain (costs false) select c1 from testing4 where c1 LIKE 'Jon%'; +select c1 from testing4 where c1 LIKE 'jone_'; +explain (costs false) select c1 from testing4 where c1 LIKE 'jone_'; +select c1 from testing4 where c1 LIKE '_one_'; +explain (costs false) select c1 from testing4 where c1 LIKE '_one_'; +select c1 from testing4 where c1 LIKE '%on%s'; +explain (costs false) select c1 from testing4 where c1 LIKE '%on%s'; +-- test that like is accent-senstive for CI_AS collation +select c1 from testing4 where c1 LIKE 'ab%'; +select c1 from testing4 where c1 LIKE 'äb%'; +select c1 from testing4 where c1 LIKE 'äḃĆ_'; +-- test not like +select c1 from testing4 where c1 NOT LIKE 'jones'; +explain (costs false) select c1 from testing4 where c1 NOT LIKE 'jones'; +select c1 from testing4 where c1 NOT LIKE 'jone%'; +explain (costs false) select c1 from testing4 where c1 NOT LIKE 'jone%'; +select c1 from testing4 where c1 NOT LIKE 'ä%'; +explain (costs false) select c1 from testing4 where c1 NOT LIKE 'ä%'; +-- test escape function and wildcard literal +select c1 from testing4 where c1 LIKE E'\_ones'; +explain (costs false) select c1 from testing4 where c1 LIKE E'\_ones'; +select c1 from testing4 where c1 LIKE E'\%ones'; +explain (costs false) select c1 from testing4 where c1 LIKE E'\%ones'; +-- wild card literals are transformed to equal +select c1 from testing4 where c1 LIKE '\%ones'; +explain(costs false) select c1 from testing4 where c1 LIKE '\%ones'; +select c1 from testing4 where c1 LIKE '\_ones'; +explain(costs false) select c1 from testing4 where c1 LIKE '\_ones'; +-- test combining with other string functions +select c1 from testing4 where c1 LIKE lower('_ones'); +select c1 from testing4 where c1 LIKE upper('_ones'); +select c1 from testing4 where c1 LIKE concat('_on','_s'); +select c1 from testing4 where c1 LIKE concat('a','%d'); +select c1 from testing4 where c1 NOT LIKE lower('%s'); +-- test sub-queries +Select * from testing4 where c1 LIKE (select c1 from testing4 where c1 LIKE 'AbcD'); +Select * from testing4 where c2 NOT LIKE (select c2 from testing4 where c2 NOT LIKE 'jo%' AND c2 NOT LIKE 'ä%'); +Select * from testing4 where c3 LIKE (select c3 from testing4 where c3 NOT LIKE'jo%' AND c3 NOT LIKE 'ä%'); +with p1 as (select c1 from testing4 where c1 LIKE '__Ć_'), +p2 as (select c3 from testing4 where c3 LIKE 'äƀ__') +select * from p1 union all select * from p2; +-- test case expression +select c1,(case c1 LIKE 'j%' when true then 1 when false then 2 end) from testing4; +select c2,(case when c2 LIKE '_bc%' then 1 when c2 LIKE 'jon%' then 2 when c3 LIKE 'ä%' then 3 end) from testing4; +-- test that LIKE transformation is applied only for CI_AS column +create table testing5(c1 varchar(20) COLLATE SQL_Latin1_General_CP1_CS_AS); +insert into testing5 values ('JONES'); +insert into testing5 values ('JoneS'); +insert into testing5 values ('abcD'); +insert into testing5 values ('äbĆD'); +select * from testing5 where c1 LIKE 'jo%'; -- does not use the transformation +explain(costs false) select * from testing5 where c1 LIKE 'jo%'; +select * from testing5 where c1 NOT LIKE 'j%'; +select * from testing5 where c1 LIKE 'AB%'; + +-- test explicitly specify collation as CI_AS, like transformation is also applied. +SELECT 'JONES' like 'jo%'; +SELECT 'JONES' COLLATE SQL_Latin1_General_CP1_CI_AS like 'jo%' ; + +-- test when pattern is empty string or NULL +SELECT 'JONES' like ''; +SELECT 'JONES' like NULL; +SELECT * from testing5 where c1 like ''; +explain (costs false) SELECT * from testing5 where c1 like ''; +SELECT * from testing5 where c1 like NULL; +explain (costs false) SELECT * from testing5 where c1 like NULL; + +SELECT * FROM testing5 where c1 COLLATE French_CI_AS like 'jo%' ; +explain (costs false) SELECT * FROM testing5 where c1 COLLATE French_CI_AS like 'jo%' ; +SELECT * FROM testing5 where c1 COLLATE Chinese_PRC_CI_AS like 'jo%' ; +explain (costs false) SELECT * FROM testing5 where c1 COLLATE Chinese_PRC_CI_AS like 'jo%' ; + +-- tsql collations +alter table testing1 alter column col nvarchar(60) collate Arabic_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Chinese_PRC_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Cyrillic_General_CS_AS; +alter table testing1 alter column col nvarchar(60) collate French_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Korean_Wansung_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Traditional_Spanish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Modern_Spanish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate SQL_Latin1_General_CP1_CS_AS; +alter table testing1 alter column col nvarchar(60) collate SQL_Latin1_General_CP1_CI_AS; +alter table testing1 alter column col nvarchar(60) collate Traditional_Spanish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Thai_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Turkish_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Ukrainian_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Vietnamese_CS_AS; +alter table testing1 alter column col nvarchar(60) collate Finnish_Swedish_CS_AS; +-- expect different result order from previous select +select * from testing1 order by col; + +-- test expression level collate, expect the same result order +select * from testing1 order by col collate Finnish_Swedish_CS_AS; + +-- test catalog +select * from sys.fn_helpcollations(); + +-- test the TYPE keyword is only required in postgres dialect, but not in tsql dialect +alter table testing1 alter column col varchar(60) collate Finnish_Swedish_CS_AS; +alter table testing1 alter column col TYPE varchar(60) collate Finnish_Swedish_CS_AS; +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); +alter table testing1 alter column col varchar(60) collate sys.Finnish_Swedish_CS_AS; +alter table testing1 alter column col TYPE varchar(60) collate sys.Finnish_Swedish_CS_AS; +SELECT set_config('babelfishpg_tsql.sql_dialect', 'tsql', false); + +-- test collation list sys table +SELECT collation_name, l1_priority, l2_priority, l3_priority, l4_priority, l5_priority FROM sys.babelfish_collation_list() order by collation_name; + +-- clean up +drop table testing1; +drop table testing2; +drop table testing3; +drop table testing4; +drop table testing5; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_datatype.sql b/contrib/babelfishpg_tsql/sql/test/babel_datatype.sql new file mode 100644 index 00000000000..0d4819ef9db --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_datatype.sql @@ -0,0 +1,1128 @@ +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql" CASCADE; + +-- The default scale is 2 in PG. +select CAST('$100,123.4567' AS money); +-- Currency symbol followed by number without being quoted is not recognized +-- as Money in postgres dialect. +select CAST($100123.4567 AS money); + +-- Scale changes to the sql server default 4 in tsql dialect +-- Currency symbol followed by number without being quoted is recognized +-- as Money type in tsql dialect. +set babelfishpg_tsql.sql_dialect = "tsql"; +select CAST($100123.4567 AS money); +select CAST($100123. AS money); +select CAST($.4567 AS money); +select CAST('$100,123.4567' AS money); + +-- Test numeric types with brackets +create table testing1 (a [tinyint]); +drop table testing1; +create table testing1 (a [smallint]); +drop table testing1; +create table testing1 (a [int]); +drop table testing1; +create table testing1 (a [bigint]); +drop table testing1; +create table testing1 (a [real]); +drop table testing1; +create table testing1 (a [float]); +drop table testing1; + +-- Comma separated format without quote is not allowed in sql server +select CAST($100,123.4567 AS money); + +-- Smallmoney in tsql dialect +select CAST($100123.4567 AS smallmoney); +select CAST('$100,123.4567' AS smallmoney); +-- Comma separated format without quote is not allowed in sql server +select CAST($100,123.4567 AS smallmoney); + +create table testing1(mon money, smon smallmoney); +insert into testing1 (mon, smon) values ('$100,123.4567', '$123.9999'); +insert into testing1 (mon, smon) values ($100123.4567, $123.9999); +select * from testing1; +select avg(CAST(mon AS numeric(38,4))), avg(CAST(smon AS numeric(38,4))) from testing1; +select mon+smon as total from testing1; +-- Comma separated format without quote is not allowed in sql server +insert into testing1 (mon, smon) values ($100,123.4567, $123.9999); + +-- Test other allowed currency symbols with/without quote +-- TODO: fix BABEL-2636 "Money datatype doesn't support any currency symbol other than Dollar" +select CAST(€100.123 AS money); +select CAST('€100.123' AS money); +select CAST(¢100.123 AS money); +select CAST(£100.123 AS money); +select CAST('£100.123' AS money); +select CAST(¤100.123 AS money); +select CAST(¥100.123 AS money); +select CAST(৲100.123 AS money); +select CAST(৳100.123 AS money); +select CAST(฿100.123 AS money); +select CAST(៛100.123 AS money); +select CAST(₠100.123 AS money); +select CAST(₡100.123 AS money); +select CAST(₢100.123 AS money); +select CAST(₣100.123 AS money); +select CAST(₤100.123 AS money); +select CAST(₥100.123 AS money); +select CAST(₦100.123 AS money); +select CAST(₧100.123 AS money); +select CAST(₨100.123 AS money); +select CAST(₩100.123 AS money); +select CAST(₪100.123 AS money); +select CAST(₫100.123 AS money); +select CAST(₭100.123 AS money); +select CAST(₮100.123 AS money); +select CAST(₯100.123 AS money); +select CAST(₰100.123 AS money); +select CAST(₱100.123 AS money); +select CAST(﷼100.123 AS money); +select CAST(﹩100.123 AS money); +select CAST($100.123 AS money); +select CAST(¢100.123 AS money); +select CAST(£100.123 AS money); +select CAST(¥100.123 AS money); +select CAST('¥100.123' AS money); +select CAST(₩100.123 AS money); + +-- Test unsupoorted currency symbol +select CAST(←100.123 AS money); +select CAST('←100.123' AS money); + +-- Test that space is allowed between currency symbol and number, this is +-- a TSQL behavior +select CAST($ 123.5 AS money); +select CAST('$ 123.5' AS money); + +-- Test inexact result mutliply/divide money with money, to match +-- SQL Server behavior +select CAST(100 AS money)/CAST(339 AS money)*CAST(10000 AS money); + +-- Test postgres dialect +-- Test currency symbol without quote is not allowed in postgres dialect +reset babelfishpg_tsql.sql_dialect; +select CAST(€100.123 AS money); + +-- Test exact result multiply/divide money with money in postgres dialect +select CAST(100 AS money)/CAST(339 AS money)*CAST(10000 AS money); + +-- Clean up +drop table testing1; + +-- BABEL-109 test no more not unique operator error caused by fixeddeciaml +select CAST(2 AS numeric) > 1; +select CAST(2 AS decimal) > 1; + +-- Test that numeric > int and fixeddecimal > int is different +select CAST(2.00001 AS numeric) > 2; +select CAST(2.00001 AS sys.fixeddecimal) > 2; + +-- test TSQL Money (based on fixeddecimal) cross datatype operators +set babelfishpg_tsql.sql_dialect = "tsql"; +select CAST(2 AS money) > 1; +select CAST(2 AS money) > CAST(1 AS int); +select CAST(2 AS money) > CAST(1 AS int2); +select CAST(2 AS money) > CAST(1 AS int4); +select CAST(2 AS money) > CAST(1 AS numeric); +select CAST(2 AS money) > CAST(1 AS decimal); + +select CAST(2 AS money) >= 1; +select CAST(2 AS money) >= CAST(1 AS int); +select CAST(2 AS money) >= CAST(1 AS int2); +select CAST(2 AS money) >= CAST(1 AS int4); +select CAST(2 AS money) >= CAST(1 AS numeric); +select CAST(2 AS money) >= CAST(1 AS decimal); + +select CAST(2 AS money) < 1; +select CAST(2 AS money) < CAST(1 AS int); +select CAST(2 AS money) < CAST(1 AS int2); +select CAST(2 AS money) < CAST(1 AS int4); +select CAST(2 AS money) < CAST(1 AS numeric); +select CAST(2 AS money) < CAST(1 AS decimal); + +select CAST(2 AS money) <= 1; +select CAST(2 AS money) <= CAST(1 AS int); +select CAST(2 AS money) <= CAST(1 AS int2); +select CAST(2 AS money) <= CAST(1 AS int4); +select CAST(2 AS money) <= CAST(1 AS numeric); +select CAST(2 AS money) <= CAST(1 AS decimal); + +select CAST(2 AS money) <> 1; +select CAST(2 AS money) <> CAST(1 AS int); +select CAST(2 AS money) <> CAST(1 AS int2); +select CAST(2 AS money) <> CAST(1 AS int4); +select CAST(2 AS money) <> CAST(1 AS numeric); +select CAST(2 AS money) <> CAST(1 AS decimal); + +select CAST(2 AS money) + 1; +select CAST(2 AS money) + CAST(1 AS int); +select CAST(2 AS money) + CAST(1 AS int2); +select CAST(2 AS money) + CAST(1 AS int4); +select CAST(2 AS money) + CAST(1 AS numeric); +select CAST(2 AS money) + CAST(1 AS decimal); + +select CAST(2 AS money) - 1; +select CAST(2 AS money) - CAST(1 AS int); +select CAST(2 AS money) - CAST(1 AS int2); +select CAST(2 AS money) - CAST(1 AS int4); +select CAST(2 AS money) - CAST(1 AS numeric); +select CAST(2 AS money) - CAST(1 AS decimal); + +select CAST(2 AS money) * 2; +select CAST(2 AS money) * CAST(2 AS int); +select CAST(2 AS money) * CAST(2 AS int2); +select CAST(2 AS money) * CAST(2 AS int4); +select CAST(2 AS money) * CAST(2 AS numeric); +select CAST(2 AS money) * CAST(2 AS decimal); + +select CAST(2 AS money) / 0.5; +select CAST(2 AS money) / CAST(2 AS int); +select CAST(2 AS money) / CAST(2 AS int2); +select CAST(2 AS money) / CAST(2 AS int4); +select CAST(2 AS money) / CAST(0.5 AS numeric(4,2)); +select CAST(2 AS money) / CAST(0.5 AS decimal(4,2)); + +reset babelfishpg_tsql.sql_dialect; + +-- Test DATE, DATETIME, DATETIMEOFFSET, DATETIME2 +set babelfishpg_tsql.sql_dialect = "tsql"; + +-- DATE DATETIME, DATETIMEOFFSET, DATETIME2 and SMALLDATETIME are defined in tsql dialect +select CAST('2020-03-15' AS date); +select CAST('2020-03-15 09:00:00+8' AS datetimeoffset); +select CAST('2020-03-15 09:00:00' AS datetime2); +select CAST('2020-03-15 09:00:00' AS smalldatetime); +-- test the range of date +select CAST('0001-01-01' AS date); +select CAST('9999-12-31' AS date); +-- test the range of datetime2 +select CAST('0001-01-01 12:00:00.12345' AS datetime2); +select CAST('9999-12-31 12:00:00.12345' AS datetime2); +-- precision +select CAST('2020-03-15 09:00:00+8' AS datetimeoffset(7)) ; +create table testing1(ts DATETIME, tstz DATETIMEOFFSET(7)); +insert into testing1 (ts, tstz) values ('2020-03-15 09:00:00', '2020-03-15 09:00:00+8'); +select * from testing1; +drop table testing1; + +select CAST('2020-03-15 09:00:00' AS datetime2(7)); +select CAST('2020-03-15 09:00:00.123456' AS datetime2(3)); +select CAST('2020-03-15 09:00:00.123456' AS datetime2(0)); +select CAST('2020-03-15 09:00:00.123456' AS datetime2(-1)); +create table testing1(ts DATETIME, tstz DATETIME2(7)); +insert into testing1 (ts, tstz) values ('2020-03-15 09:00:00', '2020-03-15 09:00:00'); +select * from testing1; +drop table testing1; + +-- DATETIME, DATETIMEOFFSET, DATETIME2 and SMALLDATETIME are not defined in +-- postgres dialect +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); +select CAST('2020-03-15 09:00:00+8' AS datetimeoffset); +create table testing1(ts DATETIME); +create table testing1(tstz DATETIMEOFFSET); +select CAST('2020-03-15 09:00:00' AS datetime2); +create table testing1(ts SMALLDATETIME); +create table testing1(tstz DATETIME2); + +-- Test DATETIME, DATETIMEOFFSET, DATETIME2 and SMALLDATETIME can be used as identifier +create table testing1(DATETIME int); +insert into testing1 (DATETIME) values (1); +select * from testing1; +drop table testing1; + +create table testing1(DATETIMEOFFSET int); +insert into testing1 (DATETIMEOFFSET) values (1); +select * from testing1; +drop table testing1; + +create table testing1(DATETIME2 int); +insert into testing1 (DATETIME2) values (1); +select * from testing1; +drop table testing1; + +create table testing1(SMALLDATETIME int); +insert into testing1 (SMALLDATETIME) values (1); +select * from testing1; + +set babelfishpg_tsql.sql_dialect = 'tsql'; +insert into testing1 (SMALLDATETIME) values (2); +select * from testing1; + +-- Test conversion between DATE and other date/time types +select CAST(CAST('2020-03-15' AS date) AS datetime); +select CAST(CAST('2020-03-15' AS date) AS smalldatetime); +select CAST(CAST('2020-03-15' AS date) AS datetimeoffset(3)); +select CAST(CAST('2020-03-15' AS date) AS datetime2(3)); + +-- Clean up +reset babelfishpg_tsql.sql_dialect; +drop table testing1; + +-- Test SYS.NCHAR, SYS.NVARCHAR and SYS.VARCHAR +-- nchar is already available in postgres dialect +select CAST('£' AS nchar(1)); +-- nvarchar is not available in postgres dialect +select CAST('£' AS nvarchar); + +-- both are available in tsql dialect +set babelfishpg_tsql.sql_dialect = 'tsql'; +select CAST('£' AS nchar(2)); +select CAST('£' AS nvarchar(2)); + +-- multi-byte character doesn't fit in nchar(1) in tsql if it +-- would require a UTF16-surrogate-pair on output +select CAST('£' AS char(1)); -- allowed +select CAST('£' AS sys.nchar(1)); -- allowed +select CAST('£' AS sys.nvarchar(1)); -- allowed +select CAST('£' AS sys.varchar(1)); -- allowed +select CAST('😀' AS char(1)); -- not allowed +select CAST('😀' AS sys.nchar(1)); -- not allowed +select CAST('😀' AS sys.nvarchar(1)); -- not allowed +select CAST('😀' AS sys.varchar(1)); -- not allowed + +-- Check that things work the same in postgres dialect +reset babelfishpg_tsql.sql_dialect; +select CAST('£' AS char(1)); +select CAST('£' AS sys.nchar(1)); +select CAST('£' AS sys.nvarchar(1)); +select CAST('£' AS sys.varchar(1)); +select CAST('😀' AS char(1)); +select CAST('😀' AS sys.nchar(1)); -- this should not be allowed as nchar is T-SQL type +select CAST('😀' AS sys.nvarchar(1)); -- this should not be allowed as nvarchar is T-SQL type +select CAST('😀' AS sys.varchar(1)); -- this should not be allowed as sys.varchar is T-SQL type +set babelfishpg_tsql.sql_dialect = 'tsql'; + +-- truncate input on explicit cast +select CAST('ab' AS char(1)); +select CAST('ab' AS nchar(1)); +select CAST('ab' AS nvarchar(1)); +select CAST('ab' AS sys.varchar(1)); + +-- But still don't allow surrogate pairs to exceed max length +select CAST('😀b' AS char(1)); +select CAST('😀b' AS nchar(1)); +select CAST('😀b' AS nvarchar(1)); +select CAST('😀b' AS sys.varchar(1)); + +-- default length of nchar/char is 1 in tsql (and pg) +create table testing1(col nchar); +\d testing1; + +-- check length at insert +insert into testing1 (col) select 'a'; +insert into testing1 (col) select '£'; +insert into testing1 (col) select '😀'; +insert into testing1 (col) select 'ab'; +-- space is automatically truncated +insert into testing1 (col) select 'c '; +select * from testing1; + +-- default length of nvarchar in tsql is 1 +create table testing2(col nvarchar); + +insert into testing2 (col) select 'a'; +insert into testing2 (col) select '£'; +insert into testing2 (col) select '😀'; +insert into testing2 (col) select 'ab'; +-- space is automatically truncated +insert into testing2 (col) select 'c '; +select * from testing2; + +-- default length of varchar in tsql is 1 +create table testing4(col sys.varchar); + +insert into testing4 (col) select 'a'; +insert into testing4 (col) select '£'; +insert into testing4 (col) select '😀'; +insert into testing4 (col) select 'ab'; +-- space is automatically truncated +insert into testing4 (col) select 'c '; +insert into testing2 (col) select '£ '; +insert into testing2 (col) select '😀 '; +select * from testing4; + +-- test sys.varchar(max) and sys.nvarchar(max) syntax is allowed in tsql dialect +select CAST('abcdefghijklmn' AS sys.varchar(max)); +select CAST('abcdefghijklmn' AS varchar(max)); +select CAST('abcdefghijklmn' AS sys.nvarchar(max)); +select CAST('abcdefghijklmn' AS nvarchar(max)); + +-- test char(max), nchar(max) is invalid syntax in tsql dialect +select cast('abc' as char(max)); +select cast('abc' as nchar(max)); + +-- test max can still be used as an identifier +create table max (max int); +insert into max (max) select 100; +select * from max; +drop table max; + +-- test sys.varchar(max) and nvarchar(max) syntax is not allowed in postgres dialect +reset babelfishpg_tsql.sql_dialect; +select CAST('abcdefghijklmn' AS sys.varchar(max)); +select CAST('abcdefghijklmn' AS varchar(max)); +select CAST('abcdefghijklmn' AS sys.nvarchar(max)); +select CAST('abcdefghijklmn' AS nvarchar(max)); + +-- test max max character length is (10 * 1024 * 1024) = 10485760 +select CAST('abc' AS varchar(10485761)); +select CAST('abc' AS varchar(10485760)); + +-- test column type nvarchar(max) +set babelfishpg_tsql.sql_dialect = 'tsql'; +create table testing5(col nvarchar(max)); +\d testing5 +insert into testing5 (col) select 'ab'; +insert into testing5 (col) select 'abcdefghijklmn'; +select * from testing5; + +--test COPY command works with sys.nvarchar +COPY public.testing5 (col) FROM stdin; +c +ab +abcdefghijk +\. +select * from testing5; + +-- [BABEL-220] test varchar(max) as a column +drop table testing5; +create table testing5(col varchar(max)); +\d testing5 +insert into testing5 (col) select 'ab'; +insert into testing5 (col) select 'abcdefghijklmn'; +select * from testing5; + +-- test type modifer persist if babelfishpg_tsql.sql_dialect changes +create table testing3(col nvarchar(2)); +insert into testing3 (col) select 'ab'; +insert into testing3 (col) select 'a£'; +insert into testing3 (col) select 'a😀'; +insert into testing3 (col) select 'abc'; + +reset babelfishpg_tsql.sql_dialect; +insert into testing3 (col) select 'ab'; +insert into testing3 (col) select 'a£'; +insert into testing3 (col) select 'a😀'; +insert into testing3 (col) select 'abc'; + +set babelfishpg_tsql.sql_dialect = 'tsql'; +insert into testing3 (col) select 'ab'; +insert into testing3 (col) select 'a£'; +insert into testing3 (col) select 'a😀'; +insert into testing3 (col) select 'abc'; + +-- test normal create domain works when apg_enable_domain_typmod is enabled +set apg_enable_domain_typmod true; +create domain varchar3 as varchar(3); +select CAST('abc' AS varchar3); +select CAST('ab£' AS varchar3); +select CAST('ab😀' AS varchar3); +select CAST('abcd' AS varchar3); +reset apg_enable_domain_typmod; + +-- [BABEL-191] test typmod of sys.varchar/nvarchar engages when the input +-- is casted multiple times +select CAST(CAST('abc' AS text) AS sys.varchar(3)); +select CAST(CAST('abc' AS pg_catalog.varchar(3)) AS sys.varchar(3)); + +select CAST(CAST('abc' AS text) AS sys.nvarchar(3)); +select CAST(CAST('abc' AS text) AS sys.nchar(3)); + +select CAST(CAST(CAST(CAST('abc' AS text) AS sys.varchar(3)) AS sys.nvarchar(3)) AS sys.nchar(3)); + +-- test truncation on explicit cast through multiple levels +select CAST(CAST(CAST(CAST('abcde' AS text) AS sys.varchar(5)) AS sys.nvarchar(4)) AS sys.nchar(3)); +select CAST(CAST(CAST(CAST('abcde' AS text) AS sys.varchar(3)) AS sys.nvarchar(4)) AS sys.nchar(5)); + +-- test sys.ntext is available +select CAST('abc£' AS sys.ntext); +-- pg_catalog.text +select CAST('abc£' AS text); + +-- [BABEL-218] test varchar defaults to sys.varchar in tsql dialect +-- test default length of sys.varchar is 30 in CAST/CONVERT +-- expect the last 'e' to be truncated +select cast('abcdefghijklmnopqrstuvwxyzabcde' as varchar); +select cast('abcdefghijklmnopqrstuvwxyzabcde' as sys.varchar); +select convert(varchar, 'abcdefghijklmnopqrstuvwxyzabcde'); +select convert(sys.varchar, 'abcdefghijklmnopqrstuvwxyzabcde'); + +-- default length of pg_catalog.varchar is unlimited, no truncation in output +select cast('abcdefghijklmnopqrstuvwxyzabcde' as pg_catalog.varchar); + +-- varchar defaults to pg_catalog.varchar in PG dialect +reset babelfishpg_tsql.sql_dialect; +select cast('abcdefghijklmnopqrstuvwxyzabcde' as pg_catalog.varchar); -- default length of pg_catalog.varchar is unlimited, no truncation +set babelfishpg_tsql.sql_dialect = 'tsql'; + +-- [BABEL-255] test nchar defaults to sys.nchar in tsql dialect +create table test_nchar (col1 nchar); +\d test_nchar +drop table test_nchar; +-- test nchar defaults to bpchar in pg dialect +reset babelfishpg_tsql.sql_dialect; +create table test_nchar (col1 nchar); +\d test_nchar +drop table test_nchar; +set babelfishpg_tsql.sql_dialect = 'tsql'; + +-- [BABEL-257] test varchar defaults to sys.varchar in new +-- database and new schema +SELECT current_database(); + +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); +CREATE DATABASE demo; +\c demo +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql" CASCADE; +-- Reconnect to make sure CLUSTER_COLLATION_OID is initialized +\c postgres +\c demo +set babelfishpg_tsql.sql_dialect = 'tsql'; +-- Test varchar is mapped to sys.varchar +-- Expect truncated output because sys.varchar defaults to sys.varchar(30) in CAST function +select cast('abcdefghijklmnopqrstuvwxyzabcde' as varchar); +-- Expect non-truncated output because pg_catalog.varchar has unlimited length +select cast('abcdefghijklmnopqrstuvwxyzabcde' as pg_catalog.varchar); + +-- Test bit is mapped to sys.bit +-- sys.bit allows numeric input +select CAST(1.5 AS bit); +-- pg_catalog.bit doesn't allow numeric input +select CAST(1.5 AS pg_catalog.bit); + +-- Test varchar is mapped to sys.varchar in a new schema and a new table +CREATE SCHEMA s1; +create table s1.test1 (col varchar); +-- Test sys.varchar is created for test1.col, expect an error +-- because sys.varchar defaults to sys.varchar(1) +insert into s1.test1 values('abc'); +insert into s1.test1 values('a'); +select * from s1.test1; +drop schema s1 cascade; +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); +\c regression +drop database demo; +set babelfishpg_tsql.sql_dialect = 'tsql'; + +-- test tinyint data type +select CAST(100 AS tinyint); +select CAST(10 AS tinyint) / CAST(3 AS tinyint); +select CAST(256 AS tinyint); +select CAST((-1) AS tinyint); + +-- test bit data type, bit defaults to sys.bit in tsql dialect +-- test 'true'/'false' input is allowed. 't'/'f' is not allowed. +select CAST('true' AS bit); +select CAST('True' AS bit); +select CAST('TRUE' AS bit); +select CAST('t' AS bit); +select CAST('T' AS bit); +select CAST('false' AS bit); +select CAST('False' AS bit); +select CAST('FALSE' AS bit); +select CAST('f' AS bit); +select CAST('F' AS bit); + +-- test '1'/'0' +select CAST('1' AS bit); +select CAST('0' AS bit); +select CAST('000' AS bit); +select CAST('010' AS bit); + +-- test 'abc' is not allowed +select CAST('abc' AS bit); + +-- test NULL is allowed +select CAST(NULL AS bit); + +-- bit defaults to pg_catalog.bit in pg dialect +reset babelfishpg_tsql.sql_dialect; +-- pg_catalog.bit doesn't recognize 'true' +select CAST('true' AS bit); +select CAST('true' AS pg_catalog.bit); +select CAST('1' AS bit); +select CAST('1' AS pg_catalog.bit); + +-- test numeric and integer input +set babelfishpg_tsql.sql_dialect = 'tsql'; +select CAST(1 AS bit); +select CAST(2 AS bit); +select CAST(0 AS bit); +select CAST(000 AS bit); +select CAST(0.0 AS bit); +select CAST(0.00 AS bit); +select CAST(0.5 AS bit); + +-- test negative operator +select CAST(-1 AS bit); +select CAST(-0.5 AS bit); + +-- test int2 int4 int8 input +select CAST(CAST(2 AS int2) AS bit); +select CAST(CAST(0 AS int2) AS bit); +select CAST(CAST(2 AS int4) AS bit); +select CAST(CAST(0 AS int4) AS bit); +select CAST(CAST(2 AS int8) AS bit); +select CAST(CAST(0 AS int8) AS bit); + +-- test real, double precision input +select CAST(CAST(1.5 AS real) AS bit); +select CAST(CAST(0.0 AS real) AS bit); +select CAST(CAST(1.5 AS double precision) AS bit); +select CAST(CAST(0.0 AS double precision) AS bit); + +-- test decimal, numeric input +select CAST(CAST(1.5 AS decimal(4,2)) AS bit); +select CAST(CAST(0.0 AS decimal(4,2)) AS bit); +select CAST(CAST(1.5 AS numeric(4,2)) AS bit); +select CAST(CAST(0.0 AS numeric(4,2)) AS bit); + +-- test operators of bit +create table testing6 (col1 bit, col2 bit); +insert into testing6 (col1, col2) select 'true', 'false'; +insert into testing6 (col1, col2) select 0, 1; +insert into testing6 (col1, col2) select '1', '2'; +insert into testing6 (col1, col2) select 0.5, -1.5; +select * from testing6; +select count(*) from testing6 where col1 = col2; +select count(*) from testing6 where col1 <> col2; +select count(*) from testing6 where col1 > col2; +select count(*) from testing6 where col1 >= col2; +select count(*) from testing6 where col1 < col2; +select count(*) from testing6 where col1 <= col2; + +-- test casting of bits to other numeric types +select cast(cast (1 as bit) as tinyint); +select cast(cast (1 as bit) as smallint); +select cast(cast (1 as bit) as int); +select cast(cast (1 as bit) as bigint); +select cast(cast (1 as bit) as numeric(2,1)); +select cast(cast (1 as bit) as money); +select cast(cast (1 as bit) as smallmoney); + +-- test comparisions +select 1 = cast (1 as bit); + +-- test varbinary is available +select cast('abc' as varbinary(3)); +-- test not throwing error if input would be truncated +select cast('abc' as varbinary(2)); + +-- test throwing error when not explicit casting +drop table testing6; +create table testing6(col varbinary(2)); +insert into testing6 values(cast('ab' as varchar)); +insert into testing6 values(cast('ab' as varbinary(2))); +-- test throwing error if input would be truncated during table insert +insert into testing6 values(cast('abc' as varbinary(3))); +select * from testing6; + +-- test casting varbinary to varchar +select cast(cast('a' AS varchar(10)) as varbinary(2)); +select cast(cast(cast('a' AS varchar(10)) as varbinary(2)) as varchar(2)); +select cast(cast('ab' AS varchar(10)) as varbinary(2)); +select cast(cast(cast('ab' AS varchar(10)) as varbinary(2)) as varchar(2)); +select cast(cast('abc' AS varchar(10)) as varbinary(2)); +select cast(cast(cast('abc' AS varchar(10)) as varbinary(2)) as varchar(2)); + +-- test casting varbinary to nvarchar +select cast(cast('a' AS nvarchar(10)) as varbinary(2)); +select cast(cast(cast('a' AS nvarchar(10)) as varbinary(2)) as nvarchar(2)); +select cast(cast('ab' AS nvarchar(10)) as varbinary(2)); +select cast(cast(cast('ab' AS nvarchar(10)) as varbinary(2)) as nvarchar(2)); +select cast(cast('abc' AS nvarchar(10)) as varbinary(2)); +select cast(cast(cast('abc' AS nvarchar(10)) as varbinary(2)) as nvarchar(2)); + +-- test sys.image is available +select cast('abc' as image); + +-- test sys.binary is available +select cast('abc' as binary(3)); +-- test not throwing error if input would be truncated +select cast('abc' as binary(2)); + +drop table testing6; +create table testing6(col binary(2)); +-- test throwing error when not explicit casting +insert into testing6 values (cast('ab' as varchar)); +insert into testing6 values (cast('ab' as binary(2))); +-- test throwing error if input would be truncated +insert into testing6 values (cast('abc' as binary(3))); +-- test null padding extra space for binary type +insert into testing6 values (cast('a' as binary(2))); +select * from testing6; + +-- test casting binary to varchar +select cast(cast('a' AS varchar(10)) as binary(2)); +-- BABEL-1030 +select cast(cast(cast('a' AS varchar(10)) as binary(2)) as varchar(2)); +select cast(cast('ab' AS varchar(10)) as binary(2)); +select cast(cast(cast('ab' AS varchar(10)) as binary(2)) as varchar(2)); +select cast(cast('abc' AS varchar(10)) as binary(2)); +select cast(cast(cast('abc' AS varchar(10)) as binary(2)) as varchar(2)); + +-- test casting binary to nvarchar +select cast(cast('a' AS nvarchar(10)) as binary(2)); +-- BABEL-1030 +select cast(cast(cast('a' AS nvarchar(10)) as binary(2)) as nvarchar(2)); +select cast(cast('ab' AS nvarchar(10)) as binary(2)); +select cast(cast(cast('ab' AS nvarchar(10)) as binary(2)) as nvarchar(2)); +select cast(cast('abc' AS nvarchar(10)) as binary(2)); +select cast(cast(cast('abc' AS nvarchar(10)) as binary(2)) as nvarchar(2)); + +-- test varbinary(max) syntax +select CAST('010 ' AS varbinary(max)); +select CAST('010' AS varbinary(max)); + +-- test binary(max) is invalid syntax +select cast('abc' as binary(max)); + +-- test varbinary(max) as a column +drop table testing6; +create table testing6(col varbinary(max)); +insert into testing6 values ('abc'); +select * from testing6; + +-- test binary max length is 8000 +select CAST('010' AS binary(8001)); + +-- test default length is 1 +drop table testing6; +create table testing6(col varbinary); +insert into testing6 values (cast('a' as varbinary)); +insert into testing6 values (cast('ab' as varbinary)); +select * from testing6; +drop table testing6; +create table testing6(col binary); +insert into testing6 values (cast('a' as varbinary)); +insert into testing6 values (cast('ab' as varbinary)); +select * from testing6; + +-- test default length of varbinary in cast/convert is 30 +-- truncation silently +select cast('abcdefghijklmnopqrstuvwxyzabcde' as varbinary); +-- no truncation +select cast('abcdefghijklmnopqrstuvwxyzabcd' as varbinary); + +-- truncation silently +select convert(varbinary, 'abcdefghijklmnopqrstuvwxyzabcde'); +-- no truncation +select convert(varbinary, 'abcdefghijklmnopqrstuvwxyzabcd'); + + +-- test escape format '\' is not specially handled for varbinary +-- but it is escaped handled for bytea +select CAST('\13' AS varbinary(5)); +select CAST('\13' AS bytea); +select CAST('\x13' AS varbinary(5)); +select CAST('\x13' AS bytea); +select CAST('\\' AS varbinary(5)); +select CAST('\\' AS bytea); +select CAST('\' AS varbinary); +select CAST('\' AS bytea); + +-- test NULL pad extra space for binary type, not for varbinary and image +select CAST('\\' AS binary(3)); +select CAST('\\' AS varbinary(3)); +select CAST('\\' AS image); + +-- [BABEL-254] test integer input is allowed for varbinary +select cast(16 as varbinary(4)); +select cast(16*16 as varbinary(4)); +select cast(16*16*16 as varbinary(4)); +select cast(511 as varbinary(4)); +-- test truncation to the left if the number input is too large +select cast(16*16*16*16 as varbinary(2)); +-- test same behavior on table insert +drop table testing6; +create table testing6 (col varbinary(2)); +insert into testing6 values (16); +insert into testing6 values (16*16); +insert into testing6 values (16*16*16); +insert into testing6 values (16*16*16*16); +select * from testing6; + +-- test int2, int4, int8 to varbinary +select cast(16*CAST(16 AS int2) as varbinary(2)); +select cast(16*CAST(16 AS int4) as varbinary(4)); +select cast(16*CAST(16 AS int8) as varbinary(8)); + +-- test truncation to the left if maxlen is shorter than the input +select cast(CAST(16 AS int2) as varbinary(1)); +select cast(CAST(16 AS int2) as varbinary(2)); +-- test varbinary will only use 2 bytes (the size of the input) rather +-- than maxlen +select cast(CAST(16 AS int2) as varbinary(3)); +select cast(CAST(16 AS int2) as varbinary(4)); +select cast(CAST(16 AS int2) as varbinary(8)); + +select cast(CAST(16 AS int4) as varbinary(1)); +select cast(CAST(16 AS int4) as varbinary(2)); +select cast(CAST(16 AS int4) as varbinary(3)); +select cast(CAST(16 AS int4) as varbinary(4)); +select cast(CAST(16 AS int4) as varbinary(8)); + +select cast(CAST(16 AS int8) as varbinary(1)); +select cast(CAST(16 AS int8) as varbinary(2)); +select cast(CAST(16 AS int8) as varbinary(3)); +select cast(CAST(16 AS int8) as varbinary(4)); +select cast(CAST(16 AS int8) as varbinary(8)); + +-- [BABEL-254] test integer iput is allowed for binary +select cast(16 as binary(2)); +select cast(16*16 as binary(2)); +select cast(16*16*16 as binary(2)); +-- test truncation to the left if the number input is too large +select cast(16*16*16*16 as binary(2)); +-- test same behavior on table insert +drop table testing6; +create table testing6 (col binary(2)); +insert into testing6 values (16); +insert into testing6 values (16*16); +insert into testing6 values (16*16*16); +insert into testing6 values (16*16*16*16); +select * from testing6; + +-- test int2, int4, int8 to binary +select cast(16*CAST(16 AS int2) as binary(2)); +select cast(16*CAST(16 AS int4) as binary(4)); +select cast(16*CAST(16 AS int8) as binary(8)); + +-- test truncation to the left if maxlen is shorter than the input +select cast(CAST(16 AS int2) as binary(1)); +select cast(CAST(16 AS int2) as binary(2)); +select cast(CAST(16 AS int2) as binary(3)); +-- test 0 padding to the left if maxlen is longer than the input +select cast(CAST(16 AS int2) as binary(4)); +select cast(CAST(16 AS int2) as binary(8)); + +select cast(CAST(16 AS int4) as binary(1)); +select cast(CAST(16 AS int4) as binary(2)); +select cast(CAST(16 AS int4) as binary(3)); +select cast(CAST(16 AS int4) as binary(4)); +-- test 0 padding to the left if maxlen is longer than the input +select cast(CAST(16 AS int4) as binary(8)); + +select cast(CAST(16 AS int8) as binary(1)); +select cast(CAST(16 AS int8) as binary(2)); +select cast(CAST(16 AS int8) as binary(3)); +select cast(CAST(16 AS int8) as binary(4)); +select cast(CAST(16 AS int8) as binary(8)); +-- test 0 padding to the left if maxlen is longer than the input +select cast(CAST(16 AS int8) as binary(10)); + +-- test casting varbinary to int4 +CREATE PROCEDURE cast_varbinary(@val int) AS +BEGIN + DECLARE @BinaryVariable varbinary(4) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int) +END; + +call cast_varbinary(16); +call cast_varbinary(16*16); +call cast_varbinary(511); +drop procedure cast_varbinary; + +-- test casting varbinary to int4 when the varbinary size is longer than 4 +CREATE PROCEDURE cast_varbinary(@val int) AS +BEGIN + DECLARE @BinaryVariable varbinary(8) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int) +END; + +call cast_varbinary(16); +call cast_varbinary(16*16); +drop procedure cast_varbinary; + +-- test truncation varbinary to int4 +CREATE PROCEDURE cast_varbinary(@val int) AS +BEGIN + DECLARE @BinaryVariable varbinary(1) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int) +END; + +call cast_varbinary(16); +call cast_varbinary(16*16); +drop procedure cast_varbinary; + +-- test casting varbinary to int2 +CREATE PROCEDURE cast_varbinary(@val int2) AS +BEGIN + DECLARE @BinaryVariable varbinary(2) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int2) +END; + +call cast_varbinary(CAST(16 AS int2)); +call cast_varbinary(CAST(256 AS int2)); +drop procedure cast_varbinary; + +-- test truncation varbinary to int2 +CREATE PROCEDURE cast_varbinary(@val int2) AS +BEGIN + DECLARE @BinaryVariable varbinary(1) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int2) +END; + +call cast_varbinary(CAST(16 AS int2)); +call cast_varbinary(CAST(256 AS int2)); +drop procedure cast_varbinary; + +-- test casting varbinary to int8 +CREATE PROCEDURE cast_varbinary(@val int8) AS +BEGIN + DECLARE @BinaryVariable varbinary(8) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int8) +END; + +call cast_varbinary(CAST(16 AS int8)); +call cast_varbinary(16*CAST(16 AS int8)); +drop procedure cast_varbinary; + +-- test truncation varbinary to int8 +CREATE PROCEDURE cast_varbinary(@val int8) AS +BEGIN + DECLARE @BinaryVariable varbinary(1) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int8) +END; + +call cast_varbinary(CAST(16 AS int8)); +call cast_varbinary(16*CAST(16 AS int8)); +drop procedure cast_varbinary; + +-- test casting binary to int4 +CREATE PROCEDURE cast_binary(@val int) AS +BEGIN + DECLARE @BinaryVariable binary(4) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int) +END; + +call cast_binary(16); +call cast_binary(256); +drop procedure cast_binary; + +-- test casting binary to int4 when the binary size is greater than 4 +CREATE PROCEDURE cast_binary(@val int) AS +BEGIN + DECLARE @BinaryVariable binary(8) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int) +END; + +call cast_binary(16); +call cast_binary(256); +drop procedure cast_binary; + +-- test truncation binary to int4 +CREATE PROCEDURE cast_binary(@val int) AS +BEGIN + DECLARE @BinaryVariable binary(1) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int) +END; + +call cast_binary(16); +call cast_binary(256); +drop procedure cast_binary; + +-- test casting binary to int2 +CREATE PROCEDURE cast_binary(@val int2) AS +BEGIN + DECLARE @BinaryVariable binary(2) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int2) +END; + +call cast_binary(CAST(16 AS int2)); +call cast_binary(CAST(256 AS int2)); +drop procedure cast_binary; + +-- test casting binary to int2 when the binary size is greater than 2 +CREATE PROCEDURE cast_binary(@val int2) AS +BEGIN + DECLARE @BinaryVariable binary(8) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int2) +END; + +call cast_binary(CAST(16 AS int2)); +call cast_binary(CAST(256 AS int2)); +drop procedure cast_binary; + +-- test truncation binary to int2 +CREATE PROCEDURE cast_binary(@val int2) AS +BEGIN + DECLARE @BinaryVariable binary(1) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int2) +END; + +call cast_binary(CAST(16 AS int2)); +call cast_binary(CAST(256 AS int2)); +drop procedure cast_binary; + +-- test casting binary to int8 +CREATE PROCEDURE cast_binary(@val int8) AS +BEGIN + DECLARE @BinaryVariable binary(8) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int8) +END; + +call cast_binary(CAST(16 AS int8)); +call cast_binary(CAST(256 AS int8)); +drop procedure cast_binary; + +-- test casting binary to int8 when the binary size is greater than 8 +CREATE PROCEDURE cast_binary(@val int8) AS +BEGIN + DECLARE @BinaryVariable binary(12) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int8) +END; + +call cast_binary(CAST(16 AS int8)); +call cast_binary(CAST(256 AS int8)); +drop procedure cast_binary; + +-- test truncation binary to int8 +CREATE PROCEDURE cast_binary(@val int8) AS +BEGIN + DECLARE @BinaryVariable binary(1) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as int8) +END; + +call cast_binary(CAST(16 AS int8)); +call cast_binary(CAST(256 AS int8)); +drop procedure cast_binary; + +-- test real to varbinary +select cast(CAST(0.125 AS real) as varbinary(4)); +drop table testing6; +create table testing6 (col varbinary(4)); +insert into testing6 values (CAST(0.125 AS real)); +insert into testing6 values (CAST(3.125 AS real)); +select * from testing6; + +-- test truncation rule when input is too long/varbinary length is too short +select cast(CAST(0.125 AS real) as varbinary(2)); +select cast(CAST(0.125 AS real) as varbinary(4)); + +-- test casting varbinary back to real +CREATE PROCEDURE cast_varbinary(@val real) AS +BEGIN + DECLARE @BinaryVariable varbinary(4) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as real) +END; +call cast_varbinary(0.125); +call cast_varbinary(3.125); +drop procedure cast_varbinary; + +-- test dobule precision to varbinary +select cast(CAST(0.123456789 AS double precision) as varbinary(8)); +drop table testing6; +create table testing6 (col varbinary(8)); +insert into testing6 values (CAST(0.123456789 AS double precision)); +insert into testing6 values (CAST(3.123456789 AS double precision)); +select * from testing6; + +-- test truncation rule when input is too long/varbinary length is too short +select cast(CAST(0.123456789 AS double precision) as varbinary(2)); +select cast(CAST(0.123456789 AS double precision) as varbinary(8)); + +-- test casting varbinary back to double precision +CREATE PROCEDURE cast_varbinary(@val double precision) AS +BEGIN + DECLARE @BinaryVariable varbinary(8) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as double precision) +END; +call cast_varbinary(0.123456789); +call cast_varbinary(3.123456789); +drop procedure cast_varbinary; + +-- test real to binary +select cast(CAST(0.125 AS real) as binary(4)); +drop table testing6; +create table testing6 (col binary(4)); +insert into testing6 values (CAST(0.125 AS real)); +insert into testing6 values (CAST(3.125 AS real)); +select * from testing6; + +-- test truncation rule when input is too long/binary length is too short +select cast(CAST(0.125 AS real) as binary(2)); +select cast(CAST(0.125 AS real) as binary(4)); + +-- test casting binary back to real +CREATE PROCEDURE cast_binary(@val real) AS +BEGIN + DECLARE @BinaryVariable binary(4) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as real) +END; +call cast_binary(0.125); +call cast_binary(3.125); +drop procedure cast_binary; + +-- test dobule precision to binary +select cast(CAST(0.123456789 AS double precision) as binary(8)); +drop table testing6; +create table testing6 (col binary(8)); +insert into testing6 values (CAST(0.123456789 AS double precision)); +insert into testing6 values (CAST(3.123456789 AS double precision)); +select * from testing6; + +-- test truncation rule when input is too long/binary length is too short +select cast(CAST(0.123456789 AS double precision) as binary(2)); +select cast(CAST(0.123456789 AS double precision) as binary(8)); + +-- test casting binary back to double precision +CREATE PROCEDURE cast_binary(@val double precision) AS +BEGIN + DECLARE @BinaryVariable binary(8) = @val + PRINT @BinaryVariable + PRINT cast(@BinaryVariable as double precision) +END; +call cast_binary(0.123456789); +call cast_binary(3.123456789); +drop procedure cast_binary; + +-- sys.sysname +select CAST('£' AS sysname); -- allowed +select CAST(NULL AS sysname); -- not allowed + +-- sys.sysname is working in both dialects +select CAST('£' AS sys.sysname); -- allowed +select CAST(NULL AS sys.sysname); -- not allowed +reset babelfishpg_tsql.sql_dialect; +select CAST('£' AS sys.sysname); -- allowed +select CAST(NULL AS sys.sysname); -- not allowed + +set babelfishpg_tsql.sql_dialect = 'tsql'; +create table test_sysname (col sys.sysname); +insert into test_sysname values (repeat('£', 128)); -- allowed +insert into test_sysname values (repeat('😀', 128)); -- not allowed due to UTF check +reset babelfishpg_tsql.sql_dialect; +insert into test_sysname values (repeat('😀', 128)); -- not allowed due to UTF check + +set babelfishpg_tsql.sql_dialect = 'tsql'; + +-- clean up +drop table testing1; +drop table testing2; +drop table testing3; +drop table testing4; +drop table testing5; +drop table testing6; +drop table test_sysname; +reset babelfishpg_tsql.sql_dialect; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_ddl.sql b/contrib/babelfishpg_tsql/sql/test/babel_ddl.sql new file mode 100644 index 00000000000..117269928d9 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_ddl.sql @@ -0,0 +1,207 @@ +-- CLUSTERED INDEX / NONCLUSTERED IDNEX +create table t1 ( a int, b int); + +create nonclustered index t1_idx1 on t1 (a); +create clustered index t1_idx2 on t1(a); + +create table t2 ( a int, b int, primary key nonclustered (a)); +create table t3 ( a int, b int, primary key clustered (a)); +create table t4 ( a int, b int, unique nonclustered (a)); +create table t5 ( a int, b int, unique clustered (a)); + +create table t6 ( a int primary key nonclustered, b int); +create table t7 ( a int primary key clustered, b int); +create table t8 ( a int unique nonclustered, b int); +create table t9 ( a int unique clustered, b int); + +set babelfishpg_tsql.sql_dialect = "tsql"; + +create index t1_idx1 on t1 (a); +create index t1_idx2 on t1(a); + +create table t2 ( a int, b int, primary key (a)); +create table t3 ( a int, b int, primary key (a)); +create table t4 ( a int, b int, unique (a)); +create table t5 ( a int, b int, unique (a)); + +create table t6 ( a int primary key, b int); +create table t7 ( a int primary key, b int); +create table t8 ( a int unique not null, b int); +create table t9 ( a int unique not null, b int); + +-- CREATE INDEX ... ON syntax +create index t1_idx3 on t1 (a) on [primary]; +create index t1_idx4 on t1 (a) on "default"; + +-- CREATE TABLE WITH ( [,...n]) syntax +create table t10 (a int) +with (fillfactor = 90, FILETABLE_COLLATE_FILENAME = database_default); +create table t11 (a int) +with (data_compression = row on partitions (2, 4, 6 to 8)); +create table t12 (a int) +with (system_versioning = on (history_table = aaa.bbb, data_consistency_check = off)); +create table t13 (a int) +with (remote_data_archive = on (filter_predicate = null, migration_state = outbound)); +create table t14 (a int) +with (data_deletion = on (filter_column = a, retention_period = 14 day)); + +-- CREATE INDEX WHERE... WITH ( [,...n]) syntax +create index t1_idx5 on t1(a) where a is not null +with (pad_index = off, fillfactor = 90, maxdop = 1, sort_in_tempdb = off, max_duration = 2 minutes); +create index t1_idx6 on t1(a) +with (data_compression = page on partitions (2, 4, 6 to 8)); + +-- CREATE COLUMNSTORE INDEX +create columnstore index t1_idx7 on t1 (a) with (drop_existing = on); +create clustered columnstore index t1_idx8 on t1 (a) on [primary]; + +-- CREATE TABLE... WITH FILLFACTOR = num +create table t15 (a int primary key with fillfactor=50); +-- ALTER TABLE... WITH FILLFACTOR = num +create table t16 (a int not null); +alter table t16 add primary key (a) with fillfactor=50; + +-- check property of the index +select indexname, indexdef from pg_indexes where tablename like 't_' order by indexname; + +-- CREATE TABLE(..., { PRIMARY KEY | UNIQUE } ... +-- ON { partition_scheme | filegroup | "default" }) syntax +-- ^ +create table t17(a int, primary key clustered (a) on [PRIMARY]); +create table t18(a int, primary key clustered (a) on [PRIMARY]); +create table t19(a int, unique clustered (a) on [PRIMARY]); +create table t20(a int, unique clustered (a) on [PRIMARY]); + +-- ALTER TABLE ... ADD [CONSTRAINT ...] DEFAULT ... FOR ... +create table t21 (a int, b int); +alter table t21 add default 99 for a; +insert into t21(b) values (10); +select * from t21; + +alter table t21 alter a drop default; +alter table t21 add constraint dflt11 default 11 for a; +insert into t21(b) values (20); +select * from t21; + +-- Invalid default value +alter table t21 add default 'test' for a; +-- Invalid column +alter table t21 add default 99 for c; +-- Invalid table +alter table t_invalid add default 99 for a; + +-- ALTER TABLE ... WITH [NO]CHECK ADD CONSTRAINT ... +alter table t21 with check add constraint chk1 check (a > 0); -- add chk1 and enable it +alter table t21 with nocheck add constraint chk2 check (b > 0); -- add chk2 and disable it +insert into t21 values (1, 1); +-- error, not fulfilling constraint chk1 +insert into t21 values (0, 1); +-- should pass after CHECK/NOCHECK is fully supported +insert into t21 values (1, 0); +select * from t21; + +-- ALTER TABLE ... [NO]CHECK CONSTRAINT ... +-- should pass after CHECK/NOCHECK is fully supported +alter table t21 nocheck constraint chk1; -- disable chk1 +alter table t21 check constraint chk2; -- enable chk2 + +-- CREATE TABLE ... ( a int identity(...) NOT FOR REPLICATION) +create table t22 (a int identity(1,1) NOT FOR REPLICATION); +create table t23 (a int identity(1,1) NOT FOR REPLICATION NOT NULL); +-- ROWGUIDCOL syntax support +create table t24 (a uniqueidentifier ROWGUIDCOL); +create table t25 (a int); +alter table t25 add b uniqueidentifier ROWGUIDCOL; + +-- computed columns +-- CREATE TABLE(..., AS +-- ^ [ PERSISTED ] ) +create table computed_column_t1 (a nvarchar(10), b AS substring(a,1,3) UNIQUE NOT NULL); +insert into computed_column_t1 values('abcd'); +select * from computed_column_t1; + +-- test whether other constraints are working with computed columns +insert into computed_column_t1 values('abcd'); -- throws error + +-- check PERSISTED keyword +-- should be able to use columns from left and right in the expression +create table computed_column_t2 (a int, b AS (a + c) / 4 PERSISTED, c int); +insert into computed_column_t2 (a,c) values (12, 12); +select * from computed_column_t2; + +-- should throw error - order matters +create table computed_column_error (a int, b AS a/4 NOT NULL PERSISTED); + +-- should throw error if postgres syntax is used in TSQL dialect +create table computed_column_error (a int, b numeric generated always as (a/4) stored); + +-- should throw error if there is any error in computed column expression +create table computed_column_error (a nvarchar(10), b AS non_existant_function(a,1,3) UNIQUE NOT NULL); + +-- should throw error in case of nested computed columns +create table computed_column_error (a int, b as c, c as a); +create table computed_column_error (a int, b as b + 1); + +-- in case of multiple computed column, the entire statement should be rolled +-- back even when the last one throws error +create table computed_column_error (a int, b as a, c as b); +select * from computed_column_error; + +-- ALTER TABLE... ADD AS +-- ^ [ PERSISTED ] ) +alter table computed_column_t1 add c int; +alter table computed_column_t1 add d as c / 4; +insert into computed_column_t1(a, c) VALUES ('efgh', 12); +select * from computed_column_t1; + +--should thow error in case of nested computed columns + alter table computed_column_t1 add e as d; + alter table computed_column_t1 add e as e + 1; + +-- should throw error if any of the dependant columns is modified or dropped. +alter table computed_column_t1 drop column a; +alter table computed_column_t1 alter column a varchar; + +-- should throw error as rand is non-deterministic +alter table computed_column_t1 add e as rand() persisted; + +-- but rand[seed] should succeed +alter table computed_column_t1 add e as rand(1) persisted; + +-- should throw error in postgres dialect +select set_config('babelfishpg_tsql.sql_dialect', 'postgres', null); +create table computed_column_error (a int, b AS (a/4) PERSISTED NOT NULL); + +-- since we're in postgres dialect, also check the table definition whether +-- the computed column got resolved to correct datatype +\d computed_column_t1 + +set babelfishpg_tsql.sql_dialect = "tsql"; + +drop table t1; +drop table t2; +drop table t3; +drop table t4; +drop table t5; +drop table t6; +drop table t7; +drop table t8; +drop table t9; +drop table t10; +drop table t11; +drop table t12; +drop table t13; +drop table t14; +drop table t15; +drop table t16; +drop table t17; +drop table t18; +drop table t19; +drop table t20; +drop table t21; +drop table t22; +drop table t23; +drop table t24; +drop table t25; +drop table computed_column_t1; +drop table computed_column_t2; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_delete.sql b/contrib/babelfishpg_tsql/sql/test/babel_delete.sql new file mode 100644 index 00000000000..e4e33a1fd26 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_delete.sql @@ -0,0 +1,170 @@ +-- +-- Tests for DELETE clause +-- + +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql"; + +-- Negative cases when using postgres dialect + +RESET babelfishpg_tsql.sql_dialect; +SHOW babelfishpg_tsql.sql_dialect; + +CREATE TABLE delete_test_tbl ( + age int, + fname char(10), + lname char(10), + city nchar(20) +); +INSERT INTO delete_test_tbl(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (35, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname4', 'new york'), + (26, 'fname5', 'lname5', 'los angeles'), + (74, 'fname6', 'lname6', 'tokyo'), + (44, 'fname7', 'lname7', 'oslo'), + (19, 'fname8', 'lname8', 'hong kong'), + (61, 'fname9', 'lname9', 'shanghai'), + (29, 'fname10', 'lname10', 'mumbai'); + +SELECT * FROM delete_test_tbl; + +\set ON_ERROR_STOP 0 +DELETE delete_test_tbl; + +-- Positive cases when using tsql dialect +SET babelfishpg_tsql.sql_dialect = "tsql"; +SHOW babelfishpg_tsql.sql_dialect; +\set ON_ERROR_STOP 1 + +-- Prove that a user may delete rows from a table without using the FROM clause +SELECT * FROM delete_test_tbl; + +-- Test that that WHERE clause can be used without FROM +DELETE delete_test_tbl WHERE city='hong kong'; +SELECT * FROM delete_test_tbl; + +DELETE delete_test_tbl WHERE age > 50; +SELECT * FROM delete_test_tbl; + +DELETE delete_test_tbl WHERE fname IN ('fname1', 'fname2'); +SELECT * FROM delete_test_tbl; + +-- Test that DELETE works without any other clauses +DELETE delete_test_tbl; +SELECT * FROM delete_test_tbl; + +-- Test delete for joined table +CREATE TABLE delete_test_tbl2 ( + age int, + fname char(10), + lname char(10), + city nchar(20) +); + +INSERT INTO delete_test_tbl2(age, fname, lname, city) +VALUES (50, 'fname1', 'lname1', 'london'), + (34, 'fname2', 'lname2', 'paris'), + (50, 'fname3', 'lname3', 'brussels'), + (90, 'fname4', 'lname4', 'new york'), + (26, 'fname5', 'lname5', 'los angeles'), + (74, 'fname6', 'lname6', 'tokyo'), + (44, 'fname7', 'lname7', 'oslo'), + (19, 'fname8', 'lname8', 'hong kong'), + (61, 'fname9', 'lname9', 'shanghai'), + (29, 'fname10', 'lname10', 'mumbai'); + +CREATE TABLE delete_test_tbl3 ( + year int, + lname char(10), +); + +INSERT INTO delete_test_tbl3(year, lname) +VALUES (51, 'lname1'), + (34, 'lname3'), + (25, 'lname8'), + (95, 'lname9'), + (36, 'lname10'); + +CREATE TABLE delete_test_tbl4 ( + lname char(10), + city char(10), +); + +INSERT INTO delete_test_tbl4(lname, city) +VALUES ('lname8','london'), + ('lname9','tokyo'), + ('lname10','mumbai'); + +SELECT * FROM delete_test_tbl2 ORDER BY lname; +SELECT * FROM delete_test_tbl3 ORDER BY lname; +SELECT * FROM delete_test_tbl4 ORDER BY lname; + + +DELETE delete_test_tbl2 +FROM delete_test_tbl2 t2 +INNER JOIN delete_test_tbl3 t3 +ON t2.lname = t3.lname +WHERE year > 50; + +SELECT * FROM delete_test_tbl2 ORDER BY lname; + +DELETE delete_test_tbl2 +FROM delete_test_tbl3 t3 +LEFT JOIN delete_test_tbl2 t2 +ON t2.lname = t3.lname +WHERE t3.year < 30 AND t2.age > 40; + +SELECT * FROM delete_test_tbl2 ORDER BY lname; + +-- delete with outer join on multiple tables +DELETE delete_test_tbl2 +FROM delete_test_tbl4 t4 +LEFT JOIN delete_test_tbl2 t2 +ON t4.city = t2.city +LEFT JOIN delete_test_tbl3 t3 +ON t2.lname = t3.lname +WHERE t4.city = 'mumbai'; + +SELECT * FROM delete_test_tbl2 ORDER BY lname; + +-- delete when target table not shown in JoinExpr +DELETE delete_test_tbl2 +FROM delete_test_tbl4 t4 +LEFT JOIN delete_test_tbl3 t3 +ON t3.lname = t4.lname +WHERE t4.city = 'mumbai'; + +SELECT * FROM delete_test_tbl2 ORDER BY lname; + +-- delete with self join +DELETE delete_test_tbl3 +FROM delete_test_tbl3 t1 +INNER JOIN delete_test_tbl3 t2 +on t1.lname = t2.lname; + +SELECT * FROM delete_test_tbl3 ORDER BY lname; + +DELETE delete_test_tbl2 +FROM delete_test_tbl2 c +JOIN +(SELECT lname, fname, age from delete_test_tbl2) b +on b.lname = c.lname +JOIN +(SELECT lname, city, age from delete_test_tbl2) a +on a.city = c.city; +SELECT * FROM delete_test_tbl2 ORDER BY lname; + +DELETE delete_test_tbl4 +FROM +(SELECT lname, city from delete_test_tbl4) b +JOIN +(SELECT lname from delete_test_tbl4) a +on a.lname = b.lname; + +SELECT * FROM delete_test_tbl4 ORDER BY lname; + +DROP TABLE delete_test_tbl; +DROP TABLE delete_test_tbl2; +DROP TABLE delete_test_tbl3; +DROP TABLE delete_test_tbl4; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_function.sql b/contrib/babelfishpg_tsql/sql/test/babel_function.sql new file mode 100644 index 00000000000..8c0bcea77a7 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_function.sql @@ -0,0 +1,901 @@ +-- tsql stype create function/procedure is not supported in postgres dialect +CREATE FUNCTION hi_func("@message" varchar(20)) RETURNS VOID AS BEGIN PRINT @message END; +CREATE PROCEDURE hi_proc("@message" varchar(20)) AS BEGIN PRINT @message END; + +set babelfishpg_tsql.sql_dialect = "tsql"; +-- it's supported in tsql dialect +CREATE FUNCTION hi_func("@message" varchar(20)) RETURNS VOID AS BEGIN PRINT @message END; +CREATE PROCEDURE hi_proc("@message" varchar(20)) AS BEGIN PRINT @message END; +-- PROC is also supported in tsql dialect +create proc proc_1 as print 'Hello World from Babel'; +-- BABEL-219 typmod/length of sys.varchar works correctly in procudure parameter +call hi_proc('Hello World'); +call proc_1(); + +-- clean up +drop function hi_func; +drop procedure hi_proc; +drop proc proc_1; + +-- test executing pltsql function in postgres dialect +reset babelfishpg_tsql.sql_dialect; + +CREATE OR REPLACE FUNCTION test_func() RETURNS int AS $$ +BEGIN + DECLARE @a int = 1; + RETURN @a +END; +$$ LANGUAGE pltsql; + +-- should be able execute a pltsql function in postgres dialect +show babelfishpg_tsql.sql_dialect; +select test_func(); +show babelfishpg_tsql.sql_dialect; + +-- test executing pltsql trigger in postgres dialect +CREATE TABLE employees( + id SERIAL PRIMARY KEY, + first_name VARCHAR(40) NOT NULL, + last_name VARCHAR(40) NOT NULL +); + +CREATE TABLE employee_audits ( + id SERIAL PRIMARY KEY, + employee_id INT NOT NULL, + last_name VARCHAR(40) NOT NULL +); + +CREATE OR REPLACE FUNCTION log_last_name_changes() RETURNS trigger AS $$ +BEGIN + IF NEW.last_name <> OLD.last_name THEN + INSERT INTO employee_audits(employee_id,last_name) + VALUES(OLD.id,OLD.last_name); + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER last_name_changes +BEFORE UPDATE +ON employees +FOR EACH ROW +EXECUTE PROCEDURE log_last_name_changes(); + +INSERT INTO employees (first_name, last_name) VALUES ('A', 'B'); +INSERT INTO employees (first_name, last_name) VALUES ('C', 'D'); +SELECT * FROM employees; +show babelfishpg_tsql.sql_dialect; +UPDATE employees SET last_name = 'E' WHERE ID = 2; +show babelfishpg_tsql.sql_dialect; +SELECT * FROM employees; +SELECT * FROM employee_audits; + +-- test executing a plpgsql function in tsql dialect +CREATE OR REPLACE FUNCTION test_increment(i integer) RETURNS integer AS $$ +BEGIN + RETURN i + "1"; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION test_increment1(i integer) RETURNS integer AS $$ +BEGIN + RETURN i + CAST(n'1' AS varchar); +END; +$$ LANGUAGE plpgsql; + +-- test that sql_dialect is restored even when the function has error in it +set babelfishpg_tsql.sql_dialect = "tsql"; +show babelfishpg_tsql.sql_dialect; +select test_increment(1); +show babelfishpg_tsql.sql_dialect; +select test_increment1(1); +show babelfishpg_tsql.sql_dialect; + +-- test OBJECT_NAME function +select OBJECT_NAME('sys.columns'::regclass::Oid::int); +select OBJECT_NAME('boolin'::regproc::Oid::int); +select OBJECT_NAME('int4'::regtype::Oid::int); +select OBJECT_NAME(1); + +-- test OBJECT_ID function +select (OBJECT_NAME(OBJECT_ID('sys.columns')) = 'columns'); +select (OBJECT_NAME(OBJECT_ID('[columns]')) = 'columns'); +select (OBJECT_NAME(OBJECT_ID('contrib_regression.sys.columns')) = 'columns'); +select (OBJECT_NAME(OBJECT_ID('[sys].[columns]')) = 'columns'); +select (OBJECT_NAME(OBJECT_ID(N'[contrib_regression].sys.[columns]')) = 'columns'); +select (OBJECT_ID('db.sys.[tb].[col]') IS NULL); +select (OBJECT_ID('sys.babelfish_sysmail_mailitems') IS NULL); +select (OBJECT_NAME(OBJECT_ID('sys.columns', 'U')) = 'columns'); +select (OBJECT_NAME(OBJECT_ID('pg_catalog.boolin', 'FN')) = 'boolin'); +select (OBJECT_ID('sysmail_mailitems', 'P') IS NULL); +select (OBJECT_ID('boolin', 'C') IS NULL); +create table #tt(a int); +select (OBJECT_ID('#tt') IS NOT NULL); +select (OBJECT_ID('tempdb..#tt') IS NOT NULL); +select (OBJECT_ID('tempdb..#tt2') IS NULL); +drop table #tt; + +-- test SYSDATETIME function +-- Returns of type datetime2 +select pg_typeof(SYSDATETIME()); +-- test GETDATE function +-- Returns of type datetime +select pg_typeof(GETDATE()); + +-- test current_timestamp function +select pg_typeof(current_timestamp); +-- test calling with parenthesis, should fail +select current_timestamp(); + +-- test CONVERT function +-- Conversion between varchar and date/time/datetime +select CONVERT(varchar(30), CAST('2017-08-25' AS date), 102); +select CONVERT(varchar(30), CAST('13:01:59' AS time), 8); +select CONVERT(varchar(30), CAST('13:01:59' AS time), 22); +select CONVERT(varchar(30), CAST('13:01:59' AS time), 22); +select CONVERT(varchar(30), CAST('2017-08-25 13:01:59' AS datetime), 100); +select CONVERT(varchar(30), CAST('2017-08-25 13:01:59' AS datetime), 109); +select CONVERT(date, '08/25/2017', 101); +select CONVERT(time, '12:01:59', 101); +select CONVERT(datetime, '2017-08-25 01:01:59PM', 120); +select CONVERT(varchar, CONVERT(datetime2(7), '9999-12-31 23:59:59.9999999')); + +-- Conversion from float to varchar +select CONVERT(varchar(30), 11234561231231.234::float, 0); +select CONVERT(varchar(30), 11234561231231.234::float, 1); +select CONVERT(varchar(30), 11234561231231.234::float, 2); +select CONVERT(varchar(30), 11234561231231.234::float, 3); + +-- Conversion from money to varchar +select CONVERT(varchar(10), CAST(4936.56 AS MONEY), 0); +select CONVERT(varchar(10), CAST(4936.56 AS MONEY), 1); +select CONVERT(varchar(10), CAST(4936.56 AS MONEY), 2); +select CONVERT(varchar(10), CAST(-4936.56 AS MONEY), 0); + +-- Floor conversion to smallint, int, bigint +SELECT CONVERT(int, 99.9); +SELECT CONVERT(smallint, 99.9); +SELECT CONVERT(bigint, 99.9); +SELECT CONVERT(int, -99.9); +SELECT CONVERT(int, '99'); +SELECT CONVERT(int, CAST(99.9 AS double precision)); +SELECT CONVERT(int, CAST(99.9 AS real)); + +-- test TRY_CONVERT function +-- Conversion between different types and varchar +select TRY_CONVERT(varchar(30), CAST('2017-08-25' AS date), 102); +select TRY_CONVERT(varchar(30), CAST('13:01:59' AS time), 8); +select TRY_CONVERT(varchar(30), CAST('13:01:59' AS time), 22); +select TRY_CONVERT(varchar(30), CAST('2017-08-25 13:01:59' AS datetime), 109); +select TRY_CONVERT(varchar(30), 11234561231231.234::float, 0); +select TRY_CONVERT(varchar(30), 11234561231231.234::float, 1); +select TRY_CONVERT(varchar(10), CAST(4936.56 AS MONEY), 0); + +-- Wrong conversions that return NULL +select TRY_CONVERT(date, 123); +select TRY_CONVERT(time, 123); +select TRY_CONVERT(datetime, 123); +select TRY_CONVERT(money, 'asdf'); + +-- test PARSE function +-- Conversion from string to date/time/datetime +select PARSE('2017-08-25' AS date); +select PARSE('2017-08-25' AS date USING 'Cs-CZ'); +select PARSE('08/25/2017' AS date USING 'en-US'); +select PARSE('25/08/2017' AS date USING 'de-DE'); +select PARSE('13:01:59' AS time); +select PARSE('13:01:59' AS time USING 'en-US'); +select PARSE('13:01:59' AS time USING 'zh-CN'); +select PARSE('2017-08-25 13:01:59' AS datetime); +select PARSE('2017-08-25 13:01:59' AS datetime USING 'zh-CN'); +select PARSE('12:01:59' AS time); +select PARSE('2017-08-25 01:01:59PM' AS datetime); + +-- Test if unnecessary culture arg given +select PARSE('123' AS int USING 'de-DE'); + +-- test TRY_PARSE function +-- Expect null return on error +-- Conversion from string to date/time/datetime +select TRY_PARSE('2017-08-25' AS date); +select TRY_PARSE('2017-08-25' AS date USING 'Cs-CZ'); +select TRY_PARSE('789' AS date USING 'en-US'); +select TRY_PARSE('asdf' AS date USING 'de-DE'); +select TRY_PARSE('13:01:59' AS time); +select TRY_PARSE('asdf' AS time USING 'en-US'); +select TRY_PARSE('13-12-21' AS time USING 'zh-CN'); +select TRY_PARSE('2017-08-25 13:01:59' AS datetime); +select TRY_PARSE('20asdf17' AS datetime USING 'de-DE'); + +-- Wrong conversions that return NULL +select TRY_PARSE('asdf' AS numeric(3,2)); +select TRY_PARSE('123' AS datetime2); +select TRY_PARSE('asdf' AS MONEY); +select TRY_PARSE('asdf' AS int USING 'de-DE'); + +-- test serverproperty() function +-- invalid property name, should reutnr NULL +select serverproperty(n'invalid property'); +-- valid supported properties +select serverproperty(n'collation'); +select serverproperty(n'collationId'); +select serverproperty(n'IsSingleUser'); +select serverproperty(n'ServerName'); + +-- test has_dbaccess() function +SELECT set_config('babelfishpg_tsql.sql_dialect', 'postgres', false); +create role test_role; +set role 'test_role'; +show role; +set babelfishpg_tsql.sql_dialect = 'tsql'; +-- test access to current database, should return 1 +select has_dbaccess(CAST(current_database() as text)); +-- test access to an invalid database, should return NULL +select has_dbaccess(n'invalid database'); +reset role; +drop role test_role; + +-- test ISDATE function +-- test valid argument +SELECT ISDATE('12/26/2016'); +SELECT ISDATE('12-26-2016'); +SELECT ISDATE('12.26.2016'); +SELECT ISDATE('2016-12-26 23:30:05.523456'); +-- test invalid argument +SELECT ISDATE('02/30/2016'); +SELECT ISDATE('12/32/2016'); +SELECT ISDATE('1995-10-1a'); +SELECT ISDATE(NULL); + +-- test DATEFROMPARTS function +-- test valid arguments +select datefromparts(2020,12,31); +-- test invalid arguments, should fail +select datefromparts(2020, 2, 30); +select datefromparts(2020, 13, 1); +select datefromparts(-4, 3, 150); +select datefromparts(10, 55, 10.1); +select datefromparts('2020', 55, 100.1); + +-- test DATETIMEFROMPARTS function +-- test valid arguments +select datetimefromparts(2016, 12, 26, 23, 30, 5, 32); +select datetimefromparts(2016.0, 12, 26, 23, 30, 5, 32); +select datetimefromparts(2016.1, 12, 26, 23, 30, 5, 32); +select datetimefromparts(2016, 12, 26.99, 23, 30, 5, 32); +select datetimefromparts(2016, 12.90, 26, 23, 30, 5, 32); +-- test invalid arguments +select datetimefromparts(2016, 2, 30, 23, 30, 5, 32); +select datetimefromparts(2016, 12, 26, 23, 30, 5); +select datetimefromparts(2016, 12, 26, 23, 30, 5, NULL); + +-- test DATEPART function +-- test all valid datepart arguments +select datepart(year, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(yyyy, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(yy, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(quarter, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(qq, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(q, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(month, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(mm, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(m, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(dayofyear, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(dy, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(day, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(dd, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(d, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(week, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(wk, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(ww, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(weekday, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(dw, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(hour, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(hh, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(minute, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(n, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(second, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(ss, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(s, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(millisecond, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(ms, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(microsecond, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(mcs, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(nanosecond, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(ns, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(tzoffset, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(tz, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(iso_week, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(isowk, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datepart(isoww, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +-- test different types of date/time arguments +select datepart(month, '2016-12-26 23:30:05.523'::sys.datetime); +select datepart(quarter, '2016-12-26 23:30:05.523456'::datetime2); +select datepart(hour, '2016-12-26 23:30:05'::smalldatetime); +select datepart(dayofyear, '2016-12-26'::date); +select datepart(second, '04:12:34.876543'::time); +-- test edge cases: try to get datepart that does not exist in the argument +select datepart(year, cast('12:10:30.123' as time)); +select datepart(yyyy, cast('12:10:30.123' as time)); +select datepart(yy, cast('12:10:30.123' as time)); +select datepart(quarter, cast('12:10:30.123' as time)); +select datepart(qq, cast('12:10:30.123' as time)); +select datepart(q, cast('12:10:30.123' as time)); +select datepart(month, cast('12:10:30.123' as time)); +select datepart(mm, cast('12:10:30.123' as time)); +select datepart(m, cast('12:10:30.123' as time)); +select datepart(dayofyear, cast('12:10:30.123' as time)); +select datepart(dy, cast('12:10:30.123' as time)); +select datepart(y, cast('12:10:30.123' as time)); +select datepart(day, cast('12:10:30.123' as time)); +select datepart(dd, cast('12:10:30.123' as time)); +select datepart(d, cast('12:10:30.123' as time)); +select datepart(week, cast('12:10:30.123' as time)); +select datepart(wk, cast('12:10:30.123' as time)); +select datepart(ww, cast('12:10:30.123' as time)); +select datepart(weekday, cast('12:10:30.123' as time)); +select datepart(dw, cast('12:10:30.123' as time)); +select datepart(tzoffset, cast('12:10:30.123' as time)); +select datepart(tz, cast('12:10:30.123' as time)); +select datepart(iso_week, cast('12:10:30.123' as time)); +select datepart(isowk, cast('12:10:30.123' as time)); +select datepart(isoww, cast('12:10:30.123' as time)); +select datepart(hour, cast('2016-12-26' as date)); +select datepart(hh, cast('2016-12-26' as date)); +select datepart(minute, cast('2016-12-26' as date)); +select datepart(n, cast('2016-12-26' as date)); +select datepart(second, cast('2016-12-26' as date)); +select datepart(ss, cast('2016-12-26' as date)); +select datepart(s, cast('2016-12-26' as date)); +select datepart(millisecond, cast('2016-12-26' as date)); +select datepart(ms, cast('2016-12-26' as date)); +select datepart(microsecond, cast('2016-12-26' as date)); +select datepart(mcs, cast('2016-12-26' as date)); +select datepart(nanosecond, cast('2016-12-26' as date)); +select datepart(ns, cast('2016-12-26' as date)); +-- test invalid interval, expect error +select datepart(invalid_interval, cast('2016-12-26 23:30:05.523456' as date)); +select datepart(invalidinterval, cast('12:10:30.123' as time)); + +-- test DATENAME function +select datename(year, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datename(dd, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datename(weekday, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datename(dw, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datename(month, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datename(mm, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datename(m, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select datename(isowk, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +-- test invalid argument, expect error +select datename(invalid_interval, cast('2016-12-26 23:30:05.523456' as date)); + +-- test DATEFIRST option, together DATEPART function +-- This shows the return value for the week and weekday datepart for '2007-04-21' for each SET DATEFIRST argument. +-- January 1, 2007 falls on a Monday. April 21, 2007 falls on a Saturday. +-- DATEFIRST week weekday +-- 1 16 6 +-- 2 17 5 +-- 3 17 4 +-- 4 17 3 +-- 5 17 2 +-- 6 17 1 +-- 7 16 7 +select @@datefirst; +set datefirst 1; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); +set datefirst 2; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); +set datefirst 3; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); +set datefirst 4; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); +set datefirst 5; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); +set datefirst 6; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); +set datefirst 7; +select datepart(week, '2007-04-21'::date), datepart(weekday, '2007-04-21'::date); +-- test edge case: date within the week of Jan. 1st +select datepart(week, '2007-01-01'::date), datepart(weekday, '2007-01-01'::date); +select datepart(week, '2007-01-02'::date), datepart(weekday, '2007-01-02'::date); +select datepart(week, '2007-01-03'::date), datepart(weekday, '2007-01-03'::date); +select datepart(week, '2007-01-04'::date), datepart(weekday, '2007-01-04'::date); +select datepart(week, '2007-01-05'::date), datepart(weekday, '2007-01-05'::date); +select datepart(week, '2007-01-06'::date), datepart(weekday, '2007-01-06'::date); +-- test edge case: date just outside the week of Jan. 1st +select datepart(week, '2007-01-07'::date), datepart(weekday, '2007-01-07'::date); + +-- test DATEDIFF function +select datediff(year, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(quarter, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(month, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(dayofyear, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(day, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(week, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(hour, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(minute, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(second, '2037-03-01 23:30:05.523'::sys.datetime, '2036-02-28 23:30:05.523'::sys.datetime); +select datediff(millisecond, '2036-02-28 01:23:45.234'::sys.datetime, '2036-02-28 01:23:45.123'::sys.datetime); +select datediff(microsecond, '2036-02-28 01:23:45.234'::sys.datetime, '2036-02-28 01:23:45.123'::sys.datetime); +select datediff(nanosecond, '2036-02-28 01:23:45.234'::sys.datetime, '2036-02-28 01:23:45.123'::sys.datetime); +-- test different types of date/time arguments +select datediff(minute, '2016-12-26 23:30:05.523456+8'::datetimeoffset, '2016-12-31 23:30:05.523456+8'::datetimeoffset); +select datediff(quarter, '2016-12-26 23:30:05.523456'::datetime2, '2018-08-31 23:30:05.523456'::datetime2); +select datediff(hour, '2016-12-26 23:30:05'::smalldatetime, '2016-12-28 21:29:05'::smalldatetime); +select datediff(year, '2037-03-01'::date, '2036-02-28'::date); + +-- test DATEADD function +select dateadd(year, 2, '20060830'::datetime); +select dateadd(quarter, 2, '20060830'::datetime); +select dateadd(month, 1, '20060831'::datetime); +select dateadd(dayofyear, 2, '20060830'::datetime); +select dateadd(day, 2, '20060830'::datetime); +select dateadd(week, 2, '20060830'::datetime); +select dateadd(weekday, 2, '20060830'::datetime); +select dateadd(hour, 2, '20060830'::datetime); +select dateadd(minute, 2, '20060830'::datetime); +select dateadd(second, 2, '20060830'::datetime); +select dateadd(millisecond, 123, '20060830'::datetime); +select dateadd(microsecond, 123456, '20060830'::datetime); +select dateadd(nanosecond, 123456, '20060830'::datetime); +-- test different types of date/time arguments +select dateadd(hour, 2, '23:12:34.876543'::time); +select dateadd(quarter, 3, '2037-03-01'::date); +select dateadd(minute, 70, '2016-12-26 23:30:05.523456+8'::datetimeoffset); +select dateadd(month, 2, '2016-12-26 23:30:05.523456'::datetime2); +select dateadd(second, 56, '2016-12-26 23:30:05'::smalldatetime); +-- test negative argument +select dateadd(year, -2, '20060830'::datetime); +select dateadd(month, -20, '2016-12-26 23:30:05.523456'::datetime2); +select dateadd(hour, -2, '01:12:34.876543'::time); +select dateadd(minute, -70, '2016-12-26 00:30:05.523456+8'::datetimeoffset); +select dateadd(second, -56, '2016-12-26 00:00:55'::smalldatetime); +-- test return type +select pg_typeof(dateadd(hour, -2, '01:12:34.876543'::time)); +select pg_typeof(dateadd(second, -56, '2016-12-26 00:00:55'::smalldatetime)); +select pg_typeof(dateadd(year, -2, '20060830'::datetime)); +select pg_typeof(dateadd(month, -20, '2016-12-26 23:30:05.523456'::datetime2)); +select pg_typeof(dateadd(minute, -70, '2016-12-26 00:30:05.523456+8'::datetimeoffset)); +-- test illegal usage +select dateadd(minute, 2, '2037-03-01'::date); +select dateadd(day, 4, '04:12:34.876543'::time); +-- test using variables, instead of constants, for the second parameter +create table dateadd_table(a int, b datetime); +insert into dateadd_table values(1, '2020-10-29'::datetime); +select * from dateadd_table; +update dateadd_table set b = dateadd(dd, a, '2020-10-30'::datetime); +select * from dateadd_table; +create procedure dateadd_procedure as +begin + declare @d int = 1 + update dateadd_table set b = dateadd(dd, @d, CAST('2020-10-31' AS datetime)) +end; +call dateadd_procedure(); +select * from dateadd_table; + +-- test CHARINDEX function +select CHARINDEX('hello', 'hello world'); +select CHARINDEX('hello ', 'hello world'); +select CHARINDEX('hello world', 'hello'); +-- test NULL input +select CHARINDEX(NULL, NULL); +select CHARINDEX(NULL, 'string'); +select CHARINDEX('pattern', NULL); +select CHARINDEX('pattern', 'string', NULL); +-- test start_location parameter +select CHARINDEX('hello', 'hello world', -1); +select CHARINDEX('hello', 'hello world', 0); +select CHARINDEX('hello', 'hello world', 1); +select CHARINDEX('hello', 'hello world', 2); +select CHARINDEX('world', 'hello world', 6); +select CHARINDEX('world', 'hello world', 7); +select CHARINDEX('world', 'hello world', 8); +select CHARINDEX('is', 'This is a string'); +select CHARINDEX('is', 'This is a string', 4); + +-- test STUFF function +select STUFF(n'abcdef', 2, 3, n'ijklmn'); +select STUFF(N' abcdef', 2, 3, N'ijklmn '); +select STUFF(N'abcdef', 2, 3, N' ijklmn '); +select STUFF(N'abcdef', 2, 3, N'ijklmn '); +-- test corner cases +-- when start is negative or zero or longer than expr, return NULL +select STUFF(n'abcdef', -1, 3, n'ijklmn'); +select STUFF(n'abcdef', 0, 3, n'ijklmn'); +select STUFF(n'abcdef', 7, 3, n'ijklmn'); +-- when length is negative, return NULL +select STUFF(n'abcdef', 2, -3, n'ijklmn'); +-- when length is zero, just insert without deleting +select STUFF(n'abcdef', 2, 0, n'ijklmn'); +-- when length is longer than expr, delete up to the last character in expr +select STUFF(n'abcdef', 2, 7, n'ijklmn'); +-- when replace_expr is NULL, just delete without inserting +select STUFF(n'abcdef', 2, 3, NULL); +-- when argument are type unknown +select STUFF('abcdef', 2, 3, 'ijklmn'); +select STUFF('abcdef', 2, 3, n'ijklmn'); +select STUFF(n'abcdef', 2, 3, 'ijklmn'); +-- when argument are type text +SELECT STUFF(CAST('abcdef' as text), 2, 3, CAST('ijklmn' as text)); +SELECT STUFF(CAST('abcdef' as text), 2, 3, 'ijklmn'); +SELECT STUFF('abcdef', 2, 3, CAST('ijklmn' as text)); +-- when argument are type sys.varchar +SELECT STUFF(CAST('abcdef' as sys.varchar), 2, 3, CAST('ijklmn' as sys.varchar)); +SELECT STUFF('abcdef', 2, 3, CAST('ijklmn' as sys.varchar)); +SELECT STUFF(CAST('abcdef' as sys.varchar), 2, 3, 'ijklmn'); + +-- test ROUND function +-- test rounding to the left of decimal point +select ROUND(748.58, -1); +select ROUND(748.58, -2); +select ROUND(748.58, -3); +select ROUND(748.58, -4); +select ROUND(-648.1234, -2); +select ROUND(-648.1234, -3); +select ROUND(-1548.1234, -3); +select ROUND(-1548.1234, -4); +-- test NULL input +select ROUND(NULL, -3); +select ROUND(748.58, NULL); +-- test rounding +SELECT ROUND(123.9994, 3); +SELECT ROUND(123.9995, 3); +SELECT ROUND(123.4545, 2); +SELECT ROUND(123.45, -2); +-- test function parameter, i.e. truncation when not NULL or 0 +SELECT ROUND(150.75, 0); +SELECT ROUND(150.75, 0, 0); +SELECT ROUND(150.75, 0, NULL); +SELECT ROUND(150.75, 0, 1); +-- test negative numbers +SELECT ROUND(-150.49, 0); +SELECT ROUND(-150.75, 0); +SELECT ROUND(-150.49, 0, 1); +SELECT ROUND(-150.75, 0, 1); + +-- test SELECT ROUND(col, ) +create table t1 (col numeric(4,2)); +insert into t1 values (64.24); +insert into t1 values (79.65); +insert into t1 values (NULL); +select ROUND(col, 3) from t1; +select ROUND(col, 2) from t1; +select ROUND(col, 1) from t1; +select ROUND(col, 0) from t1; +select ROUND(col, -1) from t1; +select ROUND(col, -2) from t1; +select ROUND(col, -3) from t1; +select ROUND(col, 1, 1) from t1; +drop table t1; + +-- test DAY function +select DAY(CAST('2016-12-26 23:30:05.523456+8' AS datetimeoffset)); +select DAY(CAST('2016-12-26 23:30:05.523456' AS datetime2)); +select DAY(CAST('2016-12-26 23:30:05' AS smalldatetime)); +select DAY(CAST('04:12:34.876543' AS time)); +select DAY(CAST('2037-03-01' AS date)); +select DAY(CAST('2037-03-01 23:30:05.523' AS sys.datetime)); +-- test MONTH function +select MONTH('2016-12-26 23:30:05.523456+8'::datetimeoffset); +select MONTH('2016-12-26 23:30:05.523456'::datetime2); +select MONTH('2016-12-26 23:30:05'::smalldatetime); +select MONTH('04:12:34.876543'::time); +select MONTH('2037-03-01'::date); +select MONTH('2037-03-01 23:30:05.523'::sys.datetime); +-- test YEAR function +select YEAR('2016-12-26 23:30:05.523456+8'::datetimeoffset); +select YEAR('2016-12-26 23:30:05.523456'::datetime2); +select YEAR('2016-12-26 23:30:05'::smalldatetime); +select YEAR('04:12:34.876543'::time); +select YEAR('2037-03-01'::date); +select YEAR('2037-03-01 23:30:05.523'::sys.datetime); + +-- test SPACE function +select SPACE(NULL); +select SPACE(2); +select LEN(SPACE(5)); +select DATALENGTH(SPACE(5)); + +-- test COUNT and COUNT_BIG aggregate function +CREATE TABLE t2(a int, b int); +INSERT INTO t2 VALUES(1, 100); +INSERT INTO t2 VALUES(2, 200); +INSERT INTO t2 VALUES(NULL, 300); +INSERT INTO t2 VALUES(2, 400); +CREATE TABLE t3(a varchar(255), b varchar(255),c int); +INSERT INTO t3 VALUES('xyz', 'a',1); +INSERT INTO t3 VALUES('xyz', 'b',1); +INSERT INTO t3 VALUES('abc', 'a',2); +INSERT INTO t3 VALUES('abc', 'b',2); +INSERT INTO t3 VALUES('efg', 'a',3); +INSERT INTO t3 VALUES('efg', 'b',3); +INSERT INTO t3 VALUES(NULL, NULL, 1); + +-- Aggregation Function Syntax +-- COUNT[_BIG] ( { [ [ ALL | DISTINCT ] expression ] | * } ) +-- should return all rows - 4 +SELECT COUNT(*) from t2; +SELECT pg_typeof(COUNT(*)) from t2; +SELECT COUNT_BIG(*) from t2; +SELECT pg_typeof(COUNT_BIG(*)) from t2; +-- should return all rows where a is not NULL - 3 +SELECT COUNT(a) from t2; +SELECT pg_typeof(COUNT(a)) from t2; +SELECT COUNT_BIG(a) from t2; +SELECT pg_typeof(COUNT_BIG(a)) from t2; +-- should return all rows where a is not NULL - 3 +SELECT COUNT(ALL a) from t2; +SELECT pg_typeof(COUNT(ALL a)) from t2; +SELECT COUNT_BIG(ALL a) from t2; +SELECT pg_typeof(COUNT_BIG(ALL a)) from t2; +-- should return all rows where a is distinct - 2 +SELECT COUNT(DISTINCT a) from t2; +SELECT pg_typeof(COUNT(DISTINCT a)) from t2; +SELECT COUNT_BIG(DISTINCT a) from t2; +SELECT pg_typeof(COUNT_BIG(DISTINCT a)) from t2; + +-- Analytic Function Syntax +-- COUNT[_BIG] ( [ ALL ] { expression | * } ) OVER ( [ ] ) +SELECT pg_typeof(COUNT(*) OVER (PARTITION BY a)) from t2; +SELECT pg_typeof(COUNT_BIG(*) OVER (PARTITION BY a)) from t2; +SELECT pg_typeof(COUNT(a) OVER (PARTITION BY a)) from t2; +SELECT pg_typeof(COUNT_BIG(a) OVER (PARTITION BY a)) from t2; +SELECT pg_typeof(COUNT(ALL a) OVER (PARTITION BY a)) from t2; +SELECT pg_typeof(COUNT_BIG(ALL a) OVER (PARTITION BY a)) from t2; +SELECT COUNT(*) from t3; +SELECT a, b, COUNT(*) OVER () from t3; +-- The result for order by is different in sql server because we have +-- an ordering issue for null type (JIRA: BABEL-788) +SELECT a, b, COUNT(*) OVER (ORDER BY a) from t3; +SELECT a, b, COUNT(*) OVER (ORDER BY a DESC) from t3; +SELECT a, b, COUNT(*) OVER(PARTITION BY a) from t3; +SELECT a, b, COUNT(*) OVER(PARTITION BY a ORDER BY b) from t3; +SELECT a, b, COUNT(*) OVER(PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) from t3; +SELECT COUNT_BIG(*) from t3; +SELECT a, b, COUNT_BIG(*) OVER () from t3; +SELECT a, b, COUNT_BIG(*) OVER (ORDER BY a) from t3; +SELECT a, b, COUNT_BIG(*) OVER (ORDER BY a DESC) from t3; +SELECT a, b, COUNT_BIG(*) OVER(PARTITION BY a) from t3; +SELECT a, b, COUNT_BIG(*) OVER(PARTITION BY a ORDER BY b) from t3; +SELECT a, b, COUNT_BIG(*) OVER(PARTITION BY a ORDER BY b ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) from t3; + +-- COUNT(*) takes no parameters and does not support the use of DISTINC, expect error +SELECT COUNT(DISTINCT *) from t3; +SELECT COUNT(ALL *) from t3; +DROP TABLE t2; +DROP TABLE t3; + +-- clean up +drop function test_func; +drop table employees; +drop table employee_audits; +drop function log_last_name_changes; +drop function test_increment; +drop function test_increment1; +drop table dateadd_table; +drop procedure dateadd_procedure; + +-- test inline table-valued functions +-- simple case +create function itvf1 (@number int) returns table as return (select 1 as a, 2 as b); +select * from itvf1(5); +-- should fail because column names are not specified +create function itvf2 (@number int) returns table as return (select 1, 2); + +-- select from a table +create table example_table(name text, age int); +insert into example_table values('hello', 3); +-- should have 'a' and 'b' as result column names +create function itvf3 (@number int) returns table as return (select name as a, age as b from example_table); +select * from itvf3(5); +-- test returning multiple rows +insert into example_table values('hello1', 4); +insert into example_table values('hello2', 5); +insert into example_table values('hello3', 6); +select * from itvf3(5); + +-- invoke a function +create function itvf4 (@number int) returns table as +return (select sys.serverproperty(N'collation') as property1, sys.serverproperty(N'IsSingleUser') as property2); +select * from itvf4(5); + +-- case where the return table has only one column - Postgres considers these as +-- scalar functions +create or replace function itvf5 (@number int) returns table as return (select 1 as a); +select * from itvf5(5); +create or replace function itvf6 (@number int) returns table as +return (select sys.serverproperty(N'collation') as property); +select * from itvf6(5); + +-- complex queries with use of function parameter +create table id_name(id int, name text); +insert into id_name values(1001, 'adam'); +insert into id_name values(1002, 'bob'); +insert into id_name values(1003, 'chaz'); +insert into id_name values(1004, 'dave'); +insert into id_name values(1005, 'ed'); + +create table id_score(id int, score int); +insert into id_score values(1001, 90); +insert into id_score values(1001, 70); +insert into id_score values(1002, 90); +insert into id_score values(1002, 80); +insert into id_score values(1003, 80); +insert into id_score values(1003, 70); +insert into id_score values(1004, 80); +insert into id_score values(1004, 60); +insert into id_score values(1005, 80); +insert into id_score values(1005, 100); + +create function itvf7 (@number int) returns table as return ( +select n.id, n.name as first_name, sum(s.score) as total_score +from id_name as n +join id_score as s +on n.id = s.id +where s.id <= @number +group by n.id, n.name +order by n.id +); + +select * from itvf7(1004); + +-- test inline table-valued function with table-valued parameter +create type tableType as table( + a text not null, + b int primary key, + c int); + +create function itvf8 (@number int, @tableVar tableType READONLY) returns table as return ( +select n.id, n.name as first_name, sum(s.score) as total_score +from id_name as n +join id_score as s +on n.id = s.id +where s.id <= @number and s.id in (select c from @tableVar) +group by n.id, n.name +order by n.id +); + +create procedure itvf8_proc as +begin + declare @tableVariable tableType + insert into @tableVariable values('hello1', 1, 1001) + insert into @tableVariable values('hello2', 2, 1002) + select * from itvf8(1004, @tableVariable) +end; + +call itvf8_proc(); + +-- test using parameter in projection list +create function itvf9(@number int) returns table as return ( +select @number as a from id_name +); + +select * from itvf9(1); + +-- test invalid ITVFs +-- function does not have RETURN QUERY +create function itvf10(@number int) returns table as BEGIN select * from id_name END; +-- function has more than one RETURN QUERY +create function itvf11(@number int) returns table as +BEGIN + return select * from id_name + return select id from id_name +END; + +-- test creating ITVF in a transaction and rollback - should still work as +-- normal despite the function validator's modification of the pg_proc entry +begin transaction; +create function itvf12(@number int) returns table as return ( +select @number as a from id_name +); +rollback; +select * from itvf12(1); + +-- "AS" keyword is optional in TSQL function +\tsql on +create function babel651_f() returns int +begin + return 1 +end +go +create table babel651_t(a int); +go +create function babel651_itvf() returns table + return (select * from babel651_t) +go +create function babel651_mstvf(@i int) returns @tableVar table +( + a text not null +) +begin + insert into @tableVar values('hello1'); +end; +go + +select babel651_f(); +go +select * from babel651_itvf(); +go +select * from babel651_mstvf(1); +go +\tsql off + +-- clean up +drop function itvf1; +drop table example_table; +drop function itvf3; +drop function itvf4; +drop function itvf5; +drop function itvf6; +drop table id_name; +drop table id_score; +drop function itvf7; +drop procedure itvf8_proc; +drop function itvf8; +drop type tableType; +drop function itvf9; +drop table babel651_t; +drop function babel651_f; +drop function babel651_itvf; +drop function babel651_mstvf; + +-- test RETURN not followed by a semicolon +\tsql on +create function test_return1(@stringToSplit VARCHAR(MAX)) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + RETURN +END +GO +select * from test_return1('test'); +GO +drop function test_return1; +GO +create function test_return2(@stringToSplit VARCHAR(MAX)) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + RETURN; +END +GO +select * from test_return2('test'); +GO +drop function test_return2; +GO +create function test_return3(@a int) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + IF @a = 1 + RETURN + SELECT @a = 2 + INSERT into @returnList values('abc') + RETURN +END +GO +select * from test_return3(1); +GO +select * from test_return3(2); +GO +drop function test_return3; +GO +create function test_return4(@a int) +RETURNS @returnList TABLE([Name] [nvarchar] (500)) +AS +BEGIN + IF @a = 1 + RETURN + ELSE + SELECT @a = 2 + INSERT into @returnList values('abc') + RETURN +END +GO +select * from test_return4(1); +GO +select * from test_return4(2); +GO +drop function test_return4; +GO +\tsql off diff --git a/contrib/babelfishpg_tsql/sql/test/babel_init.sql b/contrib/babelfishpg_tsql/sql/test/babel_init.sql new file mode 100644 index 00000000000..0182abea18a --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_init.sql @@ -0,0 +1,12 @@ +SET client_min_messages TO WARNING; +CREATE EXTENSION "babelfishpg_tsql" cascade; +RESET client_min_messages; +SET babelfishpg_tsql.sql_dialect = 'tsql'; +reset babelfishpg_tsql.sql_dialect; + +--BABEL-978: make sure error stack is not exceeded in/after extension creation. +CREATE PROCEDURE myproc() LANGUAGE plpgsql AS $$ +BEGIN +CREATE invalid_syntax; +END; +$$; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_like.sql b/contrib/babelfishpg_tsql/sql/test/babel_like.sql new file mode 100644 index 00000000000..61aeead8b20 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_like.sql @@ -0,0 +1,29 @@ +set babelfishpg_tsql.sql_dialect = 'tsql'; +select relname from pg_class where relname like '['; +select relname from pg_class where relname like ']'; +select relname from pg_class where relname like '[]'; +select relname from pg_class where relname like NULL; +select relname from pg_class where relname like ''; +select relname from pg_class where relname like 'pg[1:9]class'; +select relname from pg_class where relname like 'pg\[1:9\]class'; +select relname from pg_class where relname like 'pg\[1:9 ]class'; +select relname from pg_class where relname like 'pg [1:9\]class'; + +select relname from pg_class where relname like 'pg*[1:9*]class' escape '*'; +select relname from pg_class where relname like 'pg [1:9*]class' escape '*'; +select relname from pg_class where relname like 'pg*[1:9 ]class' escape '*'; + +set babelfishpg_tsql.sql_dialect = 'postgres'; +select relname from pg_class where relname like '['; +select relname from pg_class where relname like ']'; +select relname from pg_class where relname like '[]'; +select relname from pg_class where relname like NULL; +select relname from pg_class where relname like ''; +select relname from pg_class where relname like 'pg[1:9]class'; +select relname from pg_class where relname like 'pg\[1:9\]class'; +select relname from pg_class where relname like 'pg\[1:9 ]class'; +select relname from pg_class where relname like 'pg [1:9\]class'; + +select relname from pg_class where relname like 'pg*[1:9*]class' escape '*'; +select relname from pg_class where relname like 'pg [1:9*]class' escape '*'; +select relname from pg_class where relname like 'pg*[1:9 ]class' escape '*'; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_procedures.sql b/contrib/babelfishpg_tsql/sql/test/babel_procedures.sql new file mode 100644 index 00000000000..1f97245ed1a --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_procedures.sql @@ -0,0 +1,80 @@ +CREATE EXTENSION IF NOT EXISTS "babelfishpg_tsql"; + +-- test sp_unprepare +create table t1 ( a int ); +insert into t1 values (1); +insert into t1 values (2); + +prepare s_100 (int) as select * from t1 where a = $1; +execute s_100(1); + +set babelfishpg_tsql.sql_dialect = "tsql"; + +call sp_unprepare(null); +call sp_unprepare(10); +call sp_unprepare(100); + +execute s_100(1); + +\tsql on + +-- Test 'with' in procedure language that shouldn't be recognized as CTE +create or replace procedure prc1 as begin +select count(*) from t1 with (tablockx) +end +go + +call prc1(); +go + +create or replace procedure prc2 as begin +insert into t1 select * from t1 with (tablockx); +end +go + +call prc2(); +go + +-- Test C-style comment with a following '@' +create procedure prc3 +/* comment */ +@p int +as +select 123; +go + +-- BABEL-831 +CREATE PROCEDURE babel_831_proc +AS +BEGIN + CREATE TABLE #t(id INT) + DECLARE Babel831Cursor CURSOR FOR SELECT id FROM #t + OPEN Babel831Cursor + CLOSE Babel831Cursor +END +GO + +exec babel_831_proc +GO + +--BABEL-1099 +CREATE TABLE babel_1099_t1 (a int); +CREATE TABLE babel_1099_t2 (a int); +CREATE PROCEDURE babel_1099_proc AS + TRUNCATE TABLE babel_1099_t1 + TRUNCATE TABLE babel_1099_t2 +GO + +EXEC babel_1099_proc +GO + +\tsql off + +drop table t1; +drop table babel_1099_t1; +drop table babel_1099_t2; +drop procedure prc1; +drop procedure prc2; +drop procedure prc3; +drop procedure babel_831_proc; +drop procedure babel_1099_proc; \ No newline at end of file diff --git a/contrib/babelfishpg_tsql/sql/test/babel_set_command.sql b/contrib/babelfishpg_tsql/sql/test/babel_set_command.sql new file mode 100644 index 00000000000..32adda02419 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_set_command.sql @@ -0,0 +1,57 @@ +-- Simple SET +SET lc_messages ='fr_FR.utf8'; +show lc_messages; +reset lc_messages; + +-- Inside transaction with commit +BEGIN; + SET lc_messages = 'fr_FR.utf8'; +COMMIT; +show lc_messages; +reset lc_messages; + +-- Inside transaction with rollback +BEGIN; + SET lc_messages = 'fr_FR.utf8'; +ROLLBACK; +show lc_messages; +reset lc_messages; + +-- Inside transaction with rollback to savepoint +BEGIN; + SET lc_messages = 'en_GB.utf8'; + SAVEPOINT SP1; + SET lc_messages = 'fr_FR.utf8'; + show lc_messages; + ROLLBACK TO SAVEPOINT SP1; + show lc_messages; +ROLLBACK; +show lc_messages; +reset lc_messages; + +-- Inside procedure +CREATE PROCEDURE lc_proc() +AS $$ +begin + SET lc_messages ='fr_FR.utf8'; + commit; +end; +$$ LANGUAGE plpgsql; +CALL lc_proc(); +show lc_messages; +drop procedure lc_proc(); +reset lc_messages; + +CREATE PROCEDURE lc_proc() +AS $$ +begin + SET lc_messages ='fr_FR.utf8'; + rollback; +end; +$$ LANGUAGE plpgsql; +CALL lc_proc(); +show lc_messages; + +-- Cleanup +drop procedure lc_proc(); +reset lc_messages; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_table_type.sql b/contrib/babelfishpg_tsql/sql/test/babel_table_type.sql new file mode 100644 index 00000000000..ad15d03d739 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_table_type.sql @@ -0,0 +1,589 @@ +CREATE USER my_test_user; +GRANT CREATE ON DATABASE contrib_regression TO my_test_user; +SET SESSION AUTHORIZATION my_test_user; + +-- table type is only supported in tsql dialect +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int); + +set babelfishpg_tsql.sql_dialect = 'tsql'; +\tsql ON + +-- table type supports all CREATE TABLE element lists +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int); +GO + +-- a table with the same name is created +select * from tableType; +GO + +-- create type with same name, should fail +CREATE TYPE tableType as (d int, e int); +GO + +-- create table with same name, should succeed +-- TODO: BABEL-689: Postgres doesn't support this yet, because CREATE TABLE will automatically +-- create a composite type as well, which will cause name collision +CREATE TABLE tableType(d int, e int); +GO + +-- dropping the table should fail, as it depends on the table type +DROP TABLE tableType; +GO + +-- dropping the table type should drop the table as well +DROP TYPE tableType; +GO +SELECT * FROM tableType; +GO + +-- creating index (other than primary and unique keys) during table type creation is not +-- yet supported +-- TODO: BABEL-688: fully support TSQL CREATE TABLE syntax +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int, + d int index idx1 nonclustered, + index idx2(c), + index idx3(e), + e varchar); +GO + +-- test dotted prefixes of the table type name +-- allowed to have one dotted prefix +CREATE TYPE public.tableType AS table(a int, b int); +GO + +DROP TYPE public.tableType; +GO + +-- not allowed to have more than one dotted prefix +CREATE TYPE postgres.public.tableType AS table(a int, b int); +GO + +CREATE TYPE tableType AS table( + a text not null, + b int primary key, + c int); +GO + +-- test declaring variables with table type +create procedure table_var_procedure as +begin + declare @a int; + declare @b tableType; + insert into @b values('hello', 4, 100); + select count(*) from @b; +end; +GO +CALL table_var_procedure(); +GO +DROP PROCEDURE table_var_procedure; +GO + +-- test declaring table variable without table type, and doing DMLs +create procedure table_var_procedure as +begin + declare @tableVar table (a int, b int); + insert into @tableVar values(1, 100); + insert into @tableVar values(2, 200); + update @tableVar set b = 1000 where a = 1; + delete from @tableVar where a = 2; + select * from @tableVar; +end; +GO +CALL table_var_procedure(); +GO +DROP PROCEDURE table_var_procedure; +GO + +-- test declaring table variable with whitespace before column definition +create procedure table_var_procedure as +begin + declare @tableVar1 table + (a int, b int); + insert into @tableVar1 values(1, 100); + declare @tableVar2 table (c int, d varchar); + insert into @tableVar2 values(1, 'a'); + select * from @tableVar1 t1 join @tableVar2 t2 on t1.a = t2.c; +end; +GO +CALL table_var_procedure(); +GO +DROP PROCEDURE table_var_procedure; +GO + +-- test MERGE on table variables +-- TODO: BABEL-877 Support MERGE +/* +create procedure merge_proc as +begin + declare @tv1 table(a int); + insert into @tv1 values (200); + + declare @tv2 table(b int); + insert into @tv2 values (100); + insert into @tv2 values (200); + + merge into @tv1 using @tv2 on a=b + when not matched then insert (a) values(b) + when matched then update set a = a + b; + + select * from @tv1; +end; +GO +CALL merge_proc(); +GO +-- result should have two rows, 400 and 100. +DROP PROCEDURE merge_proc; +GO +*/ + +-- test declaring a variable whose name is already used - should throw error +create procedure dup_var_name_procedure as +begin + declare @a int; + declare @a tableType; +end; +GO + +-- test declaring a variable whose name is already used as table name - should work +create table @test_table (d int); +GO +create procedure dup_var_name_procedure as +begin + declare @test_table tableType; + insert into @test_table values('hello1', 1, 100); + select * from @test_table; +end; +GO +call dup_var_name_procedure(); +GO +drop procedure dup_var_name_procedure; +GO +drop table @test_table; +GO + +-- test assigning to table variables, should not be allowed +create table test_table(a int, b int); +GO +insert into test_table values(1, 10); +GO +create procedure assign_proc as +begin + declare @tableVar table (a int, b int); + set @tableVar = test_table; +end; +GO + +-- test selecting into table variables, should not be allowed +create procedure select_into_proc as +begin + declare @tableVar table (a int, b int); + select * into @tableVar from test_table; +end; +GO + +-- test truncating table variables, should not be allowed +create procedure truncate_proc as +begin + declare @tableVar table (a int, b int); + insert into @tableVar values(1, 2); + truncate table @tableVar; + select * from @tableVar; +end; +GO + +-- test JOIN on table variables, on both sides +create procedure join_proc1 as +begin + declare @tableVar table (a int, b int, c int); + insert into @tableVar values(1, 2, 3); + select * from test_table t inner join @tableVar tv on t.a = tv.a; +end; +GO +CALL join_proc1(); +GO +DROP PROCEDURE join_proc1; + +create procedure join_proc2 as +begin + declare @tableVar table (a int, b int, c int); + insert into @tableVar values(1, 2, 3); + select * from @tableVar tv inner join test_table t on tv.a = t.a; +end; +GO +CALL join_proc2(); +GO +DROP PROCEDURE join_proc2; +GO + +-- test altering table variables, should not be allowed +create procedure alter_proc as +begin + declare @tableVar table (a int); + alter table @tableVar add b int; + select * from @tableVar; +end; +GO + +-- test using the same variables as source and target +create procedure source_target_proc as +begin + declare @tv table (a int); + insert into @tv values (1); + insert into @tv select a+1 from @tv; + insert into @tv select a+2 from @tv; + insert into @tv select a+4 from @tv; + select * from @tv; +end; +GO +CALL source_target_proc(); +GO +DROP PROCEDURE source_target_proc; +GO + +-- test multiple '@' characters in table variable name +-- TODO: BABEL-476 Support variable name with multiple '@' characters +/* +create procedure nameing_proc as +begin + declare @@@tv@1@@@ as table(a int); + insert @@@tv@1@@@ values(1); + select * from @@@tv@1@@@; +end; +GO +CALL naming_proc(); +GO +DROP PROCEDURE naming_proc; +GO +*/ + +-- test nested functions using table variables with the same name, each should +-- have its own variable +create function inner_func() returns int as +begin + declare @result int; + declare @tableVar table (a int); + insert into @tableVar values(1); + select @result = count(*) from @tableVar; -- should be 1 + return @result; +end; +GO +create function outer_func() returns int as +begin + declare @result int; + declare @tableVar table(b int); + select @result = count(*) from @tableVar; -- should be 0 + select @result = @result + inner_func(); -- should be 0 + 1 = 1 + -- the temp table in inner_func() should have been dropped by now, so the + -- next call to inner_func() should still return 1 + select @result = @result + inner_func(); -- should be 1 + 1 = 2 + return @result; +end; +GO +select outer_func(); +GO +DROP FUNCTION outer_func; +GO + +-- test calling a function with table variables in a loop, each should have its +-- own variable +create procedure loop_func_proc as +begin + declare @result int; + declare @counter int; + select @result = 0; + set @counter = 1; + while (@counter < 6) + begin + select @result = @result + inner_func(); -- each call to inner_func should return 1 + set @counter = @counter + 1; + end + select @result; +end; +GO +call loop_func_proc(); +GO +DROP PROCEDURE loop_func_proc; +GO + +DROP FUNCTION inner_func; +GO + +-- test declaring the same variable in a loop - should not have any error, and +-- should all refer to the same underlying table +create procedure loop_proc as +begin + declare @result int; + declare @curr int; + declare @counter int; + select @result = 0; + set @counter = 1; + while (@counter < 6) + begin + declare @a tableType; + insert into @a values('hello', @counter, 100); + select @curr = count(*) from @a; -- @curr in each loop should be 1,2,3,4,5 + select @result = @result + @curr; + set @counter = @counter + 1; + end + select @result; +end; +GO +call loop_proc() +GO +DROP PROCEDURE loop_proc; +GO + +-- test using table variables in CTE, both in with clause and in main query +create procedure cte_proc as +begin + declare @tablevar1 tableType; + insert into @tablevar1 values('hello1', 1, 100); + declare @tablevar2 tableType; + insert into @tablevar2 values('hello1', 1, 100); + insert into @tablevar2 values('hello2', 2, 200); + WITH t1 (a) AS (SELECT a FROM @tablevar1) SELECT * FROM @tablevar2 t2 JOIN t1 ON t2.a = t1.a; +end; +GO +call cte_proc() +GO +DROP PROCEDURE cte_proc; +GO + +-- BABEL-894: test PLtsql_expr->tsql_tablevars is initialized to NIL so that it +-- won't cause seg faults when looked up during execution. One place missed +-- earlier is when parsing the SET command. +create procedure pl_set_proc as +begin + set datefirst 7; +end; +GO +call pl_set_proc() +GO +DROP PROCEDURE pl_set_proc; +GO + +-- test select from multiple table variables +create procedure select_multi_tablevars as +begin + declare @tablevar1 tableType; + insert into @tablevar1 values('hello1', 1, 100); + declare @tablevar2 tableType; + insert into @tablevar2 values('hello1', 1, 100); + insert into @tablevar2 values('hello2', 2, 200); + select * from @tablevar1, @tablevar2; +end; +GO +call select_multi_tablevars() +GO +DROP PROCEDURE select_multi_tablevars; +GO + +-- test select from table and table variable +create procedure select_table_tablevar as +begin + declare @tablevar tableType; + insert into @tablevar values('hello1', 1, 100); + select * from test_table, @tablevar; +end; +GO +call select_table_tablevar() +GO +DROP PROCEDURE select_table_tablevar; +GO + +-- test table-valued parameters +-- if no READONLY behind table-valued param: report error +create function error_func(@tableVar tableType) returns int as +begin + return 1; +end +GO +-- if READONLY on other param type: report error +create function error_func(@a int, @b int READONLY) returns int as +begin + return 1; +end +GO +-- correct syntax +create function tvp_func(@tableVar tableType READONLY) returns int as +begin + declare @result int; + select @result = count(*) from @tableVar; + return @result; +end +GO +-- test passing in a table variable whose type is different from what the function wants +-- TODO: BABEL-899: error message should be "Operand type clash: table is incompatible with tableType" +create procedure error_proc as +begin + declare @tableVar as table (a text, b int, c int); + insert into @tableVar values('hello1', 1, 100); + select tvp_func(@tableVar); +end; +GO +call error_proc() +GO +DROP PROCEDURE error_proc; +GO +create procedure tvp_proc as +begin + declare @tableVar tableType; + insert into @tablevar values('hello1', 1, 100); + select tvp_func(@tableVar); +end; +GO +call tvp_proc() +GO +DROP PROCEDURE tvp_proc; +GO +DROP FUNCTION tvp_func; +GO +-- test multiple table-valued parameters +CREATE TYPE tableType1 AS table(d int, e int); +GO +create function multi_tvp_func(@tableVar tableType READONLY, + @tableVar1 tableType1 READONLY) returns int as +begin + declare @result int; + select @result = count(*) from @tableVar tv inner join @tableVar1 tv1 on tv.b = tv1.d; + return @result; +end +GO +create procedure multi_tvp_proc as +begin + declare @v1 tableType; + declare @v2 tableType1; + insert into @v1 values('hello1', 1, 100); + insert into @v2 values(1, 100); + insert into @v2 values(2, 200); + select multi_tvp_func(@v1, @v2); +end; +GO +call multi_tvp_proc() +GO +DROP PROCEDURE multi_tvp_proc; +GO +DROP FUNCTION multi_tvp_func; +GO +DROP TYPE tableType1; +GO + +-- test multi-statement table-valued functions +create function mstvf(@i int) returns @tableVar table +( + a text not null, + b int primary key, + c int +) +as +begin + insert into @tableVar values('hello1', 1, 100); + insert into @tableVar values('hello2', 2, 200); +end; +GO +select * from mstvf(1); +GO +DROP FUNCTION mstvf; +GO +-- test mstvf whose return table has only one column +create function mstvf_one_col(@i int) returns @tableVar table +( + a text not null +) +as +begin + insert into @tableVar values('hello1'); +end; +GO +select * from mstvf_one_col(1); +GO +DROP FUNCTION mstvf_one_col; +GO +-- test mstvf whose return table has only one column +create function mstvf_return(@i int) returns @tableVar table +( + a text not null +) +as +begin + insert into @tableVar values('hello2'); + return; +end; +GO +select * from mstvf_return(1); +GO +DROP FUNCTION mstvf_return; +GO +-- test mstvf's with same names in different schemas +create function mstvf_schema(@i int) returns @resultTable table +( + name varchar(128) not null +) +as +begin + insert into @resultTable (name) select 'test_name'; + RETURN; +end; +GO +create schema test_schema; +GO +create function test_schema.mstvf_schema(@i int) returns @resultTable table +( + name1 varchar(128) not null +) +as +begin + insert into @resultTable (name1) select 'test_name1'; + RETURN; +end; +GO +select * from mstvf_schema(1); +GO +select * from test_schema.mstvf_schema(1); +GO +drop function mstvf_schema; +GO +drop function test_schema.mstvf_schema; +GO +drop schema test_schema; +GO +-- test mstvf with constraints in result table +create function mstvf_constraints(@i int) returns @resultTable table +( + name varchar(128) not null, + unique (name), + id int, + primary key clustered (id) +) +as +begin + insert into @resultTable (name, id) select 'test_name', @i; + RETURN; +end; +GO +select * from mstvf_constraints(1); +GO +drop function mstvf_constraints; +GO + +-- cleanup +DROP TYPE tableType; +GO +DROP TABLE test_table; +GO +\tsql OFF +reset babelfishpg_tsql.sql_dialect; +RESET SESSION AUTHORIZATION; +-- if we are able to drop the user, then it means that all the underlying tables +-- of table variables have been dropped because they depend on the user. +REVOKE CREATE ON DATABASE contrib_regression FROM my_test_user; +DROP USER my_test_user; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_transaction.sql b/contrib/babelfishpg_tsql/sql/test/babel_transaction.sql new file mode 100644 index 00000000000..dfa0264ca5f --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_transaction.sql @@ -0,0 +1,224 @@ +SET babelfishpg_tsql.sql_dialect = 'tsql'; + +-- setup +drop table TxnTable; +create table TxnTable(c1 int); + + +-- Begin transaction -> commit transaction +begin transaction; +select @@trancount; +begin transaction; +select @@trancount; +set transaction isolation level read committed; +show transaction_isolation; +show default_transaction_isolation; +insert into TxnTable values(1); +commit transaction; +select @@trancount; +commit transaction; +select @@trancount; +select c1 from TxnTable; + +-- Begin transaction -> rollback transaction +begin transaction; +insert into TxnTable values(2); +rollback transaction; +select c1 from TxnTable; + +-- Begin tran -> commit tran +begin tran; +insert into TxnTable values(2); +commit tran; +select c1 from TxnTable; + +-- Begin tran -> rollback tran +begin tran; +select @@trancount; +begin tran; +set transaction isolation level read uncommitted; +show transaction_isolation; +show default_transaction_isolation; +insert into TxnTable values(3); +select @@trancount; +rollback tran; +select @@trancount; +select c1 from TxnTable; + +set transaction isolation level repeatable read; +show transaction_isolation; +show default_transaction_isolation; + +-- Begin transaction -> commit +begin transaction; +insert into TxnTable values(4); +commit; +select c1 from TxnTable; + +-- Begin transaction -> commit work +begin transaction; +insert into TxnTable values(5); +commit work; +select c1 from TxnTable; + +-- Begin transaction -> rollback +begin transaction; +insert into TxnTable values(6); +rollback; +select c1 from TxnTable; + +-- Begin transaction -> rollback work +begin transaction; +insert into TxnTable values(7); +rollback work; +select c1 from TxnTable; + +-- Begin transaction name -> commit transaction name +begin transaction txn1; +insert into TxnTable values(8); +commit transaction txn1; +select c1 from TxnTable; + +-- Begin transaction name -> rollback transaction name +begin transaction txn1; +insert into TxnTable values(9); +rollback transaction txn1; +select c1 from TxnTable; + +-- Begin tran name -> commit tran name +begin tran txn1; +insert into TxnTable values(10); +commit tran txn1; +select c1 from TxnTable; + +-- Begin tran name -> rollback tran name +begin tran txn1; +insert into TxnTable values(10); +rollback tran txn1; +select c1 from TxnTable; + +truncate table TxnTable; + +-- save tran name -> rollback tran name +set transaction isolation level snapshot; +show transaction_isolation; +show default_transaction_isolation; +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +select @@trancount; +insert into TxnTable values(2); +save tran sp2; +insert into TxnTable values(3); +save tran sp2; +select @@trancount; +insert into TxnTable values(4); +select c1 from TxnTable; +rollback tran sp2; +select @@trancount; +select c1 from TxnTable; +rollback tran sp2; +select @@trancount; +select c1 from TxnTable; +rollback tran sp1; +select @@trancount; +select c1 from TxnTable; +rollback tran txn1; +select @@trancount; +select c1 from TxnTable; + +-- begin transaction name -> save transaction name -> rollback to first +-- savepoint +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +insert into TxnTable values(2); +save transaction sp2; +insert into TxnTable values(3); +save transaction sp3; +insert into TxnTable values(4); +rollback tran sp1; +rollback tran sp1; +rollback tran; +select c1 from TxnTable; + +-- begin transaction name -> save transaction name -> rollback tran name +-- Rollback whole transaction +set transaction isolation level serializable; +show transaction_isolation; +show default_transaction_isolation; +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +begin transaction txn1; +insert into TxnTable values(2); +save transaction sp1; +insert into TxnTable values(3); +select @@trancount; +rollback tran txn1; +select @@trancount; +select c1 from TxnTable; + +-- begin transaction -> save transaction name -> rollback to savepoint +-- commit transaction +begin transaction txn1; +insert into TxnTable values(1); +save transaction sp1; +insert into TxnTable values(2); +select c1 from TxnTable; +rollback tran sp1; +commit transaction; +select c1 from TxnTable; + +-- begin transaction -> save transaction name -> rollback to savepoint +-- save transaction name -> commit transaction +begin transaction txn1; +insert into TxnTable values(3); +save transaction sp1; +insert into TxnTable values(4); +select c1 from TxnTable; +rollback tran sp1; +save transaction sp2; +insert into TxnTable values(5); +commit transaction; +select c1 from TxnTable; + +-- begin transaction -> save transaction name -> error -> rollback to savepoint +-- commit transaction +begin transaction txn1; +insert into TxnTable values(6); +save transaction sp1; +insert into TxnTable values(7); +select c1 frm TxnTable; +rollback tran sp1; +commit transaction; +select c1 from TxnTable; + +-- create and execute procedure with transaction commands +-- \tsql on +-- create procedure txnproc as +-- begin tran; +-- insert into TxnTable values(8); +-- select c1 from TxnTable; +-- save tran sp1; +-- commit; +-- rollback tran; +-- go +-- execute txnproc; +-- go +-- \tsql off +-- drop procedure txnproc; + +-- transaction syntax error +begin; +begin txn1; +commit txn1; +rollback txx1; + +-- invalid transaction name +begin transaction txn1; +rollback transaction txn2; +rollback; + +drop table TxnTable; +reset babelfish_pg_tsql.sql_dialect; diff --git a/contrib/babelfishpg_tsql/sql/test/babel_typecode.sql b/contrib/babelfishpg_tsql/sql/test/babel_typecode.sql new file mode 100644 index 00000000000..e9e3cf63575 --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_typecode.sql @@ -0,0 +1,4 @@ +SET babelfishpg_tsql.sql_dialect = 'tsql'; + +-- test typecode list sys table +SELECT pg_namespace, pg_typname, tsql_typname, type_family_priority, priority, sql_variant_hdr_size FROM sys.babelfish_typecode_list(); diff --git a/contrib/babelfishpg_tsql/sql/test/babel_uniqueidentifier.sql b/contrib/babelfishpg_tsql/sql/test/babel_uniqueidentifier.sql new file mode 100644 index 00000000000..b7d385de1dd --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/test/babel_uniqueidentifier.sql @@ -0,0 +1,82 @@ +SET babelfishpg_tsql.sql_dialect = 'tsql'; + +create table t1 (a uniqueidentifier, b uniqueidentifier, c uniqueidentifier, primary key(a)); +insert into t1(a) values ('6F9619FF-8B86-D011-B42D-00C04FC964FF'); +insert into t1(a) values ('6F9619FF-8B86-D011-B42D-00C04FC964FF'); -- trigger error +select * from t1; + +insert into t1 values ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', newid(), newid()); + +explain (costs off) select * from t1 where a = '6F9619FF-8B86-D011-B42D-00C04FC964FF'; -- test PK + +select count(*) from t1 where a = '6F9619FF-8B86-D011-B42D-00C04FC964FF'; +select count(*) from t1 where a > '6F9619FF-8B86-D011-B42D-00C04FC964FF'; +select count(*) from t1 where a >= '6F9619FF-8B86-D011-B42D-00C04FC964FF'; +select count(*) from t1 where a < 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; +select count(*) from t1 where a <= 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; +select count(*) from t1 where a <> 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; + +-- newid's value could not be verified +insert into t1 values (newid(), newid(), newid()); +insert into t1 values (newid(), newid(), newid()); +insert into t1 values (newid(), newid(), newid()); +select count(a) from t1; + +create table t2 (like t1); +insert into t2 select * from t1 order by a; +select count(distinct a) from t2; + +-- test index (need more data) +create table t3 ( a uniqueidentifier, b uniqueidentifier); +-- create inital distinct values +insert into t3 values (newid(), newid()); +insert into t3 values (newid(), newid()); +insert into t3 values (newid(), newid()); +insert into t3 values (newid(), newid()); + +create index t3_a on t3 using btree (a); +create index t3_b on t3 using hash (b); + +-- test truncate feature of uniqueidentifier_in +create table t4 ( a uniqueidentifier); +insert into t4 values ('6F9619FF-8B86-D011-B42D-00C04FC964FF'); +insert into t4 values ('6F9619FF-8B86-D011-B42D-00C04FC964FFwrong'); -- characters exceeding are truncated +insert into t4 values ('{6F9619FF-8B86-D011-B42D-00C04FC964FF}'); -- with braces +insert into t4 values ('{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong'); -- error due to no matching brace +insert into t4 values ('6F9619FF-8B86-D011-B42D-00C04FC964FF}'); -- single brace at the end are truncated +select * from t4; + +reset babelfishpg_tsql.sql_dialect; +SET ENABLE_SEQSCAN = OFF; +SET ENABLE_BITMAPSCAN = OFF; +SET SEARCH_PATH = sys, public; +select name, setting from pg_settings where name in ('enable_seqscan', 'enable_bitmapscan'); +explain (costs off) select * from t3 where a = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; -- test btree index +explain (costs off) select * from t3 where b = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; -- test hash index + +-- assignment cast, should have same behavior as normal insert +set babelfishpg_tsql.sql_dialect = "tsql"; +create table t5 ( a uniqueidentifier); +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as varchar(50))); +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as varchar(50))); -- characters exceeding are truncated +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FF}' as varchar(50))); -- with braces +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as varchar(50))); -- error due to no matching brace +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF}' as varchar(50))); -- single brace at the end are truncated + +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as nvarchar(50))); +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as nvarchar(50))); -- characters exceeding are truncated +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FF}' as nvarchar(50))); -- with braces +insert into t5 values (cast('{6F9619FF-8B86-D011-B42D-00C04FC964FFwrong' as nvarchar(50))); -- error due to no matching brace +insert into t5 values (cast('6F9619FF-8B86-D011-B42D-00C04FC964FF}' as nvarchar(50))); -- single brace at the end are truncated + +-- error cases, implicit cast not supported +select * from t5 where a = cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as varchar(50)); +select * from t5 where a = cast('6F9619FF-8B86-D011-B42D-00C04FC964FF' as nvarchar(50)); + +reset babelfishpg_tsql.sql_dialect; + +drop table t1; +drop table t2; +drop table t3; +drop table t4; +drop table t5; 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..7590758cae5 --- /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); \ No newline at end of file diff --git a/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.1.0--1.2.0.sql b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.1.0--1.2.0.sql new file mode 100644 index 00000000000..c75744bfefa --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.1.0--1.2.0.sql @@ -0,0 +1,4273 @@ +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION ""babelfishpg_tsql"" UPDATE TO '1.2.0'" to load this file. \quit + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +CREATE DOMAIN sys._ci_sysname as sys.sysname; + +CREATE OR REPLACE PROCEDURE sys.sp_babelfish_grant_usage_to_all() +AS $$ +DECLARE + schema_name text; +BEGIN + FOR schema_name IN SELECT nspname FROM sys.babelfish_namespace_ext + LOOP + EXECUTE format('GRANT USAGE ON SCHEMA %I TO PUBLIC', schema_name); + END LOOP; +END; +$$ LANGUAGE plpgsql; + +CALL sys.sp_babelfish_grant_usage_to_all(); +DROP PROCEDURE sys.sp_babelfish_grant_usage_to_all; + +CREATE OR REPLACE PROCEDURE sys.sp_babelfish_grant_usage_to_all() +AS $$ +DECLARE + schema_name text; +BEGIN + FOR schema_name IN SELECT nspname FROM sys.babelfish_namespace_ext + LOOP + EXECUTE format('GRANT USAGE ON SCHEMA %I TO PUBLIC', schema_name); + END LOOP; +END; +$$ LANGUAGE plpgsql; + +CALL sys.sp_babelfish_grant_usage_to_all(); +DROP PROCEDURE sys.sp_babelfish_grant_usage_to_all; + +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 = 'babelfishpg_tsql.lock_timeout'); + RETURN return_value; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.lock_timeout() TO PUBLIC; + +CREATE OR REPLACE VIEW sys.endpoints +AS +SELECT CAST('TSQL Default TCP' AS sys.sysname) AS name + , CAST(4 AS int) AS endpoint_id + , CAST(1 AS int) AS principal_id + , CAST(2 AS sys.tinyint) AS protocol + , CAST('TCP' AS sys.nvarchar(60)) AS protocol_desc + , CAST(2 AS sys.tinyint) AS type + , CAST('TSQL' AS sys.nvarchar(60)) AS type_desc + , CAST(0 AS tinyint) AS state + , CAST('STARTED' AS sys.nvarchar(60)) AS state_desc + , CAST(0 AS sys.bit) AS is_admin_endpoint; +GRANT SELECT ON sys.endpoints TO PUBLIC; + +CREATE OR REPLACE FUNCTION COLUMNS_UPDATED () + RETURNS sys.VARBINARY AS 'babelfishpg_tsql', 'columnsupdated' LANGUAGE C; + +CREATE OR REPLACE FUNCTION UPDATE (TEXT) + RETURNS BOOLEAN AS 'babelfishpg_tsql', 'updated' LANGUAGE C; + +-- Since SIDs are currently not supported in Babelfish, this essentially behaves the same as suser_name but +-- with a different input data type +CREATE OR REPLACE FUNCTION sys.suser_sname(IN server_user_sid SYS.VARBINARY(85) DEFAULT NULL) +RETURNS SYS.NVARCHAR(128) +AS $$ + SELECT sys.suser_name(CAST(server_user_sid AS INT)); +$$ +LANGUAGE SQL IMMUTABLE PARALLEL RESTRICTED; + +-- Since SIDs are currently not supported in Babelfish, this essentially behaves the same as suser_id but +-- with different input/output data types. The second argument will be ignored as its functionality is not supported +CREATE OR REPLACE FUNCTION sys.suser_sid(IN login SYS.SYSNAME DEFAULT NULL, IN Param2 INT DEFAULT NULL) +RETURNS SYS.VARBINARY(85) +AS $$ + SELECT CAST(CAST(sys.suser_id(login) AS INT) AS SYS.VARBINARY(85)); +$$ +LANGUAGE SQL IMMUTABLE PARALLEL RESTRICTED; + +-- Added for BABEL-1544 +CREATE OR REPLACE FUNCTION sys.len(expr sys.BBF_VARBINARY) RETURNS INTEGER AS +'babelfishpg_common', 'varbinary_length' +STRICT +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +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); + default_value text; + 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; + + -- Each setting has a boot_val which is the wired-in default value + -- Assuming that escape hatches cannot be modified using ALTER SYTEM/config file + -- we are setting the boot_val as the default value for the escape hatches + SELECT boot_val INTO default_value FROM pg_catalog.pg_settings WHERE name = eh_name; + IF lower("@option_value") = 'default' THEN + PERFORM pg_catalog.set_config(eh_name, default_value, 'false'); + ELSE + PERFORM pg_catalog.set_config(eh_name, "@option_value", 'false'); + END IF; + IF server THEN + SELECT current_user INTO prev_user; + PERFORM sys.babelfish_set_role(session_user); + IF lower("@option_value") = 'default' THEN + EXECUTE format('ALTER DATABASE %s SET %s = %s', CURRENT_DATABASE(), eh_name, default_value); + ELSE + -- 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"); + END IF; + 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; + +-- For getting host os from PG_VERSION_STR +CREATE OR REPLACE FUNCTION sys.get_host_os() +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'host_os' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE VIEW sys.dm_os_host_info AS +SELECT + -- get_host_os() depends on a Postgres function created separately. + cast( sys.get_host_os() as sys.nvarchar(256) ) as host_platform + -- Hardcoded at the moment. Should likely be GUC with default '' (empty string). + , cast( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.host_distribution') as sys.nvarchar(256) ) as host_distribution + -- documentation on one hand states this is empty string on linux, but otoh shows an example with "ubuntu 16.04" + , cast( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.host_release') as sys.nvarchar(256) ) as host_release + -- empty string on linux. + , cast( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.host_service_pack_level') as sys.nvarchar(256) ) + as host_service_pack_level + -- windows stock keeping unit. null on linux. + , cast( null as int ) as host_sku + -- lcid + , cast( sys.collationproperty( (select setting FROM pg_settings WHERE name = 'babelfishpg_tsql.server_collation_name') , 'lcid') as int ) + as "os_language_version"; +GRANT SELECT ON sys.dm_os_host_info TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.is_table_type(object_id oid) RETURNS bool AS +$BODY$ +SELECT + EXISTS( + SELECT 1 + FROM pg_catalog.pg_type pt + INNER JOIN pg_catalog.pg_depend dep + ON pt.typrelid = dep.objid + join sys.schemas sch on pt.typnamespace = sch.schema_id + JOIN pg_catalog.pg_class pc ON pc.oid = dep.objid + WHERE pt.typtype = 'c' AND dep.deptype = 'i' AND pt.typrelid = object_id AND pc.relkind = 'r'); +$BODY$ +LANGUAGE SQL VOLATILE STRICT; + +create or replace view sys.tables as +select + t.relname as name + , t.oid as object_id + , null::integer as principal_id + , sch.schema_id 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 sys.schemas sch on t.relnamespace = sch.schema_id +where t.relpersistence in ('p', 'u', 't') +and t.relkind = 'r' +and not sys.is_table_type(t.oid) +and has_schema_privilege(sch.schema_id, 'USAGE') +and has_table_privilege(t.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +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 + , sch.schema_id 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 sys.schemas sch on t.relnamespace = sch.schema_id +where t.relkind = 'v' +and has_schema_privilege(sch.schema_id, 'USAGE') +and has_table_privilege(t.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +GRANT SELECT ON sys.views TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.tsql_type_scale_helper(IN type TEXT, IN typemod INT, IN return_null_for_rest bool) RETURNS sys.TINYINT +AS $$ +DECLARE + scale INT; +BEGIN + IF type IS NULL THEN + RETURN -1; + END IF; + + IF typemod = -1 THEN + CASE type + WHEN 'date' THEN scale = 0; + WHEN 'datetime' THEN scale = 3; + WHEN 'smalldatetime' THEN scale = 0; + WHEN 'datetime2' THEN scale = 6; + WHEN 'datetimeoffset' THEN scale = 6; + WHEN 'decimal' THEN scale = 38; + WHEN 'numeric' THEN scale = 38; + WHEN 'money' THEN scale = 4; + WHEN 'smallmoney' THEN scale = 4; + WHEN 'time' THEN scale = 6; + WHEN 'tinyint' THEN scale = 0; + ELSE + IF return_null_for_rest + THEN scale = NULL; + ELSE scale = 0; + END IF; + END CASE; + RETURN scale; + END IF; + + CASE type + WHEN 'decimal' THEN scale = (typemod - 4) & 65535; + WHEN 'numeric' THEN scale = (typemod - 4) & 65535; + WHEN 'smalldatetime' THEN scale = 0; + WHEN 'datetime2' THEN + CASE typemod + WHEN 0 THEN scale = 0; + WHEN 1 THEN scale = 1; + WHEN 2 THEN scale = 2; + WHEN 3 THEN scale = 3; + WHEN 4 THEN scale = 4; + WHEN 5 THEN scale = 5; + WHEN 6 THEN scale = 6; + -- typemod = 7 is not possible for datetime2 in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN scale = 7; + END CASE; + WHEN 'datetimeoffset' THEN + CASE typemod + WHEN 0 THEN scale = 0; + WHEN 1 THEN scale = 1; + WHEN 2 THEN scale = 2; + WHEN 3 THEN scale = 3; + WHEN 4 THEN scale = 4; + WHEN 5 THEN scale = 5; + WHEN 6 THEN scale = 6; + -- typemod = 7 is not possible for datetimeoffset in Babelfish + -- but adding the case just in case we support it in future + WHEN 7 THEN scale = 7; + END CASE; + WHEN 'time' THEN + CASE typemod + WHEN 0 THEN scale = 0; + WHEN 1 THEN scale = 1; + WHEN 2 THEN scale = 2; + WHEN 3 THEN scale = 3; + WHEN 4 THEN scale = 4; + WHEN 5 THEN scale = 5; + WHEN 6 THEN scale = 6; + -- typemod = 7 is not possible for time in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN scale = 7; + END CASE; + ELSE + IF return_null_for_rest + THEN scale = NULL; + ELSE scale = 0; + END IF; + END CASE; + RETURN scale; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.tsql_type_precision_helper(IN type TEXT, IN typemod INT) RETURNS sys.TINYINT +AS $$ +DECLARE + precision INT; +BEGIN + IF type IS NULL THEN + RETURN -1; + END IF; + + IF typemod = -1 THEN + CASE type + WHEN 'bigint' THEN precision = 19; + WHEN 'bit' THEN precision = 1; + WHEN 'date' THEN precision = 10; + WHEN 'datetime' THEN precision = 23; + WHEN 'datetime2' THEN precision = 26; + WHEN 'datetimeoffset' THEN precision = 33; + WHEN 'decimal' THEN precision = 38; + WHEN 'numeric' THEN precision = 38; + WHEN 'float' THEN precision = 53; + WHEN 'int' THEN precision = 10; + WHEN 'money' THEN precision = 19; + WHEN 'real' THEN precision = 24; + WHEN 'smalldatetime' THEN precision = 16; + WHEN 'smallint' THEN precision = 5; + WHEN 'smallmoney' THEN precision = 10; + WHEN 'time' THEN precision = 15; + WHEN 'tinyint' THEN precision = 3; + ELSE precision = 0; + END CASE; + RETURN precision; + END IF; + + CASE type + WHEN 'numeric' THEN precision = ((typemod - 4) >> 16) & 65535; + WHEN 'decimal' THEN precision = ((typemod - 4) >> 16) & 65535; + WHEN 'smalldatetime' THEN precision = 16; + WHEN 'datetime2' THEN + CASE typemod + WHEN 0 THEN precision = 19; + WHEN 1 THEN precision = 21; + WHEN 2 THEN precision = 22; + WHEN 3 THEN precision = 23; + WHEN 4 THEN precision = 24; + WHEN 5 THEN precision = 25; + WHEN 6 THEN precision = 26; + -- typemod = 7 is not possible for datetime2 in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN precision = 27; + END CASE; + WHEN 'datetimeoffset' THEN + CASE typemod + WHEN 0 THEN precision = 26; + WHEN 1 THEN precision = 28; + WHEN 2 THEN precision = 29; + WHEN 3 THEN precision = 30; + WHEN 4 THEN precision = 31; + WHEN 5 THEN precision = 32; + WHEN 6 THEN precision = 33; + -- typemod = 7 is not possible for datetimeoffset in Babelfish + -- but adding the case just in case we support it in future + WHEN 7 THEN precision = 34; + END CASE; + WHEN 'time' THEN + CASE typemod + WHEN 0 THEN precision = 8; + WHEN 1 THEN precision = 10; + WHEN 2 THEN precision = 11; + WHEN 3 THEN precision = 12; + WHEN 4 THEN precision = 13; + WHEN 5 THEN precision = 14; + WHEN 6 THEN precision = 15; + -- typemod = 7 is not possible for time in Babelfish but + -- adding the case just in case we support it in future + WHEN 7 THEN precision = 16; + END CASE; + ELSE precision = 0; + END CASE; + RETURN precision; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + + +CREATE OR REPLACE FUNCTION sys.tsql_type_max_length_helper(IN type TEXT, IN typelen INT, IN typemod INT, IN for_sys_types boolean DEFAULT false) +RETURNS SMALLINT +AS $$ +DECLARE + max_length SMALLINT; + precision INT; +BEGIN + -- unknown tsql type + IF type IS NULL THEN + RETURN CAST(typelen as SMALLINT); + END IF; + + IF typelen != -1 THEN + CASE type + WHEN 'tinyint' THEN max_length = 1; + WHEN 'date' THEN max_length = 3; + WHEN 'smalldatetime' THEN max_length = 4; + WHEN 'smallmoney' THEN max_length = 4; + WHEN 'datetime2' THEN + IF typemod = -1 THEN max_length = 8; + ELSIF typemod <= 2 THEN max_length = 6; + ELSIF typemod <= 4 THEN max_length = 7; + ELSEIF typemod <= 7 THEN max_length = 8; + -- typemod = 7 is not possible for datetime2 in Babel + END IF; + WHEN 'datetimeoffset' THEN + IF typemod = -1 THEN max_length = 10; + ELSIF typemod <= 2 THEN max_length = 8; + ELSIF typemod <= 4 THEN max_length = 9; + ELSIF typemod <= 7 THEN max_length = 10; + -- typemod = 7 is not possible for datetimeoffset in Babel + END IF; + WHEN 'time' THEN + IF typemod = -1 THEN max_length = 5; + ELSIF typemod <= 2 THEN max_length = 3; + ELSIF typemod <= 4 THEN max_length = 4; + ELSIF typemod <= 7 THEN max_length = 5; + END IF; + WHEN 'timestamp' THEN max_length = 8; + ELSE max_length = typelen; + END CASE; + RETURN max_length; + END IF; + + IF typemod = -1 THEN + CASE + WHEN type in ('image', 'text', 'ntext') THEN max_length = 16; + WHEN type = 'sql_variant' THEN max_length = 8016; + WHEN type in ('varbinary', 'varchar', 'nvarchar') THEN + IF for_sys_types THEN max_length = 8000; + ELSE max_length = -1; + END IF; + WHEN type in ('binary', 'char', 'bpchar', 'nchar') THEN max_length = 8000; + WHEN type in ('decimal', 'numeric') THEN max_length = 17; + ELSE max_length = typemod; + END CASE; + RETURN max_length; + END IF; + + CASE + WHEN type in ('char', 'bpchar', 'varchar', 'binary', 'varbinary') THEN max_length = typemod - 4; + WHEN type in ('nchar', 'nvarchar') THEN max_length = (typemod - 4) * 2; + WHEN type = 'sysname' THEN max_length = (typemod - 4) * 2; + WHEN type in ('numeric', 'decimal') THEN + precision = ((typemod - 4) >> 16) & 65535; + IF precision >= 1 and precision <= 9 THEN max_length = 5; + ELSIF precision <= 19 THEN max_length = 9; + ELSIF precision <= 28 THEN max_length = 13; + ELSIF precision <= 38 THEN max_length = 17; + ELSE max_length = typelen; + END IF; + ELSE + max_length = typemod; + END CASE; + RETURN max_length; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +-- internal function in order to workaround BABEL-1597 +CREATE OR REPLACE 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), + CASE + WHEN tsql_type_name IS NOT NULL OR t.typbasetype = 0 THEN + -- either tsql or PG base type + CAST(a.atttypid AS int) + ELSE + CAST(t.typbasetype AS int) + END, + CAST(a.atttypid AS int), + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, a.atttypmod) + ELSE + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod) + ELSE + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod, false) + ELSE + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod, false) + END, + 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 sys.schemas sch on c.relnamespace = sch.schema_id + INNER JOIN sys.pg_namespace_ext ext on sch.schema_id = ext.oid + INNER JOIN information_schema.columns isc ON c.relname = isc.table_name AND ext.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 + , sys.translate_pg_type_to_tsql(a.atttypid) AS tsql_type_name + , sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_base_type_name + 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 has_schema_privilege(sch.schema_id, 'USAGE') + AND has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES') + union all + -- system tables information + SELECT CAST(c.oid AS int), + CAST(a.attname AS sys.sysname), + CAST(a.attnum AS int), + CASE + WHEN tsql_type_name IS NOT NULL OR t.typbasetype = 0 THEN + -- either tsql or PG base type + CAST(a.atttypid AS int) + ELSE + CAST(t.typbasetype AS int) + END, + CAST(a.atttypid AS int), + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, a.atttypmod) + ELSE + sys.tsql_type_max_length_helper(coalesce(tsql_type_name, tsql_base_type_name), a.attlen, t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod) + ELSE + sys.tsql_type_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod) + END, + CASE + WHEN a.atttypmod != -1 THEN + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), a.atttypmod, false) + ELSE + sys.tsql_type_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), t.typtypmod, false) + END, + 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 nsp ON (nsp.oid = c.relnamespace and nsp.nspname = 'sys') + INNER JOIN information_schema.columns isc ON c.relname = isc.table_name AND nsp.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 + , sys.translate_pg_type_to_tsql(a.atttypid) AS tsql_type_name + , sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_base_type_name + WHERE NOT a.attisdropped + AND a.attnum > 0 + AND c.relkind = 'r' + AND has_schema_privilege(nsp.oid, 'USAGE') + AND has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +END; +$$ +language plpgsql; + +create or replace view sys.types As +-- For System types +select tsql_type_name as name + , t.oid as system_type_id + , t.oid as user_type_id + , s.oid as schema_id + , cast(NULL as INT) as principal_id + , sys.tsql_type_max_length_helper(tsql_type_name, t.typlen, t.typtypmod, true) as max_length + , cast(sys.tsql_type_precision_helper(tsql_type_name, t.typtypmod) as int) as precision + , cast(sys.tsql_type_scale_helper(tsql_type_name, t.typtypmod, false) as int) as scale + , CASE c.collname + WHEN 'default' THEN cast(current_setting('babelfishpg_tsql.server_collation_name') as name) + ELSE c.collname + END as collation_name + , case when typnotnull then 0 else 1 end as is_nullable + , 0 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 +, sys.translate_pg_type_to_tsql(t.oid) AS tsql_type_name +where tsql_type_name IS NOT NULL +and pg_type_is_visible(t.oid) +and (s.nspname = 'pg_catalog' OR s.nspname = 'sys') +union all +-- For User Defined Types +select cast(t.typname as text) as name + , t.typbasetype as system_type_id + , t.oid as user_type_id + , s.oid as schema_id + , null::integer as principal_id + , case when is_tbl_type then -1::smallint else sys.tsql_type_max_length_helper(tsql_base_type_name, t.typlen, t.typtypmod) end as max_length + , case when is_tbl_type then 0::smallint else cast(sys.tsql_type_precision_helper(tsql_base_type_name, t.typtypmod) as int) end as precision + , case when is_tbl_type then 0::smallint else cast(sys.tsql_type_scale_helper(tsql_base_type_name, t.typtypmod, false) as int) end as scale + , CASE c.collname + WHEN 'default' THEN cast(current_setting('babelfishpg_tsql.server_collation_name') as name) + ELSE c.collname + END as collation_name + , case when is_tbl_type then 0 + else case when typnotnull then 0 else 1 end + end + as is_nullable + -- CREATE TYPE ... FROM is implemented as CREATE DOMAIN in babel + , 1 as is_user_defined + , 0 as is_assembly_type + , 0 as default_object_id + , 0 as rule_object_id + , case when is_tbl_type then 1 else 0 end as is_table_type +from pg_type t +inner join pg_namespace s on s.oid = t.typnamespace +join sys.schemas sch on t.typnamespace = sch.schema_id +left join pg_collation c on c.oid = t.typcollation +, sys.translate_pg_type_to_tsql(t.oid) AS tsql_type_name +, sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_base_type_name +, sys.is_table_type(t.typrelid) as is_tbl_type +-- we want to show details of user defined datatypes created under babelfish database +where tsql_type_name IS NULL +and + ( + -- show all user defined datatypes created under babelfish database except table types + t.typtype = 'd' + or + -- only for table types + sys.is_table_type(t.typrelid) + ); +GRANT SELECT ON sys.types TO PUBLIC; + +create or replace view sys.table_types as +select st.* + , pt.typrelid::int as type_table_object_id + , 0::sys.bit as is_memory_optimized -- return 0 until we support in-memory tables +from sys.types st +inner join pg_catalog.pg_type pt on st.user_type_id = pt.oid +where is_table_type = 1; +GRANT SELECT ON sys.table_types TO PUBLIC; + +create or replace view sys.default_constraints +AS +select CAST(('DF_' || tab.name || '_' || d.oid) as sys.sysname) as name + , d.oid as object_id + , null::int as principal_id + , tab.schema_id 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_attribute a on a.attrelid = d.adrelid and d.adnum = a.attnum +inner join sys.tables tab on d.adrelid = tab.object_id +WHERE a.atthasdef = 't' and a.attgenerated = '' +AND has_schema_privilege(tab.schema_id, 'USAGE') +AND has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES'); +GRANT SELECT ON sys.default_constraints 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 +inner join pg_class c on i.indrelid = c.oid +inner join sys.schemas sch on sch.schema_id = c.relnamespace +where has_schema_privilege(sch.schema_id, 'USAGE') +and has_table_privilege(c.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +GRANT SELECT ON sys.index_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 + , sch.schema_id 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 sys.schemas sch on sch.schema_id = c.connamespace +where has_schema_privilege(sch.schema_id, 'USAGE') +and c.contype = 'f'; +GRANT SELECT ON sys.foreign_keys 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 + , sch.schema_id 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 sys.schemas sch on sch.schema_id = c.connamespace +where has_schema_privilege(sch.schema_id, 'USAGE') +and c.contype in ('p', 'u'); +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 + , sch.schema_id as schema_id + , cast (case when tr.tgrelid is not null + then tr.tgrelid + else 0 end as int) + as parent_object_id + , case p.prokind + when 'p' then 'P'::varchar(2) + when 'a' then 'AF'::varchar(2) + else + case format_type(p.prorettype, null) when 'trigger' + then 'TR'::varchar(2) + else 'FN'::varchar(2) + end + end as type + , case p.prokind + when 'p' then 'SQL_STORED_PROCEDURE'::varchar(60) + when 'a' then 'AGGREGATE_FUNCTION'::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 sys.schemas sch on sch.schema_id = p.pronamespace +left join pg_trigger tr on tr.tgfoid = p.oid +where has_schema_privilege(sch.schema_id, 'USAGE') +and has_function_privilege(p.oid, 'EXECUTE'); +GRANT SELECT ON sys.procedures TO PUBLIC; + +CREATE or replace VIEW sys.check_constraints AS +SELECT CAST(c.conname as sys.sysname) as name + , oid::integer as object_id + , NULL::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 +INNER JOIN sys.schemas s on c.connamespace = s.schema_id +WHERE has_schema_privilege(s.schema_id, 'USAGE') +AND c.contype = 'c' and c.conrelid != 0; +GRANT SELECT ON sys.check_constraints TO PUBLIC; + +create or replace 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 +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 +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 +where p.type = 'PK' +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 + def.name::name + , def.object_id + , def.principal_id + , def.schema_id + , def.parent_object_id + , def.type + , def.type_desc + , def.create_date + , def.modified_date as modify_date + , def.is_ms_shipped::int + , def.is_published::int + , def.is_schema_published::int + from sys.default_constraints def +union all +select + chk.name::name + , chk.object_id + , chk.principal_id + , chk.schema_id + , chk.parent_object_id + , chk.type + , chk.type_desc + , chk.create_date + , chk.modify_date + , chk.is_ms_shipped::int + , chk.is_published::int + , chk.is_schema_published::int + from sys.check_constraints chk +union all +select + p.relname as name + ,p.oid as object_id + , null::integer as principal_id + , s.schema_id 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 sys.schemas s on s.schema_id = p.relnamespace +and p.relkind = 'S' +and has_schema_privilege(s.schema_id, 'USAGE') +union all +select + ('TT_' || tt.name || '_' || tt.type_table_object_id)::name as name + , tt.type_table_object_id as object_id + , tt.principal_id as principal_id + , tt.schema_id as schema_id + , 0 as parent_object_id + , 'TT'::varchar(2) as type + , 'TABLE_TYPE'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 1 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from sys.table_types tt; +GRANT SELECT ON sys.objects 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 sys.schemas sch on c.relnamespace = sch.schema_id +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 has_schema_privilege(sch.schema_id, 'USAGE'); +GRANT SELECT ON sys.indexes 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 sys.schemas s on s.schema_id = p.pronamespace +inner join pg_type t on t.oid = p.prorettype +left join pg_collation c on c.oid = t.typcollation +where has_schema_privilege(s.schema_id, 'USAGE') +and has_function_privilege(p.oid, 'EXECUTE'); +GRANT SELECT ON sys.sql_modules TO PUBLIC; + +-- USER extension +CREATE TABLE sys.babelfish_authid_user_ext ( +rolname NAME NOT NULL, +login_name NAME NOT NULL, +type CHAR(1) NOT NULL DEFAULT 'S', +owning_principal_id INT, +is_fixed_role INT NOT NULL DEFAULT 0, +authentication_type INT, +default_language_lcid INT, +allow_encrypted_value_modifications INT NOT NULL DEFAULT 0, +create_date timestamptz NOT NULL, +modify_date timestamptz NOT NULL, +orig_username SYS.NVARCHAR(128) NOT NULL, +database_name SYS.NVARCHAR(128) NOT NULL, +default_schema_name SYS.NVARCHAR(128) NOT NULL, +default_language_name SYS.NVARCHAR(128), +authentication_type_desc SYS.NVARCHAR(60), +PRIMARY KEY (rolname)); + +CREATE INDEX babelfish_authid_user_ext_login_db_idx ON sys.babelfish_authid_user_ext (login_name, database_name); + +GRANT SELECT ON sys.babelfish_authid_user_ext TO PUBLIC; + +-- DATABASE_PRINCIPALS +CREATE VIEW sys.database_principals AS SELECT +Ext.orig_username AS name, +CAST(Base.OID AS INT) AS principal_id, +Ext.type, +CAST(CASE WHEN Ext.type = 'S' THEN 'SQL_USER' ELSE NULL END AS SYS.NVARCHAR(60)) AS type_desc, +Ext.default_schema_name, +Ext.create_date, +Ext.modify_date, +Ext.owning_principal_id, +CAST(CAST(Base2.oid AS INT) AS SYS.VARBINARY(85)) AS SID, +CAST(Ext.is_fixed_role AS SYS.BIT) AS is_fixed_role, +Ext.authentication_type, +Ext.authentication_type_desc, +Ext.default_language_name, +Ext.default_language_lcid, +CAST(Ext.allow_encrypted_value_modifications AS SYS.BIT) AS allow_encrypted_value_modifications +FROM pg_catalog.pg_authid AS Base INNER JOIN sys.babelfish_authid_user_ext AS Ext +ON Base.rolname = Ext.rolname +LEFT OUTER JOIN pg_catalog.pg_roles Base2 +ON Ext.login_name = Base2.rolname +WHERE Ext.database_name = DB_NAME(); + +GRANT SELECT ON sys.database_principals TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.babel_add_existing_users_to_catalog() +LANGUAGE C +AS 'babelfishpg_tsql', 'add_existing_users_to_catalog'; + +CALL sys.babel_add_existing_users_to_catalog(); + +CREATE OR REPLACE PROCEDURE sys.babel_drop_all_users() +LANGUAGE C +AS 'babelfishpg_tsql', 'drop_all_users'; + +CREATE OR REPLACE PROCEDURE remove_babelfish () +LANGUAGE plpgsql +AS $$ +BEGIN + CALL sys.babel_drop_all_dbs(); + CALL sys.babel_drop_all_users(); + 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 +$$; + +create or replace view sys.identity_columns AS +select out_object_id::bigint 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 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::character varying(60) as generated_always_type_desc + , out_encryption_type::integer as encryption_type + , out_encryption_type_desc::character varying(64) as encryption_type_desc + , out_encryption_algorithm_name::character varying as encryption_algorithm_name + , out_column_encryption_key_id::integer as column_encryption_key_id + , out_column_encryption_key_database_name::character varying as column_encryption_key_database_name + , out_is_hidden::integer as is_hidden + , out_is_masked::integer as is_masked + , sys.ident_seed(OBJECT_NAME(sc.out_object_id))::bigint as seed_value + , sys.ident_incr(OBJECT_NAME(sc.out_object_id))::bigint as increment_value + , sys.babelfish_get_sequence_value(pg_get_serial_sequence(quote_ident(ext.nspname)||'.'||quote_ident(c.relname), a.attname)) as last_value +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_class c on c.oid = a.attrelid +inner join sys.pg_namespace_ext ext on ext.oid = c.relnamespace +where not a.attisdropped +and sc.out_is_identity::integer = 1 +and pg_get_serial_sequence(quote_ident(ext.nspname)||'.'||quote_ident(c.relname), a.attname) is not null +and has_sequence_privilege(pg_get_serial_sequence(quote_ident(ext.nspname)||'.'||quote_ident(c.relname), a.attname), 'USAGE,SELECT,UPDATE'); +GRANT SELECT ON sys.identity_columns TO PUBLIC; + +CREATE OR REPLACE 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) +left join sys.schemas sch on sch.schema_id = pgproc.pronamespace +where has_schema_privilege(sch.schema_id, 'USAGE'); +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; + +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 (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +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.all_objects as +-- details of user defined and system tables +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' as type + , 'USER_TABLE' 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 t inner join pg_namespace s on s.oid = t.relnamespace +where t.relpersistence in ('p', 'u', 't') +and t.relkind = 'r' +and (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and has_table_privilege(t.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER') +union all +-- details of user defined and system views +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 +-- details of user defined and system foreign key constraints +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' as type + , 'FOREIGN_KEY_CONSTRAINT' + , 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_constraint c +inner join pg_namespace s on s.oid = c.connamespace +where (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and c.contype = 'f' +union all +-- details of user defined and system primary key constraints +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 + , 'PK' as type + , 'PRIMARY_KEY_CONSTRAINT' 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_constraint c +inner join pg_namespace s on s.oid = c.connamespace +where (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and c.contype = 'p' +union all +-- details of user defined and system defined procedures +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 p.prokind + when 'p' then 'P'::varchar(2) + when 'a' then 'AF'::varchar(2) + else + case format_type(p.prorettype, null) when 'trigger' + then 'TR'::varchar(2) + else 'FN'::varchar(2) + end + end as type + , case p.prokind + when 'p' then 'SQL_STORED_PROCEDURE'::varchar(60) + when 'a' then 'AGGREGATE_FUNCTION'::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.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and has_function_privilege(p.oid, 'EXECUTE') +union all +-- details of all default constraints +select + ('DF_' || o.relname || '_' || d.oid)::name 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 modify_date + , 0 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_catalog.pg_attrdef d +inner join pg_attribute a on a.attrelid = d.adrelid and d.adnum = a.attnum +inner join pg_class o on d.adrelid = o.oid +inner join pg_namespace s on s.oid = o.relnamespace +where a.atthasdef = 't' and a.attgenerated = '' +and (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and has_column_privilege(a.attrelid, a.attname, 'SELECT,INSERT,UPDATE,REFERENCES') +union all +-- details of all check constraints +select + c.conname::name + , c.oid::integer as object_id + , NULL::integer as principal_id + , c.connamespace::integer as schema_id + , c.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 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from pg_catalog.pg_constraint as c +inner join pg_namespace s on s.oid = c.connamespace +where (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +and c.contype = 'c' and c.conrelid != 0 +union all +-- details of user defined and system defined sequence objects +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 (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +and has_schema_privilege(s.oid, 'USAGE') +union all +-- details of user defined table types +select + ('TT_' || tt.name || '_' || tt.type_table_object_id)::name as name + , tt.type_table_object_id as object_id + , tt.principal_id as principal_id + , tt.schema_id as schema_id + , 0 as parent_object_id + , 'TT'::varchar(2) as type + , 'TABLE_TYPE'::varchar(60) as type_desc + , null::timestamp as create_date + , null::timestamp as modify_date + , 1 as is_ms_shipped + , 0 as is_published + , 0 as is_schema_published +from sys.table_types tt; +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 = 'sys'; +GRANT SELECT ON sys.system_objects 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 +and (s.oid in (select schema_id from sys.schemas) or s.nspname = 'sys') +-- 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_schema_privilege(s.oid, 'USAGE') +and has_column_privilege(quote_ident(s.nspname) ||'.'||quote_ident(c.relname), a.attname, 'SELECT,INSERT,UPDATE,REFERENCES') +and a.attnum > 0; +GRANT SELECT ON sys.all_columns TO PUBLIC; + +CREATE OR REPLACE 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 +AND (t1.relnamespace IN (SELECT schema_id FROM sys.schemas)) +AND has_schema_privilege(t1.relnamespace, 'USAGE') +AND has_table_privilege(t1.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +GRANT SELECT on sys.sp_tables_view 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' +and (c.connamespace in (select schema_id from sys.schemas)) +and has_schema_privilege(c.connamespace, 'USAGE'); +GRANT SELECT ON sys.foreign_key_columns 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_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 (c.connamespace in (select schema_id from sys.schemas)) +and has_schema_privilege(c.connamespace, 'USAGE'); +GRANT SELECT ON sys.sysforeignkeys TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.get_current_full_xact_id() + RETURNS XID8 AS 'babelfishpg_tsql' LANGUAGE C STABLE; + +CREATE OR REPLACE FUNCTION sys.DBTS() +RETURNS sys.ROWVERSION AS +$$ +DECLARE + eh_setting text; +BEGIN + eh_setting = (select s.setting FROM pg_catalog.pg_settings s where name = 'babelfishpg_tsql.escape_hatch_rowversion'); + IF eh_setting = 'strict' THEN + RAISE EXCEPTION 'DBTS is not currently supported in Babelfish. please use babelfishpg_tsql.escape_hatch_rowversion to ignore'; + ELSE + RETURN pg_snapshot_xmin(pg_current_snapshot())::sys.ROWVERSION; + END IF; +END; +$$ +STRICT +LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION sys.schema_id() +RETURNS INT +LANGUAGE plpgsql +STRICT +AS $$ +BEGIN + RETURN (select oid from sys.pg_namespace_ext where nspname = (select current_schema()))::INT; +EXCEPTION + WHEN others THEN + RETURN NULL; +END; +$$; +GRANT EXECUTE ON FUNCTION sys.schema_id() TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.getdate() RETURNS sys.datetime + AS $$select date_trunc('millisecond', clock_timestamp()::pg_catalog.timestamp)::sys.datetime;$$ + LANGUAGE SQL; +GRANT EXECUTE ON FUNCTION sys.getdate() TO PUBLIC; + +CREATE SCHEMA information_schema_tsql; +GRANT USAGE ON SCHEMA information_schema_tsql TO PUBLIC; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_truetypid(nt pg_namespace, at pg_attribute, tp pg_type) RETURNS oid + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT CASE WHEN nt.nspname = 'pg_catalog' OR nt.nspname = 'sys' THEN at.atttypid ELSE tp.typbasetype END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_truetypmod(nt pg_namespace, at pg_attribute, tp pg_type) RETURNS int4 + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT CASE WHEN nt.nspname = 'pg_catalog' OR nt.nspname = 'sys' THEN at.atttypmod ELSE tp.typtypmod END$$; + + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_char_max_length(type text, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN type IN ('char', 'nchar', 'varchar', 'nvarchar', 'binary', 'varbinary') + THEN CASE WHEN typmod = -1 + THEN -1 + ELSE typmod - 4 + END + WHEN type IN ('text', 'image') + THEN 2147483647 + WHEN type = 'ntext' + THEN 1073741823 + WHEN type = 'sysname' + THEN 128 + WHEN type = 'xml' + THEN -1 + WHEN type = 'sql_variant' + THEN 0 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_char_octet_length(type text, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN type IN ('char', 'varchar', 'binary', 'varbinary') + THEN CASE WHEN typmod = -1 /* default typmod */ + THEN -1 + ELSE typmod - 4 + END + WHEN type IN ('nchar', 'nvarchar') + THEN CASE WHEN typmod = -1 /* default typmod */ + THEN -1 + ELSE (typmod - 4) * 2 + END + WHEN type IN ('text', 'image') + THEN 2147483647 /* 2^30 + 1 */ + WHEN type = 'ntext' + THEN 2147483646 /* 2^30 */ + WHEN type = 'sysname' + THEN 256 + WHEN type = 'sql_variant' + THEN 0 + WHEN type = 'xml' + THEN -1 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_numeric_precision(type text, typid oid, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE typid + WHEN 21 /*int2*/ THEN 5 + WHEN 23 /*int4*/ THEN 10 + WHEN 20 /*int8*/ THEN 19 + WHEN 1700 /*numeric*/ THEN + CASE WHEN typmod = -1 + THEN null + ELSE ((typmod - 4) >> 16) & 65535 + END + WHEN 700 /*float4*/ THEN 24 + WHEN 701 /*float8*/ THEN 53 + ELSE + CASE WHEN type = 'tinyint' THEN 3 + WHEN type = 'money' THEN 19 + WHEN type = 'smallmoney' THEN 10 + ELSE null + END + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_numeric_precision_radix(type text, typid oid, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN typid IN (700, 701) THEN 2 + WHEN typid IN (20, 21, 23, 1700) THEN 10 + WHEN type IN ('tinyint', 'money', 'smallmoney') THEN 10 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_numeric_scale(type text, typid oid, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN typid IN (21, 23, 20) THEN 0 + WHEN typid IN (1700) THEN + CASE WHEN typmod = -1 + THEN null + ELSE (typmod - 4) & 65535 + END + WHEN type = 'tinyint' THEN 0 + WHEN type IN ('money', 'smallmoney') THEN 4 + ELSE null + END$$; + +CREATE OR REPLACE FUNCTION information_schema_tsql._pgtsql_datetime_precision(type text, typmod int4) RETURNS integer + LANGUAGE sql + IMMUTABLE + PARALLEL SAFE + RETURNS NULL ON NULL INPUT + AS +$$SELECT + CASE WHEN type = 'date' + THEN 0 + WHEN type = 'datetime' + THEN 3 + WHEN type IN ('time', 'datetime2', 'smalldatetime', 'datetimeoffset') + THEN CASE WHEN typmod < 0 THEN 6 ELSE typmod END + ELSE null + END$$; + +CREATE OR REPLACE VIEW information_schema_tsql.columns AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "TABLE_CATALOG", + CAST(ext.orig_name AS sys.nvarchar(128)) AS "TABLE_SCHEMA", + CAST(c.relname AS sys.nvarchar(128)) AS "TABLE_NAME", + CAST(a.attname AS sys.nvarchar(128)) AS "COLUMN_NAME", + CAST(a.attnum AS int) AS "ORDINAL_POSITION", + CAST(CASE WHEN a.attgenerated = '' THEN pg_get_expr(ad.adbin, ad.adrelid) END AS sys.nvarchar(4000)) AS "COLUMN_DEFAULT", + CAST(CASE WHEN a.attnotnull OR (t.typtype = 'd' AND t.typnotnull) THEN 'NO' ELSE 'YES' END + AS varchar(3)) + AS "IS_NULLABLE", + + CAST( + CASE WHEN tsql_type_name = 'sysname' THEN sys.translate_pg_type_to_tsql(t.typbasetype) + ELSE tsql_type_name END + AS sys.nvarchar(128)) + AS "DATA_TYPE", + + CAST( + information_schema_tsql._pgtsql_char_max_length(tsql_type_name, true_typmod) + AS int) + AS "CHARACTER_MAXIMUM_LENGTH", + + CAST( + information_schema_tsql._pgtsql_char_octet_length(tsql_type_name, true_typmod) + AS int) + AS "CHARACTER_OCTET_LENGTH", + + CAST( + /* Handle Tinyint separately */ + information_schema_tsql._pgtsql_numeric_precision(tsql_type_name, true_typid, true_typmod) + AS sys.tinyint) + AS "NUMERIC_PRECISION", + + CAST( + information_schema_tsql._pgtsql_numeric_precision_radix(tsql_type_name, true_typid, true_typmod) + AS smallint) + AS "NUMERIC_PRECISION_RADIX", + + CAST( + information_schema_tsql._pgtsql_numeric_scale(tsql_type_name, true_typid, true_typmod) + AS int) + AS "NUMERIC_SCALE", + + CAST( + information_schema_tsql._pgtsql_datetime_precision(tsql_type_name, true_typmod) + AS smallint) + AS "DATETIME_PRECISION", + + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_CATALOG", + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_SCHEMA", + /* + * TODO: We need to first create mapping of collation name to char-set name; + * Until then return null. + */ + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_NAME", + + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_CATALOG", + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_SCHEMA", + + /* Returns Babelfish specific collation name. */ + CAST(co.collname AS sys.nvarchar(128)) AS "COLLATION_NAME", + + CAST(CASE WHEN t.typtype = 'd' AND nt.nspname <> 'pg_catalog' AND nt.nspname <> 'sys' + THEN nc.dbname ELSE null END + AS sys.nvarchar(128)) AS "DOMAIN_CATALOG", + CAST(CASE WHEN t.typtype = 'd' AND nt.nspname <> 'pg_catalog' AND nt.nspname <> 'sys' + THEN ext.orig_name ELSE null END + AS sys.nvarchar(128)) AS "DOMAIN_SCHEMA", + CAST(CASE WHEN t.typtype = 'd' AND nt.nspname <> 'pg_catalog' AND nt.nspname <> 'sys' + THEN t.typname ELSE null END + AS sys.nvarchar(128)) AS "DOMAIN_NAME" + + FROM (pg_attribute a LEFT JOIN pg_attrdef ad ON attrelid = adrelid AND attnum = adnum) + JOIN (pg_class c JOIN sys.pg_namespace_ext nc ON (c.relnamespace = nc.oid)) ON a.attrelid = c.oid + JOIN (pg_type t JOIN pg_namespace nt ON (t.typnamespace = nt.oid)) ON a.atttypid = t.oid + LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON (bt.typnamespace = nbt.oid)) + ON (t.typtype = 'd' AND t.typbasetype = bt.oid) + LEFT JOIN pg_collation co on co.oid = a.attcollation + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on nc.nspname = ext.nspname, + information_schema_tsql._pgtsql_truetypid(nt, a, t) AS true_typid, + information_schema_tsql._pgtsql_truetypmod(nt, a, t) AS true_typmod, + sys.translate_pg_type_to_tsql(true_typid) AS tsql_type_name + + WHERE (NOT pg_is_other_temp_schema(nc.oid)) + AND a.attnum > 0 AND NOT a.attisdropped + AND c.relkind IN ('r', 'v', 'p') + AND (pg_has_role(c.relowner, 'USAGE') + OR has_column_privilege(c.oid, a.attnum, + 'SELECT, INSERT, UPDATE, REFERENCES')) + AND ext.dbid = cast(sys.db_id() as oid); +GRANT SELECT ON information_schema_tsql.columns TO PUBLIC; + +CREATE OR REPLACE VIEW information_schema_tsql.domains AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "DOMAIN_CATALOG", + CAST(ext.orig_name AS sys.nvarchar(128)) AS "DOMAIN_SCHEMA", + CAST(t.typname AS sys.sysname) AS "DOMAIN_NAME", + CAST(case when is_tbl_type THEN 'table type' ELSE tsql_type_name END AS sys.sysname) AS "DATA_TYPE", + + CAST(information_schema_tsql._pgtsql_char_max_length(tsql_type_name, t.typtypmod) + AS int) + AS "CHARACTER_MAXIMUM_LENGTH", + + CAST(information_schema_tsql._pgtsql_char_octet_length(tsql_type_name, t.typtypmod) + AS int) + AS "CHARACTER_OCTET_LENGTH", + + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_CATALOG", + CAST(NULL as sys.nvarchar(128)) AS "COLLATION_SCHEMA", + + /* Returns Babelfish specific collation name. */ + CAST( + CASE co.collname + WHEN 'default' THEN current_setting('babelfishpg_tsql.server_collation_name') + ELSE co.collname + END + AS sys.nvarchar(128)) AS "COLLATION_NAME", + + CAST(null AS sys.varchar(6)) AS "CHARACTER_SET_CATALOG", + CAST(null AS sys.varchar(3)) AS "CHARACTER_SET_SCHEMA", + /* + * TODO: We need to first create mapping of collation name to char-set name; + * Until then return null. + */ + CAST(null AS sys.nvarchar(128)) AS "CHARACTER_SET_NAME", + + CAST(information_schema_tsql._pgtsql_numeric_precision(tsql_type_name, t.typbasetype, t.typtypmod) + AS sys.tinyint) + AS "NUMERIC_PRECISION", + + CAST(information_schema_tsql._pgtsql_numeric_precision_radix(tsql_type_name, t.typbasetype, t.typtypmod) + AS smallint) + AS "NUMERIC_PRECISION_RADIX", + + CAST(information_schema_tsql._pgtsql_numeric_scale(tsql_type_name, t.typbasetype, t.typtypmod) + AS int) + AS "NUMERIC_SCALE", + + CAST(information_schema_tsql._pgtsql_datetime_precision(tsql_type_name, t.typtypmod) + AS smallint) + AS "DATETIME_PRECISION", + + CAST(case when is_tbl_type THEN NULL ELSE t.typdefault END AS sys.nvarchar(4000)) AS "DOMAIN_DEFAULT" + + FROM (pg_type t JOIN sys.pg_namespace_ext nc ON t.typnamespace = nc.oid) + LEFT JOIN pg_collation co ON t.typcollation = co.oid + LEFT JOIN sys.babelfish_namespace_ext ext on nc.nspname = ext.nspname, + sys.translate_pg_type_to_tsql(t.typbasetype) AS tsql_type_name, + sys.is_table_type(t.typrelid) as is_tbl_type + + WHERE (pg_has_role(t.typowner, 'USAGE') + OR has_type_privilege(t.oid, 'USAGE')) + AND (t.typtype = 'd' OR is_tbl_type) + AND ext.dbid = cast(sys.db_id() as oid); + +GRANT SELECT ON information_schema_tsql.domains TO PUBLIC; + + +CREATE VIEW information_schema_tsql.tables AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "TABLE_CATALOG", + CAST(ext.orig_name AS sys.nvarchar(128)) AS "TABLE_SCHEMA", + CAST( + CASE WHEN c.reloptions[1] LIKE 'bbf_original_rel_name%' THEN substring(c.reloptions[1], 23) + ELSE c.relname END + AS sys._ci_sysname) AS "TABLE_NAME", + + CAST( + CASE WHEN c.relkind IN ('r', 'p') THEN 'BASE TABLE' + WHEN c.relkind = 'v' THEN 'VIEW' + ELSE null END + AS varchar(10)) AS "TABLE_TYPE" + + FROM sys.pg_namespace_ext nc JOIN pg_class c ON (nc.oid = c.relnamespace) + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on nc.nspname = ext.nspname + + WHERE c.relkind IN ('r', 'v', 'p') + AND (NOT pg_is_other_temp_schema(nc.oid)) + AND (pg_has_role(c.relowner, 'USAGE') + OR has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') + OR has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES') ) + AND ext.dbid = cast(sys.db_id() as oid); +GRANT SELECT ON information_schema_tsql.tables TO PUBLIC; + +CREATE OR REPLACE VIEW sys.spt_columns_view_managed AS +SELECT + o.object_id AS OBJECT_ID, + isc."TABLE_CATALOG"::information_schema.sql_identifier AS TABLE_CATALOG, + isc."TABLE_SCHEMA"::information_schema.sql_identifier AS TABLE_SCHEMA, + o.name AS TABLE_NAME, + c.name AS COLUMN_NAME, + isc."ORDINAL_POSITION"::information_schema.cardinal_number AS ORDINAL_POSITION, + isc."COLUMN_DEFAULT"::information_schema.character_data AS COLUMN_DEFAULT, + isc."IS_NULLABLE"::information_schema.yes_or_no AS IS_NULLABLE, + isc."DATA_TYPE"::information_schema.character_data AS DATA_TYPE, + + CAST (CASE WHEN isc."CHARACTER_MAXIMUM_LENGTH" < 0 THEN 0 ELSE isc."CHARACTER_MAXIMUM_LENGTH" END + AS information_schema.cardinal_number) AS CHARACTER_MAXIMUM_LENGTH, + + CAST (CASE WHEN isc."CHARACTER_OCTET_LENGTH" < 0 THEN 0 ELSE isc."CHARACTER_OCTET_LENGTH" END + AS information_schema.cardinal_number) AS CHARACTER_OCTET_LENGTH, + + CAST (CASE WHEN isc."NUMERIC_PRECISION" < 0 THEN 0 ELSE isc."NUMERIC_PRECISION" END + AS information_schema.cardinal_number) AS NUMERIC_PRECISION, + + CAST (CASE WHEN isc."NUMERIC_PRECISION_RADIX" < 0 THEN 0 ELSE isc."NUMERIC_PRECISION_RADIX" END + AS information_schema.cardinal_number) AS NUMERIC_PRECISION_RADIX, + + CAST (CASE WHEN isc."NUMERIC_SCALE" < 0 THEN 0 ELSE isc."NUMERIC_SCALE" END + AS information_schema.cardinal_number) AS NUMERIC_SCALE, + + CAST (CASE WHEN isc."DATETIME_PRECISION" < 0 THEN 0 ELSE isc."DATETIME_PRECISION" END + AS information_schema.cardinal_number) AS DATETIME_PRECISION, + + isc."CHARACTER_SET_CATALOG"::information_schema.sql_identifier AS CHARACTER_SET_CATALOG, + isc."CHARACTER_SET_SCHEMA"::information_schema.sql_identifier AS CHARACTER_SET_SCHEMA, + isc."CHARACTER_SET_NAME"::information_schema.sql_identifier AS CHARACTER_SET_NAME, + isc."COLLATION_CATALOG"::information_schema.sql_identifier AS COLLATION_CATALOG, + isc."COLLATION_SCHEMA"::information_schema.sql_identifier AS COLLATION_SCHEMA, + c.collation_name AS COLLATION_NAME, + isc."DOMAIN_CATALOG"::information_schema.sql_identifier AS DOMAIN_CATALOG, + isc."DOMAIN_SCHEMA"::information_schema.sql_identifier AS DOMAIN_SCHEMA, + isc."DOMAIN_NAME"::information_schema.sql_identifier 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_tsql.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'); +GRANT SELECT ON sys.spt_columns_view_managed TO PUBLIC; + +CREATE OR REPLACE 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 LOWER(in_catalog)) AND + (in_owner IS NULL OR s_cv.TABLE_SCHEMA LIKE LOWER(in_owner)) AND + (in_table IS NULL OR s_cv.TABLE_NAME LIKE LOWER(in_table)) AND + (in_column IS NULL OR s_cv.COLUMN_NAME LIKE LOWER(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 OR REPLACE 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; + +CREATE OR REPLACE FUNCTION sys.tsql_stat_get_activity( + IN view_name text, + OUT procid int, + OUT client_version int, + OUT library_name VARCHAR(32), + OUT language VARCHAR(128), + OUT quoted_identifier bool, + OUT arithabort bool, + OUT ansi_null_dflt_on bool, + OUT ansi_defaults bool, + OUT ansi_warnings bool, + OUT ansi_padding bool, + OUT ansi_nulls bool, + OUT concat_null_yields_null bool, + OUT textsize int, + OUT datefirst int, + OUT lock_timeout int, + OUT transaction_isolation int2, + OUT client_pid int, + OUT row_count bigint, + OUT error int, + OUT trancount int, + OUT protocol_version int, + OUT packet_size int, + OUT encrypyt_option VARCHAR(40), + OUT database_id int2) +AS 'babelfishpg_tsql', 'tsql_stat_get_activity' +LANGUAGE C VOLATILE STRICT; + +create or replace view sys.dm_exec_sessions + as + select a.pid as session_id + , a.backend_start::sys.datetime as login_time + , a.client_hostname::sys.nvarchar(128) as host_name + , a.application_name::sys.nvarchar(128) as program_name + , d.client_pid as host_process_id + , d.client_version as client_version + , d.library_name::sys.nvarchar(32) as client_interface_name + , null::sys.varbinary(85) as security_id + , a.usename::sys.nvarchar(128) as login_name + , (select sys.default_domain())::sys.nvarchar(128) as nt_domain + , null::sys.nvarchar(128) as nt_user_name + , a.state::sys.nvarchar(30) as status + , null::sys.nvarchar(128) as context_info + , null::integer as cpu_time + , null::integer as memory_usage + , null::integer as total_scheduled_time + , null::integer as total_elapsed_time + , a.client_port as endpoint_id + , a.query_start::sys.datetime as last_request_start_time + , a.state_change::sys.datetime as last_request_end_time + , null::bigint as "reads" + , null::bigint as "writes" + , null::bigint as logical_reads + , case when a.client_port > 0 then 1::sys.bit else 0::sys.bit end as is_user_process + , d.textsize as text_size + , d.language::sys.nvarchar(128) as language + , 'ymd'::sys.nvarchar(3) as date_format-- Bld 173 lacks support for SET DATEFORMAT and always expects ymd + , d.datefirst::smallint as date_first -- Bld 173 lacks support for SET DATEFIRST and always returns 7 + , CAST(CAST(d.quoted_identifier as integer) as sys.bit) as quoted_identifier + , CAST(CAST(d.arithabort as integer) as sys.bit) as arithabort + , CAST(CAST(d.ansi_null_dflt_on as integer) as sys.bit) as ansi_null_dflt_on + , CAST(CAST(d.ansi_defaults as integer) as sys.bit) as ansi_defaults + , CAST(CAST(d.ansi_warnings as integer) as sys.bit) as ansi_warnings + , CAST(CAST(d.ansi_padding as integer) as sys.bit) as ansi_padding + , CAST(CAST(d.ansi_nulls as integer) as sys.bit) as ansi_nulls + , CAST(CAST(d.concat_null_yields_null as integer) as sys.bit) as concat_null_yields_null + , d.transaction_isolation::smallint as transaction_isolation_level + , d.lock_timeout as lock_timeout + , 0 as deadlock_priority + , d.row_count as row_count + , d.error as prev_error + , null::sys.varbinary(85) as original_security_id + , a.usename::sys.nvarchar(128) as original_login_name + , null::sys.datetime as last_successful_logon + , null::sys.datetime as last_unsuccessful_logon + , null::bigint as unsuccessful_logons + , null::int as group_id + , d.database_id::smallint as database_id + , 0 as authenticating_database_id + , d.trancount as open_transaction_count + from pg_catalog.pg_stat_activity AS a + RIGHT JOIN sys.tsql_stat_get_activity('sessions') AS d ON (a.pid = d.procid); + GRANT SELECT ON sys.dm_exec_sessions TO PUBLIC; + +create or replace view sys.dm_exec_connections + as + select a.pid as session_id + , a.pid as most_recent_session_id + , a.backend_start::sys.datetime as connect_time + , 'TCP'::sys.nvarchar(40) as net_transport + , 'TSQL'::sys.nvarchar(40) as protocol_type + , d.protocol_version as protocol_version + , 4 as endpoint_id + , d.encrypyt_option::sys.nvarchar(40) as encrypt_option + , null::sys.nvarchar(40) as auth_scheme + , null::smallint as node_affinity + , null::int as num_reads + , null::int as num_writes + , null::sys.datetime as last_read + , null::sys.datetime as last_write + , d.packet_size as net_packet_size + , a.client_addr::varchar(48) as client_net_address + , a.client_port as client_tcp_port + , null::varchar(48) as local_net_address + , null::int as local_tcp_port + , null::sys.uniqueidentifier as connection_id + , null::sys.uniqueidentifier as parent_connection_id + , a.pid::sys.varbinary(64) as most_recent_sql_handle + from pg_catalog.pg_stat_activity AS a + RIGHT JOIN sys.tsql_stat_get_activity('connections') AS d ON (a.pid = d.procid); + GRANT SELECT ON sys.dm_exec_connections TO PUBLIC; + +-- BABEL-1782 +CREATE OR REPLACE VIEW sys.sp_tables_view AS +SELECT +t2.dbname AS TABLE_QUALIFIER, +CAST(t3.name AS name) 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, sys.schemas AS t3 +WHERE t1.relnamespace = t3.schema_id AND t1.relnamespace = t2.oid AND t1.relkind IN ('r','v','m') +AND has_schema_privilege(t1.relnamespace, 'USAGE') +AND has_table_privilege(t1.oid, 'SELECT,INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER'); +GRANT SELECT ON sys.sp_tables_view TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sp_tables_internal( + in_table_name sys.nvarchar(384) = '', + in_table_owner sys.nvarchar(384) = '', + in_table_qualifier sys.sysname = '', + in_table_type sys.varchar(100) = '', + in_fusepattern sys.bit = '1') + RETURNS TABLE ( + out_table_qualifier sys.sysname, + out_table_owner sys.sysname, + out_table_name sys.sysname, + out_table_type sys.varchar(32), + out_remarks sys.varchar(254) + ) + AS $$ + DECLARE opt_table sys.varchar(16) = ''; + DECLARE opt_view sys.varchar(16) = ''; + BEGIN + + IF (SELECT count(*) FROM unnest(string_to_array(in_table_type, ',')) WHERE upper(trim(unnest)) = 'TABLE' OR upper(trim(unnest)) = '''TABLE''') >= 1 THEN + opt_table = 'TABLE'; + END IF; + IF (SELECT count(*) from unnest(string_to_array(in_table_type, ',')) WHERE upper(trim(unnest)) = 'VIEW' OR upper(trim(unnest)) = '''VIEW''') >= 1 THEN + opt_view = 'VIEW'; + END IF; + IF in_fusepattern = 1 THEN + RETURN query + SELECT + CAST(table_qualifier AS sys.sysname) AS TABLE_QUALIFIER, + CAST(table_owner AS sys.sysname) AS TABLE_OWNER, + CAST(table_name AS sys.sysname) AS TABLE_NAME, + CAST(table_type AS sys.varchar(32)) AS TABLE_TYPE, + CAST(remarks AS sys.varchar(254)) AS REMARKS + FROM sys.sp_tables_view + WHERE ((SELECT coalesce(in_table_name,'')) = '' OR lower(table_name) LIKE lower(in_table_name)) + AND ((SELECT coalesce(in_table_owner,'')) = '' OR lower(table_owner) LIKE lower(in_table_owner)) + AND ((SELECT coalesce(in_table_qualifier,'')) = '' OR lower(table_qualifier) LIKE lower(in_table_qualifier)) + AND ((SELECT coalesce(in_table_type,'')) = '' OR table_type = opt_table OR table_type = opt_view) + ORDER BY table_qualifier, table_owner, table_name; + ELSE + RETURN query + SELECT + CAST(table_qualifier AS sys.sysname) AS TABLE_QUALIFIER, + CAST(table_owner AS sys.sysname) AS TABLE_OWNER, + CAST(table_name AS sys.sysname) AS TABLE_NAME, + CAST(table_type AS sys.varchar(32)) AS TABLE_TYPE, + CAST(remarks AS sys.varchar(254)) AS REMARKS + FROM sys.sp_tables_view + WHERE ((SELECT coalesce(in_table_name,'')) = '' OR lower(table_name) = lower(in_table_name)) + AND ((SELECT coalesce(in_table_owner,'')) = '' OR lower(table_owner) = lower(in_table_owner)) + AND ((SELECT coalesce(in_table_qualifier,'')) = '' OR lower(table_qualifier) = lower(in_table_qualifier)) + AND ((SELECT coalesce(in_table_type,'')) = '' OR table_type = opt_table OR table_type = opt_view) + ORDER BY table_qualifier, table_owner, table_name; + END IF; + END; +$$ +LANGUAGE plpgsql; + + +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 $$ + DECLARE @opt_table sys.varchar(16) = ''; + DECLARE @opt_view sys.varchar(16) = ''; +BEGIN + IF (@table_qualifier != '') AND (LOWER(@table_qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + SELECT + CAST(out_table_qualifier AS sys.sysname) AS TABLE_QUALIFIER, + CAST(out_table_owner AS sys.sysname) AS TABLE_OWNER, + CAST(out_table_name AS sys.sysname) AS TABLE_NAME, + CAST(out_table_type AS sys.varchar(32)) AS TABLE_TYPE, + CAST(out_remarks AS sys.varchar(254)) AS REMARKS + FROM sys.sp_tables_internal(@table_name, @table_owner, @table_qualifier, CAST(@table_type AS varchar(100)), @fusepattern); +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE ON PROCEDURE sys.sp_tables 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 + , coalesce(t.database_id, 0)::oid 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 sys.tsql_stat_get_activity('sessions') as t on a.pid = t.procid +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 + where a.datname = current_database(); /* current physical database will always be babelfish database */ +GRANT SELECT ON sys.sysprocesses TO PUBLIC; + +-- For some cases, T-SQL throws an error in DML-time even though it can be detected in DDL-time. +-- This function can be used in DDL-time to postpone errors without impacting general DML performance. +CREATE OR REPLACE FUNCTION sys.babelfish_runtime_error(msg ANYCOMPATIBLE) +RETURNS ANYCOMPATIBLE AS +$$ +BEGIN + RAISE EXCEPTION '%', msg; +END; +$$ +LANGUAGE PLPGSQL; +GRANT ALL on FUNCTION sys.babelfish_runtime_error TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sp_column_privileges_view AS +SELECT +CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS TABLE_OWNER, +CAST(t1.relname AS sys.sysname) AS TABLE_NAME, +CAST(COALESCE(SPLIT_PART(t6.attoptions[1], '=', 2), t5.column_name) AS sys.sysname) AS COLUMN_NAME, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t5.grantor) AS sys.sysname) AS GRANTOR, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t5.grantee) AS sys.sysname) AS GRANTEE, +CAST(t5.privilege_type AS sys.varchar(32)) AS PRIVILEGE, +CAST(t5.is_grantable AS sys.varchar(3)) AS IS_GRANTABLE +FROM pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN information_schema.column_privileges t5 ON t1.relname = t5.table_name AND t2.nspname = t5.table_schema + JOIN pg_attribute t6 ON t6.attrelid = t1.oid AND t6.attname = t5.column_name +WHERE t5.privilege_type NOT IN ('TRIGGER', 'TRUNCATE'); +GRANT SELECT ON sys.sp_column_privileges_view TO PUBLIC; + + +CREATE OR REPLACE PROCEDURE sys.sp_column_privileges( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@table_qualifier" sys.sysname = '', + "@column_name" sys.nvarchar(384) = '' +) +AS $$ +BEGIN + IF (@table_qualifier != '') AND (LOWER(@table_qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + IF (COALESCE(@table_owner, '') = '') + BEGIN + + IF EXISTS ( + SELECT * FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) and LOWER(SCHEMA_NAME()) = LOWER(table_qualifier) + ) + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + COLUMN_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE + FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND (LOWER(SCHEMA_NAME()) = LOWER(table_owner)) + AND ((SELECT COALESCE(@table_qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@table_qualifier)) + AND ((SELECT COALESCE(@column_name,'')) = '' OR LOWER(column_name) LIKE LOWER(@column_name)) + ORDER BY table_qualifier, table_owner, table_name, column_name, privilege; + END + ELSE + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + COLUMN_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE + FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND (LOWER('dbo')= LOWER(table_owner)) + AND ((SELECT COALESCE(@table_qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@table_qualifier)) + AND ((SELECT COALESCE(@column_name,'')) = '' OR LOWER(column_name) LIKE LOWER(@column_name)) + ORDER BY table_qualifier, table_owner, table_name, column_name, privilege; + END + END + ELSE + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + COLUMN_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE + FROM sys.sp_column_privileges_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT COALESCE(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT COALESCE(@table_qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@table_qualifier)) + AND ((SELECT COALESCE(@column_name,'')) = '' OR LOWER(column_name) LIKE LOWER(@column_name)) + ORDER BY table_qualifier, table_owner, table_name, column_name, privilege; + END +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE ON PROCEDURE sys.sp_column_privileges TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.sp_datatype_info_helper( + IN odbcVer smallint, + IN is_100 bool, + OUT TYPE_NAME VARCHAR(20), + OUT DATA_TYPE INT, + OUT "PRECISION" BIGINT, + OUT LITERAL_PREFIX VARCHAR(20), + OUT LITERAL_SUFFIX VARCHAR(20), + OUT CREATE_PARAMS VARCHAR(20), + OUT NULLABLE INT, + OUT CASE_SENSITIVE INT, + OUT SEARCHABLE INT, + OUT UNSIGNED_ATTRIBUTE INT, + OUT MONEY INT, + OUT AUTO_INCREMENT INT, + OUT LOCAL_TYPE_NAME VARCHAR(20), + OUT MINIMUM_SCALE INT, + OUT MAXIMUM_SCALE INT, + OUT SQL_DATA_TYPE INT, + OUT SQL_DATETIME_SUB INT, + OUT NUM_PREC_RADIX INT, + OUT INTERVAL_PRECISION INT, + OUT USERTYPE INT, + OUT LENGTH INT, + OUT SS_DATA_TYPE smallint, +-- below column is added in order to join PG's information_schema.columns for sys.sp_columns_100_view + OUT PG_TYPE_NAME VARCHAR(20) +) +AS 'babelfishpg_tsql', 'sp_datatype_info_helper' +LANGUAGE C IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.sp_special_columns_precision_helper(IN type TEXT, IN sp_columns_precision INT, IN sp_columns_max_length SMALLINT, IN sp_datatype_info_precision BIGINT) RETURNS INT +AS $$ +SELECT + CASE + WHEN type in ('real','float') THEN sp_columns_max_length * 2 - 1 + WHEN type in ('char','varchar','binary','varbinary') THEN sp_columns_max_length + WHEN type in ('nchar','nvarchar') THEN sp_columns_max_length / 2 + WHEN type in ('sysname','uniqueidentifier') THEN sp_datatype_info_precision + ELSE sp_columns_precision + END; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.sp_special_columns_length_helper(IN type TEXT, IN sp_columns_precision INT, IN sp_columns_max_length SMALLINT, IN sp_datatype_info_precision BIGINT) RETURNS INT +AS $$ +SELECT + CASE + WHEN type in ('decimal','numeric','money','smallmoney') THEN sp_columns_precision + 2 + WHEN type in ('time','date','datetime2','datetimeoffset') THEN sp_columns_precision * 2 + WHEN type in ('smalldatetime') THEN sp_columns_precision + WHEN type in ('datetime') THEN sp_columns_max_length * 2 + WHEN type in ('sql_variant') THEN sp_datatype_info_precision + ELSE sp_columns_max_length + END; +$$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION sys.sp_special_columns_scale_helper(IN type TEXT, IN sp_columns_scale INT) RETURNS INT +AS $$ +SELECT + CASE + WHEN type in ('bit','real','float','char','varchar','nchar','nvarchar','time','date','datetime2','datetimeoffset','varbinary','binary','sql_variant','sysname','uniqueidentifier') THEN NULL + ELSE sp_columns_scale + END; +$$ LANGUAGE SQL IMMUTABLE; + +-- TODO: BABEL-2838 +CREATE OR REPLACE VIEW sys.sp_special_columns_view AS +SELECT DISTINCT +CAST(1 as smallint) AS SCOPE, +CAST(coalesce (split_part(pa.attoptions[1], '=', 2) ,c1.name) AS sys.sysname) AS COLUMN_NAME, -- get original column name if exists +CAST(t6.data_type AS smallint) AS DATA_TYPE, + +CASE -- cases for when they are of type identity. + WHEN c1.is_identity = 1 AND (t8.name = 'decimal' or t8.name = 'numeric') + THEN CAST(CONCAT(t8.name, '() identity') AS sys.sysname) + WHEN c1.is_identity = 1 AND (t8.name != 'decimal' AND t8.name != 'numeric') + THEN CAST(CONCAT(t8.name, ' identity') AS sys.sysname) + ELSE CAST(t8.name AS sys.sysname) +END AS TYPE_NAME, + +CAST(sys.sp_special_columns_precision_helper(coalesce(tsql_type_name, tsql_base_type_name), c1.precision, c1.max_length, t6."PRECISION") AS int) AS PRECISION, +CAST(sys.sp_special_columns_length_helper(coalesce(tsql_type_name, tsql_base_type_name), c1.precision, c1.max_length, t6."PRECISION") AS int) AS LENGTH, +CAST(sys.sp_special_columns_scale_helper(coalesce(tsql_type_name, tsql_base_type_name), c1.scale) AS smallint) AS SCALE, +CAST(1 AS smallint) AS PSEUDO_COLUMN, +CAST(c1.is_nullable AS int) AS IS_NULLABLE, +CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS TABLE_OWNER, +CAST(t1.relname AS sys.sysname) AS TABLE_NAME, + +CASE + WHEN idx.is_unique = 1 AND (idx.is_unique_constraint !=1 AND idx.is_primary_key != 1) + THEN CAST('u' AS sys.sysname) -- if it is a unique index, then we should cast it as 'u' for filtering purposes + ELSE CAST(t5.contype AS sys.sysname) +END AS CONSTRAINT_TYPE + +FROM pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + LEFT JOIN pg_constraint t5 ON t1.oid = t5.conrelid + LEFT JOIN sys.indexes idx ON idx.object_id = t1.oid + JOIN sys.columns c1 ON t1.oid = c1.object_id + + JOIN pg_catalog.pg_type AS t7 ON t7.oid = c1.system_type_id + JOIN sys.types as t8 ON c1.user_type_id = t8.user_type_id + LEFT JOIN sys.sp_datatype_info_helper(2::smallint, false) AS t6 ON t7.typname = t6.pg_type_name OR t7.typname = t6.type_name --need in order to get accurate DATA_TYPE value + LEFT JOIN pg_catalog.pg_attribute AS pa ON t1.oid = pa.attrelid AND c1.name = pa.attname + , sys.translate_pg_type_to_tsql(t8.user_type_id) AS tsql_type_name + , sys.translate_pg_type_to_tsql(t8.system_type_id) AS tsql_base_type_name + WHERE (t5.contype = 'p' OR t5.contype = 'u' + OR ((idx.is_unique = 1) AND (idx.is_primary_key !=1 AND idx.is_unique_constraint !=1))) -- Only looking for unique indexes + AND (CAST(c1.column_id AS smallint) = ANY (t5.conkey) OR ((idx.is_unique = 1) AND (idx.is_primary_key !=1 AND idx.is_unique_constraint !=1))) + AND has_schema_privilege(s1.schema_id, 'USAGE'); + +GRANT SELECT ON sys.sp_special_columns_view TO PUBLIC; + + +CREATE OR REPLACE PROCEDURE sys.sp_special_columns( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@qualifier" sys.sysname = '', + "@col_type" char(1) = 'R', + "@scope" char(1) = 'T', + "@nullable" char(1) = 'U', + "@odbcver" int = 2 +) +AS $$ +DECLARE @special_col_type sys.sysname; +BEGIN + IF (@qualifier != '') AND (LOWER(@qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + + END + + IF (LOWER(@col_type) = LOWER('V')) + BEGIN + THROW 33557097, N'TIMESTAMP datatype is not currently supported in Babelfish', 1; + END + + IF (LOWER(@nullable) = LOWER('O')) + BEGIN + SELECT TOP 1 @special_col_type=constraint_type FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) + ORDER BY constraint_type, column_name; + + IF @special_col_type='u' + BEGIN + IF @scope='C' + BEGIN + SELECT TOP 1 + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + ORDER BY scope, column_name; + + END + ELSE + BEGIN + SELECT TOP 1 + SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + END + + END + + ELSE + BEGIN + IF @scope='C' + BEGIN + SELECT + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + ELSE + BEGIN + SELECT SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND (is_nullable = 0) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + END + END + + ELSE + BEGIN + SELECT TOP 1 @special_col_type=constraint_type FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) + ORDER BY constraint_type, column_name; + + IF @special_col_type='u' + BEGIN + IF @scope='C' + BEGIN + SELECT TOP 1 + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + ORDER BY scope, column_name; + END + + ELSE + BEGIN + SELECT TOP 1 SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + ORDER BY scope, column_name; + END + + END + ELSE + BEGIN + IF @scope='C' + BEGIN + SELECT + CAST(0 AS smallint) AS SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + + ELSE + BEGIN + SELECT SCOPE, + COLUMN_NAME, + DATA_TYPE, + TYPE_NAME, + PRECISION, + LENGTH, + SCALE, + PSEUDO_COLUMN FROM sys.sp_special_columns_view + WHERE LOWER(@table_name) = LOWER(table_name) + AND ((SELECT coalesce(@table_owner,'')) = '' OR LOWER(table_owner) = LOWER(@table_owner)) + AND ((SELECT coalesce(@qualifier,'')) = '' OR LOWER(table_qualifier) = LOWER(@qualifier)) AND LOWER(constraint_type) = LOWER(@special_col_type) + AND CONSTRAINT_TYPE = 'p' + ORDER BY scope, column_name; + END + + END + END + +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_special_columns TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_special_columns_100( + "@table_name" sys.sysname, + "@table_owner" sys.sysname = '', + "@qualifier" sys.sysname = '', + "@col_type" char(1) = 'R', + "@scope" char(1) = 'T', + "@nullable" char(1) = 'U', + "@odbcver" int = 2 +) +AS $$ +BEGIN + EXEC sp_special_columns @table_name, @table_owner, @qualifier, @col_type, @scope, @nullable, @odbcver +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_special_columns_100 TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sp_table_privileges_view AS +SELECT DISTINCT +CAST(t2.dbname AS sys.sysname) AS TABLE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS TABLE_OWNER, +CAST(t1.relname AS sys.sysname) AS TABLE_NAME, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t4.grantor) AS sys.sysname) AS GRANTOR, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = t4.grantee) AS sys.sysname) AS GRANTEE, +CAST(t4.privilege_type AS sys.sysname) AS PRIVILEGE, +CAST(t4.is_grantable AS sys.sysname) AS IS_GRANTABLE +FROM pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN information_schema.table_privileges t4 ON t1.relname = t4.table_name +WHERE t4.privilege_type NOT IN ('TRIGGER', 'TRUNCATE'); +GRANT SELECT on sys.sp_table_privileges_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_table_privileges( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = '', + "@table_qualifier" sys.sysname = '', + "@fusepattern" sys.bit = 1 +) +AS $$ +BEGIN + + IF (@table_qualifier != '') AND (LOWER(@table_qualifier) != LOWER(sys.db_name())) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + IF @fusepattern = 1 + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE FROM sys.sp_table_privileges_view + WHERE LOWER(TABLE_NAME) LIKE LOWER(@table_name) + AND ((SELECT COALESCE(@table_owner,'')) = '' OR LOWER(TABLE_OWNER) LIKE LOWER(@table_owner)) + ORDER BY table_qualifier, table_owner, table_name, privilege; + END + ELSE + BEGIN + SELECT + TABLE_QUALIFIER, + TABLE_OWNER, + TABLE_NAME, + GRANTOR, + GRANTEE, + PRIVILEGE, + IS_GRANTABLE FROM sys.sp_table_privileges_view + WHERE LOWER(TABLE_NAME) = LOWER(@table_name) + AND ((SELECT COALESCE(@table_owner,'')) = '' OR LOWER(TABLE_OWNER) = LOWER(@table_owner)) + ORDER BY table_qualifier, table_owner, table_name, privilege; + END + +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE ON PROCEDURE sys.sp_table_privileges TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.tsql_type_radix_for_sp_columns_helper(IN type TEXT) +RETURNS SMALLINT +AS $$ +DECLARE + radix SMALLINT; +BEGIN + CASE type + WHEN 'tinyint' THEN radix = 10; + WHEN 'money' THEN radix = 10; + WHEN 'smallmoney' THEN radix = 10; + WHEN 'sql_variant' THEN radix = 10; + ELSE + radix = NULL; + END CASE; + RETURN radix; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION sys.tsql_type_length_for_sp_columns_helper(IN type TEXT, IN typelen INT, IN typemod INT) +RETURNS INT +AS $$ +DECLARE + length INT; + precision INT; +BEGIN + -- unknown tsql type + IF type IS NULL THEN + RETURN typelen::INT; + END IF; + + IF typemod = -1 AND (type = 'varchar' OR type = 'nvarchar' OR type = 'varbinary') THEN + length = 0; + RETURN length; + END IF; + + IF typelen != -1 THEN + CASE type + WHEN 'tinyint' THEN length = 1; + WHEN 'date' THEN length = 6; + WHEN 'smalldatetime' THEN length = 16; + WHEN 'smallmoney' THEN length = 12; + WHEN 'money' THEN length = 21; + WHEN 'datetime' THEN length = 16; + WHEN 'datetime2' THEN length = 16; + WHEN 'datetimeoffset' THEN length = 20; + WHEN 'time' THEN length = 12; + WHEN 'timestamp' THEN length = 8; + ELSE length = typelen; + END CASE; + RETURN length; + END IF; + + CASE + WHEN type in ('char', 'bpchar', 'varchar', 'binary', 'varbinary') THEN length = typemod - 4; + WHEN type in ('nchar', 'nvarchar') THEN length = (typemod - 4) * 2; + WHEN type in ('text', 'image') THEN length = 2147483647; + WHEN type = 'ntext' THEN length = 2147483646; + WHEN type = 'xml' THEN length = 0; + WHEN type = 'sql_variant' THEN length = 8000; + WHEN type = 'money' THEN length = 21; + WHEN type = 'sysname' THEN length = (typemod - 4) * 2; + WHEN type in ('numeric', 'decimal') THEN + precision = ((typemod - 4) >> 16) & 65535; + length = precision + 2; + ELSE + length = typemod; + END CASE; + RETURN length; +END; +$$ LANGUAGE plpgsql IMMUTABLE STRICT; + +CREATE OR REPLACE VIEW sys.sp_columns_100_view AS + SELECT + CAST(t4."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, + CAST(t4."TABLE_SCHEMA" AS sys.sysname) AS TABLE_OWNER, + CAST(t4."TABLE_NAME" 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(coalesce(tsql_type_name, t.typname) AS sys.sysname) AS TYPE_NAME, + + CASE WHEN t4."CHARACTER_MAXIMUM_LENGTH" = -1 THEN 0::INT + WHEN a.atttypmod != -1 + THEN + CAST(coalesce(t4."NUMERIC_PRECISION", t4."CHARACTER_MAXIMUM_LENGTH", sys.tsql_type_precision_helper(t4."DATA_TYPE", a.atttypmod)) AS INT) + WHEN tsql_type_name = 'timestamp' + THEN 8 + ELSE + CAST(coalesce(t4."NUMERIC_PRECISION", t4."CHARACTER_MAXIMUM_LENGTH", sys.tsql_type_precision_helper(t4."DATA_TYPE", t.typtypmod)) AS INT) + END AS PRECISION, + + CASE WHEN a.atttypmod != -1 + THEN + CAST(sys.tsql_type_length_for_sp_columns_helper(t4."DATA_TYPE", a.attlen, a.atttypmod) AS int) + ELSE + CAST(sys.tsql_type_length_for_sp_columns_helper(t4."DATA_TYPE", a.attlen, t.typtypmod) AS int) + END AS LENGTH, + + + CASE WHEN a.atttypmod != -1 + THEN + CAST(coalesce(t4."NUMERIC_SCALE", sys.tsql_type_scale_helper(t4."DATA_TYPE", a.atttypmod, true)) AS smallint) + ELSE + CAST(coalesce(t4."NUMERIC_SCALE", sys.tsql_type_scale_helper(t4."DATA_TYPE", t.typtypmod, true)) AS smallint) + END AS SCALE, + + + CAST(coalesce(t4."NUMERIC_PRECISION_RADIX", sys.tsql_type_radix_for_sp_columns_helper(t4."DATA_TYPE")) 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, + + CASE WHEN t4."DATA_TYPE" = 'xml' THEN 0::INT + WHEN t4."DATA_TYPE" = 'sql_variant' THEN 8000::INT + WHEN t4."CHARACTER_MAXIMUM_LENGTH" = -1 THEN 0::INT + ELSE CAST(t4."CHARACTER_OCTET_LENGTH" AS int) + END AS CHAR_OCTET_LENGTH, + + CAST(t4."ORDINAL_POSITION" 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, + CAST(t6.is_computed as smallint) AS SS_IS_COMPUTED, + CAST(t6.is_identity as smallint) 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 + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on t2.nspname = ext.nspname + JOIN information_schema_tsql.columns t4 ON (t1.relname = t4."TABLE_NAME" AND ext.orig_name = t4."TABLE_SCHEMA") + LEFT JOIN pg_attribute a on a.attrelid = t1.oid AND a.attname = t4."COLUMN_NAME" + LEFT JOIN pg_type t ON t.oid = a.atttypid + LEFT JOIN sys.columns t6 ON + ( + t1.oid = t6.object_id AND + t4."ORDINAL_POSITION" = t6.column_id + ) + , sys.translate_pg_type_to_tsql(a.atttypid) AS tsql_type_name + , sys.spt_datatype_info_table AS t5 + WHERE (t4."DATA_TYPE" = t5.TYPE_NAME) + AND ext.dbid = cast(sys.db_id() as oid); + +GRANT SELECT on sys.sp_columns_100_view TO PUBLIC; + +create or replace 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 lower(table_name) similar to lower(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, ordinal_position; + 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, ordinal_position; + END IF; +end; +$$ +LANGUAGE plpgsql; + +-- Need to rename and recreate the object due to previous incorrect definition. +ALTER FUNCTION sys.sp_describe_undeclared_parameters_internal RENAME TO sp_describe_undeclared_parameters_internal_deprecated_1_2; +-- 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 OR REPLACE FUNCTION sys.sp_describe_undeclared_parameters_internal( + tsqlquery sys.nvarchar(4000), + params sys.nvarchar(4000) = 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; + +-- Need to rename and recreate the object due to previous incorrect definition. +ALTER PROCEDURE sys.sp_describe_undeclared_parameters RENAME TO sp_describe_undeclared_parameters_deprecated_1_2; + +CREATE OR REPLACE PROCEDURE sys.sp_describe_undeclared_parameters ( + "@tsql" sys.nvarchar(4000), + "@params" sys.nvarchar(4000) = 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; + +CREATE OR REPLACE VIEW sys.sp_fkeys_view AS +SELECT +-- primary key info +CAST(t2.dbname AS sys.sysname) AS PKTABLE_QUALIFIER, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = ref.table_schema) AS sys.sysname) AS PKTABLE_OWNER, +CAST(ref.table_name AS sys.sysname) AS PKTABLE_NAME, +CAST(coalesce(split_part(pkname_table.attoptions[1], '=', 2), ref.column_name) AS sys.sysname) AS PKCOLUMN_NAME, + +-- foreign key info +CAST(t2.dbname AS sys.sysname) AS FKTABLE_QUALIFIER, +CAST((select orig_name from sys.babelfish_namespace_ext where dbid = sys.db_id() and nspname = fk.table_schema) AS sys.sysname) AS FKTABLE_OWNER, +CAST(fk.table_name AS sys.sysname) AS FKTABLE_NAME, +CAST(coalesce(split_part(fkname_table.attoptions[1], '=', 2), fk.column_name) AS sys.sysname) AS FKCOLUMN_NAME, + +CAST(seq AS smallint) AS KEY_SEQ, +CASE + WHEN map.update_rule = 'NO ACTION' THEN CAST(1 AS smallint) + WHEN map.update_rule = 'SET NULL' THEN CAST(2 AS smallint) + WHEN map.update_rule = 'SET DEFAULT' THEN CAST(3 AS smallint) + ELSE CAST(0 AS smallint) +END AS UPDATE_RULE, + +CASE + WHEN map.delete_rule = 'NO ACTION' THEN CAST(1 AS smallint) + WHEN map.delete_rule = 'SET NULL' THEN CAST(2 AS smallint) + WHEN map.delete_rule = 'SET DEFAULT' THEN CAST(3 AS smallint) + ELSE CAST(0 AS smallint) +END AS DELETE_RULE, +CAST(fk.constraint_name AS sys.sysname) AS FK_NAME, +CAST(ref.constraint_name AS sys.sysname) AS PK_NAME + +FROM information_schema.referential_constraints AS map + +-- join unique constraints (e.g. PKs constraints) to ref columns info +INNER JOIN information_schema.key_column_usage AS ref + JOIN pg_catalog.pg_class p1 -- Need to join this in order to get oid for pkey's original bbf name + JOIN sys.pg_namespace_ext p2 ON p1.relnamespace = p2.oid + JOIN information_schema.columns p4 ON p1.relname = p4.table_name AND p1.relnamespace::regnamespace::text = p4.table_schema + JOIN pg_constraint p5 ON p1.oid = p5.conrelid + ON (p1.relname=ref.table_name AND p4.column_name=ref.column_name AND ref.table_schema = p2.nspname AND ref.table_schema = p4.table_schema) + + ON ref.constraint_catalog = map.unique_constraint_catalog + AND ref.constraint_schema = map.unique_constraint_schema + AND ref.constraint_name = map.unique_constraint_name + +-- join fk columns to the correct ref columns using ordinal positions +INNER JOIN information_schema.key_column_usage AS fk + ON fk.constraint_catalog = map.constraint_catalog + AND fk.constraint_schema = map.constraint_schema + AND fk.constraint_name = map.constraint_name + AND fk.position_in_unique_constraint = ref.ordinal_position + +INNER JOIN pg_catalog.pg_class t1 + JOIN sys.pg_namespace_ext t2 ON t1.relnamespace = t2.oid + JOIN information_schema.columns t4 ON t1.relname = t4.table_name AND t1.relnamespace::regnamespace::text = t4.table_schema + JOIN pg_constraint t5 ON t1.oid = t5.conrelid + ON (t1.relname=fk.table_name AND t4.column_name=fk.column_name AND fk.table_schema = t2.nspname AND fk.table_schema = t4.table_schema) + +-- get foreign key's original bbf name +JOIN pg_catalog.pg_attribute fkname_table + ON (t1.oid = fkname_table.attrelid) AND (fk.column_name = fkname_table.attname) + +-- get primary key's original bbf name +JOIN pg_catalog.pg_attribute pkname_table + ON (p1.oid = pkname_table.attrelid) AND (ref.column_name = pkname_table.attname) + + , generate_series(1,16) seq -- BBF has max 16 columns per primary key +WHERE t5.contype = 'f' +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_fkeys_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_fkeys( + "@pktable_name" sys.sysname = '', + "@pktable_owner" sys.sysname = '', + "@pktable_qualifier" sys.sysname = '', + "@fktable_name" sys.sysname = '', + "@fktable_owner" sys.sysname = '', + "@fktable_qualifier" sys.sysname = '' +) +AS $$ +BEGIN + + IF coalesce(@pktable_name,'') = '' AND coalesce(@fktable_name,'') = '' + BEGIN + THROW 33557097, N'Primary or foreign key table name must be given.', 1; + END + + IF (@pktable_qualifier != '' AND (SELECT sys.db_name()) != @pktable_qualifier) OR + (@fktable_qualifier != '' AND (SELECT sys.db_name()) != @fktable_qualifier) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + SELECT + PKTABLE_QUALIFIER, + PKTABLE_OWNER, + PKTABLE_NAME, + PKCOLUMN_NAME, + FKTABLE_QUALIFIER, + FKTABLE_OWNER, + FKTABLE_NAME, + FKCOLUMN_NAME, + KEY_SEQ, + UPDATE_RULE, + DELETE_RULE, + FK_NAME, + PK_NAME + FROM sys.sp_fkeys_view + WHERE ((SELECT coalesce(@pktable_name,'')) = '' OR LOWER(pktable_name) = LOWER(@pktable_name)) + AND ((SELECT coalesce(@fktable_name,'')) = '' OR LOWER(fktable_name) = LOWER(@fktable_name)) + AND ((SELECT coalesce(@pktable_owner,'')) = '' OR LOWER(pktable_owner) = LOWER(@pktable_owner)) + AND ((SELECT coalesce(@pktable_qualifier,'')) = '' OR LOWER(pktable_qualifier) = LOWER(@pktable_qualifier)) + AND ((SELECT coalesce(@fktable_owner,'')) = '' OR LOWER(fktable_owner) = LOWER(@fktable_owner)) + AND ((SELECT coalesce(@fktable_qualifier,'')) = '' OR LOWER(fktable_qualifier) = LOWER(@fktable_qualifier)) + ORDER BY fktable_qualifier, fktable_owner, fktable_name, key_seq; + +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE ON PROCEDURE sys.sp_fkeys TO PUBLIC; + +-- Need to rename and recreate the object due to previous incorrect definition. +ALTER FUNCTION sys.checksum RENAME TO checksum_deprecated_1_2; +CREATE OR REPLACE FUNCTION sys.checksum(VARIADIC arr TEXT[]) +RETURNS INTEGER +AS 'babelfishpg_tsql', 'checksum' +LANGUAGE C IMMUTABLE PARALLEL SAFE; + +-- Need to rename and recreate the object due to previous incorrect definition. +ALTER FUNCTION sys.babelfish_inconsistent_metadata RENAME TO babelfish_inconsistent_metadata_deprecated_1_2; +CREATE OR REPLACE FUNCTION sys.babelfish_inconsistent_metadata(return_consistency boolean default false) +RETURNS table ( + object_type varchar(32), + schema_name varchar(128), + object_name varchar(128), + detail jsonb +) AS 'babelfishpg_tsql', 'babelfish_inconsistent_metadata' LANGUAGE C; + +CREATE OR REPLACE FUNCTION is_srvrolemember(role sys.SYSNAME, login sys.SYSNAME DEFAULT suser_name()) +RETURNS INTEGER AS +$$ +DECLARE has_role BOOLEAN; +DECLARE login_valid BOOLEAN; +BEGIN + role := TRIM(trailing from LOWER(role)); + login := TRIM(trailing from LOWER(login)); + + login_valid = (login = suser_name()) OR + (EXISTS (SELECT name + FROM sys.server_principals + WHERE + LOWER(name) = login + AND type = 'S')); + + IF NOT login_valid THEN + RETURN NULL; + + ELSIF role = 'public' THEN + RETURN 1; + + ELSIF role = 'sysadmin' THEN + has_role = pg_has_role(login::TEXT, role::TEXT, 'MEMBER'); + IF has_role THEN + RETURN 1; + ELSE + RETURN 0; + END IF; + + ELSIF role IN ( + 'serveradmin', + 'securityadmin', + 'setupadmin', + 'securityadmin', + 'processadmin', + 'dbcreator', + 'diskadmin', + 'bulkadmin') THEN + RETURN 0; + + ELSE + RETURN NULL; + END IF; + + EXCEPTION WHEN OTHERS THEN + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE VIEW sys.sp_stored_procedures_view AS +SELECT +CAST(d.name AS sys.sysname) AS PROCEDURE_QUALIFIER, +CAST(s1.name AS sys.sysname) AS PROCEDURE_OWNER, + +CASE + WHEN p.prokind = 'p' THEN CAST(concat(p.proname, ';1') AS sys.nvarchar(134)) + ELSE CAST(concat(p.proname, ';0') AS sys.nvarchar(134)) +END AS PROCEDURE_NAME, + +-1 AS NUM_INPUT_PARAMS, +-1 AS NUM_OUTPUT_PARAMS, +-1 AS NUM_RESULT_SETS, +CAST(NULL AS varchar(254)) AS REMARKS, +cast(2 AS smallint) AS PROCEDURE_TYPE + +FROM pg_catalog.pg_proc p + +INNER JOIN sys.schemas s1 ON p.pronamespace = s1.schema_id +INNER JOIN sys.databases d ON d.database_id = sys.db_id() +WHERE has_schema_privilege(s1.schema_id, 'USAGE') + +UNION + +SELECT CAST((SELECT sys.db_name()) AS sys.sysname) AS PROCEDURE_QUALIFIER, +CAST(nspname AS sys.sysname) AS PROCEDURE_OWNER, + +CASE + WHEN prokind = 'p' THEN cast(concat(proname, ';1') AS sys.nvarchar(134)) + ELSE cast(concat(proname, ';0') AS sys.nvarchar(134)) +END AS PROCEDURE_NAME, + +-1 AS NUM_INPUT_PARAMS, +-1 AS NUM_OUTPUT_PARAMS, +-1 AS NUM_RESULT_SETS, +CAST(NULL AS varchar(254)) AS REMARKS, +cast(2 AS smallint) AS PROCEDURE_TYPE + +FROM pg_catalog.pg_namespace n +JOIN pg_catalog.pg_proc p +ON pronamespace = n.oid +WHERE nspname = 'sys' AND (proname LIKE 'sp\_%' OR proname LIKE 'xp\_%' OR proname LIKE 'dm\_%' OR proname LIKE 'fn\_%'); + +GRANT SELECT ON sys.sp_stored_procedures_view TO PUBLIC; + +CREATE OR REPLACE PROCEDURE sys.sp_stored_procedures( + "@sp_name" sys.nvarchar(390) = '', + "@sp_owner" sys.nvarchar(384) = '', + "@sp_qualifier" sys.sysname = '', + "@fusepattern" sys.bit = '1' +) +AS $$ +BEGIN + IF (@sp_qualifier != '') AND LOWER(sys.db_name()) != LOWER(@sp_qualifier) + BEGIN + THROW 33557097, N'The database name component of the object qualifier must be the name of the current database.', 1; + END + + -- If @sp_name or @sp_owner = '%', it gets converted to NULL or '' regardless of @fusepattern + IF @sp_name = '%' + BEGIN + SELECT @sp_name = '' + END + + IF @sp_owner = '%' + BEGIN + SELECT @sp_owner = '' + END + + -- Changes fusepattern to 0 if no wildcards are used. NOTE: Need to add [] wildcard pattern when it is implemented. Wait for BABEL-2452 + IF @fusepattern = 1 + BEGIN + IF (CHARINDEX('%', @sp_name) != 0 AND CHARINDEX('_', @sp_name) != 0 AND CHARINDEX('%', @sp_owner) != 0 AND CHARINDEX('_', @sp_owner) != 0 ) + BEGIN + SELECT @fusepattern = 0; + END + END + + -- Condition for when sp_name argument is not given or is null, or is just a wildcard (same order) + IF COALESCE(@sp_name, '') = '' + BEGIN + IF @fusepattern=1 + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE ((SELECT COALESCE(@sp_owner,'')) = '' OR LOWER(procedure_owner) LIKE LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE ((SELECT COALESCE(@sp_owner,'')) = '' OR LOWER(procedure_owner) LIKE LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + END + -- When @sp_name is not null + ELSE + BEGIN + -- When sp_owner is null and fusepattern = 0 + IF (@fusepattern = 0 AND COALESCE(@sp_owner,'') = '') + BEGIN + IF EXISTS ( -- Search in the sys schema + SELECT * FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = 'sys')) + BEGIN + SELECT PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = 'sys') + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE IF EXISTS ( + SELECT * FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = LOWER(SCHEMA_NAME())) + ) + BEGIN + SELECT PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = LOWER(SCHEMA_NAME())) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE -- Search in the dbo schema (if nothing exists it should just return nothing). + BEGIN + SELECT PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = 'dbo') + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + + END + ELSE IF (@fusepattern = 0 AND COALESCE(@sp_owner,'') != '') + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE (LOWER(LEFT(procedure_name, -2)) = LOWER(@sp_name)) + AND (LOWER(procedure_owner) = LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + ELSE -- fusepattern = 1 + BEGIN + SELECT + PROCEDURE_QUALIFIER, + PROCEDURE_OWNER, + PROCEDURE_NAME, + NUM_INPUT_PARAMS, + NUM_OUTPUT_PARAMS, + NUM_RESULT_SETS, + REMARKS, + PROCEDURE_TYPE FROM sys.sp_stored_procedures_view + WHERE ((SELECT COALESCE(@sp_name,'')) = '' OR LOWER(LEFT(procedure_name, -2)) LIKE LOWER(@sp_name)) + AND ((SELECT COALESCE(@sp_owner,'')) = '' OR LOWER(procedure_owner) LIKE LOWER(@sp_owner)) + ORDER BY procedure_qualifier, procedure_owner, procedure_name; + END + END +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_stored_procedures TO PUBLIC; + +CREATE OR REPLACE PROCEDURE xp_qv(IN nvarchar(256), IN nvarchar(256)) + AS 'babelfishpg_tsql', 'xp_qv_internal' LANGUAGE C; + +CREATE OR REPLACE PROCEDURE sys.create_xp_qv_in_master_dbo() + LANGUAGE C + AS 'babelfishpg_tsql', 'create_xp_qv_in_master_dbo_internal'; + +CALL sys.create_xp_qv_in_master_dbo(); +ALTER PROCEDURE master_dbo.xp_qv OWNER TO sysadmin; +DROP PROCEDURE sys.create_xp_qv_in_master_dbo; + +CREATE OR REPLACE FUNCTION sys.servicename() + RETURNS sys.NVARCHAR(128) AS 'babelfishpg_tsql' LANGUAGE C; + +CREATE FUNCTION fulltextserviceproperty (TEXT) + RETURNS sys.int AS 'babelfishpg_tsql', 'fulltextserviceproperty' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- JSON Functions +CREATE OR REPLACE FUNCTION sys.isjson(json_string text) +RETURNS INTEGER +AS 'babelfishpg_tsql', 'tsql_isjson' LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.json_value(json_string text, path text) +RETURNS sys.NVARCHAR(4000) +AS 'babelfishpg_tsql', 'tsql_json_value' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION sys.json_query(json_string text, path text default '$') +RETURNS sys.NVARCHAR +AS 'babelfishpg_tsql', 'tsql_json_query' LANGUAGE C IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE VIEW sys.sp_statistics_view AS +SELECT +CAST(t3."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, +CAST(t3."TABLE_SCHEMA" AS sys.sysname) AS TABLE_OWNER, +CAST(t3."TABLE_NAME" AS sys.sysname) AS TABLE_NAME, +CAST(NULL AS smallint) AS NON_UNIQUE, +CAST(NULL AS sys.sysname) AS INDEX_QUALIFIER, +CAST(NULL AS sys.sysname) AS INDEX_NAME, +CAST(0 AS smallint) AS TYPE, +CAST(NULL AS smallint) AS SEQ_IN_INDEX, +CAST(NULL AS sys.sysname) AS COLUMN_NAME, +CAST(NULL AS sys.varchar(1)) AS COLLATION, +CAST(t1.reltuples AS int) AS CARDINALITY, +CAST(t1.relpages AS int) AS PAGES, +CAST(NULL AS sys.varchar(128)) AS FILTER_CONDITION +FROM pg_catalog.pg_class t1 + JOIN sys.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN information_schema_tsql.columns t3 ON (t1.relname = t3."TABLE_NAME" AND s1.name = t3."TABLE_SCHEMA") + , generate_series(0,31) seq -- SQL server has max 32 columns per index +UNION +SELECT +CAST(t4."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, +CAST(t4."TABLE_SCHEMA" AS sys.sysname) AS TABLE_OWNER, +CAST(t4."TABLE_NAME" 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.schemas s1 ON s1.schema_id = t1.relnamespace + JOIN pg_catalog.pg_roles t3 ON t1.relowner = t3.oid + JOIN information_schema_tsql.columns t4 ON (t1.relname = t4."TABLE_NAME" AND s1.name = t4."TABLE_SCHEMA") + 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."ORDINAL_POSITION" AS smallint) = ANY (t5.indkey) + AND CAST(t4."ORDINAL_POSITION" AS smallint) = t5.indkey[seq]; +GRANT SELECT on sys.sp_statistics_view TO PUBLIC; + +create or replace 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 = 'Y' and (non_unique IS NULL or non_unique = 0)) or (in_is_unique = 'N')) + order by non_unique, type, index_name, seq_in_index; +end; +$$ +LANGUAGE plpgsql; + +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 VIEW sys.sp_pkeys_view AS +SELECT +CAST(t4."TABLE_CATALOG" AS sys.sysname) AS TABLE_QUALIFIER, +CAST(t4."TABLE_SCHEMA" 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 + LEFT OUTER JOIN sys.babelfish_namespace_ext ext on t2.nspname = ext.nspname + JOIN information_schema_tsql.columns t4 ON (t1.relname = t4."TABLE_NAME" AND ext.orig_name = t4."TABLE_SCHEMA") + 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."ORDINAL_POSITION" AS smallint) = ANY (t5.conkey) + AND CAST(t4."ORDINAL_POSITION" AS smallint) = t5.conkey[seq] + AND ext.dbid = cast(sys.db_id() as oid); + +GRANT SELECT on sys.sp_pkeys_view TO PUBLIC; + +-- internal function in order to workaround BABEL-1597 +create or replace 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 table_owner = coalesce(in_table_owner,'dbo') + 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) = 'dbo', + "@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 OR REPLACE PROCEDURE sys.sp_pkeys( + "@table_name" sys.nvarchar(384), + "@table_owner" sys.nvarchar(384) = 'dbo', + "@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 OR REPLACE FUNCTION sys.has_dbaccess(database_name SYSNAME) RETURNS INTEGER AS +'babelfishpg_tsql', 'has_dbaccess' +LANGUAGE C IMMUTABLE STRICT; + +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::CHAR(20), 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.sp_datatype_info_helper(@odbcver, false) where @data_type = 0 or data_type = @data_type + order by DATA_TYPE, AUTO_INCREMENT, MONEY, USERTYPE; +END; +$$ +LANGUAGE 'pltsql'; + +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::CHAR(20), 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.sp_datatype_info_helper(@odbcver, true) where @data_type = 0 or data_type = @data_type + order by DATA_TYPE, AUTO_INCREMENT, MONEY, USERTYPE; +END; +$$ +LANGUAGE 'pltsql'; +CREATE TABLE sys.babelfish_configurations ( + configuration_id INT, + name sys.nvarchar(35), + value sys.sql_variant, + minimum sys.sql_variant, + maximum sys.sql_variant, + value_in_use sys.sql_variant, + description sys.nvarchar(255), + is_dynamic sys.BIT, + is_advanced sys.BIT, + comment_syscurconfigs sys.nvarchar(255), + comment_sysconfigures sys.nvarchar(255) +) WITH (OIDS = FALSE); + +SELECT pg_catalog.pg_extension_config_dump('sys.babelfish_configurations', ''); + +CREATE OR REPLACE VIEW sys.configurations +AS +SELECT configuration_id, + name, + value, + minimum, + maximum, + value_in_use, + description, + is_dynamic, + is_advanced +FROM sys.babelfish_configurations; +GRANT SELECT ON sys.configurations TO PUBLIC; + +CREATE OR REPLACE VIEW sys.syscurconfigs +AS +SELECT value, + configuration_id AS config, + comment_syscurconfigs AS comment, + CASE + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 0 THEN CAST(0 as smallint) + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 1 THEN CAST(1 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 0 THEN CAST(2 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 1 THEN CAST(3 as smallint) + END AS status +FROM sys.babelfish_configurations; +GRANT SELECT ON sys.syscurconfigs TO PUBLIC; + +CREATE OR REPLACE VIEW sys.sysconfigures +AS +SELECT value_in_use AS value, + configuration_id AS config, + comment_sysconfigures AS comment, + CASE + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 0 THEN CAST(0 as smallint) + WHEN CAST(is_advanced as int) = 0 AND CAST(is_dynamic as int) = 1 THEN CAST(1 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 0 THEN CAST(2 as smallint) + WHEN CAST(is_advanced as int) = 1 AND CAST(is_dynamic as int) = 1 THEN CAST(3 as smallint) + END AS status +FROM sys.babelfish_configurations; +GRANT SELECT ON sys.sysconfigures TO PUBLIC; + +-- The value and value_in_use is set to 1 because SSMS-Babelfish connectivity requires it. +INSERT INTO sys.babelfish_configurations + VALUES (16387, + 'SMO and DMO XPs', + 1, + 0, + 1, + 1, + 'Enable or disable SMO and DMO XPs', + sys.bitin('1'), + sys.bitin('1'), + 'Enable or disable SMO and DMO XPs', + 'Enable or disable SMO and DMO XPs' + ); + +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(sys.babelfish_truncate_identifier(@table_name), + sys.babelfish_truncate_identifier(@table_owner), + sys.babelfish_truncate_identifier(@table_qualifier), + sys.babelfish_truncate_identifier(@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(sys.babelfish_truncate_identifier(@table_name), + sys.babelfish_truncate_identifier(@table_owner), + sys.babelfish_truncate_identifier(@table_qualifier), + sys.babelfish_truncate_identifier(@column_name), @NameScope,@ODBCVer, @fusepattern); +END; +$$ +LANGUAGE 'pltsql'; +GRANT ALL on PROCEDURE sys.sp_columns_100 TO PUBLIC; + +CREATE VIEW information_schema_tsql.table_constraints AS + SELECT CAST(nc.dbname AS sys.nvarchar(128)) AS "CONSTRAINT_CATALOG", + CAST(extc.orig_name AS sys.nvarchar(128)) AS "CONSTRAINT_SCHEMA", + CAST(c.conname AS sys.sysname) AS "CONSTRAINT_NAME", + CAST(nr.dbname AS sys.nvarchar(128)) AS "TABLE_CATALOG", + CAST(extr.orig_name AS sys.nvarchar(128)) AS "TABLE_SCHEMA", + CAST(r.relname AS sys.sysname) AS "TABLE_NAME", + CAST( + CASE c.contype WHEN 'c' THEN 'CHECK' + WHEN 'f' THEN 'FOREIGN KEY' + WHEN 'p' THEN 'PRIMARY KEY' + WHEN 'u' THEN 'UNIQUE' END + AS sys.varchar(11)) AS "CONSTRAINT_TYPE", + CAST('NO' AS sys.varchar(2)) AS "IS_DEFERRABLE", + CAST('NO' AS sys.varchar(2)) AS "INITIALLY_DEFERRED" + + FROM sys.pg_namespace_ext nc LEFT OUTER JOIN sys.babelfish_namespace_ext extc ON nc.nspname = extc.nspname, + sys.pg_namespace_ext nr LEFT OUTER JOIN sys.babelfish_namespace_ext extr ON nr.nspname = extr.nspname, + pg_constraint c, + pg_class r + + WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace + AND c.conrelid = r.oid + AND c.contype NOT IN ('t', 'x') + AND r.relkind IN ('r', 'p') + AND (NOT pg_is_other_temp_schema(nr.oid)) + AND (pg_has_role(r.relowner, 'USAGE') + OR has_table_privilege(r.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') + OR has_any_column_privilege(r.oid, 'SELECT, INSERT, UPDATE, REFERENCES') ) + AND extc.dbid = cast(sys.db_id() as oid); + +GRANT SELECT ON information_schema_tsql.table_constraints TO PUBLIC; + +CREATE OR REPLACE FUNCTION sys.role_id(role_name SYS.SYSNAME) +RETURNS INT +AS 'babelfishpg_tsql', 'role_id' +LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE; +GRANT EXECUTE ON FUNCTION sys.role_id TO PUBLIC; + +CREATE PROCEDURE sys.sp_helpuser("@name_in_db" sys.SYSNAME = NULL) AS +$$ +BEGIN + IF @name_in_db IS NULL + BEGIN + SELECT CAST(Ext.orig_username AS SYS.SYSNAME) AS 'UserName', + CAST(CASE WHEN Ext.orig_username = 'dbo' THEN 'db_owner' ELSE 'PUBLIC' END AS SYS.SYSNAME) AS 'RoleName', + CAST(Ext.login_name AS SYS.SYSNAME) AS 'LoginName', + CAST(LogExt.default_database_name AS SYS.SYSNAME) AS 'DefDBName', + CAST(Ext.default_schema_name AS SYS.SYSNAME) AS 'DefSchemaName', + CAST(Base.oid AS INT) AS 'UserID', + CAST(CAST(Base.oid AS INT) AS SYS.VARBINARY(85)) AS 'SID' + FROM pg_catalog.pg_roles AS Base INNER JOIN sys.babelfish_authid_user_ext AS Ext + ON Base.rolname = Ext.rolname + LEFT OUTER JOIN sys.babelfish_authid_login_ext As LogExt + ON LogExt.rolname = Ext.orig_username + WHERE Ext.database_name = DB_NAME() + END + ELSE IF @name_in_db = 'db_owner' + BEGIN + -- simplification of role case, since no user defined roles exist yet + SELECT CAST('db_owner' AS SYS.SYSNAME) AS 'Role_name', + ROLE_ID('db_owner') AS 'Role_id', + CAST('dbo' AS SYS.SYSNAME) AS Users_in_role, + USER_ID('dbo') AS 'Userid'; + END + ELSE IF EXISTS (SELECT 1 + FROM sys.babelfish_authid_user_ext + WHERE (orig_username = @name_in_db + OR lower(orig_username) = lower(@name_in_db)) + AND database_name = DB_NAME()) + BEGIN + SELECT CAST(Ext.orig_username AS SYS.SYSNAME) AS 'UserName', + CAST(CASE WHEN Ext.orig_username = 'dbo' THEN 'db_owner' ELSE 'PUBLIC' END AS SYS.SYSNAME) AS 'RoleName', + CAST(Ext.login_name AS SYS.SYSNAME) AS 'LoginName', + CAST(LogExt.default_database_name AS SYS.SYSNAME) AS 'DefDBName', + CAST(Ext.default_schema_name AS SYS.SYSNAME) AS 'DefSchemaName', + CAST(Base.oid AS INT) AS 'UserID', + CAST(CAST(Base.oid AS INT) AS SYS.VARBINARY(85)) AS 'SID' + FROM pg_catalog.pg_roles AS Base INNER JOIN sys.babelfish_authid_user_ext AS Ext + ON Base.rolname = Ext.rolname + LEFT OUTER JOIN sys.babelfish_authid_login_ext As LogExt + ON LogExt.rolname = Ext.orig_username + WHERE Ext.database_name = DB_NAME() + AND (orig_username = @name_in_db OR lower(orig_username) = lower(@name_in_db)); + END + ELSE + RAISERROR ( 'The name supplied (%s) is not a user, role, or aliased login.', 16, 1, @name_in_db); +END; +$$ +LANGUAGE 'pltsql'; +GRANT EXECUTE on PROCEDURE sys.sp_helpuser 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 sys.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; + +-- 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/sql/upgrades/babelfishpg_tsql--1.2.0--1.2.1.sql b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.2.0--1.2.1.sql new file mode 100644 index 00000000000..a44c26ba87a --- /dev/null +++ b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--1.2.0--1.2.1.sql @@ -0,0 +1,7 @@ +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION ""babelfishpg_tsql"" UPDATE TO '1.2.1'" to load this file. \quit + +SELECT set_config('search_path', 'sys, '||current_setting('search_path'), false); + +-- Reset search_path to not affect any subsequent scripts +SELECT set_config('search_path', trim(leading 'sys, ' from current_setting('search_path')), false); \ No newline at end of file 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..cc2d8a6daa4 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/applock.c @@ -0,0 +1,891 @@ +/*------------------------------------------------------------------------- + * + * 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; \ + (ENTRY)->resource[0] = '\0'; \ + 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]; \ + snprintf(timeout_str, 16, "%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); + entry->resource[0] = '\0'; + } + 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; + entry->resource[0] = '\0'; + strncat(entry->resource, resource, strlen(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); + entry->resource[0] = '\0'; + strncat(entry->resource, resource, strlen(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..ceb87e7bedb --- /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.3.0" +#define BABELFISH_INTERNAL_VERSION_STR "Babelfish 13.7.0.1" +#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..84f7666e8e7 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-decl.y @@ -0,0 +1,111 @@ +%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_CreateUserStmt tsql_DropRoleStmt tsql_AlterUserStmt +%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_create_user_options tsql_alter_user_options +%type tsql_create_user_login +%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_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_DEFAULT_SCHEMA 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 UPDATE_paren 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..70582400293 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-epilogue.y.c @@ -0,0 +1,1365 @@ +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 void +tsql_update_delete_stmt_from_clause_alias(RangeVar *relation, List *from_clause) +{ + ListCell *lc; + foreach(lc, from_clause) + { + Node *n = lfirst(lc); + if (IsA(n, RangeVar)) + { + RangeVar *rv = (RangeVar *) n; + if (rv->alias && rv->alias->aliasname && + strcmp(rv->alias->aliasname, relation->relname) == 0) + { + if (relation->schemaname) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("The correlation name \'%s\' has the same exposed name as table \'%s.%s\'.", + rv->alias->aliasname, relation->schemaname, + relation->relname))); + } + else + { + /* + * Save the original alias name so that "inserted" and + * "deleted" tables in OUTPUT clause can be linked to it + */ + update_delete_target_alias = relation->relname; + + /* + * Update the relation to have the real table name as + * relname, and the original alias name as an alias + */ + relation->catalogname = rv->catalogname; + relation->schemaname = rv->schemaname; + relation->relname = rv->relname; + relation->inh = rv->inh; + relation->relpersistence = rv->relpersistence; + relation->alias = rv->alias; + + /* + * To avoid alias collision, remove the alias of the table + * in the FROM clause, because it will already be an alias + * of the target relation + */ + rv->alias = NULL; + return; + } + } + } + } +} + +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; + + snprintf(ctename, NAMEDATALEN, "internal_output_cte##sys_gen##%p", (void*) i); + internal_ctename = pstrdup(ctename); + + // PreparableStmt inside CTE + i->cols = insert_column_list; + 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 = NIL; + 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; + } + + output_into_insert_transformation = true; + + 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; + + snprintf(ctename, NAMEDATALEN, "internal_output_cte##sys_gen##%p", (void*) i); + internal_ctename = pstrdup(ctename); + + // PreparableStmt inside CTE + d->relation = relation_expr_opt_alias; + tsql_update_delete_stmt_from_clause_alias(d->relation, from_clause); + 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; + + snprintf(ctename, NAMEDATALEN, "internal_output_cte##sys_gen##%p", (void*) i); + internal_ctename = pstrdup(ctename); + + // PreparableStmt inside CTE + u->relation = relation_expr_opt_alias; + tsql_update_delete_stmt_from_clause_alias(u->relation, from_clause); + 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; + local_variable = false; + + 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 + { + 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; +} 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..6014e4f7c02 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-prologue.y.h @@ -0,0 +1,73 @@ +/***************************************************************************** + * + * 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 bool output_into_insert_transformation; +extern char *update_delete_target_alias; +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 void tsql_update_delete_stmt_from_clause_alias(RangeVar *relation, List *from_clause); +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..ff3b7f773b6 --- /dev/null +++ b/contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-rule.y @@ -0,0 +1,4042 @@ +/* + * 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 + ; + +tsql_CreateUserStmt: + CREATE USER RoleId tsql_create_user_login tsql_create_user_options + { + CreateRoleStmt *n = makeNode(CreateRoleStmt); + RoleSpec *login; + List *rolelist; + + n->stmt_type = ROLESTMT_USER; + n->role = $3; + n->options = list_make1(makeDefElem("isuser", + (Node *)makeInteger(true), + @1)); /* Must be first */ + n->options = lappend(n->options, + makeDefElem("inherit", + (Node *)makeInteger(true), + @1)); + n->options = lappend(n->options, + makeDefElem("canlogin", + (Node *)makeInteger(false), + @1)); + login = makeRoleSpec(ROLESPEC_CSTRING, @1); + if ($4 != NULL) + login->rolename = $4; + else + login->rolename = pstrdup($3); + rolelist = list_make1(login); /* Login must be first */ + n->options = lappend(n->options, + makeDefElem("rolemembers", + (Node *)rolelist, + @1)); + if ($5 != NULL) + n->options = lappend(n->options, $5); + n->options = lappend(n->options, + makeDefElem("name_location", + (Node *)makeInteger(@3), + @3)); + $$ = (Node *) n; + } + ; + +tsql_create_user_login: + FOR TSQL_LOGIN RoleId { $$ = $3; } + | FROM TSQL_LOGIN RoleId { $$ = $3; } + | /* EMPTY */ { $$ = NULL; } + ; + +tsql_create_user_options: + WITH TSQL_DEFAULT_SCHEMA '=' ColId + { + $$ = makeDefElem("default_schema", + (Node *)makeString($4), + @1); + } + | /* EMPTY */ { $$ = NULL; } + ; + +tsql_AlterUserStmt: + ALTER USER RoleSpec WITH tsql_alter_user_options + { + AlterRoleStmt *n = makeNode(AlterRoleStmt); + n->role = $3; + n->action = +1; /* add, if there are members */ + n->options = list_make1(makeDefElem("isuser", + (Node *)makeInteger(true), + @1)); /* Must be first */ + n->options = lappend(n->options, $5); + $$ = (Node *) n; + } + +tsql_alter_user_options: + TSQL_DEFAULT_SCHEMA '=' ColId + { + $$ = makeDefElem("default_schema", + (Node *)makeString($3), + @1); + } + | TSQL_DEFAULT_SCHEMA '=' NULL_P + { + $$ = makeDefElem("default_schema", + (Node *)makeString(""), + @1); + } + | NAME_P '=' ColId + { + $$ = makeDefElem("rename", + (Node *)makeString($3), + @1); + } + ; + +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_DropRoleStmt: + DROP ROLE role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = false; + n->roles = $3; + $$ = (Node *)n; + } + | DROP ROLE IF_P EXISTS role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + n->missing_ok = true; + n->roles = $5; + $$ = (Node *)n; + } + | DROP USER role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + RoleSpec *is_user; + + is_user = makeRoleSpec(ROLESPEC_CSTRING, @1); + is_user->rolename = "is_user"; + n->missing_ok = false; + n->roles = lcons(is_user, $3); + $$ = (Node *)n; + } + | DROP USER IF_P EXISTS role_list + { + DropRoleStmt *n = makeNode(DropRoleStmt); + RoleSpec *is_user; + + is_user = makeRoleSpec(ROLESPEC_CSTRING, @1); + is_user->rolename = "is_user"; + n->missing_ok = true; + n->roles = lcons(is_user, $5); + $$ = (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; + tsql_update_delete_stmt_from_clause_alias(n->relation, $6); + 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; + tsql_update_delete_stmt_from_clause_alias(n->relation, $7); + 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; + tsql_update_delete_stmt_from_clause_alias(n->relation, $8); + 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; + tsql_update_delete_stmt_from_clause_alias(n->relation, $7); + 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; + tsql_update_delete_stmt_from_clause_alias(n->relation, + $8); + 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; + tsql_update_delete_stmt_from_clause_alias(n->relation, + $8); + 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: + UPDATE_paren '(' NonReservedWord_or_Sconst ')' + { + $$ = (Node *) makeFuncCall(TsqlSystemFuncName2("update"), + list_make1(makeStringConst($3,@3)), + @1); + } + | 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 + | tsql_AlterUserStmt + | 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 + | tsql_CreateUserStmt + | CreatedbStmt + | DeallocateStmt + | DeclareCursorStmt + | DefineStmt + | tsql_DeleteStmt + | DiscardStmt + | DoStmt + | DropCastStmt + | tsql_DropLoginStmt + | DropOpClassStmt + | DropOpFamilyStmt + | DropOwnedStmt + | DropPLangStmt + | tsql_DropIndexStmt + | DropStmt + | DropSubscriptionStmt + | DropTableSpaceStmt + | DropTransformStmt + | DropUserMappingStmt + | tsql_DropRoleStmt + | 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_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; + + nt_inserted = makeNode(TriggerTransition); + nt_inserted->name = "inserted"; + nt_inserted->isNew = true; + nt_inserted->isTable = true; + n1->transitionRels = lappend(n1->transitionRels, nt_inserted); + + 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 =

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