Skip to content

Commit 5380950

Browse files
author
anju15bharti
committed
Merge branch 'BABEL_6_X_DEV' into BABEL-6433
2 parents 70c3a56 + 92a83aa commit 5380950

107 files changed

Lines changed: 8333 additions & 196 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

contrib/babelfishpg_tds/src/backend/tds/tdslogin.c

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,46 @@ TdsResetLoginFlags()
11621162
* Returns STATUS_OK or STATUS_ERROR, or might call ereport(FATAL) and
11631163
* not return at all.
11641164
*/
1165+
1166+
/*
1167+
* tds_truncate_identifier_md5
1168+
*
1169+
* Truncate an identifier exceeding NAMEDATALEN using clip + MD5 hash,
1170+
* matching the behavior of pltsql_truncate_identifier for CI_AS collations.
1171+
* Used for pre-auth user name truncation (where the tsql hook is unavailable)
1172+
* and for database name truncation (for consistency with the stored form).
1173+
*/
1174+
#define MD5_HASH_LEN 32
1175+
1176+
static void
1177+
tds_truncate_identifier_md5(char *ident, int len)
1178+
{
1179+
char md5[MD5_HASH_LEN + 1];
1180+
char buf[NAMEDATALEN];
1181+
const char *errstr = NULL;
1182+
char *downcased;
1183+
1184+
Assert(len >= NAMEDATALEN);
1185+
1186+
downcased = downcase_identifier(ident, len, false, false);
1187+
1188+
if (!pg_md5_hash(downcased, strlen(downcased), md5, &errstr))
1189+
ereport(FATAL,
1190+
(errcode(ERRCODE_INTERNAL_ERROR),
1191+
errmsg("could not compute %s hash: %s", "MD5", errstr)));
1192+
1193+
len = pg_mbcliplen(ident, len, NAMEDATALEN - MD5_HASH_LEN - 1);
1194+
Assert(len + MD5_HASH_LEN < NAMEDATALEN);
1195+
memcpy(buf, ident, len);
1196+
memcpy(buf + len, md5, MD5_HASH_LEN);
1197+
buf[len + MD5_HASH_LEN] = '\0';
1198+
1199+
pfree(downcased);
1200+
memcpy(ident, buf, len + MD5_HASH_LEN + 1);
1201+
}
1202+
1203+
#undef MD5_HASH_LEN
1204+
11651205
static int
11661206
ProcessLoginInternal(Port *port)
11671207
{
@@ -1251,13 +1291,19 @@ ProcessLoginInternal(Port *port)
12511291
loginInfo = request;
12521292

12531293
/*
1254-
* Truncate given database and user names to length of a Postgres name.
1255-
* This avoids lookup failures when overlength names are given.
1294+
* Truncate user name to fit Postgres NAMEDATALEN using TSQL-aware
1295+
* truncation (clip + MD5 hash) so the result matches the role name
1296+
* stored in the pg catalog. We cannot use truncate_identifier_hook here
1297+
* because babelfishpg_tsql is not yet loaded at this stage.
1298+
* Database name truncation is handled similarly in TdsSetDbContext().
12561299
*/
1257-
if (strlen(port->database_name) >= NAMEDATALEN)
1258-
port->database_name[NAMEDATALEN - 1] = '\0';
12591300
if (strlen(port->user_name) >= NAMEDATALEN)
1260-
port->user_name[NAMEDATALEN - 1] = '\0';
1301+
{
1302+
tds_truncate_identifier_md5(port->user_name, strlen(port->user_name));
1303+
1304+
pfree(loginInfo->username);
1305+
loginInfo->username = pstrdup(port->user_name);
1306+
}
12611307

12621308
/*
12631309
* Done putting stuff in TopMemoryContext.
@@ -2043,6 +2089,9 @@ TdsSetDbContext()
20432089
* SQL injection.
20442090
*/
20452091
StartTransactionCommand();
2092+
if (strlen(loginInfo->database) >= NAMEDATALEN)
2093+
tds_truncate_identifier_md5(loginInfo->database,
2094+
strlen(loginInfo->database));
20462095
db_id = pltsql_plugin_handler_ptr->pltsql_get_database_oid(loginInfo->database);
20472096
CommitTransactionCommand();
20482097
MemoryContextSwitchTo(oldContext);

contrib/babelfishpg_tsql/runtime/functions.c

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6155,3 +6155,115 @@ openxml_simple(PG_FUNCTION_ARGS)
61556155
#endif /* USE_LIBXML */
61566156
}
61576157

6158+
6159+
PG_FUNCTION_INFO_V1(bbf_xmlquery);
6160+
6161+
/*
6162+
* bbf_xmlquery - C implementation of XML .query() method
6163+
*
6164+
* Signature:
6165+
* sys.bbf_xmlquery(xpath_pattern TEXT, xml_element ANYELEMENT)
6166+
*
6167+
* Returns XML result of evaluating the XPath expression against the input.
6168+
* Returns empty XML if no nodes match.
6169+
*
6170+
* Validates:
6171+
* - Input must be XML type (or UDT based on XML)
6172+
* - QUOTED_IDENTIFIER must be ON
6173+
*/
6174+
Datum
6175+
bbf_xmlquery(PG_FUNCTION_ARGS)
6176+
{
6177+
text *xpath_expr;
6178+
Datum xml_datum;
6179+
Oid arg_type;
6180+
Oid immediate_base_type;
6181+
ArrayType *namespaces;
6182+
Datum xpath_result;
6183+
ArrayType *result_arr;
6184+
Datum *elems;
6185+
bool *nulls;
6186+
int nitems;
6187+
StringInfoData buf;
6188+
int i;
6189+
6190+
xpath_expr = PG_GETARG_TEXT_PP(0);
6191+
xml_datum = PG_GETARG_DATUM(1);
6192+
6193+
/* Lookup the datatype of the supplied argument */
6194+
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
6195+
6196+
/* UDT handling: resolve to immediate base type if it's a UDT */
6197+
immediate_base_type = get_immediate_base_type_of_UDT_internal(arg_type);
6198+
if (OidIsValid(immediate_base_type))
6199+
arg_type = immediate_base_type;
6200+
6201+
if (arg_type != XMLOID)
6202+
{
6203+
const char *typname = NULL;
6204+
6205+
/* Get T-SQL type name for error message */
6206+
if (common_utility_plugin_ptr)
6207+
typname = (*common_utility_plugin_ptr->resolve_pg_type_to_tsql)(arg_type);
6208+
if (typname == NULL)
6209+
typname = format_type_be(arg_type);
6210+
6211+
ereport(ERROR,
6212+
(errcode(ERRCODE_DATATYPE_MISMATCH),
6213+
errmsg("Cannot call methods on %s.", typname)));
6214+
}
6215+
6216+
/* Check QUOTED_IDENTIFIER setting (required for XML methods in T-SQL) */
6217+
if (!pltsql_quoted_identifier)
6218+
ereport(ERROR,
6219+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6220+
errmsg("SELECT failed because the following SET options have "
6221+
"incorrect settings: 'QUOTED_IDENTIFIER'. Verify that "
6222+
"SET options are correct for XML data type methods.")));
6223+
6224+
/*
6225+
* Call the built-in xpath(text, xml, text[][]) directly with an empty
6226+
* namespace array. Returns xml[] (array of XML fragments).
6227+
*
6228+
* TODO: when WITH XMLNAMESPACES is supported, populate this array with
6229+
* the declared (prefix, uri) pairs from the active namespace context.
6230+
*/
6231+
namespaces = construct_empty_array(TEXTOID);
6232+
xpath_result = DirectFunctionCall3(xpath,
6233+
PointerGetDatum(xpath_expr),
6234+
xml_datum,
6235+
PointerGetDatum(namespaces));
6236+
6237+
result_arr = DatumGetArrayTypeP(xpath_result);
6238+
6239+
/* Deconstruct the result array */
6240+
deconstruct_array(result_arr, XMLOID, -1, false, TYPALIGN_INT,
6241+
&elems, &nulls, &nitems);
6242+
6243+
/* Empty result → return empty string as XML (matches T-SQL behavior) */
6244+
if (nitems == 0)
6245+
PG_RETURN_XML_P((xmltype *) cstring_to_text(""));
6246+
6247+
/* Single result → return directly (common fast path) */
6248+
if (nitems == 1 && !nulls[0])
6249+
PG_RETURN_DATUM(elems[0]);
6250+
6251+
/*
6252+
* Multiple results - concatenate all XML fragments.
6253+
* Equivalent to: SELECT xmlagg(x) FROM unnest(result_set) AS x
6254+
*/
6255+
initStringInfo(&buf);
6256+
for (i = 0; i < nitems; i++)
6257+
{
6258+
if (!nulls[i])
6259+
{
6260+
text *fragment = DatumGetTextPP(elems[i]);
6261+
6262+
appendBinaryStringInfo(&buf,
6263+
VARDATA_ANY(fragment),
6264+
VARSIZE_ANY_EXHDR(fragment));
6265+
}
6266+
}
6267+
6268+
PG_RETURN_XML_P((xmltype *) cstring_to_text_with_len(buf.data, buf.len));
6269+
}

contrib/babelfishpg_tsql/sql/sys_functions.sql

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,17 @@ BEGIN
159159
result := pg_catalog.replace(result, '&quot;', '"');
160160
result := pg_catalog.replace(result, '&amp;', '&');
161161
return result;
162-
END IF;
162+
END IF;
163163
END
164164
$BODY$
165165
LANGUAGE plpgsql STABLE STRICT PARALLEL SAFE;
166166

167+
-- helper function for XML QUERY(xpath)
168+
CREATE OR REPLACE FUNCTION sys.bbf_xmlquery(xpath_pattern TEXT, xml_element ANYELEMENT)
169+
RETURNS XML
170+
AS 'babelfishpg_tsql', 'bbf_xmlquery'
171+
LANGUAGE C STABLE STRICT PARALLEL SAFE;
172+
167173
-- SELECT FOR JSON
168174
CREATE OR REPLACE FUNCTION sys.tsql_query_to_json_sfunc(
169175
state INTERNAL,

contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--6.1.0--6.2.0.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ $$
7373
end;
7474
$$;
7575

76+
-- helper function for XML QUERY(xpath)
77+
CREATE OR REPLACE FUNCTION sys.bbf_xmlquery(xpath_pattern TEXT, xml_element ANYELEMENT)
78+
RETURNS XML
79+
AS 'babelfishpg_tsql', 'bbf_xmlquery'
80+
LANGUAGE C STABLE STRICT PARALLEL SAFE;
81+
7682
-- BABELFISH_FUNCTION_EXT (antlr_parse_cache)
7783
SET allow_system_table_mods = on;
7884
ALTER TABLE sys.babelfish_function_ext ADD COLUMN IF NOT EXISTS antlr_parse_cache_tree TEXT DEFAULT NULL;

contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-epilogue.y.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ construct_unique_index_name(char *index_name, char *relation_name)
6060
index_len = strlen(index_name);
6161
relation_len = strlen(relation_name);
6262

63+
/* Validate original index name against T-SQL 128-char limit */
64+
if (pg_mbstrlen_with_len(index_name, index_len) > 128)
65+
{
66+
int cliplen = pg_mbcliplen(index_name, index_len, 128);
67+
68+
ereport(ERROR,
69+
(errcode(ERRCODE_NAME_TOO_LONG),
70+
errmsg("The identifier that starts with '%.*s' is too long. Maximum length is 128.",
71+
cliplen, index_name)));
72+
}
73+
6374
success = pg_md5_hash(index_name, index_len, md5, &errstr);
6475
if (unlikely(!success))
6576
{ /* OOM */

contrib/babelfishpg_tsql/src/backend_parser/gram-tsql-prologue.y.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "access/htup_details.h"
88
#include "catalog/pg_type.h"
9+
#include "mb/pg_wchar.h"
910
#include "parser/parse_type.h"
1011
#include "parser/scansup.h"
1112
#include "utils/builtins.h"

contrib/babelfishpg_tsql/src/backend_parser/scan-tsql-prologue-top.l.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,16 @@
66
#include "common/string.h"
77
#include "nodes/parsenodes.h"
88
#include "src/backend_parser/gramparse.h"
9+
10+
#define CHECK_LOCAL_TEMP_TABLE_LENGTH(ident) \
11+
do { \
12+
if ((ident)[0] == '#' && (ident)[1] != '#' && \
13+
pg_mbstrlen(ident) > 116) \
14+
{ \
15+
int cliplen = pg_mbcliplen((ident), strlen(ident), 116); \
16+
ereport(ERROR, \
17+
(errcode(ERRCODE_NAME_TOO_LONG), \
18+
errmsg("The identifier that starts with '%.*s' is too long. Maximum length for local temporary table is 116.", \
19+
cliplen, (ident)))); \
20+
} \
21+
} while (0)

contrib/babelfishpg_tsql/src/backend_parser/scan-tsql-rule.l

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
if (yyextra->literallen == 0)
7777
yyerror("zero-length delimited identifier");
7878
ident = litbufdup(yyscanner);
79+
CHECK_LOCAL_TEMP_TABLE_LENGTH(ident);
7980
if (yyextra->literallen >= NAMEDATALEN)
8081
truncate_identifier(ident, yyextra->literallen, true);
8182
yylval->str = ident;
@@ -152,6 +153,7 @@
152153

153154
<tsql>{tsql_ttname} {
154155
SET_YYLLOC();
156+
CHECK_LOCAL_TEMP_TABLE_LENGTH(yytext);
155157
yylval->str = pstrdup(yytext);
156158
if (yyleng >= NAMEDATALEN)
157159
truncate_identifier(yylval->str, yyleng, true);

contrib/babelfishpg_tsql/src/cursor.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,18 @@ pltsql_declare_cursor(PLtsql_execstate *estate, PLtsql_var *var, PLtsql_expr *ex
194194
Portal portal;
195195
char mangled_name[NAMEDATALEN];
196196

197+
/* Enforce 128-character limit on cursor name */
198+
Assert(var->refname != NULL);
199+
if (pg_mbstrlen(var->refname) > 128)
200+
{
201+
int cliplen = pg_mbcliplen(var->refname, strlen(var->refname), 128);
202+
203+
ereport(ERROR,
204+
(errcode(ERRCODE_NAME_TOO_LONG),
205+
errmsg("The identifier that starts with '%.*s' is too long. Maximum length is 128.",
206+
cliplen, var->refname)));
207+
}
208+
197209
if (!var->isnull)
198210
{
199211
curname = TextDatumGetCString(var->value);
@@ -223,12 +235,6 @@ pltsql_declare_cursor(PLtsql_execstate *estate, PLtsql_var *var, PLtsql_expr *ex
223235
* cursor, its lifespan is longer so we have to use different memory
224236
* context.
225237
*/
226-
Assert(var->refname != NULL);
227-
if (strlen(var->refname) + strlen(LOCAL_CURSOR_INFIX) + 19 > NAMEDATALEN)
228-
ereport(ERROR,
229-
(errcode(ERRCODE_INTERNAL_ERROR),
230-
errmsg("internal cursor name is too long: %s", var->refname)));
231-
232238
snprintf(mangled_name, NAMEDATALEN, "%s%s%p", var->refname, LOCAL_CURSOR_INFIX, var);
233239

234240
assign_text_var(estate, var, mangled_name);

contrib/babelfishpg_tsql/src/multidb.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "postgres.h"
22

3+
#include "mb/pg_wchar.h"
34
#include "miscadmin.h"
45
#include "nodes/parsenodes.h"
56
#include "nodes/primnodes.h"
@@ -1195,6 +1196,17 @@ get_physical_schema_name_by_mode(char *db_name, const char *schema_name, Migrati
11951196
if (len == 0)
11961197
return NULL;
11971198

1199+
/* Validate original schema name against T-SQL 128-char limit */
1200+
if (pg_mbstrlen_with_len(schema_name, len) > 128)
1201+
{
1202+
int cliplen = pg_mbcliplen(schema_name, len, 128);
1203+
1204+
ereport(ERROR,
1205+
(errcode(ERRCODE_NAME_TOO_LONG),
1206+
errmsg("The identifier that starts with '%.*s' is too long. Maximum length is 128.",
1207+
cliplen, schema_name)));
1208+
}
1209+
11981210
/* always return a new copy */
11991211
len = len > MAX_BBF_NAMEDATALEND ? len : MAX_BBF_NAMEDATALEND;
12001212
name = palloc0(len + 1);

0 commit comments

Comments
 (0)