diff --git a/contrib/babelfishpg_common/sql/string_operators.sql b/contrib/babelfishpg_common/sql/string_operators.sql index 6eed4a593a..811a84a020 100644 --- a/contrib/babelfishpg_common/sql/string_operators.sql +++ b/contrib/babelfishpg_common/sql/string_operators.sql @@ -1,19 +1,9 @@ -- 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 STABLE; +CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper(leftarg text, rightarg text) RETURNS TEXT +AS 'babelfishpg_tsql', 'babelfish_concat_wrapper' +LANGUAGE C STABLE PARALLEL SAFE; CREATE OR REPLACE FUNCTION sys.babelfish_concat_wrapper_outer(leftarg text, rightarg text) RETURNS sys.varchar(8000) AS $$ diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c b/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c index c801979bc2..38191c77fa 100644 --- a/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c +++ b/contrib/babelfishpg_tds/src/backend/tds/tdstypeio.c @@ -153,10 +153,10 @@ typedef struct FunctionCacheByTdsIdEntry TdsIoFunctionData data; } FunctionCacheByTdsIdEntry; -/* +/* * This is a modified copy of a function from POSTGIS to get SRID from GSERIALIZED struct */ -static int32_t +static int32_t get_srid(uint8_t *id) { int32_t srid = 0; @@ -242,9 +242,9 @@ getSendFunc(int funcId) case TDS_SEND_DATETIMEOFFSET: return TdsSendTypeDatetimeoffset; case TDS_SEND_GEOMETRY: - return TdsSendTypeGeometry; - case TDS_SEND_GEOGRAPHY: - return TdsSendTypeGeography; + return TdsSendTypeGeometry; + case TDS_SEND_GEOGRAPHY: + return TdsSendTypeGeography; /* TODO: should Assert here once all types are implemented */ default: return NULL; @@ -321,8 +321,8 @@ getRecvFunc(int funcId) case TDS_RECV_DATETIMEOFFSET: return TdsRecvTypeDatetimeoffset; case TDS_RECV_GEOMETRY: - return TdsRecvTypeGeometry; - case TDS_RECV_GEOGRAPHY: + return TdsRecvTypeGeometry; + case TDS_RECV_GEOGRAPHY: return TdsRecvTypeGeography; /* TODO: should Assert here once all types are implemented */ default: @@ -2014,7 +2014,7 @@ TdsRecvTypeDatetime2(const char *message, const ParameterToken token) Datum TdsRecvTypeGeometry(const char *message, const ParameterToken token) { - Datum result = 0; + Datum result = 0; /* Decode binary and convert if needed */ StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); @@ -2023,17 +2023,17 @@ TdsRecvTypeGeometry(const char *message, const ParameterToken token) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("Prepared Queries for Geometry DataType Currently not Supported in BabelFish"))); + errmsg("Prepared Queries for Geometry DataType Currently not Supported in BabelFish"))); - pfree(buf); - return result; + pfree(buf); + return result; } /* ------------------------------- * TdsRecvTypeGeography - converts external binary format to - * Geography data type + * Geography data type * -------------------------------- - */ + */ /* * It is a Placeholder Function for now * TODO: Will need to address it in subsequent Code Changes @@ -2041,7 +2041,7 @@ TdsRecvTypeGeometry(const char *message, const ParameterToken token) Datum TdsRecvTypeGeography(const char *message, const ParameterToken token) { - Datum result = 0; + Datum result = 0; /* Decode binary and convert if needed */ StringInfo buf = TdsGetStringInfoBufferFromToken(message, token); @@ -2052,8 +2052,8 @@ TdsRecvTypeGeography(const char *message, const ParameterToken token) (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("Prepared Queries for Geography DataType Currently not Supported in BabelFish"))); - pfree(buf); - return result; + pfree(buf); + return result; } static inline uint128 @@ -2426,8 +2426,8 @@ TdsRecvTypeTable(const char *message, const ParameterToken token) case TDS_TYPE_SQLVARIANT: values[i] = TdsTypeSqlVariantToDatum(temp); break; - case TDS_TYPE_SPATIAL: - break; + case TDS_TYPE_SPATIAL: + break; } /* Build a string for bind parameters. */ if (colMetaData[currentColumn].columnTdsType != TDS_TYPE_NVARCHAR || row->isNull[currentColumn] == 'n') @@ -2700,10 +2700,13 @@ TdsSendTypeBinary(FmgrInfo *finfo, Datum value, void *vMetaData) maxLen = 0; bytea *vlena = DatumGetByteaPCopy(value); bytea *buf; + int copyLen = 0; TdsColumnMetaData *col = (TdsColumnMetaData *) vMetaData; maxLen = col->metaEntry.type7.maxSize; - buf = (bytea *) palloc0(sizeof(bytea) * maxLen); + copyLen = Max((sizeof(bytea) * maxLen), VARSIZE_ANY_EXHDR(vlena)); + + buf = (bytea *) palloc0(copyLen); memcpy(buf, VARDATA_ANY(vlena), VARSIZE_ANY_EXHDR(vlena)); if ((rc = TdsPutUInt16LE(maxLen)) == 0) @@ -3139,6 +3142,7 @@ TdsSendTypeNumeric(FmgrInfo *finfo, Datum value, void *vMetaData) TdsColumnMetaData *col = (TdsColumnMetaData *) vMetaData; uint8_t max_scale = col->metaEntry.type5.scale; uint8_t max_precision = col->metaEntry.type5.precision; + int target_precision = 0; out = OutputFunctionCall(finfo, value); if (out[0] == '-') @@ -3175,24 +3179,34 @@ TdsSendTypeNumeric(FmgrInfo *finfo, Datum value, void *vMetaData) if (scale == -1) scale = 0; + /* Perform the overflow check before scribbling on to decString. */ + target_precision = precision + (max_scale - scale); + if (target_precision > TDS_MAX_NUM_PRECISION || + target_precision > max_precision) + ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("Arithmetic overflow error for data type numeric."))); + /* - * 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. - */ + * 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'; - Assert(precision <= outlen); + Assert(precision == target_precision); - 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."))); + /* + * Verify that we did not go beyond the memory allocated. + * We allow precision < outlen. Consider the case when + * out="123.456", max_scale=8. Then by the end, precision=11 + * but outlen=15. + */ + Assert(precision <= outlen); if (precision >= 1 && precision < 10) length = 4; @@ -4150,16 +4164,16 @@ TdsSendTypeDatetimeoffset(FmgrInfo *finfo, Datum value, void *vMetaData) return rc; } -int +int TdsSendSpatialHelper(FmgrInfo *finfo, Datum value, void *vMetaData, int TdsInstr) { int rc = EOF, npoints, - len, /* number of bytes used to store the string. */ + len, /* number of bytes used to store the string. */ actualLen; /* Number of bytes that would be needed to * store given string in given encoding. */ - char *destBuf, - *buf, + char *destBuf, + *buf, *itr; int32_t srid; @@ -4175,7 +4189,7 @@ TdsSendSpatialHelper(FmgrInfo *finfo, Datum value, void *vMetaData, int TdsInstr * 16 -> 2 8-Byte float coordinates (TODO: Need to change when Z and M flags are defined for N-dimension Points) * 6 -> 4 Byte SRID + 2 Byte (01 0C) */ - len = npoints*16 + 6; + len = npoints*16 + 6; buf = (char *) palloc0(len); /* Driver Expects 4 Byte SRID */ @@ -4196,8 +4210,8 @@ TdsSendSpatialHelper(FmgrInfo *finfo, Datum value, void *vMetaData, int TdsInstr * First 8 Bytes of gser->data are fixed in PostGIS: * 4 Bytes -> Represents the Type * 4 Bytes -> Represents the npoints - */ - memcpy(itr, (char *) gser->data + 8, len - 6); + */ + memcpy(itr, (char *) gser->data + 8, len - 6); destBuf = TdsEncodingConversion(buf, len, PG_UTF8, col->encoding, &actualLen); diff --git a/contrib/babelfishpg_tsql/runtime/functions.c b/contrib/babelfishpg_tsql/runtime/functions.c index ee190bf126..f17125e334 100644 --- a/contrib/babelfishpg_tsql/runtime/functions.c +++ b/contrib/babelfishpg_tsql/runtime/functions.c @@ -88,6 +88,7 @@ typedef enum OBJECT_TYPE_EXTENDED_STORED_PROCEDURE } ObjectPropertyType; + PG_FUNCTION_INFO_V1(trancount); PG_FUNCTION_INFO_V1(version); PG_FUNCTION_INFO_V1(error); @@ -149,6 +150,7 @@ PG_FUNCTION_INFO_V1(pg_extension_config_remove); PG_FUNCTION_INFO_V1(objectproperty_internal); PG_FUNCTION_INFO_V1(sysutcdatetime); PG_FUNCTION_INFO_V1(getutcdate); +PG_FUNCTION_INFO_V1(babelfish_concat_wrapper); void *string_to_tsql_varchar(const char *input_str); void *get_servername_internal(void); @@ -184,6 +186,58 @@ char *bbf_language = "us_english"; #define MD5_HASH_LEN 32 +Datum +babelfish_concat_wrapper(PG_FUNCTION_ARGS) +{ + text *arg1, *arg2, *new_text; + int32 arg1_size, arg2_size, new_text_size; + bool first_param = PG_ARGISNULL(0); + bool second_param = PG_ARGISNULL(1); + + if (pltsql_concat_null_yields_null) + { + if(first_param || second_param) + { + PG_RETURN_NULL(); // If any is NULL, return NULL + } + } + else + { + if (first_param && second_param) + { + PG_RETURN_NULL(); // If both are NULL, return NULL + } + else if (second_param) + { + PG_RETURN_TEXT_P(PG_GETARG_TEXT_PP(0)); // If only the second string is NULL, return the first string + } + else if (first_param) + { + PG_RETURN_TEXT_P(PG_GETARG_TEXT_PP(1)); // If only the first string is NULL, return the second string + } + } + arg1 = PG_GETARG_TEXT_PP(0); + arg2 = PG_GETARG_TEXT_PP(1); + arg1_size = VARSIZE_ANY_EXHDR(arg1); + arg2_size = VARSIZE_ANY_EXHDR(arg2); + + new_text_size = arg1_size + arg2_size + VARHDRSZ; + new_text = (text *) palloc(new_text_size); + + SET_VARSIZE(new_text, new_text_size); + + if(arg1_size>0) + { + memcpy(VARDATA(new_text), VARDATA_ANY(arg1), arg1_size); + } + if(arg2_size>0) + { + memcpy(VARDATA(new_text) + arg1_size, VARDATA_ANY(arg2), arg2_size); + } + + PG_RETURN_TEXT_P(new_text); +} + Datum trancount(PG_FUNCTION_ARGS) { @@ -1520,7 +1574,7 @@ object_name(PG_FUNCTION_ARGS) SysScanDesc tgscan; EphemeralNamedRelation enr; bool found = false; - char *result = NULL; + text *result_text = NULL; if (input1 < 0) PG_RETURN_NULL(); @@ -1552,9 +1606,7 @@ object_name(PG_FUNCTION_ARGS) enr = get_ENR_withoid(currentQueryEnv, object_id, ENR_TSQL_TEMP); if (enr != NULL && enr->md.enrtype == ENR_TSQL_TEMP) { - result = enr->md.name; - - PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text(result)); + PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text(enr->md.name)); } /* search in pg_class by object_id */ @@ -1565,8 +1617,7 @@ object_name(PG_FUNCTION_ARGS) if (pg_class_aclcheck(object_id, user_id, ACL_SELECT) == ACLCHECK_OK) { Form_pg_class pg_class = (Form_pg_class) GETSTRUCT(tuple); - result = NameStr(pg_class->relname); - + result_text = cstring_to_text(NameStr(pg_class->relname)); // make a copy before releasing syscache schema_id = pg_class->relnamespace; } ReleaseSysCache(tuple); @@ -1583,8 +1634,7 @@ object_name(PG_FUNCTION_ARGS) if (pg_proc_aclcheck(object_id, user_id, ACL_EXECUTE) == ACLCHECK_OK) { Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(tuple); - result = NameStr(procform->proname); - + result_text = cstring_to_text(NameStr(procform->proname)); schema_id = procform->pronamespace; } ReleaseSysCache(tuple); @@ -1602,7 +1652,7 @@ object_name(PG_FUNCTION_ARGS) if (pg_type_aclcheck(object_id, user_id, ACL_USAGE) == ACLCHECK_OK) { Form_pg_type pg_type = (Form_pg_type) GETSTRUCT(tuple); - result = NameStr(pg_type->typname); + result_text = cstring_to_text(NameStr(pg_type->typname)); } ReleaseSysCache(tuple); found = true; @@ -1630,8 +1680,7 @@ object_name(PG_FUNCTION_ARGS) if (OidIsValid(pg_trigger->tgrelid) && pg_class_aclcheck(pg_trigger->tgrelid, user_id, ACL_SELECT) == ACLCHECK_OK) { - result = NameStr(pg_trigger->tgname); - + result_text = cstring_to_text(NameStr(pg_trigger->tgname)); schema_id = get_rel_namespace(pg_trigger->tgrelid); } found = true; @@ -1651,8 +1700,7 @@ object_name(PG_FUNCTION_ARGS) /* check if user have right permission on object */ if (OidIsValid(con->conrelid) && (pg_class_aclcheck(con->conrelid, user_id, ACL_SELECT) == ACLCHECK_OK)) { - result = NameStr(con->conname); - + result_text = cstring_to_text(NameStr(con->conname)); schema_id = con->connamespace; } ReleaseSysCache(tuple); @@ -1660,7 +1708,7 @@ object_name(PG_FUNCTION_ARGS) } } - if (result) + if (result_text) { /* * Check if schema corresponding to found object belongs to specified @@ -1668,9 +1716,13 @@ object_name(PG_FUNCTION_ARGS) * "information_schema_tsql". In case of pg_type schema_id will be * invalid. */ - if (!OidIsValid(schema_id) || is_schema_from_db(schema_id, database_id) - || (schema_id == get_namespace_oid("sys", true)) || (schema_id == get_namespace_oid("information_schema_tsql", true))) - PG_RETURN_VARCHAR_P((VarChar *) cstring_to_text(result)); + if (!OidIsValid(schema_id) || + is_schema_from_db(schema_id, database_id) || + (schema_id == get_namespace_oid("sys", true)) || + (schema_id == get_namespace_oid("information_schema_tsql", true))) + { + PG_RETURN_VARCHAR_P((VarChar *) result_text); + } } PG_RETURN_NULL(); } diff --git a/contrib/babelfishpg_tsql/sql/sys_functions.sql b/contrib/babelfishpg_tsql/sql/sys_functions.sql index 92b73b1511..aa61e88b39 100644 --- a/contrib/babelfishpg_tsql/sql/sys_functions.sql +++ b/contrib/babelfishpg_tsql/sql/sys_functions.sql @@ -4050,7 +4050,7 @@ BEGIN column_length := 256; ELSIF column_data_type IS NULL THEN - -- Check if it's a user-defined data type + -- Check if it ia user-defined data type SELECT sys.translate_pg_type_to_tsql(typbasetype), typlen, typtypmod INTO column_data_type, typelen, typemod FROM pg_type diff --git a/contrib/babelfishpg_tsql/src/hooks.c b/contrib/babelfishpg_tsql/src/hooks.c index a20b2f0462..eb82f8699c 100644 --- a/contrib/babelfishpg_tsql/src/hooks.c +++ b/contrib/babelfishpg_tsql/src/hooks.c @@ -155,11 +155,6 @@ static bool pltsql_bbfViewHasInsteadofTrigger(Relation view, CmdType event); static bool plsql_TriggerRecursiveCheck(ResultRelInfo *resultRelInfo); static bool bbf_check_rowcount_hook(int es_processed); -static void declare_parameter_unquoted_string(Node *paramDft, ObjectType objtype); -static void declare_parameter_unquoted_string_reset(Node *paramDft); - -static Node* call_argument_unquoted_string(Node *arg); -static void call_argument_unquoted_string_reset(Node *colref_arg); static char *get_local_schema_for_bbf_functions(Oid proc_nsp_oid); /***************************************** @@ -214,10 +209,6 @@ static validate_var_datatype_scale_hook_type prev_validate_var_datatype_scale_ho static modify_RangeTblFunction_tupdesc_hook_type prev_modify_RangeTblFunction_tupdesc_hook = NULL; static fill_missing_values_in_copyfrom_hook_type prev_fill_missing_values_in_copyfrom_hook = NULL; static check_rowcount_hook_type prev_check_rowcount_hook = NULL; -static declare_parameter_unquoted_string_hook_type prev_declare_parameter_unquoted_string_hook = NULL; -static declare_parameter_unquoted_string_reset_hook_type prev_declare_parameter_unquoted_string_reset_hook = NULL; -static call_argument_unquoted_string_hook_type prev_call_argument_unquoted_string_hook = NULL; -static call_argument_unquoted_string_reset_hook_type prev_call_argument_unquoted_string_reset_hook = NULL; static bbfCustomProcessUtility_hook_type prev_bbfCustomProcessUtility_hook = NULL; static bbfSelectIntoUtility_hook_type prev_bbfSelectIntoUtility_hook = NULL; static bbfSelectIntoAddIdentity_hook_type prev_bbfSelectIntoAddIdentity_hook = NULL; @@ -349,16 +340,6 @@ InstallExtendedHooks(void) prev_check_rowcount_hook = check_rowcount_hook; check_rowcount_hook = bbf_check_rowcount_hook; - prev_declare_parameter_unquoted_string_hook = declare_parameter_unquoted_string_hook; - declare_parameter_unquoted_string_hook = declare_parameter_unquoted_string; - prev_declare_parameter_unquoted_string_reset_hook = declare_parameter_unquoted_string_reset_hook; - declare_parameter_unquoted_string_reset_hook = declare_parameter_unquoted_string_reset; - - prev_call_argument_unquoted_string_hook = call_argument_unquoted_string_hook; - call_argument_unquoted_string_hook = call_argument_unquoted_string; - prev_call_argument_unquoted_string_reset_hook = call_argument_unquoted_string_reset_hook; - call_argument_unquoted_string_reset_hook = call_argument_unquoted_string_reset; - prev_bbfCustomProcessUtility_hook = bbfCustomProcessUtility_hook; bbfCustomProcessUtility_hook = pltsql_bbfCustomProcessUtility; @@ -442,10 +423,6 @@ UninstallExtendedHooks(void) modify_RangeTblFunction_tupdesc_hook = prev_modify_RangeTblFunction_tupdesc_hook; fill_missing_values_in_copyfrom_hook = prev_fill_missing_values_in_copyfrom_hook; check_rowcount_hook = prev_check_rowcount_hook; - declare_parameter_unquoted_string_hook = prev_declare_parameter_unquoted_string_hook; - declare_parameter_unquoted_string_reset_hook = prev_declare_parameter_unquoted_string_reset_hook; - call_argument_unquoted_string_hook = prev_call_argument_unquoted_string_hook; - call_argument_unquoted_string_reset_hook = prev_call_argument_unquoted_string_reset_hook; bbfCustomProcessUtility_hook = prev_bbfCustomProcessUtility_hook; bbfSelectIntoUtility_hook = prev_bbfSelectIntoUtility_hook; bbfSelectIntoAddIdentity_hook = prev_bbfSelectIntoAddIdentity_hook; @@ -1629,7 +1606,7 @@ pre_transform_target_entry(ResTarget *res, ParseState *pstate, if (exprKind == EXPR_KIND_SELECT_TARGET) { int alias_len = 0; - const char *colname_start; + const char *colname_start = NULL; const char *identifier_name = NULL; int open_square_bracket = 0; int double_quotes = 0; @@ -1643,10 +1620,11 @@ pre_transform_target_entry(ResTarget *res, ParseState *pstate, /* * If no alias is specified on a ColumnRef, then get the length of * the name from the ColumnRef and copy the column name from the - * sourcetext + * sourcetext. To prevent the server crash, res->location for queries + * with join statement should not be zero. */ - if (list_length(cref->fields) == 1 && - IsA(linitial(cref->fields), String)) + if (res->location != 0 && (list_length(cref->fields) == 1 && + IsA(linitial(cref->fields), String))) { identifier_name = strVal(linitial(cref->fields)); alias_len = strlen(identifier_name); @@ -1667,13 +1645,14 @@ pre_transform_target_entry(ResTarget *res, ParseState *pstate, * Case 3: Handle the case when column name is delimited with sqb. When number of sqb * are zero, it means we are out of sqb. */ - if(list_length(cref->fields) > 1 && - IsA(llast(cref->fields), String)) + else if(res->location != 0 && (list_length(cref->fields) > 1 && + IsA(llast(cref->fields), String))) { identifier_name = strVal(llast(cref->fields)); alias_len = strlen(identifier_name); colname_start = pstate->p_sourcetext + res->location; - while(true) + last_dot = colname_start; + while(*colname_start != '\0') { if(open_square_bracket == 0 && *colname_start == '"') { @@ -1744,7 +1723,7 @@ pre_transform_target_entry(ResTarget *res, ParseState *pstate, int actual_alias_len = 0; /* To handle queries like SELECT (()) from */ - while(*colname_start == '(' || scanner_isspace(*colname_start)) + while(*colname_start != '\0' && (*colname_start == '(' || scanner_isspace(*colname_start))) { colname_start++; } @@ -1759,15 +1738,16 @@ pre_transform_target_entry(ResTarget *res, ParseState *pstate, if(actual_alias_len > alias_len) { /* First 32 characters of original_name are assigned to alias. */ - memcpy(alias, original_name, (alias_len - 32) ); + memcpy(alias, original_name, (alias_len - 32)); + /* Last 32 characters of identifier_name are assigned to alias, as actual alias is truncated. */ - memcpy(alias + (alias_len) - 32, - identifier_name + (alias_len) - 32, - 32); + memcpy(alias + (alias_len - 32), + identifier_name + (alias_len - 32), + 32); + alias[alias_len] = '\0'; } - /* Identifier is not truncated. */ - else + else /* Identifier is not truncated. */ { memcpy(alias, original_name, actual_alias_len); } @@ -2636,7 +2616,13 @@ modify_insert_stmt(InsertStmt *stmt, Oid relid) if (att->attnum > 0) { - col->name = NameStr(att->attname); + /* + * Do a deep copy of attname because tuple is a pointer + * to a shared_buffer page which is released when scan + * is ended. + */ + col->name = pstrdup(NameStr(att->attname)); + col->indirection = NIL; col->val = NULL; col->location = 1; @@ -4142,120 +4128,6 @@ static void pltsql_bbfSelectIntoAddIdentity(IntoClause *into, List *tableElts) } } - -/* - * Hook functions for handling unquoted string defaults in parameter definitions - * for T-SQL CREATE PROCEDURE/CREATE FUNCTION. - */ -static void declare_parameter_unquoted_string (Node *paramDft, ObjectType objtype) -{ - if (sql_dialect == SQL_DIALECT_TSQL && - (objtype == OBJECT_PROCEDURE || objtype == OBJECT_FUNCTION) && - nodeTag(paramDft) == T_ColumnRef) - { - /* - * The node could be for a variable, which should not be treated as a - * an unquoted string, so verify it does not start with '@'. - * This will cause parameter defaults with local variables to - * fail rather than to return the local variable name as a string, - * which is identical to Babelfish behaviour before the fix - * for unquoted string parameter. - */ - ColumnRef *colref = (ColumnRef *) paramDft; - Node *colnameField = (Node *) linitial(colref->fields); - char *colname = strVal(colnameField); - if (colname[0] != '@') - { - paramDft->type = T_TSQL_UnquotedString; - } - } - return; -} - -static void declare_parameter_unquoted_string_reset (Node *paramDft) -{ - /* - * In the case of an unquoted string, restore the original node type - * or we may run into an unknown node type downstream. - */ - if (nodeTag(paramDft) == T_ColumnRef) - { - paramDft->type = T_ColumnRef; - } - return; -} - -/* - * Hook functions for handling unquoted string arguments in - * for T-SQL procedure calls. - */ -static Node* call_argument_unquoted_string (Node *arg) -{ - /* - * Intercept unquoted string arguments in T-SQL procedure calls. - * These arrive here as nodetype=T_ColumnRef. Temporarily change - * the node type to T_TSQL_UnquotedString, which is picked up and - * handled in transformExprRecurse(). - */ - Node *colref_arg = NULL; /* Points to temporarily modified node, if any. */ - if (sql_dialect == SQL_DIALECT_TSQL) - { - if (nodeTag(arg) == T_ColumnRef) - { - /* - * We get here for unnamed argument syntax, i.e. - * exec myproc mystring - * */ - colref_arg = arg; - } - else if (nodeTag(arg) == T_NamedArgExpr) - { - /* - * We get here for named argument syntax, i.e. - * exec myproc @p=mystring - */ - NamedArgExpr *na = (NamedArgExpr *) arg; - Assert(na->arg); - if (nodeTag((Node *) na->arg) == T_ColumnRef) - { - colref_arg = (Node *) na->arg; - } - } - /* - * The argument could be a variable, which should not be treated - * as an unquoted string, so verify it does not start with '@'. - */ - if (colref_arg) - { - ColumnRef *colref = (ColumnRef *) colref_arg; - Node *colnameField = (Node *) linitial(colref->fields); - char *colname = strVal(colnameField); - if (colname[0] != '@') - { - colref_arg->type = T_TSQL_UnquotedString; - } - } - } - return colref_arg; -} - - -static void call_argument_unquoted_string_reset (Node *colref_arg) -{ - /* - * In case of an unquoted string, restore original node type - * or we may run into an unknown node type downstream. - */ - if (colref_arg) - { - if (nodeTag(colref_arg) == T_TSQL_UnquotedString) - { - colref_arg->type = T_ColumnRef; - } - } - return; -} - static char * get_local_schema_for_bbf_functions(Oid proc_nsp_oid) { diff --git a/contrib/babelfishpg_tsql/src/pl_comp.c b/contrib/babelfishpg_tsql/src/pl_comp.c index 78fd303354..a4d4150056 100644 --- a/contrib/babelfishpg_tsql/src/pl_comp.c +++ b/contrib/babelfishpg_tsql/src/pl_comp.c @@ -465,6 +465,7 @@ do_compile(FunctionCallInfo fcinfo, PLtsql_type *argdtype; PLtsql_variable *argvariable; PLtsql_nsitem_type argitemtype; + int typmod = 0; /* Create $n name for variable */ snprintf(buf, sizeof(buf), "$%d", i + 1); @@ -496,9 +497,15 @@ do_compile(FunctionCallInfo fcinfo, function->is_mstvf = true; } - /* Create datatype info */ + /* + * Create datatype info. + * typmods array has length procStruct->pronargs and numargs >= procStruct->pronargs, + * (See Assert in get_func_arg_info()) + * Pass in typmods[i] while we are not out of bounds, otherwise pass in -1. + */ + typmod = (typmods && i < procStruct->pronargs) ? typmods[i] : -1; argdtype = pltsql_build_datatype(argtypeid, - (typmods ? typmods[i] : -1), + typmod, function->fn_input_collation, NULL); diff --git a/contrib/babelfishpg_tsql/src/pl_handler.c b/contrib/babelfishpg_tsql/src/pl_handler.c index 100ec27743..9d798d06a6 100644 --- a/contrib/babelfishpg_tsql/src/pl_handler.c +++ b/contrib/babelfishpg_tsql/src/pl_handler.c @@ -3498,7 +3498,7 @@ bbf_ProcessUtility(PlannedStmt *pstmt, Constraint *con; con = get_rowversion_default_constraint(makeTypeNameFromOid(attr->atttypid, attr->atttypmod)); - rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); + rawEnt = (RawColumnDefault *) palloc0(sizeof(RawColumnDefault)); rawEnt->attnum = attr_num + 1; rawEnt->raw_default = (Node *) con->raw_expr; AddRelationNewConstraints(rel, list_make1(rawEnt), NIL, diff --git a/contrib/babelfishpg_tsql/src/rolecmds.c b/contrib/babelfishpg_tsql/src/rolecmds.c index 40a574039c..eb30b2dfee 100644 --- a/contrib/babelfishpg_tsql/src/rolecmds.c +++ b/contrib/babelfishpg_tsql/src/rolecmds.c @@ -2136,8 +2136,6 @@ babelfish_add_domain_mapping_entry_internal(PG_FUNCTION_ARGS) new_record = palloc0(sizeof(Datum) * BBF_DOMAIN_MAPPING_NUM_COLS); new_record_nulls = palloc0(sizeof(bool) * BBF_DOMAIN_MAPPING_NUM_COLS); - MemSet(new_record_nulls, false, sizeof(new_record_nulls)); - new_record[0] = PG_GETARG_DATUM(0); new_record[1] = PG_GETARG_DATUM(1); diff --git a/contrib/babelfishpg_tsql/src/string.c b/contrib/babelfishpg_tsql/src/string.c index 40681a5df3..6126e60768 100644 --- a/contrib/babelfishpg_tsql/src/string.c +++ b/contrib/babelfishpg_tsql/src/string.c @@ -407,8 +407,6 @@ prepare_format_string(StringInfo buf, char *msg_string, int nargs, strncpy(fmt_seg, seg_start, seg_len); prev_fmt_seg_sz = seg_len; - arg = args[i]; - if (i >= nargs || argisnull[i]) { appendStringInfo(buf, "(null)"); @@ -416,6 +414,7 @@ prepare_format_string(StringInfo buf, char *msg_string, int nargs, continue; } + arg = args[i]; typid = argtypes[i]; type = TypeCategory(typid); diff --git a/contrib/babelfishpg_tsql/src/tsqlIface.cpp b/contrib/babelfishpg_tsql/src/tsqlIface.cpp index ee0336cf78..db21d4f1b1 100644 --- a/contrib/babelfishpg_tsql/src/tsqlIface.cpp +++ b/contrib/babelfishpg_tsql/src/tsqlIface.cpp @@ -110,6 +110,7 @@ PLtsql_stmt *makeCfl(TSqlParser::Cfl_statementContext *ctx, tsqlBuilder &builder PLtsql_stmt *makeSQL(ParserRuleContext *ctx); std::vector makeAnother(TSqlParser::Another_statementContext *ctx, tsqlBuilder &builder); PLtsql_stmt *makeExecBodyBatch(TSqlParser::Execute_body_batchContext *ctx); +PLtsql_stmt *makeExecuteProcedure(ParserRuleContext *ctx, std::string call_type); PLtsql_stmt *makeInsertBulkStatement(TSqlParser::Dml_statementContext *ctx); PLtsql_stmt *makeSetExplainModeStatement(TSqlParser::Set_statementContext *ctx, bool is_explain_only); PLtsql_expr *makeTsqlExpr(const std::string &fragment, bool addSelect); @@ -128,7 +129,6 @@ void extractJoinHintFromOption(TSqlParser::OptionContext *option); std::string extractIndexValues(std::vector index_valuesCtx, std::string table_name); static void *makeBatch(TSqlParser::Tsql_fileContext *ctx, tsqlBuilder &builder); -//static void *makeBatch(TSqlParser::Block_statementContext *ctx, tsqlBuilder &builder); static void process_execsql_destination(TSqlParser::Dml_statementContext *ctx, PLtsql_stmt_execsql *stmt); static void process_execsql_remove_unsupported_tokens(TSqlParser::Dml_statementContext *ctx, PLtsql_expr_query_mutator *exprMutator); @@ -173,6 +173,11 @@ static bool does_msg_exceeds_params_limit(const std::string& msg); static std::string getIDName(TerminalNode *dq, TerminalNode *sb, TerminalNode *id); static ANTLR_result antlr_parse_query(const char *sourceText, bool useSSLParsing); std::string rewriteDoubleQuotedString(const std::string strDoubleQuoted); +std::string escapeDoubleQuotes(const std::string strWithDoubleQuote); +static bool in_execute_body_batch = false; +static bool in_execute_body_batch_parameter = false; +static const std::string fragment_SELECT_prefix = "SELECT "; // fragment prefix for expressions +static const std::string fragment_EXEC_prefix = "EXEC "; // fragment prefix for execute_body_batch /* * Structure / Utility function for general purpose of query string modification @@ -533,7 +538,8 @@ class PLtsql_expr_query_mutator int base_idx; - // Indicate the fragment being processed is an expression that was prefixed with 'SELECT ' + // Indicate the fragment being processed is an expression that was prefixed with 'SELECT ', + // so that offsets can be adjsted when doing the rewriting bool isSelectFragment = false; int idxStart = 0; int idxEnd = 0; @@ -560,7 +566,7 @@ void PLtsql_expr_query_mutator::markSelectFragment(ParserRuleContext *ctx) { Assert(ctx); Assert(selectFragmentOffsets.count(ctx) > 0); - + auto p = selectFragmentOffsets.at(ctx); isSelectFragment = true; @@ -608,7 +614,6 @@ void PLtsql_expr_query_mutator::add(int antlr_pos, std::string orig_text, std::s m.emplace(std::make_pair(offset, std::make_pair(orig_text, repl_text))); } - void PLtsql_expr_query_mutator::run() { /* @@ -617,6 +622,8 @@ void PLtsql_expr_query_mutator::run() * To rewrite query based on token position, we have to convert a query string to std::u32string first * so that offset should indicate a correct position to be replaced. */ + if (m.size() == 0) return; // nothing to do + std::u32string query = utf8_to_utf32(expr->query); std::u32string rewritten_query; @@ -626,7 +633,7 @@ void PLtsql_expr_query_mutator::run() size_t offset = entry.first; const std::u32string& orig_text = utf8_to_utf32(entry.second.first.c_str()); const std::u32string& repl_text = utf8_to_utf32(entry.second.second.c_str()); - if (isSelectFragment) offset += 7; // because this is an expression prefixed with 'SELECT ' + if (isSelectFragment) offset += fragment_SELECT_prefix.length(); // because this is an expression prefixed with 'SELECT ' if (orig_text.length() == 0 || orig_text.c_str(), query.substr(offset, orig_text.length()) == orig_text) // local_id maybe already deleted in some cases such as select-assignment. check here if it still exists) { @@ -640,7 +647,7 @@ void PLtsql_expr_query_mutator::run() } if (cursor < strlen(expr->query)) rewritten_query += query.substr(cursor); // copy remaining expr->query - + // update query string std::string new_query = antlrcpp::utf32_to_utf8(rewritten_query); expr->query = pstrdup(new_query.c_str()); @@ -754,8 +761,8 @@ clear_tables_info() * (please see the comment on rewritten_query_fragment). * tsqlSelectMuator was introduced to cover corner cases such as CREATE-VIEW and DECLARE-CURSOR * which need to deal with inner SELECT statement. - * tsqlCommonMutator was added to cover a rewriting logic which needs to be apllied in - * batch-level statment and normal statment. + * tsqlCommonMutator was added to cover a rewriting logic which needs to be applied in + * batch-level statement and normal statement. * * TODO: * The plan is to incorporate all rewriting logics to tsqlCommonMutator. Other @@ -996,6 +1003,16 @@ class tsqlCommonMutator : public TSqlParserBaseListener // don't need to call does_object_name_need_delimiter() because problematic keywords are already allowed as function name } + void enterExecute_body_batch(TSqlParser::Execute_body_batchContext *ctx) override + { + in_execute_body_batch = true; + } + + void exitExecute_body_batch(TSqlParser::Execute_body_batchContext *ctx) override + { + in_execute_body_batch = false; + } + void exitFunc_proc_name_server_database_schema(TSqlParser::Func_proc_name_server_database_schemaContext *ctx) override { GetCtxFunc getDatabase = [](TSqlParser::Func_proc_name_server_database_schemaContext *o) { return o->database; }; @@ -1003,9 +1020,14 @@ class tsqlCommonMutator : public TSqlParserBaseListener std::string rewritten_name = rewrite_object_name_with_omitted_db_and_schema_name(ctx, getDatabase, getSchema); std::string rewritten_schema_name = rewrite_information_schema_to_information_schema_tsql(ctx, getSchema); if (!rewritten_name.empty()) - rewritten_query_fragment.emplace(std::make_pair(ctx->start->getStartIndex(), std::make_pair(::getFullText(ctx), rewritten_name))); + { + int EXEC_prepend_length = (in_execute_body_batch) ? fragment_EXEC_prefix.length() : 0; // add length of prefix prepended internally for execute_body_batch + rewritten_query_fragment.emplace(std::make_pair(ctx->start->getStartIndex()+EXEC_prepend_length, std::make_pair(::getFullText(ctx), rewritten_name))); + } if (pltsql_enable_tsql_information_schema && !rewritten_schema_name.empty()) + { rewritten_query_fragment.emplace(std::make_pair(ctx->schema->start->getStartIndex(), std::make_pair(::getFullText(ctx->schema), rewritten_schema_name))); + } #ifdef ENABLE_SPATIAL_TYPES if(!ctx->id().empty() && ctx->id()[0]->id().size() == 2) @@ -1182,7 +1204,7 @@ class tsqlBuilder : public tsqlCommonMutator std::string schema_name; std::string db_name; bool is_function = false; - bool is_schema_specified = false; + bool is_schema_specified = false; // We keep a stack of the containers that are active during a traversal. // A container will correspond to a block or a batch - these are containers @@ -1290,7 +1312,7 @@ class tsqlBuilder : public tsqlCommonMutator std::string desc{getNodeDesc(ctx)}; if (pltsql_enable_antlr_detailed_log) - std::cout << "+entering " << (void *) ctx << "[" << desc << "]" << std::endl; + std::cout << "+entering (tsqlBuilder)" << (void *) ctx << "[" << desc << "]" << std::endl; } void exitEveryRule(ParserRuleContext *ctx) override @@ -1298,7 +1320,7 @@ class tsqlBuilder : public tsqlCommonMutator std::string desc{getNodeDesc(ctx)}; if (pltsql_enable_antlr_detailed_log) - std::cout << "-leaving " << (void *) ctx << "[" << desc << "]" << std::endl; + std::cout << "-leaving (tsqlBuilder)" << (void *) ctx << "[" << desc << "]" << std::endl; } void graft(PLtsql_stmt *stmt, ParserRuleContext *container) @@ -1506,7 +1528,7 @@ class tsqlBuilder : public tsqlCommonMutator void exitCreate_or_alter_procedure(TSqlParser::Create_or_alter_procedureContext *ctx) override { // just throw away all PLtsql_stmt in the container. - // procedure body is a sequenece of sql_clauses. In create-procedure, we don't need to genereate PLtsql_stmt. + // procedure body is a sequence of sql_clauses. In create-procedure, we don't need to generate PLtsql_stmt. // // TODO: Ideally, we may stop visiting or disable vistior logic inside the procedure body. It will save the resoruce. @@ -1747,7 +1769,7 @@ class tsqlBuilder : public tsqlCommonMutator if (is_compiling_create_function()) { - /* T-SQL doens't allow side-effecting operations in CREATE FUNCTION */ + /* T-SQL doesn't allow side-effecting operations in CREATE FUNCTION */ throw PGErrorWrapperException(ERROR, ERRCODE_INVALID_FUNCTION_DEFINITION, "DDL cannot be used within a function", getLineAndPos(ctx)); } @@ -1820,7 +1842,7 @@ class tsqlBuilder : public tsqlCommonMutator selectFragmentOffsets.clear(); clear_rewritten_query_fragment(); - } + } } void exitSecurity_statement(TSqlParser::Security_statementContext *ctx) override @@ -1974,7 +1996,7 @@ class tsqlBuilder : public tsqlCommonMutator for (TSqlParser::Declare_localContext *d : ctx->declare_statement()->declare_local() ) { i++; - if (d->expression()) + if (d->expression()) { ParserRuleContext *ctx_fragment = (ParserRuleContext *) ctx; if (selectFragmentOffsets.find(d->expression()) != selectFragmentOffsets.end()) @@ -2000,64 +2022,86 @@ class tsqlBuilder : public tsqlCommonMutator void exitChar_string(TSqlParser::Char_stringContext *ctx) override { - if (ctx->STRING()) - { - std::string str = ctx->STRING()->getSymbol()->getText(); + std::string str = getFullText(ctx); - if (str.front() == 'N') - { - // This is only to make the assertion on str.front() easy (below) - str.erase(0, 1); - } + if (str.front() == 'N') + { + // Temporarily remove the leading 'N' only locally here, to make the assertion on str.front() easy (below) + str.erase(0, 1); + } - if (str.front() == '"') - { - Assert(str.back() == '"'); - - // Change double-quoted string to single-quoted string: - str = rewriteDoubleQuotedString(str); - size_t startPosition = ctx->start->getStartIndex(); - rewritten_query_fragment.emplace(std::make_pair(startPosition, std::make_pair(::getFullText(ctx), str))); - } - else - { - // This is a single-quoted string, no further action needed - Assert(str.front() == '\''); - Assert(str.back() == '\''); - } + if (str.front() == '"') + { + Assert(str.back() == '"'); + + // Change double-quoted string to single-quoted string: + str = rewriteDoubleQuotedString(str); + size_t startPosition = ctx->start->getStartIndex(); + if (in_execute_body_batch_parameter) startPosition += fragment_EXEC_prefix.length(); // add length of prefix prepended internally for execute_body_batch + rewritten_query_fragment.emplace(std::make_pair(startPosition, std::make_pair(::getFullText(ctx), str))); + } + else + { + // This is a single-quoted string, no further action needed + Assert(str.front() == '\''); + Assert(str.back() == '\''); } } + void enterExecute_parameter(TSqlParser::Execute_parameterContext *ctx) override + { + if (in_execute_body_batch) in_execute_body_batch_parameter = true; + } + void exitExecute_parameter(TSqlParser::Execute_parameterContext *ctx) override { if (ctx->id()) { // A stored procedure parameter which is parsed as a column name (= identifier) - // is either an unquoted string, or a double-quoted string. + // is either an unquoted string, a double-quoted string, or a bracketed string. // For a procedure call parameter, a double-quoted string is interpreted - // as a string even with QUOTED_IDENTIFIER=ON. - + // as a string even with QUOTED_IDENTIFIER=ON. std::string str = getFullText(ctx->id()); + size_t startPosition = ctx->id()->start->getStartIndex(); + if (in_execute_body_batch_parameter) startPosition += fragment_EXEC_prefix.length(); // add length of prefix prepended internally for execute_body_batch if (str.front() == '"') { Assert(str.back() == '"'); // Change double-quoted string to single-quoted string - str = rewriteDoubleQuotedString(str); - rewritten_query_fragment.emplace(std::make_pair(ctx->start->getStartIndex(), std::make_pair(getFullText(ctx->id()), str))); - } + str = rewriteDoubleQuotedString(str); + rewritten_query_fragment.emplace(std::make_pair(startPosition, std::make_pair(getFullText(ctx->id()), str))); + } else if (str.front() == '\'') { // This is a single-quoted string, no further action needed Assert(str.back() == '\''); } + else if (str.front() == '[') + { + // When it's a bracketed identifier, remove the delimiters as T-SQL treats it as a string + Assert(str.back() == ']'); + + // Temporarily turn this into a double-quoted string so that we can handle embedded quotes. + // Since embedded double quotes inside a bracketed identifier are not escaped (as they would be in a + // double-quoted string), escape them first. We cannot just call rewriteDoubeQuotedString() since if we'd + // have two adjacent double quotes, i.e. [a""b], this would then become [a"b] so we'd lose one quote. + str = escapeDoubleQuotes(str); + str = '"' + str.substr(1,str.length()-2) + '"'; + str = rewriteDoubleQuotedString(str); + rewritten_query_fragment.emplace(std::make_pair(startPosition, std::make_pair(getFullText(ctx->id()), str))); + } else { - // This is an unquoted string parameter which has been parsed as a column name. - // This is dealt with downstream. + // This is an unquoted string parameter which has been parsed as an identifier(column name). + // Put quotes around it: even though it is an identifier in the ANTLR parse tree, it will be + // parsed as a string by the backend parser + str = "'" + str + "'"; + rewritten_query_fragment.emplace(std::make_pair(startPosition, std::make_pair(getFullText(ctx->id()), str))); } - } + } + if (in_execute_body_batch_parameter) in_execute_body_batch_parameter = false; } void enterCfl_statement(TSqlParser::Cfl_statementContext *ctx) override @@ -2312,9 +2356,22 @@ class tsqlBuilder : public tsqlCommonMutator void enterExecute_body_batch(TSqlParser::Execute_body_batchContext *ctx) override { - graft(makeExecBodyBatch(ctx), peekContainer()); + in_execute_body_batch = true; + PLtsql_stmt *stmt = makeExecBodyBatch(ctx); + attachPLtsql_fragment(ctx, stmt); + graft(stmt, peekContainer()); + clear_rewritten_query_fragment(); + } + + void exitExecute_body_batch(TSqlParser::Execute_body_batchContext *ctx) override + { + in_execute_body_batch = false; + PLtsql_stmt_exec *stmt = (PLtsql_stmt_exec *) getPLtsql_fragment(ctx); + PLtsql_expr_query_mutator mutator(stmt->expr, ctx); + add_rewritten_query_fragment_to_mutator(&mutator); + mutator.run(); + clear_rewritten_query_fragment(); } - PLtsql_expr *rewrite_if_condition(TSqlParser::Search_conditionContext *ctx) { @@ -2361,6 +2418,8 @@ class tsqlMutator : public TSqlParserBaseListener { public: MyInputStream &stream; + bool in_procedure_parameter = false; + bool in_procedure_parameter_id = false; std::vector double_quota_places; @@ -2368,7 +2427,7 @@ class tsqlMutator : public TSqlParserBaseListener : stream(s) { } - + public: void enterFunc_proc_name_schema(TSqlParser::Func_proc_name_schemaContext *ctx) override { @@ -2382,7 +2441,7 @@ class tsqlMutator : public TSqlParserBaseListener // According to the grammar, an id can be any of the following: // - // id + // id // : ID // | DOUBLE_QUOTE_ID // | SQUARE_BRACKET_ID @@ -2543,6 +2602,95 @@ class tsqlMutator : public TSqlParserBaseListener } } } + } + + void enterProcedure_param(TSqlParser::Procedure_paramContext *ctx) override + { + if (ctx->expression()) { + in_procedure_parameter = true; + } + } + + void enterFull_column_name(TSqlParser::Full_column_nameContext *ctx) override + { + if (in_procedure_parameter) { + in_procedure_parameter_id = true; + } + } + + void exitProcedure_param(TSqlParser::Procedure_paramContext *ctx) override + { + in_procedure_parameter = false; + in_procedure_parameter_id = false; + } + + void exitId(TSqlParser::IdContext *ctx) override + { + if (in_procedure_parameter_id) + { + // This is a string parameter default which has been parsed as an identifier. + // Put quotes around it: even though it is an identifier in the ANTLR parse tree, it will then be + // parsed as a string by the backend parser + std::string str = getFullText(ctx); + + // When it's a bracketed identifier, remove the delimiters as T-SQL treats it as a string + // When it's a quoted identifier, T-SQL also treats it as a string independent of the QUOTED_IDENTIFIER setting + // (as we get here, it means QUOTED_IDENTIFIER=ON) + // When it none of the above, it is an unquoted string + if (str.front() == '[') { + Assert(str.back() == ']'); + + // Temporarily turn this into a double-quoted string so that we can handle embedded quotes. + // Since embedded double quotes inside a bracketed identifier are not escaped (as they would be in a + // double-quoted string), escape them first. We cannot just call rewriteDoubeQuotedString() since if we'd + // have two adjacent double quotes, i.e. [a""b], this would then become [a"b] so we'd lose one quote. + str = escapeDoubleQuotes(str); + str = '"' + str.substr(1,str.length()-2) + '"'; + str = rewriteDoubleQuotedString(str); + } + else if (str.front() == '"') { + Assert(str.back() == '"'); + str = rewriteDoubleQuotedString(str); + } + else { + // Unquoted string, add quotes: there cannot be any quotes in the string otherwise it would + // not have been parsed as an identifier + str = "'" + str + "'"; + } + + rewritten_query_fragment.emplace(std::make_pair(ctx->start->getStartIndex(), std::make_pair(getFullText(ctx), str))); + } + } + + void exitChar_string(TSqlParser::Char_stringContext *ctx) override + { + if (in_procedure_parameter) + { + std::string str = getFullText(ctx); + if (str.front() == 'N') + { + // This is only to make the assertion on str.front() easy (below) + str.erase(0, 1); + } + + if (str.front() == '\'') + { + // This is a single-quoted string used as parameter default: no further action needed + Assert(str.back() == '\''); + } + else + { + // This is a double-quoted string used as parameter default. + // (as we get here, it means QUOTED_IDENTIFIER=OFF) + + Assert(str.front() == '"'); + Assert(str.back() == '"'); + + // Change to PG-compatible single-quoted string + str = rewriteDoubleQuotedString(str); + rewritten_query_fragment.emplace(std::make_pair(ctx->start->getStartIndex(), std::make_pair(getFullText(ctx), str))); + } + } } }; @@ -2783,12 +2931,12 @@ antlr_parse_query(const char *sourceText, bool useSLLParsing) { tree::ParseTree *tree = nullptr; /* - * The sematnic of "RETURN SELECT ..." depends on whether it is used in Inlined Table Value Function or not. + * The semantic of "RETURN SELECT ..." depends on whether it is used in Inlined Table Value Function or not. * In ITVF, they should be interpeted as return a result tuple of SELECT statement. * but in the other case (i.e. procedure or SQL batch), it should be interpreted as two separate statements like "RETURN; SELECT ..." * * Currently, we have only proc_body in input so accessing pltsql_curr_compile to check this is a body of ITVF or not. - * If if it is ITVF, we parsed it with func_body_return_select_body grammar. + * If it is ITVF, we parsed it with func_body_return_select_body grammar. */ if (pltsql_curr_compile && pltsql_curr_compile->is_itvf) /* special path to itvf */ tree = parser.func_body_return_select_body(); @@ -3094,13 +3242,13 @@ rewriteBatchLevelStatement( /* DML trigger can have two WITH. one for trigger options and the other for WITH APPEND */ if (cctx->WITH().size() > 1 || (cctx->WITH().size() == 1 && !cctx->APPEND())) { - size_t num_commas_in_dml_trigger_operaion = cctx->COMMA().size(); + size_t num_commas_in_dml_trigger_operation = cctx->COMMA().size(); auto options = cctx->trigger_option(); /* COMMA is shared between dml_trigger_operation and WITH-clause. calculate the number of COMMA so that it can be removed properly */ - num_commas_in_dml_trigger_operaion -= (cctx->trigger_option().size() - 1); + num_commas_in_dml_trigger_operation -= (cctx->trigger_option().size() - 1); auto commas = cctx->COMMA(); std::vector commas_in_with_clause; - commas_in_with_clause.insert(commas_in_with_clause.begin(), commas.begin() , commas.end() - num_commas_in_dml_trigger_operaion); + commas_in_with_clause.insert(commas_in_with_clause.begin(), commas.begin() , commas.end() - num_commas_in_dml_trigger_operation); GetTokenFunc getToken = [](TSqlParser::Trigger_optionContext* o) { if (o->execute_as_clause()) return o->execute_as_clause()->CALLER(); @@ -3700,7 +3848,7 @@ makeTsqlExpr(const std::string &fragment, bool addSelect) PLtsql_expr *result = (PLtsql_expr *) palloc0(sizeof(*result)); if (addSelect) - result->query = pstrdup((std::string("SELECT ") + fragment).c_str()); + result->query = pstrdup((fragment_SELECT_prefix + fragment).c_str()); else result->query = pstrdup(fragment.c_str()); @@ -3708,7 +3856,7 @@ makeTsqlExpr(const std::string &fragment, bool addSelect) result->paramnos = NULL; result->rwparam = -1; result->ns = pltsql_ns_top(); - + return result; } @@ -4300,10 +4448,10 @@ makeRaiseErrorStmt(TSqlParser::Raiseerror_statementContext *ctx) // msg, severity, state result->params = lappend(result->params, makeTsqlExpr(ctx->msg->getText(), true)); recordSelectFragmentOffsets(ctx->parent, ctx->raiseerror_msg()); - + result->params = lappend(result->params, makeTsqlExpr(ctx->severity, true)); result->params = lappend(result->params, makeTsqlExpr(ctx->state, true)); - + // additional arguments if (ctx->argument.size() > 20) throw PGErrorWrapperException(ERROR, ERRCODE_SYNTAX_ERROR, "Too many substitution parameters for RAISERROR. Cannot exceed 20 substitution parameters.", getLineAndPos(ctx)); @@ -4600,7 +4748,8 @@ makeSetStatement(TSqlParser::Set_statementContext *ctx, tsqlBuilder &builder) * SET @var ^= 5 - 1 * to * SET @var = "@var" ^ (5 -1) - */ + */ + StringInfoData new_query; initStringInfo(&new_query); appendStringInfo(&new_query, "SELECT \"%s\" %s (%s)", @@ -4614,6 +4763,7 @@ makeSetStatement(TSqlParser::Set_statementContext *ctx, tsqlBuilder &builder) // "SELECT " : preceding chars before this reformatting the expression // +1 : the opening bracket posBracket = strcspn(new_query.data, "(") - strlen("SELECT ") + 1; + } // Each variable assignment becomes a fragment, for which rewriting may be required. We need to keep track of where @@ -4972,104 +5122,8 @@ makeExecuteStatement(TSqlParser::Execute_statementContext *ctx) return (PLtsql_stmt *) result; } else /* execute a stored procedure or function */ - { - std::string func_proc_name; - TSqlParser::Execute_statement_argContext *func_proc_args = body->execute_statement_arg(); - bool is_cross_db = false; - std::string proc_name; - std::string schema_name; - std::string db_name; - - if (body->func_proc_name_server_database_schema()) - { - func_proc_name = ::getFullText(body->func_proc_name_server_database_schema()); - if (body->func_proc_name_server_database_schema()->database) - { - db_name = stripQuoteFromId(body->func_proc_name_server_database_schema()->database); - if (!string_matches(db_name.c_str(), get_cur_db_name())) - is_cross_db = true; - } - if (body->func_proc_name_server_database_schema()->schema) - schema_name = stripQuoteFromId(body->func_proc_name_server_database_schema()->schema); - if (body->func_proc_name_server_database_schema()->procedure) - proc_name = stripQuoteFromId(body->func_proc_name_server_database_schema()->procedure); - } - else - { - /* LOCAL_ID can be placed on return_status and/or proc_var. choose the corresponding index, depending on whether return_status if exists or not. */ - Assert(body->proc_var); - func_proc_name = ::getFullText(body->return_status ? body->LOCAL_ID()[1] : body->LOCAL_ID()[0]); - } - Assert(!func_proc_name.empty()); - - int lineno = getLineNo(ctx); - int return_code_dno = -1; - - std::string rewritten_name = ""; - if (body->func_proc_name_server_database_schema()) - { - // rewrite func/proc name if database/schema is omitted (i.e. EXEC ..proc1) - GetCtxFunc getDatabase = [](TSqlParser::Func_proc_name_server_database_schemaContext *o) { return o->database; }; - GetCtxFunc getSchema = [](TSqlParser::Func_proc_name_server_database_schemaContext *o) { return o->schema; }; - rewritten_name = rewrite_object_name_with_omitted_db_and_schema_name(body->func_proc_name_server_database_schema(), getDatabase, getSchema); - } - - std::string name = (!rewritten_name.empty() ? rewritten_name : func_proc_name); - // Rewrite proc name to sp_* if the schema is "dbo" or "sys" and proc name starts with "sp_" - if ((pg_strncasecmp(name.c_str(), "dbo.sp_", 6) == 0) || (pg_strncasecmp(name.c_str(), "sys.sp_", 6) == 0)) - name.erase(name.begin() + 0, name.begin() + 4); - - auto *localID = body->return_status ? body->LOCAL_ID()[0] : nullptr; - if (localID) - return_code_dno = getVarno(localID); - - if (is_sp_proc(name)) - return makeSpStatement(name, func_proc_args, lineno, return_code_dno); - - PLtsql_stmt_exec *result = (PLtsql_stmt_exec *) palloc0(sizeof(*result)); - result->cmd_type = PLTSQL_STMT_EXEC; - result->lineno = lineno; - result->is_call = true; - result->return_code_dno = return_code_dno; - result->paramno = 0; - result->params = NIL; - // record whether stmt is cross-db - if (is_cross_db) - result->is_cross_db = true; - - if (!proc_name.empty()) - { - result->proc_name = pstrdup(downcase_truncate_identifier(proc_name.c_str(), proc_name.length(), true)); - } - if (!schema_name.empty()) - { - result->schema_name = pstrdup(downcase_truncate_identifier(schema_name.c_str(), schema_name.length(), true)); - } - if (!db_name.empty()) - { - result->db_name = pstrdup(downcase_truncate_identifier(db_name.c_str(), db_name.length(), true)); - } - - if (func_proc_args) - { - std::vector params; - makeSpParams(func_proc_args, params); - - for (size_t i = 0; i < params.size(); i++) - { - result->params = lappend(result->params, params[i]); - result->paramno++; - } - } - - std::stringstream ss; - ss << "EXEC " << name; - if (func_proc_args) - ss << " " << ::getFullText(func_proc_args); - std::string expr_query = ss.str(); - result->expr = makeTsqlExpr(expr_query, false); - - return (PLtsql_stmt *) result; + { + return makeExecuteProcedure(ctx, "execute_statement"); } } @@ -5163,7 +5217,7 @@ makeOpenCursorStatement(TSqlParser::Cursor_statementContext *ctx) } PLtsql_stmt * -makeFetchCurosrStatement(TSqlParser::Fetch_cursorContext *ctx) +makeFetchCursorStatement(TSqlParser::Fetch_cursorContext *ctx) { PLtsql_stmt_fetch *result = (PLtsql_stmt_fetch *) palloc(sizeof(PLtsql_stmt_fetch)); result->cmd_type = PLTSQL_STMT_FETCH; @@ -5294,7 +5348,7 @@ makeCursorStatement(TSqlParser::Cursor_statementContext *ctx) else if (ctx->OPEN()) return makeOpenCursorStatement(ctx); else if (ctx->fetch_cursor()) - return makeFetchCurosrStatement(ctx->fetch_cursor()); + return makeFetchCursorStatement(ctx->fetch_cursor()); else if (ctx->CLOSE()) return makeCloseCursorStatement(ctx); else if (ctx->DEALLOCATE()) @@ -5576,46 +5630,156 @@ makeAnother(TSqlParser::Another_statementContext *ctx, tsqlBuilder &builder) { attachPLtsql_fragment(ctx->set_statement(), stmt); } - else { + else + { attachPLtsql_fragment(ctx, stmt); } } return result; } - + +// For stored procedure calls without EXECUTE keyword, with the procedure name as first thing in the batch: PLtsql_stmt * makeExecBodyBatch(TSqlParser::Execute_body_batchContext *ctx) { + return makeExecuteProcedure(ctx, "execute_body_batch"); +} + +// For stored procedure calls, or functions called with EXECUTE: +PLtsql_stmt * +makeExecuteProcedure(ParserRuleContext *ctx, std::string call_type) +{ + Assert(string_matches(call_type.c_str(), "execute_statement") || string_matches(call_type.c_str(), "execute_body_batch")); + Assert(ctx); + + TSqlParser::Func_proc_name_server_database_schemaContext *ctx_name = nullptr; + TSqlParser::Execute_statement_argContext *func_proc_args = nullptr; + TSqlParser::Execute_bodyContext *body = nullptr; + std::string schema_name; std::string proc_name; - bool is_cross_db = false; std::string db_name; - std::string func_proc_name = ::getFullText(ctx->func_proc_name_server_database_schema()); - - // Rewrite proc name to sp_* if the schema is "dbo" or "sys" and proc name starts with "sp_" - if ((pg_strncasecmp(func_proc_name.c_str(), "dbo.sp_", 6) == 0) || (pg_strncasecmp(func_proc_name.c_str(), "sys.sp_", 6) == 0)) - func_proc_name.erase(func_proc_name.begin() + 0, func_proc_name.begin() + 4); + std::string name; + bool is_cross_db = false; + int lineno = getLineNo(ctx); + int return_code_dno = -1; + std::string execKeywd = "EXEC"; // DO NOT CHANGE! + + // Use a boolean vor convenience + bool execute_statement = string_matches(call_type.c_str(), "execute_statement") ? true : false; + + size_t startPos = ctx->start->getStartIndex(); // start position of statement + size_t namePos = -1; // start position of procedure name + size_t argPos = -1; // start position of first argument - if (ctx->func_proc_name_server_database_schema()->database) + // Set up calltype-dependent values + if (execute_statement) { - db_name = stripQuoteFromId(ctx->func_proc_name_server_database_schema()->database); - if (!string_matches(db_name.c_str(), get_cur_db_name())) - is_cross_db = true; + TSqlParser::Execute_statementContext *ctxES = (TSqlParser::Execute_statementContext *) ctx; + + if (ctxES->EXECUTE()) execKeywd = "EXEC "; // same length as EXECUTE. DO NOT CHANGE! + body = ctxES->execute_body(); + Assert(body); + + ctx_name = body->func_proc_name_server_database_schema(); + func_proc_args = body->execute_statement_arg(); } - if (ctx->func_proc_name_server_database_schema()->schema) - schema_name = stripQuoteFromId(ctx->func_proc_name_server_database_schema()->schema); - if (ctx->func_proc_name_server_database_schema()->procedure) - proc_name = stripQuoteFromId(ctx->func_proc_name_server_database_schema()->procedure); - Assert(!func_proc_name.empty()); - TSqlParser::Execute_statement_argContext *func_proc_args = ctx->execute_statement_arg(); - - int lineno = getLineNo(ctx); - int return_code_dno = -1; + else // execute_body_batch + { + TSqlParser::Execute_body_batchContext *ctxEBB = (TSqlParser::Execute_body_batchContext *) ctx; + ctx_name = ctxEBB->func_proc_name_server_database_schema(); + func_proc_args = ctxEBB->execute_statement_arg(); + Assert(ctx_name); + } + - if (is_sp_proc(func_proc_name)) - return makeSpStatement(func_proc_name, func_proc_args, lineno, return_code_dno); + if (ctx_name) + { + // Get the name of procedure being executed, and split up in parts + name = ::getFullText(ctx_name); + Assert(!name.empty()); + + // Original position of the name + namePos = ctx_name->start->getStartIndex(); + + if (ctx_name->database) + { + db_name = stripQuoteFromId(ctx_name->database); + if (!string_matches(db_name.c_str(), get_cur_db_name())) + { + is_cross_db = true; + } + } + + if (ctx_name->schema) + { + schema_name = stripQuoteFromId(ctx_name->schema); + } + + if (ctx_name->procedure) + { + proc_name = stripQuoteFromId(ctx_name->procedure); + } + + // Note: previous code performed rewriting here for procedure names with leading dots (EXEC ..proc1) + // This is now performed in exitFunc_proc_name_server_database_schema() which is called via the mutator (previously, it wasn't). + + // For sp_* procs, truncate proc name to sp_* if the schema is "dbo" or "sys" or has leading dots + // ToDo: handle 'EXEC mydb..sp_proc' where sp_proc gets executed in the context of 'mydb', even when the current DB is not 'mydb' + if ((pg_strncasecmp(name.c_str(), "..dbo.sp_", 9) == 0) || (pg_strncasecmp(name.c_str(), "..sys.sp_", 9) == 0)) + { + name.erase(name.begin() + 0, name.begin() + 6); + } + else if ((pg_strncasecmp(name.c_str(), ".dbo.sp_", 8) == 0) || (pg_strncasecmp(name.c_str(), ".sys.sp_", 8) == 0)) + { + name.erase(name.begin() + 0, name.begin() + 5); + } + else if ((pg_strncasecmp(name.c_str(), "dbo.sp_", 7) == 0) || (pg_strncasecmp(name.c_str(), "sys.sp_", 7) == 0)) + { + name.erase(name.begin() + 0, name.begin() + 4); + } + else if (pg_strncasecmp(name.c_str(), ".sp_", 4) == 0) + { + name.erase(name.begin() + 0, name.begin() + 1); + } + else if (pg_strncasecmp(name.c_str(), "..sp_", 5) == 0) + { + name.erase(name.begin() + 0, name.begin() + 2); + } + else if (pg_strncasecmp(name.c_str(), "...sp_", 6) == 0) + { + name.erase(name.begin() + 0, name.begin() + 3); + } + } + + if (!ctx_name && execute_statement) + { + // LOCAL_ID can be placed on return_status and/or proc_var. choose the corresponding index, depending on whether return_status exists or not + Assert(body->proc_var); + name = ::getFullText(body->return_status ? body->LOCAL_ID()[1] : body->LOCAL_ID()[0]); + Assert(!name.empty()); + + namePos = body->proc_var->getStartIndex(); + } + if (execute_statement) + { + // Check for return status variable: EXEC @var = proc + auto *localID = body->return_status ? body->LOCAL_ID()[0] : nullptr; + if (localID) + { + return_code_dno = getVarno(localID); + } + } + + if (is_sp_proc(name)) + { + // If this is one of the special stored procs, exit here + return makeSpStatement(name, func_proc_args, lineno, return_code_dno); + } + + // Build the statement PLtsql_stmt_exec *result = (PLtsql_stmt_exec *) palloc0(sizeof(*result)); result->cmd_type = PLTSQL_STMT_EXEC; result->lineno = lineno; @@ -5623,22 +5787,28 @@ makeExecBodyBatch(TSqlParser::Execute_body_batchContext *ctx) result->return_code_dno = return_code_dno; result->paramno = 0; result->params = NIL; - // record whether stmt is cross-db - if (is_cross_db) - result->is_cross_db = true; + result->is_cross_db = is_cross_db; // Record whether this is a cross-db call + // Handle name parts if (!proc_name.empty()) + { result->proc_name = pstrdup(downcase_truncate_identifier(proc_name.c_str(), proc_name.length(), true)); + } if (!schema_name.empty()) + { result->schema_name = pstrdup(downcase_truncate_identifier(schema_name.c_str(), schema_name.length(), true)); + } if (!db_name.empty()) + { result->db_name = pstrdup(downcase_truncate_identifier(db_name.c_str(), db_name.length(), true)); + } + // Handle arguments if (func_proc_args) { + argPos = func_proc_args->start->getStartIndex(); std::vector params; makeSpParams(func_proc_args, params); - for (size_t i = 0; i < params.size(); i++) { result->params = lappend(result->params, params[i]); @@ -5646,10 +5816,54 @@ makeExecBodyBatch(TSqlParser::Execute_body_batchContext *ctx) } } - std::stringstream ss; - ss << "EXEC " << func_proc_name; - if (func_proc_args) - ss << " " << ::getFullText(func_proc_args); + // For execute_body_batch, there was no EXEC[UTE] keyword, so 'EXEC ' will be prepended; + // must account for the resulting offset + if (!execute_statement) + { + namePos += fragment_EXEC_prefix.length(); + argPos += fragment_EXEC_prefix.length(); + } + + // If there is a comment preceding the statement, startPos will be > 0 + namePos -= startPos; + Assert(namePos >= 0); + + // Build the statement text + std::stringstream ss; + if (execute_statement) + { + // In order for rewriting of the arguments to work correctly, they should be at the same position as in the original + // SQL text. + ss << execKeywd; + } + else + { + // For proc execution without EXECUTE, prepend 'EXEC ' + ss << fragment_EXEC_prefix; + } + int ssPos = ss.str().length(); + + // Because whitespace and comments will be ignored by the logic below, spaces are added as needed + // to keep the tokens at the same offsets as originally; this is required for rewriting + int spacesNeeded = (namePos - ssPos); + + ss << std::string(spacesNeeded, ' '); + ssPos += spacesNeeded; + + ss << name; + ssPos += name.length(); + + if (func_proc_args) + { + argPos -= startPos; + spacesNeeded = (argPos - ssPos); + Assert(spacesNeeded >= 0); + + ss << std::string(spacesNeeded, ' '); + ssPos += spacesNeeded; + + ss << ::getFullText(func_proc_args); + } std::string expr_query = ss.str(); result->expr = makeTsqlExpr(expr_query, false); @@ -7147,46 +7361,51 @@ getIDName(TerminalNode *dq, TerminalNode *sb, TerminalNode *id) // - change the enclosing quotes to single quotes // - escape any single quotes in the string by doubling them // - unescape any double quotes -std::string rewriteDoubleQuotedString(const std::string strDoubleQuoted) +std::string +rewriteDoubleQuotedString(const std::string strDoubleQuoted) { std::string str = strDoubleQuoted; Assert(str.front() == '"'); Assert(str.back() == '"'); - if (str.find('\'') == std::string::npos) + // For any embedded single-quotes, these must be escaped by doubling them + for (size_t i = str.find("\'", 1); // start at pos 1: char 0 has the enclosing quote + i != std::string::npos; + i = str.find("\'", i + 2) ) { - // String contains no embedded single-quotes, so no further action needed + str.replace(i, 1, "''"); // Change single quote to 2 single-quotes } - else - { - // String contains embedded single-quotes; these must be escaped by doubling them - for (size_t i = str.find("\'", 1); // start at pos 1: char 0 has the enclosing quote - i != std::string::npos; - i = str.find("\'", i + 2) ) - { - str.replace(i, 1, "''"); // Change single quote to 2 single-quotes - } - } // Now change the enclosing quotes, i.e. from "foo" to 'foo' // Must do this after embedded single-quote handling above str.front() = '\''; str.back() = '\''; - if (str.find('\"') == std::string::npos) + // For any embedded double-quotes, these must be un-escaped by removing one of the two + for (size_t i = str.find("\"\"", 1); // Start at pos 1: char 0 has the enclosing quote + i != std::string::npos; + i = str.find("\"\"", i + 1) ) { - // String contains no embedded double-quotes, so no further action needed + str.replace(i, 2, "\""); // Remove one of the double quotes } - else + + return str; +} + +// Escape double quotes by doubling them +std::string +escapeDoubleQuotes(const std::string strWithDoubleQuote) +{ + std::string quote = "\""; + std::string str = strWithDoubleQuote; + + // If the string contains embedded quotes, these must be escaped by doubling them + for (size_t i = str.find(quote, 1); // Start at pos 1: char 0 has the enclosing delimiter + i != std::string::npos; + i = str.find(quote, i + 2) ) { - // String contains embedded double-quotes; these must be un-escaped by removing one of the two - for (size_t i = str.find("\"\"", 1); // start at pos 1: char 0 has the enclosing quote - i != std::string::npos; - i = str.find("\"\"", i + 1) ) - { - str.replace(i, 2, "\""); // Remove one of the double quotes - } + str.replace(i, 1, quote+quote); // Change quote to 2 quotes } return str; diff --git a/test/JDBC/expected/BABEL-2844.out b/test/JDBC/expected/BABEL-2844.out index 22b3e9a950..e5ec6df10a 100644 --- a/test/JDBC/expected/BABEL-2844.out +++ b/test/JDBC/expected/BABEL-2844.out @@ -235,7 +235,7 @@ execute babel_2844_proc 3; go ~~START~~ text -Query Text: EXEC babel_2844_proc 3 +Query Text: EXEC babel_2844_proc 3 Query Text: insert babel_2844_t1 values (3, 3); -> Insert on babel_2844_t1 (cost=0.00..0.01 rows=0 width=0) -> Result (cost=0.00..0.01 rows=1 width=8) diff --git a/test/JDBC/expected/BABEL-2903.out b/test/JDBC/expected/BABEL-2903.out index 03ecc93832..3c4fabea92 100644 --- a/test/JDBC/expected/BABEL-2903.out +++ b/test/JDBC/expected/BABEL-2903.out @@ -58,7 +58,7 @@ Query Text: ASSIGN @a = SELECT 5 Query Text: ASSIGN @b = SELECT 5 Query Text: SELECT 5 -> Result (cost=0.00..0.01 rows=1 width=4) -Query Text: EXEC babel_2903_outer_proc @a, @b +Query Text: EXEC babel_2903_outer_proc @a, @b Query Text: DECLARE TABLE @t Query Text: CREATE TEMPORARY TABLE IF NOT EXISTS @t_1 (a int, b int) Query Text: ASSIGN @a = SELECT 3 diff --git a/test/JDBC/expected/BABEL-4484-vu-cleanup.out b/test/JDBC/expected/BABEL-4484-vu-cleanup.out new file mode 100644 index 0000000000..01ba0a9be5 --- /dev/null +++ b/test/JDBC/expected/BABEL-4484-vu-cleanup.out @@ -0,0 +1,8 @@ +DROP TABLE test_babel_4484_t1; +GO + +DROP TABLE test_babel_4484_t2; +GO + +DROP TABLE test_babel_4484_t3; +GO diff --git a/test/JDBC/expected/BABEL-4484-vu-prepare.out b/test/JDBC/expected/BABEL-4484-vu-prepare.out new file mode 100644 index 0000000000..81bcc1e47b --- /dev/null +++ b/test/JDBC/expected/BABEL-4484-vu-prepare.out @@ -0,0 +1,8 @@ +CREATE TABLE test_babel_4484_t1(ABC int, ced varchar(10)); +GO + +CREATE TABLE test_babel_4484_t2(ABC int, 您您 varchar(10)); +GO + +CREATE TABLE test_babel_4484_t3(ABC int, 您您对您对您对您对您对您对您对您对您对您您您 int); +GO diff --git a/test/JDBC/expected/BABEL-4484-vu-verify.out b/test/JDBC/expected/BABEL-4484-vu-verify.out new file mode 100644 index 0000000000..b3961349aa --- /dev/null +++ b/test/JDBC/expected/BABEL-4484-vu-verify.out @@ -0,0 +1,28 @@ +SELECT set_config('babelfishpg_tsql.enable_sll_parse_mode', 'true', false); +GO +~~START~~ +text +on +~~END~~ + + +DELETE test_babel_4484_t1 OUTPUT test_babel_4484_t1.ced FROM test_babel_4484_t1 INNER JOIN test_babel_4484_t2 ON test_babel_4484_t1.ABC = test_babel_4484_t2.ABC WHERE test_babel_4484_t1.ABC = 1; +GO +~~START~~ +varchar +~~END~~ + + +DELETE test_babel_4484_t2 OUTPUT test_babel_4484_t2.您您 FROM test_babel_4484_t2 INNER JOIN test_babel_4484_t3 ON test_babel_4484_t2.ABC = test_babel_4484_t3.ABC WHERE test_babel_4484_t2.ABC = 1; +GO +~~START~~ +varchar +~~END~~ + + +SELECT test_babel_4484_t1.ced FROM test_babel_4484_t1 INNER JOIN test_babel_4484_t2 ON test_babel_4484_t1.ABC = test_babel_4484_t2.ABC WHERE test_babel_4484_t1.ABC = 1; +GO +~~START~~ +varchar +~~END~~ + diff --git a/test/JDBC/expected/babel_4299.out b/test/JDBC/expected/babel_4299.out new file mode 100644 index 0000000000..585aa27688 --- /dev/null +++ b/test/JDBC/expected/babel_4299.out @@ -0,0 +1,309 @@ +select ''+'ps'; +GO +~~START~~ +varchar +ps +~~END~~ + + +select 'rs' + ''; +GO +~~START~~ +varchar +rs +~~END~~ + + +select '' + ''; +GO +~~START~~ +varchar + +~~END~~ + + +select '' + NULL; +GO +~~START~~ +varchar + +~~END~~ + + +select NULL + ''; +GO +~~START~~ +varchar + +~~END~~ + + +set quoted_identifier off +GO +select ""+'s'; +GO +~~START~~ +varchar +s +~~END~~ + + +set quoted_identifier off +GO +select 'rs' + ""; +GO +~~START~~ +varchar +rs +~~END~~ + + +set quoted_identifier off +GO +select '' + ""; +GO +~~START~~ +varchar + +~~END~~ + + +set quoted_identifier off +GO +select "" + NULL; +GO +~~START~~ +varchar + +~~END~~ + + +set quoted_identifier off +GO +select NULL + ""; +GO +~~START~~ +varchar + +~~END~~ + + +set quoted_identifier off +GO +select ""+"s"; +GO +~~START~~ +varchar +s +~~END~~ + + +set quoted_identifier off +GO +select "pr" + ""; +GO +~~START~~ +varchar +pr +~~END~~ + + +set quoted_identifier off +GO +select "" + ""; +GO +~~START~~ +varchar + +~~END~~ + + +set quoted_identifier off +GO +select "" + ""; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT 'Hello' + NULL + 'World'; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT 'Hello' + NULL + 'World'; +GO +~~START~~ +varchar +HelloWorld +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + 'World'; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + 'World'; +GO +~~START~~ +varchar +World +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + ''; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + ''; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT NULL + 'World'; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT NULL + 'World'; +GO +~~START~~ +varchar +World +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT 'Hello' + NULL; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT 'Hello' + NULL; +GO +~~START~~ +varchar +Hello +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT "" + NULL + 'World'; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT "" + NULL + 'World'; +GO +~~START~~ +varchar +World +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT "" + NULL + ""; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT "" + NULL + ""; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + "World"; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + "World"; +GO +~~START~~ +varchar +World +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + ""; +GO +~~START~~ +varchar + +~~END~~ + + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + ""; +GO +~~START~~ +varchar + +~~END~~ + diff --git a/test/JDBC/expected/doublequoted_string-vu-verify.out b/test/JDBC/expected/doublequoted_string-vu-verify.out index abfafc886d..695c4050a2 100644 --- a/test/JDBC/expected/doublequoted_string-vu-verify.out +++ b/test/JDBC/expected/doublequoted_string-vu-verify.out @@ -1,3 +1,9 @@ + +-- BABEL-2442: Handle embedded double quotes in a double-quoted string +-- BABEL-4387: Support double-quoted strings containing single-quote +-- This also exercises parts of the ANTLR parse tree rewriting +set quoted_identifier off +go create procedure dubquote_p @p varchar(20) = "ab'cd" , @p2 varchar(20)='xyz' as select @p go @@ -7,11 +13,11 @@ go set quoted_identifier off go -select "abc" +select "aBc" go ~~START~~ varchar -abc +aBc ~~END~~ exec dubquote_p @@ -28,31 +34,31 @@ varchar ab"cd ~~END~~ -exec dubquote_p "abc" +exec dubquote_p "aBc" go ~~START~~ varchar -abc +aBc ~~END~~ -exec dubquote_p 'abc' +exec dubquote_p 'aBc' go ~~START~~ varchar -abc +aBc ~~END~~ -exec dubquote_p abc +exec dubquote_p aBc go ~~START~~ varchar -abc +aBc ~~END~~ set quoted_identifier on go -select "abc" +select "aBc" go ~~ERROR (Code: 33557097)~~ @@ -72,25 +78,25 @@ varchar ab"cd ~~END~~ -exec dubquote_p "abc" +exec dubquote_p "aBc" go ~~START~~ varchar -abc +aBc ~~END~~ -exec dubquote_p 'abc' +exec dubquote_p 'aBc' go ~~START~~ varchar -abc +aBc ~~END~~ -exec dubquote_p abc +exec dubquote_p aBc go ~~START~~ varchar -abc +aBc ~~END~~ @@ -204,11 +210,11 @@ varchar xx'yy ~~END~~ -exec dubquote_p 'xx"yy' +exec dubquote_p 'xX"yY' go ~~START~~ varchar -xx"yy +xX"yY ~~END~~ exec dubquote_p """" @@ -269,11 +275,11 @@ varchar xx'yy ~~END~~ -exec dubquote_p @p='xx"yy' , @p2="x""y" +exec dubquote_p @p='xX"yY' , @p2="x""y" go ~~START~~ varchar -xx"yy +xX"yY ~~END~~ exec dubquote_p @p="""" , @p2="x'y" @@ -327,11 +333,11 @@ varchar -- using N'...' notation: -exec dubquote_p N'xx"yy' +exec dubquote_p N'xX"yY' go ~~START~~ varchar -xx"yy +xX"yY ~~END~~ exec dubquote_p N'''' @@ -355,11 +361,11 @@ varchar "" ~~END~~ -exec dubquote_p @p=N'xx"yy' +exec dubquote_p @p=N'xX"yY' go ~~START~~ varchar -xx"yy +xX"yY ~~END~~ exec dubquote_p @p=N'''' @@ -391,7 +397,7 @@ create function dubquote_f1(@p varchar(20) = "ab'cd") returns varchar(20) as beg go create function dubquote_f2(@p varchar(20) = "ab""cd") returns varchar(20) as begin return @p end go -create function dubquote_f3(@p varchar(20) = abcd) returns varchar(20) as begin return @p end +create function dubquote_f3(@p varchar(20) = aBcd) returns varchar(20) as begin return @p end go declare @v varchar(20) exec @v = dubquote_f1 @@ -417,7 +423,7 @@ select @v go ~~START~~ varchar -abcd +aBcd ~~END~~ @@ -481,22 +487,22 @@ go set quoted_identifier off go -create procedure dubquote_p2a @p varchar(20) ="abc" as select @p +create procedure dubquote_p2a @p varchar(20) ="aBc" as select @p go exec dubquote_p2a go ~~START~~ varchar -abc +aBc ~~END~~ -create procedure dubquote_p3 @p varchar(20) ="'abc'" as select @p +create procedure dubquote_p3 @p varchar(20) ="'aBc'" as select @p go exec dubquote_p3 go ~~START~~ varchar -'abc' +'aBc' ~~END~~ declare @v varchar(40) set @v = "It's almost ""weekend""!" select @v @@ -507,18 +513,18 @@ It's almost "weekend"! ~~END~~ -select 'abc' +select 'aBc' go ~~START~~ varchar -abc +aBc ~~END~~ -select "abc" +select "aBc" go ~~START~~ varchar -abc +aBc ~~END~~ select "a'b""c''''''''''d" @@ -535,26 +541,26 @@ varchar a'b"c'd ~~END~~ -select "'abc'",'xyz' +select "'aBc'",'xyz' go ~~START~~ varchar#!#varchar -'abc'#!#xyz +'aBc'#!#xyz ~~END~~ -declare @v varchar(20) = 'abc' select @v +declare @v varchar(20) = 'aBc' select @v go ~~START~~ varchar -abc +aBc ~~END~~ -declare @v varchar(20) = "abc" select @v +declare @v varchar(20) = "aBc" select @v go ~~START~~ varchar -abc +aBc ~~END~~ declare @v varchar(20) = "'a""bc'" select @v @@ -564,18 +570,18 @@ varchar 'a"bc' ~~END~~ -declare @v varchar(20) select @v = "abc" select @v +declare @v varchar(20) select @v = "aBc" select @v go ~~START~~ varchar -abc +aBc ~~END~~ -declare @v varchar(20) = 'x' select @v += "abc" select @v +declare @v varchar(20) = 'x' select @v += "aBc" select @v go ~~START~~ varchar -xabc +xaBc ~~END~~ declare @v varchar(20) select @v = "'a""bc'" select @v @@ -607,11 +613,11 @@ x'a"bc' ~~END~~ -declare @v varchar(20) ="abc" , @v2 varchar(10) = 'xyz' select @v +declare @v varchar(20) ="aBc" , @v2 varchar(10) = 'xyz' select @v go ~~START~~ varchar -abc +aBc ~~END~~ declare @v varchar(20), @v2 varchar(20) select @v="a""b''c'd", @v2="x""y''z" select @v, @v2 @@ -664,17 +670,17 @@ int set quoted_identifier off go -- the JDBC test cases do not capture PRINT output, but including them here for when it will -print "abc" +print "aBc" go -print "'abc'" +print "'aBc'" go print "a""b'c" go print "a""b'c," + session_user + ",d""e'f," + system_user go - /*test*/ print "abc" + /*test*/ print "aBc" go - /*hello*/ print /*hello*/ "abc" + /*hello*/ print /*hello*/ "aBc" go print /*hello*/ "a""b'c," + /*hello*/ session_user + /*hello*/ @@ -706,7 +712,7 @@ go -- RAISERROR arguments are not yet rewritten. this should raise an error -RAISERROR ('%s %s', 10, 1, 'abc', "def"); +RAISERROR ('%s %s', 10, 1, 'aBc', "def"); go ~~ERROR (Code: 33557097)~~ @@ -762,7 +768,7 @@ correct set quoted_identifier on go -print "abc" +print "aBc" go ~~ERROR (Code: 33557097)~~ @@ -786,11 +792,11 @@ varchar#!#varchar a'bc#!#a'bc ~~END~~ -exec dubquote_p4 "abc" +exec dubquote_p4 "aBc" go ~~START~~ varchar#!#varchar -abc#!#abc +aBc#!#aBc ~~END~~ exec dubquote_p4 "ab""cd" @@ -814,7 +820,7 @@ varchar ab'cd ~~END~~ -create function dubquote_f4 (@p varchar(20) = "'abc'") returns varchar(50) as begin return ((("function's return" +( " string value:" ))) +"'" + @p + "'") end +create function dubquote_f4 (@p varchar(20) = "'aBc'") returns varchar(50) as begin return ((("function's return" +( " string value:" ))) +"'" + @p + "'") end go select dbo.dubquote_f4("x") go @@ -846,16 +852,17 @@ a"b'c,dbo,d"e'f,jdbc_user CREATE function dubquote_f7() returns varchar(30) as begin return system_user end go -select select dbo.dubquote_f7(), system_user +select dbo.dubquote_f7(), system_user go -~~ERROR (Code: 33557097)~~ - -~~ERROR (Message: syntax error near 'select' at line 1 and character position 7)~~ +~~START~~ +varchar#!#nvarchar +jdbc_user#!#jdbc_user +~~END~~ create procedure dubquote_p5 @p varchar(10) as select @p go -exec dubquote_p5 'xyz' exec dubquote_p5 abc +exec dubquote_p5 'xyz' exec dubquote_p5 aBc go ~~START~~ varchar @@ -864,28 +871,28 @@ xyz ~~START~~ varchar -abc +aBc ~~END~~ -exec dubquote_p5 abcd +exec dubquote_p5 aBcd go ~~START~~ varchar -abcd +aBcd ~~END~~ -exec dubquote_p5 [abcd] +exec dubquote_p5 [aBcd] go ~~START~~ varchar -abcd +aBcd ~~END~~ -exec dubquote_p5 @p=abcde +exec dubquote_p5 @p=aBcde go ~~START~~ varchar -abcde +aBcde ~~END~~ declare @v varchar(20) exec dubquote_p5 @v @@ -924,18 +931,18 @@ varchar#!#nvarchar dbo#!#dbo ~~END~~ -declare @v varchar(20) = 'abc' + session_user select @v, session_user +declare @v varchar(20) = 'aBc' + session_user select @v, session_user go ~~START~~ varchar#!#nvarchar -abcdbo#!#dbo +aBcdbo#!#dbo ~~END~~ -declare @v varchar(20) = "abc" + session_user select @v, session_user +declare @v varchar(20) = "aBc" + session_user select @v, session_user go ~~START~~ varchar#!#nvarchar -abcdbo#!#dbo +aBcdbo#!#dbo ~~END~~ declare @v varchar(20) = "ab""c'd" + session_user select @v, session_user @@ -953,18 +960,18 @@ varchar#!#nvarchar jdbc_user#!#jdbc_user ~~END~~ -declare @v varchar(20) = 'abc' + system_user select @v, system_user +declare @v varchar(20) = 'aBc' + system_user select @v, system_user go ~~START~~ varchar#!#nvarchar -abcjdbc_user#!#jdbc_user +aBcjdbc_user#!#jdbc_user ~~END~~ -declare @v varchar(20) = "abc" + system_user select @v, system_user +declare @v varchar(20) = "aBc" + system_user select @v, system_user go ~~START~~ varchar#!#nvarchar -abcjdbc_user#!#jdbc_user +aBcjdbc_user#!#jdbc_user ~~END~~ declare @v varchar(20) = "ab""c'd" + system_user select @v, system_user @@ -981,18 +988,18 @@ varchar#!#nvarchar jdbc_user#!#jdbc_user ~~END~~ -declare @v varchar(20) = '' set @v = 'abc' + system_user select @v, system_user +declare @v varchar(20) = '' set @v = 'aBc' + system_user select @v, system_user go ~~START~~ varchar#!#nvarchar -abcjdbc_user#!#jdbc_user +aBcjdbc_user#!#jdbc_user ~~END~~ -declare @v varchar(20) = '' set @v = "abc" + system_user select @v, system_user +declare @v varchar(20) = '' set @v = "aBc" + system_user select @v, system_user go ~~START~~ varchar#!#nvarchar -abcjdbc_user#!#jdbc_user +aBcjdbc_user#!#jdbc_user ~~END~~ declare @v varchar(20) = '' set @v = "ab""c'd" + system_user select @v, system_user @@ -1044,7 +1051,7 @@ jdbc_user,a"b'c,jdbc_user,a"b'c,jdbc_user#!#jdbc_user -- all in one batch: declare @v varchar(20) = session_user select @v -declare @v1 varchar(20) = 'abc' + session_user select @v1 +declare @v1 varchar(20) = 'aBc' + session_user select @v1 declare @v2 varchar(20) = "ab""c'd" + session_user select @v2 declare @v3 varchar(20) ="a""bc" , @v4 varchar(20) = 'x''z' select @v3,@v4 declare @v5 varchar(20) ="a""bc" , @v6 varchar(20) = 'x''z' , @v7 varchar(20) = "x""y'z'z" select @v5, @v6, @v7 @@ -1056,7 +1063,7 @@ dbo ~~START~~ varchar -abcdbo +aBcdbo ~~END~~ ~~START~~ @@ -1082,18 +1089,18 @@ varchar#!#varchar#!#nvarchar#!#nvarchar dbo#!#jdbc_user#!#dbo#!#jdbc_user ~~END~~ -declare @v varchar(20) = 'abcd' + session_user, @v2 varchar(20) = 'xy' + session_user select @v, @v2, session_user +declare @v varchar(20) = 'aBcd' + session_user, @v2 varchar(20) = 'xy' + session_user select @v, @v2, session_user go ~~START~~ varchar#!#varchar#!#nvarchar -abcddbo#!#xydbo#!#dbo +aBcddbo#!#xydbo#!#dbo ~~END~~ -declare @v varchar(20) = 'abcd' + upper('x'), @v2 varchar(20) = 'xy' + upper('y') select @v, @v2 +declare @v varchar(20) = 'aBcd' + upper('x'), @v2 varchar(20) = 'xy' + upper('y') select @v, @v2 go ~~START~~ varchar#!#varchar -abcdX#!#xyY +aBcdX#!#xyY ~~END~~ declare @v varchar(20) = session_user, @v2 varchar(20)= system_user select @v,@v2,session_user, system_user @@ -1202,43 +1209,372 @@ int ~~END~~ - set quoted_identifier on go -create procedure dubquote_p6 @p varchar(20) ="abc" as select @p +create procedure dubquote_p6 @p varchar(20) ="aBc" as select @p go exec dubquote_p6 go ~~START~~ varchar -abc +aBc ~~END~~ -create procedure dubquote_p7 @p varchar(20) ="'abc'" as select @p +create procedure dubquote_p7 @p varchar(20) ="'aBc'" as select @p go exec dubquote_p7 go ~~START~~ varchar -'abc' +'aBc' +~~END~~ + +declare @v varchar(20) = 'aBc' select @v +go +~~START~~ +varchar +aBc +~~END~~ + + +set quoted_identifier off +go +create procedure dubquote_p8 @p varchar(20) as select @p +go +execute dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +exec dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +execute[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +exec[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +exec ..[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +dbo.dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +.dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +..dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/execute dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/exec dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/execute[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/exec[dubquote_p8]/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/.dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/..dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/[dubquote_p8]/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/.[dubquote_p8]/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/..[dubquote_p8]/*test*/"x'Y""z" +execute dubquote_p8 "a'B""C" +go +~~START~~ +varchar +x'Y"z ~~END~~ -declare @v varchar(20) = 'abc' select @v +~~START~~ +varchar +a'B"C +~~END~~ + + +set quoted_identifier on +go +execute dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +exec dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +execute[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +exec[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +exec ..[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +dbo.dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +.dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +..dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/execute dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/exec dubquote_p8 "x'Y""z" go ~~START~~ varchar -abc +x'Y"z ~~END~~ +/*test*/execute[dubquote_p8]"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/exec[dubquote_p8]/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/.dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/..dubquote_p8 "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/[dubquote_p8]/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/.[dubquote_p8]/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/..[dubquote_p8]/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ +"dubquote_p8" "x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/"dubquote_p8"/*test*/"x'Y""z" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +/*test*/"dubquote_p8"/*test*/"x'Y""z" +execute dubquote_p8 "a'B""C" +go +~~START~~ +varchar +x'Y"z +~~END~~ + +~~START~~ +varchar +a'B"C +~~END~~ + + +set quoted_identifier on +go -- negative tests -declare @v varchar(20) = "abc" select @v +declare @v varchar(20) = "aBc" select @v go ~~ERROR (Code: 33557097)~~ ~~ERROR (Message: column "abc" does not exist)~~ -declare @v varchar(20) = "'abc'" select @v +declare @v varchar(20) = "'aBc'" select @v go ~~ERROR (Code: 33557097)~~ @@ -1285,5 +1621,7 @@ drop procedure dubquote_p6 go drop procedure dubquote_p7 go +drop procedure dubquote_p8 +go drop table dubquote_t1 go diff --git a/test/JDBC/expected/sp_proc.out b/test/JDBC/expected/sp_proc.out index e7e2d0464c..c88ceb5dbb 100644 --- a/test/JDBC/expected/sp_proc.out +++ b/test/JDBC/expected/sp_proc.out @@ -17,6 +17,22 @@ int ~~END~~ +exec master.dbo.sp_hello +go +~~START~~ +int +1 +~~END~~ + + +exec master..sp_hello +go +~~START~~ +int +1 +~~END~~ + + create database db1 go @@ -31,7 +47,7 @@ int ~~END~~ -exec sp_hello +exec master..sp_hello go ~~START~~ int @@ -39,7 +55,7 @@ int ~~END~~ -sp_hello +exec sp_hello go ~~START~~ int @@ -71,6 +87,46 @@ int ~~END~~ +exec .dbo.sp_hello +go +~~START~~ +int +1 +~~END~~ + + +exec ..dbo.sp_hello +go +~~START~~ +int +1 +~~END~~ + + +sp_hello +go +~~START~~ +int +1 +~~END~~ + + +.sp_hello +go +~~START~~ +int +1 +~~END~~ + + +..sp_hello +go +~~START~~ +int +1 +~~END~~ + + dbo.sp_hello go ~~START~~ @@ -79,6 +135,22 @@ int ~~END~~ +.dbo.sp_hello +go +~~START~~ +int +1 +~~END~~ + + +..dbo.sp_hello +go +~~START~~ +int +1 +~~END~~ + + create proc call_sp_helllo as exec sp_hello go diff --git a/test/JDBC/expected/table-variable-vu-verify.out b/test/JDBC/expected/table-variable-vu-verify.out index c338b6f4ee..add8e88fde 100644 --- a/test/JDBC/expected/table-variable-vu-verify.out +++ b/test/JDBC/expected/table-variable-vu-verify.out @@ -68,7 +68,7 @@ Query Text: ASSIGN @a = SELECT 5 Query Text: ASSIGN @b = SELECT 5 Query Text: SELECT 5 -> Result (cost=0.00..0.01 rows=1 width=4) -Query Text: EXEC table_variable_vu_prepareouter_proc @a, @b +Query Text: EXEC table_variable_vu_prepareouter_proc @a, @b Query Text: DECLARE TABLE @t Query Text: CREATE TEMPORARY TABLE IF NOT EXISTS @t_1 (a int, b int) Query Text: ASSIGN @a = SELECT 3 diff --git a/test/JDBC/expected/unquoted_string-vu-cleanup.out b/test/JDBC/expected/unquoted_string-vu-cleanup.out index 8b0f00e319..da4728be55 100644 --- a/test/JDBC/expected/unquoted_string-vu-cleanup.out +++ b/test/JDBC/expected/unquoted_string-vu-cleanup.out @@ -3,16 +3,64 @@ drop procedure unqStr_proc_1 go drop procedure unqStr_proc_2 go +drop procedure unqStr_proc_2a +go +drop procedure unqStr_proc_2b +go +drop procedure unqStr_proc_2c +go +drop procedure unqStr_proc_2d +go +drop procedure unqStr_proc_2e +go +-- this should fail as the proc was not created +drop procedure unqStr_proc_2f +go +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "unqstr_proc_2f")~~ + +drop procedure unqStr_proc_2g +go +drop procedure unqStr_proc_2h +go +drop procedure unqStr_proc_2i +go drop procedure unqStr_proc_3 go drop procedure unqStr_proc_4 go +-- this should fail as the proc was not created +drop procedure unqStr_proc_5 +go +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "unqstr_proc_5")~~ + +-- this should fail as the proc was not created +drop procedure unqStr_proc_6 +go +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a procedure named "unqstr_proc_6")~~ + +drop procedure unqStr_proc_7 +go drop function unqStr_func_1 go drop function unqStr_func_2 go drop function unqStr_func_3 go +-- this should fail as the function was not created +drop function unqStr_func_4 +go +~~ERROR (Code: 3701)~~ + +~~ERROR (Message: could not find a function named "unqstr_func_4")~~ + +drop function unqStr_func_5 +go drop table unqStr_tab go diff --git a/test/JDBC/expected/unquoted_string-vu-prepare.out b/test/JDBC/expected/unquoted_string-vu-prepare.out index 6b2aaf5f18..8025a74a6e 100644 --- a/test/JDBC/expected/unquoted_string-vu-prepare.out +++ b/test/JDBC/expected/unquoted_string-vu-prepare.out @@ -5,3 +5,227 @@ go ~~ROW COUNT: 1~~ +-- unquoted strings in procedure call arguments +create procedure unqStr_proc_1(@par1 varchar(100), @par2 varchar(100)) +as +select @par1, @par2 +go + +-- unlike T-SQL procedure calls, function call arguments do not support unquoted strings +create function unqStr_func_1(@par varchar(20)) +returns varchar(20) +as begin +return @par +end +go + +-- unquoted string in parameter declaration +create proc unqStr_proc_2 @par1 varchar(20) = aBc +as +select @par1 +go + +-- strings longer than 63 should not be truncated, hashed or downcased +create procedure unqStr_proc_2a( +@par1 varchar(100) = a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, +@par2 varchar(100) = [AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB]) +as +select @par1, @par2 +go + +create procedure unqStr_proc_2b( +@par1 varchar(20) = [Aaa aaB], +@par2 varchar(20) = [Aa"bB'cC]) +as +select @par1, @par2 +go + +set quoted_identifier off +go +create procedure unqStr_proc_2c( +@par1 varchar(20) = "[Aaa aaB]", +@par2 varchar(20) = "[Aa""bB'cC]") +as +select @par1, @par2 +go +set quoted_identifier on +go + +set quoted_identifier on +go +create procedure unqStr_proc_2d( +@par1 varchar(20) = "[Aaa aaB]", +@par2 varchar(20) = "[Aa""bB'cC]") +as +select @par1, @par2 +go +set quoted_identifier off +go +create procedure unqStr_proc_2e( +@par1 varchar(20) = ["Aaa aaB"], +@par2 varchar(20) = ["Aa"bB'cC"]) +as +select @par1, @par2 +go +set quoted_identifier on +go + +-- double-quoted/bracketed datatype: +set quoted_identifier off +go +-- should raise error +create procedure unqStr_proc_2f(@par1 "varchar"(20) = aBc) +as +select @par1 +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near '"varchar"' at line 2 and character position 38)~~ + +create procedure unqStr_proc_2g(@par1 [varchar](20) = aBc) +as +select @par1 +go + +set quoted_identifier on +go +create procedure unqStr_proc_2h(@par1 "varchar"(20) = aBc) +as +select @par1 +go + +create procedure unqStr_proc_2i(@par1 [varchar](20) = aBc) +as +select @par1 +go + +set quoted_identifier off +go + +-- 'user' is handled as function call in Babelfish, though it fails in SQL Server +create proc unqStr_proc_3 @par1 varchar(20) = user +as +select @par1 +go + +create procedure unqStr_proc_4 @p1 varchar(20) = @@language as select @p1 +go + +create procedure unqStr_proc_5 @p1 varchar(20), @p2 varchar(20) = @p1 +as select @p2 +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: column "@p1" does not exist)~~ + + +-- should raise error: +create procedure unqStr_proc_6 @p1 varchar(20) = @@myvar +as select @p1 +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: function sys.myvar() does not exist)~~ + + +create procedure unqStr_proc_7 @p1 varchar(20) = N'aB"c''D', @p2 varchar(20) = dEfg, @p3 varchar(20) = "x'Y""z" +as select @p1, @p2, @p3 +go + +create function unqStr_func_2(@par1 varchar(20) = aBc) +returns varchar(20) +as +begin +return @par1 +end +go + +-- invalid in SQL Server, should also fail in Babelfish: +create function unqStr_func_4 (@p1 varchar(20), @p2 varchar(20) = @p1) returns varchar(20) +as +begin +return @p2 +end +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: column "@p1" does not exist)~~ + + +create function unqStr_func_5 (@p1 varchar(20) = N'aB"c''D', @p2 varchar(20) = dEfg, @p3 varchar(20) = "x'Y""z") +returns varchar(60) +as +begin + return @p1 + @p2 + @p3 +end +go +select dbo.unqStr_func_5() +go +~~START~~ +varchar +aB"c'DdEfgx'Y"z +~~END~~ + + +-- variable as column default is invalid syntax in SQL Server, but works in Babelfish, which is harmless +-- in Babelfish this only works for a global (@@) variable, not for a local variable +create function unqStr_func_3 (@p1 varchar(20) = @@language) returns varchar(20) +as +begin +return @p1 +end +go + +-- psql + +CREATE OR REPLACE PROCEDURE unqStr_pgproc_1(par1 varchar(20)) +LANGUAGE plpgsql +AS $$ +begin +raise notice 'arg=[%]', par1; +end +$$; +go + +CREATE function unqStr_pgfunc_1(par1 varchar(20)) +returns varchar(20) +LANGUAGE plpgsql +AS $$ +begin +return par1; +end +$$; +go + +-- should fail since PG does not support unquoted string defaults: +CREATE OR REPLACE PROCEDURE unqStr_pgproc_2(par1 varchar(20) = aBc) +LANGUAGE plpgsql +AS $$ +begin +raise notice 'arg=[%]', par1; +end +$$; +go +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: column "abc" does not exist + Position: 131 + Server SQLState: 42703)~~ + + +-- should fail since PG does not support unquoted string defaults: +CREATE function unqStr_pgfunc_2(par1 varchar(20) = aBc) +returns varchar(20) +LANGUAGE plpgsql +AS $$ +begin +return par1; +end +$$; +go +~~ERROR (Code: 0)~~ + +~~ERROR (Message: ERROR: column "abc" does not exist + Position: 119 + Server SQLState: 42703)~~ + diff --git a/test/JDBC/expected/unquoted_string-vu-verify.out b/test/JDBC/expected/unquoted_string-vu-verify.out index 261bae6bed..4c70a5548a 100644 --- a/test/JDBC/expected/unquoted_string-vu-verify.out +++ b/test/JDBC/expected/unquoted_string-vu-verify.out @@ -2,24 +2,20 @@ -- tsql -- BABEL-334: Support unquoted string parameter values in procedure calls/declarations --- unquoted strings in procedure call arguments -create procedure unqStr_proc_1(@par1 varchar(20), @par2 varchar(20)) -as -select @par1, @par2 -go +-- This also exercises parts of the ANTLR parse tree rewriting -- should print strings: -exec unqStr_proc_1 abc, def$ghi +exec unqStr_proc_1 aBc, def$ghi go ~~START~~ varchar#!#varchar -abc#!#def$ghi +aBc#!#def$ghi ~~END~~ -exec unqStr_proc_1 abc, 'this is a string' +exec unqStr_proc_1 aBc, 'this is a string' go ~~START~~ varchar#!#varchar -abc#!#this is a string +aBc#!#this is a string ~~END~~ exec unqStr_proc_1 'this is a string', def @@ -29,82 +25,228 @@ varchar#!#varchar this is a string#!#def ~~END~~ -exec unqStr_proc_1 abc#def, null +exec unqStr_proc_1 aBc#def, null +go +~~START~~ +varchar#!#varchar +aBc#def#!# +~~END~~ + +exec unqStr_proc_1 @par1=aBc$def, @par2=null go ~~START~~ varchar#!#varchar -abc#def#!# +aBc$def#!# ~~END~~ -exec unqStr_proc_1 @par1=abc$def, @par2=null +execute unqStr_proc_1 @par1=aBc$def, @par2=null go ~~START~~ varchar#!#varchar -abc$def#!# +aBc$def#!# ~~END~~ -exec unqStr_proc_1 @par1=null, @par2=abc#def +exec unqStr_proc_1 @par1=null, @par2=aBc#def go ~~START~~ varchar#!#varchar -#!#abc#def +#!#aBc#def +~~END~~ + +execute unqStr_proc_1 @par1=null, @par2=aBc#def +go +~~START~~ +varchar#!#varchar +#!#aBc#def ~~END~~ -- should print strings, bracketed identifiers are also interpreted as unquoted strings in SQL Server: -exec unqStr_proc_1 [abc], [def$ghi] +exec unqStr_proc_1 [aBc], [def$ghi] go ~~START~~ varchar#!#varchar -abc#!#def$ghi +aBc#!#def$ghi ~~END~~ + -- should print strings, bracketed identifiers as procedure args are also interpreted as unquoted strings in SQL Server: set quoted_identifier on go -exec unqStr_proc_1 "abc", "def$ghi" +exec unqStr_proc_1 "aBc", "def$ghi" +go +~~START~~ +varchar#!#varchar +aBc#!#def$ghi +~~END~~ + +execute unqStr_proc_1 "aBc", "def$ghi" +go +~~START~~ +varchar#!#varchar +aBc#!#def$ghi +~~END~~ + +execute unqStr_proc_1 @par1 = "aBc", @par2 = "def$ghi" +go +~~START~~ +varchar#!#varchar +aBc#!#def$ghi +~~END~~ + +-- strings longer than 63 should not be truncated, hashed or downcased +exec unqStr_proc_1 a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go +~~START~~ +varchar#!#varchar +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +~~END~~ + +execute unqStr_proc_1 @par1 = a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, @par2=AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go +~~START~~ +varchar#!#varchar +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +~~END~~ + +execute unqStr_proc_1 @par1 = "a23456789B123456789C123456789d123456789E123456789F123456789G123456789H", @par2="AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB" go ~~START~~ varchar#!#varchar -abc#!#def$ghi +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB ~~END~~ + + +-- without EXEC keyword: +unqStr_proc_1 aBc, def$ghi +go +~~START~~ +varchar#!#varchar +aBc#!#def$ghi +~~END~~ + +unqStr_proc_1 aBc, 'this is a string' +go +~~START~~ +varchar#!#varchar +aBc#!#this is a string +~~END~~ + +unqStr_proc_1 'this is a string', def +go +~~START~~ +varchar#!#varchar +this is a string#!#def +~~END~~ + +unqStr_proc_1 aBc#def, null +go +~~START~~ +varchar#!#varchar +aBc#def#!# +~~END~~ + +unqStr_proc_1 @par1=aBc$def, @par2=null +go +~~START~~ +varchar#!#varchar +aBc$def#!# +~~END~~ + +unqStr_proc_1 @par1=null, @par2=aBc#def +go +~~START~~ +varchar#!#varchar +#!#aBc#def +~~END~~ + +/*test*/unqStr_proc_1 aBc, def$ghi +go +~~START~~ +varchar#!#varchar +aBc#!#def$ghi +~~END~~ + +/*test*/ +unqStr_proc_1/*test*/ +aBc, +'this is a string' +go +~~START~~ +varchar#!#varchar +aBc#!#this is a string +~~END~~ + +-- should print strings, bracketed identifiers are also interpreted as unquoted strings in SQL Server: +unqStr_proc_1 [aBc], [def$ghi] +go +~~START~~ +varchar#!#varchar +aBc#!#def$ghi +~~END~~ + +-- strings longer than 63 should not be truncated, hashed or downcased +unqStr_proc_1 a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go +~~START~~ +varchar#!#varchar +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +~~END~~ + + unqStr_proc_1 @par1 = a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, @par2=AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go +~~START~~ +varchar#!#varchar +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +~~END~~ + + unqStr_proc_1 @par1 = "a23456789B123456789C123456789d123456789E123456789F123456789G123456789H", @par2="AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB" +go +~~START~~ +varchar#!#varchar +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +~~END~~ + + + set quoted_identifier off go -- now they are just regular double-quoted strings: -exec unqStr_proc_1 "abc abc", "def ?! ghi" +exec unqStr_proc_1 "aBc aBc", "def ?! ghi" go ~~START~~ varchar#!#varchar -abc abc#!#def ?! ghi +aBc aBc#!#def ?! ghi ~~END~~ -- should raise error since 'user' is a function and not allowed as argument -exec unqStr_proc_1 user, 'abc' +exec unqStr_proc_1 user, 'aBc' go ~~ERROR (Code: 33557097)~~ ~~ERROR (Message: syntax error near 'user' at line 2 and character position 19)~~ -exec unqStr_proc_1 @par1=user, @par2='abc' +exec unqStr_proc_1 @par1=user, @par2='aBc' go ~~ERROR (Code: 33557097)~~ ~~ERROR (Message: syntax error near 'user' at line 1 and character position 25)~~ -- should raise error because the unquoted argument cannot be interpreted as a string: -exec unqStr_proc_1 abc, def.ghi +exec unqStr_proc_1 aBc, def.ghi go ~~ERROR (Code: 33557097)~~ ~~ERROR (Message: syntax error near '.' at line 2 and character position 27)~~ -exec unqStr_proc_1 abc, def~ghi +exec unqStr_proc_1 aBc, def~ghi go ~~ERROR (Code: 33557097)~~ ~~ERROR (Message: syntax error near '~' at line 1 and character position 27)~~ + -- should print contents of variables: declare @myvar varchar(20), @myvar2 varchar(20) = 'this is myvar2' exec unqStr_proc_1 @myvar, @myvar2 @@ -158,27 +300,21 @@ varchar#!#varchar -- both before and after the fix of unquoted strings in BABEL-334 exec unqStr_proc_1 [@@language], [@@servicename] go -~~ERROR (Code: 33557097)~~ - -~~ERROR (Message: column "@@language" does not exist)~~ +~~START~~ +varchar#!#varchar +@@language#!#@@servicename +~~END~~ --- unlike T-SQL procedure calls, function call arguments do not support unquoted strings -create function unqStr_func_1(@par varchar(20)) -returns varchar(20) -as begin -return @par -end -go -- should return column value: -select dbo.unqStr_func_1(abc) from unqStr_tab +select dbo.unqStr_func_1(aBc) from unqStr_tab go ~~START~~ varchar this is column abc ~~END~~ -select dbo.unqStr_func_1([abc]) from unqStr_tab +select dbo.unqStr_func_1([aBc]) from unqStr_tab go ~~START~~ varchar @@ -202,7 +338,7 @@ varchar set quoted_identifier on go -select dbo.unqStr_func_1("abc") from unqStr_tab +select dbo.unqStr_func_1("aBc") from unqStr_tab go ~~START~~ varchar @@ -212,21 +348,21 @@ this is column abc set quoted_identifier off go -- now it's just a regular double-quoted string: -select dbo.unqStr_func_1("abc") from unqStr_tab +select dbo.unqStr_func_1("aBc") from unqStr_tab go ~~START~~ varchar -abc +aBc ~~END~~ -- should fail: -select dbo.unqStr_func_1(abc) +select dbo.unqStr_func_1(aBc) go ~~ERROR (Code: 33557097)~~ ~~ERROR (Message: column "abc" does not exist)~~ -select dbo.unqStr_func_1([abc]) +select dbo.unqStr_func_1([aBc]) go ~~ERROR (Code: 33557097)~~ @@ -235,7 +371,7 @@ go set quoted_identifier on go -- should fail: -select dbo.unqStr_func_1("abc") +select dbo.unqStr_func_1("aBc") go ~~ERROR (Code: 33557097)~~ @@ -244,32 +380,198 @@ go set quoted_identifier off go -- now it's just a regular double-quoted string: -select dbo.unqStr_func_1("abc") +select dbo.unqStr_func_1("aBc") go ~~START~~ varchar -abc +aBc ~~END~~ --- unquoted string in parameter declaration -create proc unqStr_proc_2 @par1 varchar(20) = abc -as -select @par1 -go --- should print 'abc': +-- should print 'aBc': exec unqStr_proc_2 go ~~START~~ varchar -abc +aBc +~~END~~ + + +set quoted_identifier on +go +exec unqStr_proc_2a +go +~~START~~ +varchar#!#varchar +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +~~END~~ + +set quoted_identifier off +go +exec unqStr_proc_2a +go +~~START~~ +varchar#!#varchar +a23456789B123456789C123456789d123456789E123456789F123456789G123456789H#!#AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +~~END~~ + + +set quoted_identifier on +go +exec unqStr_proc_2b +go +~~START~~ +varchar#!#varchar +Aaa aaB#!#Aa"bB'cC +~~END~~ + +set quoted_identifier off +go +exec unqStr_proc_2b +go +~~START~~ +varchar#!#varchar +Aaa aaB#!#Aa"bB'cC +~~END~~ + + +set quoted_identifier on +go +exec unqStr_proc_2c +go +~~START~~ +varchar#!#varchar +[Aaa aaB]#!#[Aa"bB'cC] +~~END~~ + +exec unqStr_proc_2c "[Xxx xxyZ]", "[Xx""yY'zZ]" +go +~~START~~ +varchar#!#varchar +[Xxx xxyZ]#!#[Xx"yY'zZ] +~~END~~ + +set quoted_identifier off +go +exec unqStr_proc_2c +go +~~START~~ +varchar#!#varchar +[Aaa aaB]#!#[Aa"bB'cC] +~~END~~ + +exec unqStr_proc_2c "[Xxx xxyZ]", "[Xx""yY'zZ]" +go +~~START~~ +varchar#!#varchar +[Xxx xxyZ]#!#[Xx"yY'zZ] +~~END~~ + + +set quoted_identifier on +go +exec unqStr_proc_2d +go +~~START~~ +varchar#!#varchar +[Aaa aaB]#!#[Aa"bB'cC] +~~END~~ + +exec unqStr_proc_2d "[Xxx xxyZ]", "[Xx""yY'zZ]" +go +~~START~~ +varchar#!#varchar +[Xxx xxyZ]#!#[Xx"yY'zZ] +~~END~~ + +set quoted_identifier off +go +exec unqStr_proc_2d +go +~~START~~ +varchar#!#varchar +[Aaa aaB]#!#[Aa"bB'cC] +~~END~~ + +exec unqStr_proc_2d "[Xxx xxyZ]", "[Xx""yY'zZ]" +go +~~START~~ +varchar#!#varchar +[Xxx xxyZ]#!#[Xx"yY'zZ] +~~END~~ + + +set quoted_identifier on +go +exec unqStr_proc_2e +go +~~START~~ +varchar#!#varchar +"Aaa aaB"#!#"Aa"bB'cC" +~~END~~ + +exec unqStr_proc_2e ["Xxx xxyZ"], ["Xx""yY'zZ"] +go +~~START~~ +varchar#!#varchar +"Xxx xxyZ"#!#"Xx""yY'zZ" +~~END~~ + +set quoted_identifier off +go +exec unqStr_proc_2e +go +~~START~~ +varchar#!#varchar +"Aaa aaB"#!#"Aa"bB'cC" +~~END~~ + +exec unqStr_proc_2e ["Xxx xxyZ"], ["Xx""yY'zZ"] +go +~~START~~ +varchar#!#varchar +"Xxx xxyZ"#!#"Xx""yY'zZ" +~~END~~ + + +-- empty bracketed identifier raises error in SQL Server, but succeeds in Babelfish, +-- both before and after the fix for BABEL-334 +exec unqStr_proc_2e [], 'x' +go +~~START~~ +varchar#!#varchar +#!#x +~~END~~ + + +-- double-quoted/bracketed datatype: +set quoted_identifier off +go +exec unqStr_proc_2g +go +~~START~~ +varchar +aBc +~~END~~ + +set quoted_identifier on +go +exec unqStr_proc_2h +go +~~START~~ +varchar +aBc ~~END~~ --- 'user' is handled as function call -create proc unqStr_proc_3 @par1 varchar(20) = user -as -select @par1 +exec unqStr_proc_2i +go +~~START~~ +varchar +aBc +~~END~~ + +set quoted_identifier off go -- should print 'dbo': exec unqStr_proc_3 @@ -280,15 +582,8 @@ dbo ~~END~~ -create function unqStr_func_2(@par1 varchar(20) = abc) -returns varchar(20) -as -begin -return @par1 -end -go -- should reflect column value: -select dbo.unqStr_func_2(abc) from unqStr_tab +select dbo.unqStr_func_2(aBc) from unqStr_tab go ~~START~~ varchar @@ -303,18 +598,18 @@ varchar dbo ~~END~~ --- should print 'abc': +-- should print 'aBc': declare @v varchar(20) execute @v = unqStr_func_2 select @v go ~~START~~ varchar -abc +aBc ~~END~~ -- this test will fail since 'default' arguments are not yet supported in Babelfish (BABEL-335) --- once supported, should print 'abc': +-- once supported, should print 'aBc': select dbo.unqStr_func_2(default) go ~~ERROR (Code: 33557097)~~ @@ -323,8 +618,6 @@ go -- should print value of @@language: -create procedure unqStr_proc_4 @p1 varchar(20) = @@language as select @p1 -go exec unqStr_proc_4 go ~~START~~ @@ -336,13 +629,6 @@ us_english -- as a default, @p2 should take value of @p1. -- however this does not work in Babelfish, both before and after -- the fix for unquoted strings in BABEL-334 -create procedure unqStr_proc_5 @p1 varchar(20), @p2 varchar(20) = @p1 -as select @p2 -go -~~ERROR (Code: 33557097)~~ - -~~ERROR (Message: column "@p1" does not exist)~~ - exec unqStr_proc_5 'this is @p1' go ~~ERROR (Code: 33557097)~~ @@ -350,23 +636,13 @@ go ~~ERROR (Message: procedure unqstr_proc_5(unknown) does not exist)~~ --- should raise error: -create procedure unqStr_proc_6 @p0 varchar(20) = @@myvar -as select @p1 +exec unqStr_proc_7 go -~~ERROR (Code: 33557097)~~ - -~~ERROR (Message: function sys.myvar() does not exist)~~ - +~~START~~ +varchar#!#varchar#!#varchar +aB"c'D#!#dEfg#!#x'Y"z +~~END~~ --- variable as column default is invalid syntax in SQL Server, but works in Babelfish, which is harmless --- in Babelfish this only works for a global (@@) variable, not for a local variable -create function unqStr_func_3 (@p1 varchar(20) = @@language) returns varchar(20) -as -begin -return @p1 -end -go -- this will not work until 'default' arguments are supported (BABEL-335) select dbo.unqStr_func_3(default) @@ -387,47 +663,28 @@ us_english ~~END~~ --- invalid in SQL Server, should also fail in Babelfish: -create function unqStr_func_4 (@p1 varchar(20), @p2 varchar(20) = @p1) returns varchar(20) -as -begin -return @p2 -end -go -~~ERROR (Code: 33557097)~~ - -~~ERROR (Message: column "@p1" does not exist)~~ - - -- psql + -- PG test cases -- call T-SQL proc from PG, T-SQL semantics still apply --- should print 'abc': +-- should print 'aBc': -- NOTE: test is disabled as it crashes the JDBC test framework with message 'Received resultset tuples, but no field structure for them' --call master_dbo.unqStr_proc_2(); --go -- PG procedure/function calls should not be affected by the T-SQL fix -- for unquoted strings in BABEL-334 -CREATE OR REPLACE PROCEDURE unqStr_pgproc_1(par1 varchar(20)) -LANGUAGE plpgsql -AS $$ -begin -raise notice 'arg=[%]', par1; -end -$$; -go --- should print 'abc': -CALL unqStr_pgproc_1('abc'); +-- should print 'aBc': +CALL unqStr_pgproc_1('aBc'); go ~~WARNING (Code: 0)~~ -~~WARNING (Message: arg=[abc] Server SQLState: 00000)~~ +~~WARNING (Message: arg=[aBc] Server SQLState: 00000)~~ -- should raise an error about column not found: -CALL unqStr_pgproc_1(abc); +CALL unqStr_pgproc_1(aBc); go ~~ERROR (Code: 0)~~ @@ -436,35 +693,24 @@ go Server SQLState: 42703)~~ -CREATE function unqStr_pgfunc_1(par1 name) -returns varchar(20) -LANGUAGE plpgsql -AS $$ -begin -return par1; -end -$$; -go --- should print 'abc': -select unqStr_pgfunc_1('abc'); +-- should print 'aBc': +select unqStr_pgfunc_1('aBc'); go ~~START~~ varchar -abc +aBc ~~END~~ -- should print column value: -select unqStr_pgfunc_1(abc) from master_dbo.unqStr_tab; +select unqStr_pgfunc_1(aBc) from master_dbo.unqStr_tab; go -~~ERROR (Code: 0)~~ - -~~ERROR (Message: ERROR: function unqstr_pgfunc_1(sys."varchar") does not exist - Hint: No function matches the given name and argument types. You might need to add explicit type casts. - Position: 38 - Server SQLState: 42883)~~ +~~START~~ +varchar +this is column abc +~~END~~ -- should raise an error about column not found: -select unqStr_pgfunc_1(abc); +select unqStr_pgfunc_1(aBc); go ~~ERROR (Code: 0)~~ @@ -475,42 +721,10 @@ go -- should print 'jdbc_user', since 'user' is a function call: select unqStr_pgfunc_1(user); go -~~START~~ -varchar -jdbc_user -~~END~~ - - --- should fail since PG does not support unquoted string defaults: -CREATE OR REPLACE PROCEDURE unqStr_pgproc_2(par1 varchar(20) = abc) -LANGUAGE plpgsql -AS $$ -begin -raise notice 'arg=[%]', par1; -end -$$; -go -~~ERROR (Code: 0)~~ - -~~ERROR (Message: ERROR: column "abc" does not exist - Position: 131 - Server SQLState: 42703)~~ - - --- should fail since PG does not support unquoted string defaults: -CREATE function unqStr_pgfunc_2(par1 varchar(20) = abc) -returns varchar(20) -LANGUAGE plpgsql -AS $$ -begin -return par1; -end -$$; -go ~~ERROR (Code: 0)~~ -~~ERROR (Message: ERROR: column "abc" does not exist - Position: 119 - Server SQLState: 42703)~~ - +~~ERROR (Message: ERROR: function unqstr_pgfunc_1(name) does not exist + Hint: No function matches the given name and argument types. You might need to add explicit type casts. + Position: 70 + Server SQLState: 42883)~~ diff --git a/test/JDBC/input/BABEL-4484-vu-cleanup.sql b/test/JDBC/input/BABEL-4484-vu-cleanup.sql new file mode 100644 index 0000000000..01ba0a9be5 --- /dev/null +++ b/test/JDBC/input/BABEL-4484-vu-cleanup.sql @@ -0,0 +1,8 @@ +DROP TABLE test_babel_4484_t1; +GO + +DROP TABLE test_babel_4484_t2; +GO + +DROP TABLE test_babel_4484_t3; +GO diff --git a/test/JDBC/input/BABEL-4484-vu-prepare.sql b/test/JDBC/input/BABEL-4484-vu-prepare.sql new file mode 100644 index 0000000000..81bcc1e47b --- /dev/null +++ b/test/JDBC/input/BABEL-4484-vu-prepare.sql @@ -0,0 +1,8 @@ +CREATE TABLE test_babel_4484_t1(ABC int, ced varchar(10)); +GO + +CREATE TABLE test_babel_4484_t2(ABC int, 您您 varchar(10)); +GO + +CREATE TABLE test_babel_4484_t3(ABC int, 您您对您对您对您对您对您对您对您对您对您您您 int); +GO diff --git a/test/JDBC/input/BABEL-4484-vu-verify.sql b/test/JDBC/input/BABEL-4484-vu-verify.sql new file mode 100644 index 0000000000..5c5f6a2e63 --- /dev/null +++ b/test/JDBC/input/BABEL-4484-vu-verify.sql @@ -0,0 +1,11 @@ +SELECT set_config('babelfishpg_tsql.enable_sll_parse_mode', 'true', false); +GO + +DELETE test_babel_4484_t1 OUTPUT test_babel_4484_t1.ced FROM test_babel_4484_t1 INNER JOIN test_babel_4484_t2 ON test_babel_4484_t1.ABC = test_babel_4484_t2.ABC WHERE test_babel_4484_t1.ABC = 1; +GO + +DELETE test_babel_4484_t2 OUTPUT test_babel_4484_t2.您您 FROM test_babel_4484_t2 INNER JOIN test_babel_4484_t3 ON test_babel_4484_t2.ABC = test_babel_4484_t3.ABC WHERE test_babel_4484_t2.ABC = 1; +GO + +SELECT test_babel_4484_t1.ced FROM test_babel_4484_t1 INNER JOIN test_babel_4484_t2 ON test_babel_4484_t1.ABC = test_babel_4484_t2.ABC WHERE test_babel_4484_t1.ABC = 1; +GO diff --git a/test/JDBC/input/babel_4299.sql b/test/JDBC/input/babel_4299.sql new file mode 100644 index 0000000000..f295fde3d7 --- /dev/null +++ b/test/JDBC/input/babel_4299.sql @@ -0,0 +1,149 @@ +select ''+'ps'; +GO + +select 'rs' + ''; +GO + +select '' + ''; +GO + +select '' + NULL; +GO + +select NULL + ''; +GO + +set quoted_identifier off +GO +select ""+'s'; +GO + +set quoted_identifier off +GO +select 'rs' + ""; +GO + +set quoted_identifier off +GO +select '' + ""; +GO + +set quoted_identifier off +GO +select "" + NULL; +GO + +set quoted_identifier off +GO +select NULL + ""; +GO + +set quoted_identifier off +GO +select ""+"s"; +GO + +set quoted_identifier off +GO +select "pr" + ""; +GO + +set quoted_identifier off +GO +select "" + ""; +GO + +set quoted_identifier off +GO +select "" + ""; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT 'Hello' + NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT 'Hello' + NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + ''; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + ''; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT 'Hello' + NULL; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT 'Hello' + NULL; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT "" + NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT "" + NULL + 'World'; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT "" + NULL + ""; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT "" + NULL + ""; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + "World"; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + "World"; +GO + +SET CONCAT_NULL_YIELDS_NULL ON; +GO +SELECT '' + NULL + ""; +GO + +SET CONCAT_NULL_YIELDS_NULL OFF; +GO +SELECT '' + NULL + ""; +GO \ No newline at end of file diff --git a/test/JDBC/input/doublequoted_string-vu-verify.sql b/test/JDBC/input/doublequoted_string-vu-verify.sql index 6c36844545..9ecad89dc4 100644 --- a/test/JDBC/input/doublequoted_string-vu-verify.sql +++ b/test/JDBC/input/doublequoted_string-vu-verify.sql @@ -1,3 +1,9 @@ +-- BABEL-2442: Handle embedded double quotes in a double-quoted string +-- BABEL-4387: Support double-quoted strings containing single-quote +-- This also exercises parts of the ANTLR parse tree rewriting + +set quoted_identifier off +go create procedure dubquote_p @p varchar(20) = "ab'cd" , @p2 varchar(20)='xyz' as select @p go @@ -7,32 +13,32 @@ go set quoted_identifier off go -select "abc" +select "aBc" go exec dubquote_p go exec dubquote_p2 go -exec dubquote_p "abc" +exec dubquote_p "aBc" go -exec dubquote_p 'abc' +exec dubquote_p 'aBc' go -exec dubquote_p abc +exec dubquote_p aBc go set quoted_identifier on go -select "abc" +select "aBc" go exec dubquote_p go exec dubquote_p2 go -exec dubquote_p "abc" +exec dubquote_p "aBc" go -exec dubquote_p 'abc' +exec dubquote_p 'aBc' go -exec dubquote_p abc +exec dubquote_p aBc go set quoted_identifier off @@ -70,7 +76,7 @@ exec dubquote_p go exec dubquote_p "xx'yy" go -exec dubquote_p 'xx"yy' +exec dubquote_p 'xX"yY' go exec dubquote_p """" go @@ -90,7 +96,7 @@ go -- same as above but with named notation exec dubquote_p @p="xx'yy" , @p2='x"y' go -exec dubquote_p @p='xx"yy' , @p2="x""y" +exec dubquote_p @p='xX"yY' , @p2="x""y" go exec dubquote_p @p="""" , @p2="x'y" go @@ -108,7 +114,7 @@ exec dubquote_p @p="""'""'""" , @p2="x''y" go -- using N'...' notation: -exec dubquote_p N'xx"yy' +exec dubquote_p N'xX"yY' go exec dubquote_p N'''' go @@ -116,7 +122,7 @@ exec dubquote_p N'"' go exec dubquote_p N'""' go -exec dubquote_p @p=N'xx"yy' +exec dubquote_p @p=N'xX"yY' go exec dubquote_p @p=N'''' go @@ -132,7 +138,7 @@ create function dubquote_f1(@p varchar(20) = "ab'cd") returns varchar(20) as beg go create function dubquote_f2(@p varchar(20) = "ab""cd") returns varchar(20) as begin return @p end go -create function dubquote_f3(@p varchar(20) = abcd) returns varchar(20) as begin return @p end +create function dubquote_f3(@p varchar(20) = aBcd) returns varchar(20) as begin return @p end go declare @v varchar(20) exec @v = dubquote_f1 @@ -169,37 +175,37 @@ go set quoted_identifier off go -create procedure dubquote_p2a @p varchar(20) ="abc" as select @p +create procedure dubquote_p2a @p varchar(20) ="aBc" as select @p go exec dubquote_p2a go -create procedure dubquote_p3 @p varchar(20) ="'abc'" as select @p +create procedure dubquote_p3 @p varchar(20) ="'aBc'" as select @p go exec dubquote_p3 go declare @v varchar(40) set @v = "It's almost ""weekend""!" select @v go -select 'abc' +select 'aBc' go -select "abc" +select "aBc" go select "a'b""c''''''''''d" go select "a'b""c'd" go -select "'abc'",'xyz' +select "'aBc'",'xyz' go -declare @v varchar(20) = 'abc' select @v +declare @v varchar(20) = 'aBc' select @v go -declare @v varchar(20) = "abc" select @v +declare @v varchar(20) = "aBc" select @v go declare @v varchar(20) = "'a""bc'" select @v go -declare @v varchar(20) select @v = "abc" select @v +declare @v varchar(20) select @v = "aBc" select @v go -declare @v varchar(20) = 'x' select @v += "abc" select @v +declare @v varchar(20) = 'x' select @v += "aBc" select @v go declare @v varchar(20) select @v = "'a""bc'" select @v go @@ -210,7 +216,7 @@ go declare @v varchar(20) = 'x' set @v += "'a""bc'" select @v go -declare @v varchar(20) ="abc" , @v2 varchar(10) = 'xyz' select @v +declare @v varchar(20) ="aBc" , @v2 varchar(10) = 'xyz' select @v go declare @v varchar(20), @v2 varchar(20) select @v="a""b''c'd", @v2="x""y''z" select @v, @v2 go @@ -233,17 +239,17 @@ go set quoted_identifier off go -- the JDBC test cases do not capture PRINT output, but including them here for when it will -print "abc" +print "aBc" go -print "'abc'" +print "'aBc'" go print "a""b'c" go print "a""b'c," + session_user + ",d""e'f," + system_user go - /*test*/ print "abc" + /*test*/ print "aBc" go - /*hello*/ print /*hello*/ "abc" + /*hello*/ print /*hello*/ "aBc" go print /*hello*/ "a""b'c," + /*hello*/ session_user + /*hello*/ @@ -259,7 +265,7 @@ go go -- RAISERROR arguments are not yet rewritten. this should raise an error -RAISERROR ('%s %s', 10, 1, 'abc', "def"); +RAISERROR ('%s %s', 10, 1, 'aBc', "def"); go @@ -291,7 +297,7 @@ go set quoted_identifier on go -print "abc" +print "aBc" go RAISERROR("Message from RAISERROR", 16,1) go @@ -302,7 +308,7 @@ create procedure dubquote_p4 @p varchar(20) ="a'bc" as select @p,@p go exec dubquote_p4 go -exec dubquote_p4 "abc" +exec dubquote_p4 "aBc" go exec dubquote_p4 "ab""cd" go @@ -310,7 +316,7 @@ exec dubquote_p4 "ab'cd" go select "ab'cd" go -create function dubquote_f4 (@p varchar(20) = "'abc'") returns varchar(50) as begin return ((("function's return" +( " string value:" ))) +"'" + @p + "'") end +create function dubquote_f4 (@p varchar(20) = "'aBc'") returns varchar(50) as begin return ((("function's return" +( " string value:" ))) +"'" + @p + "'") end go select dbo.dubquote_f4("x") go @@ -327,18 +333,18 @@ go CREATE function dubquote_f7() returns varchar(30) as begin return system_user end go -select select dbo.dubquote_f7(), system_user +select dbo.dubquote_f7(), system_user go create procedure dubquote_p5 @p varchar(10) as select @p go -exec dubquote_p5 'xyz' exec dubquote_p5 abc +exec dubquote_p5 'xyz' exec dubquote_p5 aBc go -exec dubquote_p5 abcd +exec dubquote_p5 aBcd go -exec dubquote_p5 [abcd] +exec dubquote_p5 [aBcd] go -exec dubquote_p5 @p=abcde +exec dubquote_p5 @p=aBcde go declare @v varchar(20) exec dubquote_p5 @v go @@ -351,26 +357,26 @@ go declare @v varchar(20) = session_user select @v, session_user go -declare @v varchar(20) = 'abc' + session_user select @v, session_user +declare @v varchar(20) = 'aBc' + session_user select @v, session_user go -declare @v varchar(20) = "abc" + session_user select @v, session_user +declare @v varchar(20) = "aBc" + session_user select @v, session_user go declare @v varchar(20) = "ab""c'd" + session_user select @v, session_user go declare @v varchar(20) = system_user select @v, system_user go -declare @v varchar(20) = 'abc' + system_user select @v, system_user +declare @v varchar(20) = 'aBc' + system_user select @v, system_user go -declare @v varchar(20) = "abc" + system_user select @v, system_user +declare @v varchar(20) = "aBc" + system_user select @v, system_user go declare @v varchar(20) = "ab""c'd" + system_user select @v, system_user go declare @v varchar(20) = '' set @v = system_user select @v, system_user go -declare @v varchar(20) = '' set @v = 'abc' + system_user select @v, system_user +declare @v varchar(20) = '' set @v = 'aBc' + system_user select @v, system_user go -declare @v varchar(20) = '' set @v = "abc" + system_user select @v, system_user +declare @v varchar(20) = '' set @v = "aBc" + system_user select @v, system_user go declare @v varchar(20) = '' set @v = "ab""c'd" + system_user select @v, system_user go @@ -391,7 +397,7 @@ go -- all in one batch: declare @v varchar(20) = session_user select @v -declare @v1 varchar(20) = 'abc' + session_user select @v1 +declare @v1 varchar(20) = 'aBc' + session_user select @v1 declare @v2 varchar(20) = "ab""c'd" + session_user select @v2 declare @v3 varchar(20) ="a""bc" , @v4 varchar(20) = 'x''z' select @v3,@v4 declare @v5 varchar(20) ="a""bc" , @v6 varchar(20) = 'x''z' , @v7 varchar(20) = "x""y'z'z" select @v5, @v6, @v7 @@ -399,9 +405,9 @@ go declare @v varchar(20) = session_user, @v2 varchar(20)= system_user select @v, @v2, session_user, system_user go -declare @v varchar(20) = 'abcd' + session_user, @v2 varchar(20) = 'xy' + session_user select @v, @v2, session_user +declare @v varchar(20) = 'aBcd' + session_user, @v2 varchar(20) = 'xy' + session_user select @v, @v2, session_user go -declare @v varchar(20) = 'abcd' + upper('x'), @v2 varchar(20) = 'xy' + upper('y') select @v, @v2 +declare @v varchar(20) = 'aBcd' + upper('x'), @v2 varchar(20) = 'xy' + upper('y') select @v, @v2 go declare @v varchar(20) = session_user, @v2 varchar(20)= system_user select @v,@v2,session_user, system_user go @@ -444,24 +450,123 @@ go declare @v int = 1 set @v /= len("a'bc") + next value for dubquote_myseq + len(system_user) select @v go - set quoted_identifier on go -create procedure dubquote_p6 @p varchar(20) ="abc" as select @p +create procedure dubquote_p6 @p varchar(20) ="aBc" as select @p go exec dubquote_p6 go -create procedure dubquote_p7 @p varchar(20) ="'abc'" as select @p +create procedure dubquote_p7 @p varchar(20) ="'aBc'" as select @p go exec dubquote_p7 go -declare @v varchar(20) = 'abc' select @v +declare @v varchar(20) = 'aBc' select @v go +set quoted_identifier off +go +create procedure dubquote_p8 @p varchar(20) as select @p +go +execute dubquote_p8 "x'Y""z" +go +exec dubquote_p8 "x'Y""z" +go +execute[dubquote_p8]"x'Y""z" +go +exec[dubquote_p8]"x'Y""z" +go +exec ..[dubquote_p8]"x'Y""z" +go +dubquote_p8 "x'Y""z" +go +dbo.dubquote_p8 "x'Y""z" +go +.dubquote_p8 "x'Y""z" +go +..dubquote_p8 "x'Y""z" +go +[dubquote_p8]"x'Y""z" +go +/*test*/execute dubquote_p8 "x'Y""z" +go +/*test*/exec dubquote_p8 "x'Y""z" +go +/*test*/execute[dubquote_p8]"x'Y""z" +go +/*test*/exec[dubquote_p8]/*test*/"x'Y""z" +go +/*test*/dubquote_p8 "x'Y""z" +go +/*test*/dubquote_p8 "x'Y""z" +go +/*test*/.dubquote_p8 "x'Y""z" +go +/*test*/..dubquote_p8 "x'Y""z" +go +/*test*/[dubquote_p8]/*test*/"x'Y""z" +go +/*test*/.[dubquote_p8]/*test*/"x'Y""z" +go +/*test*/..[dubquote_p8]/*test*/"x'Y""z" +execute dubquote_p8 "a'B""C" +go + +set quoted_identifier on +go +execute dubquote_p8 "x'Y""z" +go +exec dubquote_p8 "x'Y""z" +go +execute[dubquote_p8]"x'Y""z" +go +exec[dubquote_p8]"x'Y""z" +go +exec ..[dubquote_p8]"x'Y""z" +go +dubquote_p8 "x'Y""z" +go +dbo.dubquote_p8 "x'Y""z" +go +.dubquote_p8 "x'Y""z" +go +..dubquote_p8 "x'Y""z" +go +[dubquote_p8]"x'Y""z" +go +/*test*/execute dubquote_p8 "x'Y""z" +go +/*test*/exec dubquote_p8 "x'Y""z" +go +/*test*/execute[dubquote_p8]"x'Y""z" +go +/*test*/exec[dubquote_p8]/*test*/"x'Y""z" +go +/*test*/dubquote_p8 "x'Y""z" +go +/*test*/.dubquote_p8 "x'Y""z" +go +/*test*/..dubquote_p8 "x'Y""z" +go +/*test*/[dubquote_p8]/*test*/"x'Y""z" +go +/*test*/.[dubquote_p8]/*test*/"x'Y""z" +go +/*test*/..[dubquote_p8]/*test*/"x'Y""z" +go +"dubquote_p8" "x'Y""z" +go +/*test*/"dubquote_p8"/*test*/"x'Y""z" +go +/*test*/"dubquote_p8"/*test*/"x'Y""z" +execute dubquote_p8 "a'B""C" +go + +set quoted_identifier on +go -- negative tests -declare @v varchar(20) = "abc" select @v +declare @v varchar(20) = "aBc" select @v go -declare @v varchar(20) = "'abc'" select @v +declare @v varchar(20) = "'aBc'" select @v go @@ -503,6 +608,8 @@ go drop procedure dubquote_p6 go drop procedure dubquote_p7 +go +drop procedure dubquote_p8 go drop table dubquote_t1 go diff --git a/test/JDBC/input/sp_proc.sql b/test/JDBC/input/sp_proc.sql index f80169c862..997b5e8411 100644 --- a/test/JDBC/input/sp_proc.sql +++ b/test/JDBC/input/sp_proc.sql @@ -7,6 +7,12 @@ go exec dbo.sp_hello go +exec master.dbo.sp_hello +go + +exec master..sp_hello +go + create database db1 go @@ -16,10 +22,10 @@ go exec master.dbo.sp_hello go -exec sp_hello +exec master..sp_hello go -sp_hello +exec sp_hello go exec .sp_hello @@ -31,9 +37,30 @@ go exec dbo.sp_hello go +exec .dbo.sp_hello +go + +exec ..dbo.sp_hello +go + +sp_hello +go + +.sp_hello +go + +..sp_hello +go + dbo.sp_hello go +.dbo.sp_hello +go + +..dbo.sp_hello +go + create proc call_sp_helllo as exec sp_hello go diff --git a/test/JDBC/input/unquoted_string-vu-cleanup.mix b/test/JDBC/input/unquoted_string-vu-cleanup.mix index 8b0f00e319..d799da0abd 100644 --- a/test/JDBC/input/unquoted_string-vu-cleanup.mix +++ b/test/JDBC/input/unquoted_string-vu-cleanup.mix @@ -3,16 +3,48 @@ drop procedure unqStr_proc_1 go drop procedure unqStr_proc_2 go +drop procedure unqStr_proc_2a +go +drop procedure unqStr_proc_2b +go +drop procedure unqStr_proc_2c +go +drop procedure unqStr_proc_2d +go +drop procedure unqStr_proc_2e +go +-- this should fail as the proc was not created +drop procedure unqStr_proc_2f +go +drop procedure unqStr_proc_2g +go +drop procedure unqStr_proc_2h +go +drop procedure unqStr_proc_2i +go drop procedure unqStr_proc_3 go drop procedure unqStr_proc_4 go +-- this should fail as the proc was not created +drop procedure unqStr_proc_5 +go +-- this should fail as the proc was not created +drop procedure unqStr_proc_6 +go +drop procedure unqStr_proc_7 +go drop function unqStr_func_1 go drop function unqStr_func_2 go drop function unqStr_func_3 go +-- this should fail as the function was not created +drop function unqStr_func_4 +go +drop function unqStr_func_5 +go drop table unqStr_tab go diff --git a/test/JDBC/input/unquoted_string-vu-prepare.mix b/test/JDBC/input/unquoted_string-vu-prepare.mix index 0ca0f2cba1..62d7b5a6f8 100644 --- a/test/JDBC/input/unquoted_string-vu-prepare.mix +++ b/test/JDBC/input/unquoted_string-vu-prepare.mix @@ -3,3 +3,194 @@ create table unqStr_tab(abc varchar(20)) insert into unqStr_tab values('this is column abc') go +-- unquoted strings in procedure call arguments +create procedure unqStr_proc_1(@par1 varchar(100), @par2 varchar(100)) +as +select @par1, @par2 +go + +-- unlike T-SQL procedure calls, function call arguments do not support unquoted strings +create function unqStr_func_1(@par varchar(20)) +returns varchar(20) +as begin +return @par +end +go + +-- unquoted string in parameter declaration +create proc unqStr_proc_2 @par1 varchar(20) = aBc +as +select @par1 +go + +-- strings longer than 63 should not be truncated, hashed or downcased +create procedure unqStr_proc_2a( +@par1 varchar(100) = a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, +@par2 varchar(100) = [AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB]) +as +select @par1, @par2 +go + +create procedure unqStr_proc_2b( +@par1 varchar(20) = [Aaa aaB], +@par2 varchar(20) = [Aa"bB'cC]) +as +select @par1, @par2 +go + +set quoted_identifier off +go +create procedure unqStr_proc_2c( +@par1 varchar(20) = "[Aaa aaB]", +@par2 varchar(20) = "[Aa""bB'cC]") +as +select @par1, @par2 +go +set quoted_identifier on +go + +set quoted_identifier on +go +create procedure unqStr_proc_2d( +@par1 varchar(20) = "[Aaa aaB]", +@par2 varchar(20) = "[Aa""bB'cC]") +as +select @par1, @par2 +go +set quoted_identifier off +go +create procedure unqStr_proc_2e( +@par1 varchar(20) = ["Aaa aaB"], +@par2 varchar(20) = ["Aa"bB'cC"]) +as +select @par1, @par2 +go +set quoted_identifier on +go + +-- double-quoted/bracketed datatype: +set quoted_identifier off +go +-- should raise error +create procedure unqStr_proc_2f(@par1 "varchar"(20) = aBc) +as +select @par1 +go +create procedure unqStr_proc_2g(@par1 [varchar](20) = aBc) +as +select @par1 +go + +set quoted_identifier on +go +create procedure unqStr_proc_2h(@par1 "varchar"(20) = aBc) +as +select @par1 +go + +create procedure unqStr_proc_2i(@par1 [varchar](20) = aBc) +as +select @par1 +go + +set quoted_identifier off +go + +-- 'user' is handled as function call in Babelfish, though it fails in SQL Server +create proc unqStr_proc_3 @par1 varchar(20) = user +as +select @par1 +go + +create procedure unqStr_proc_4 @p1 varchar(20) = @@language as select @p1 +go + +create procedure unqStr_proc_5 @p1 varchar(20), @p2 varchar(20) = @p1 +as select @p2 +go + +-- should raise error: +create procedure unqStr_proc_6 @p1 varchar(20) = @@myvar +as select @p1 +go + +create procedure unqStr_proc_7 @p1 varchar(20) = N'aB"c''D', @p2 varchar(20) = dEfg, @p3 varchar(20) = "x'Y""z" +as select @p1, @p2, @p3 +go + +create function unqStr_func_2(@par1 varchar(20) = aBc) +returns varchar(20) +as +begin +return @par1 +end +go + +-- invalid in SQL Server, should also fail in Babelfish: +create function unqStr_func_4 (@p1 varchar(20), @p2 varchar(20) = @p1) returns varchar(20) +as +begin +return @p2 +end +go + +create function unqStr_func_5 (@p1 varchar(20) = N'aB"c''D', @p2 varchar(20) = dEfg, @p3 varchar(20) = "x'Y""z") +returns varchar(60) +as +begin + return @p1 + @p2 + @p3 +end +go +select dbo.unqStr_func_5() +go + +-- variable as column default is invalid syntax in SQL Server, but works in Babelfish, which is harmless +-- in Babelfish this only works for a global (@@) variable, not for a local variable +create function unqStr_func_3 (@p1 varchar(20) = @@language) returns varchar(20) +as +begin +return @p1 +end +go + +-- psql + +CREATE OR REPLACE PROCEDURE unqStr_pgproc_1(par1 varchar(20)) +LANGUAGE plpgsql +AS $$ +begin +raise notice 'arg=[%]', par1; +end +$$; +go + +CREATE function unqStr_pgfunc_1(par1 varchar(20)) +returns varchar(20) +LANGUAGE plpgsql +AS $$ +begin +return par1; +end +$$; +go + +-- should fail since PG does not support unquoted string defaults: +CREATE OR REPLACE PROCEDURE unqStr_pgproc_2(par1 varchar(20) = aBc) +LANGUAGE plpgsql +AS $$ +begin +raise notice 'arg=[%]', par1; +end +$$; +go + +-- should fail since PG does not support unquoted string defaults: +CREATE function unqStr_pgfunc_2(par1 varchar(20) = aBc) +returns varchar(20) +LANGUAGE plpgsql +AS $$ +begin +return par1; +end +$$; +go diff --git a/test/JDBC/input/unquoted_string-vu-verify.mix b/test/JDBC/input/unquoted_string-vu-verify.mix index f73a72efe7..7571fc77f4 100644 --- a/test/JDBC/input/unquoted_string-vu-verify.mix +++ b/test/JDBC/input/unquoted_string-vu-verify.mix @@ -1,49 +1,96 @@ -- BABEL-334: Support unquoted string parameter values in procedure calls/declarations +-- This also exercises parts of the ANTLR parse tree rewriting -- tsql --- unquoted strings in procedure call arguments -create procedure unqStr_proc_1(@par1 varchar(20), @par2 varchar(20)) -as -select @par1, @par2 -go -- should print strings: -exec unqStr_proc_1 abc, def$ghi +exec unqStr_proc_1 aBc, def$ghi go -exec unqStr_proc_1 abc, 'this is a string' +exec unqStr_proc_1 aBc, 'this is a string' go exec unqStr_proc_1 'this is a string', def go -exec unqStr_proc_1 abc#def, null +exec unqStr_proc_1 aBc#def, null +go +exec unqStr_proc_1 @par1=aBc$def, @par2=null +go +execute unqStr_proc_1 @par1=aBc$def, @par2=null go -exec unqStr_proc_1 @par1=abc$def, @par2=null +exec unqStr_proc_1 @par1=null, @par2=aBc#def go -exec unqStr_proc_1 @par1=null, @par2=abc#def +execute unqStr_proc_1 @par1=null, @par2=aBc#def go -- should print strings, bracketed identifiers are also interpreted as unquoted strings in SQL Server: -exec unqStr_proc_1 [abc], [def$ghi] +exec unqStr_proc_1 [aBc], [def$ghi] go + -- should print strings, bracketed identifiers as procedure args are also interpreted as unquoted strings in SQL Server: set quoted_identifier on go -exec unqStr_proc_1 "abc", "def$ghi" +exec unqStr_proc_1 "aBc", "def$ghi" +go +execute unqStr_proc_1 "aBc", "def$ghi" +go +execute unqStr_proc_1 @par1 = "aBc", @par2 = "def$ghi" +go +-- strings longer than 63 should not be truncated, hashed or downcased +exec unqStr_proc_1 a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go +execute unqStr_proc_1 @par1 = a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, @par2=AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go +execute unqStr_proc_1 @par1 = "a23456789B123456789C123456789d123456789E123456789F123456789G123456789H", @par2="AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB" +go + + +-- without EXEC keyword: +unqStr_proc_1 aBc, def$ghi +go +unqStr_proc_1 aBc, 'this is a string' +go +unqStr_proc_1 'this is a string', def +go +unqStr_proc_1 aBc#def, null +go +unqStr_proc_1 @par1=aBc$def, @par2=null +go +unqStr_proc_1 @par1=null, @par2=aBc#def +go +/*test*/unqStr_proc_1 aBc, def$ghi +go +/*test*/ +unqStr_proc_1/*test*/ +aBc, +'this is a string' +go +-- should print strings, bracketed identifiers are also interpreted as unquoted strings in SQL Server: +unqStr_proc_1 [aBc], [def$ghi] +go +-- strings longer than 63 should not be truncated, hashed or downcased +unqStr_proc_1 a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go + unqStr_proc_1 @par1 = a23456789B123456789C123456789d123456789E123456789F123456789G123456789H, @par2=AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB +go + unqStr_proc_1 @par1 = "a23456789B123456789C123456789d123456789E123456789F123456789G123456789H", @par2="AaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaB" go + + set quoted_identifier off go -- now they are just regular double-quoted strings: -exec unqStr_proc_1 "abc abc", "def ?! ghi" +exec unqStr_proc_1 "aBc aBc", "def ?! ghi" go -- should raise error since 'user' is a function and not allowed as argument -exec unqStr_proc_1 user, 'abc' +exec unqStr_proc_1 user, 'aBc' go -exec unqStr_proc_1 @par1=user, @par2='abc' +exec unqStr_proc_1 @par1=user, @par2='aBc' go -- should raise error because the unquoted argument cannot be interpreted as a string: -exec unqStr_proc_1 abc, def.ghi +exec unqStr_proc_1 aBc, def.ghi go -exec unqStr_proc_1 abc, def~ghi +exec unqStr_proc_1 aBc, def~ghi go + -- should print contents of variables: declare @myvar varchar(20), @myvar2 varchar(20) = 'this is myvar2' exec unqStr_proc_1 @myvar, @myvar2 @@ -68,17 +115,10 @@ go exec unqStr_proc_1 [@@language], [@@servicename] go --- unlike T-SQL procedure calls, function call arguments do not support unquoted strings -create function unqStr_func_1(@par varchar(20)) -returns varchar(20) -as begin -return @par -end -go -- should return column value: -select dbo.unqStr_func_1(abc) from unqStr_tab +select dbo.unqStr_func_1(aBc) from unqStr_tab go -select dbo.unqStr_func_1([abc]) from unqStr_tab +select dbo.unqStr_func_1([aBc]) from unqStr_tab go -- should return NULL select dbo.unqStr_func_1(null) from unqStr_tab @@ -87,97 +127,140 @@ select dbo.unqStr_func_1(null) go set quoted_identifier on go -select dbo.unqStr_func_1("abc") from unqStr_tab +select dbo.unqStr_func_1("aBc") from unqStr_tab go set quoted_identifier off go -- now it's just a regular double-quoted string: -select dbo.unqStr_func_1("abc") from unqStr_tab +select dbo.unqStr_func_1("aBc") from unqStr_tab go -- should fail: -select dbo.unqStr_func_1(abc) +select dbo.unqStr_func_1(aBc) go -select dbo.unqStr_func_1([abc]) +select dbo.unqStr_func_1([aBc]) go set quoted_identifier on go -- should fail: -select dbo.unqStr_func_1("abc") +select dbo.unqStr_func_1("aBc") go set quoted_identifier off go -- now it's just a regular double-quoted string: -select dbo.unqStr_func_1("abc") +select dbo.unqStr_func_1("aBc") go --- unquoted string in parameter declaration -create proc unqStr_proc_2 @par1 varchar(20) = abc -as -select @par1 -go --- should print 'abc': +-- should print 'aBc': exec unqStr_proc_2 go --- 'user' is handled as function call -create proc unqStr_proc_3 @par1 varchar(20) = user -as -select @par1 +set quoted_identifier on +go +exec unqStr_proc_2a +go +set quoted_identifier off +go +exec unqStr_proc_2a +go + +set quoted_identifier on +go +exec unqStr_proc_2b +go +set quoted_identifier off +go +exec unqStr_proc_2b +go + +set quoted_identifier on +go +exec unqStr_proc_2c +go +exec unqStr_proc_2c "[Xxx xxyZ]", "[Xx""yY'zZ]" +go +set quoted_identifier off +go +exec unqStr_proc_2c +go +exec unqStr_proc_2c "[Xxx xxyZ]", "[Xx""yY'zZ]" +go + +set quoted_identifier on +go +exec unqStr_proc_2d +go +exec unqStr_proc_2d "[Xxx xxyZ]", "[Xx""yY'zZ]" +go +set quoted_identifier off +go +exec unqStr_proc_2d +go +exec unqStr_proc_2d "[Xxx xxyZ]", "[Xx""yY'zZ]" +go + +set quoted_identifier on +go +exec unqStr_proc_2e +go +exec unqStr_proc_2e ["Xxx xxyZ"], ["Xx""yY'zZ"] +go +set quoted_identifier off +go +exec unqStr_proc_2e +go +exec unqStr_proc_2e ["Xxx xxyZ"], ["Xx""yY'zZ"] +go + +-- empty bracketed identifier raises error in SQL Server, but succeeds in Babelfish, +-- both before and after the fix for BABEL-334 +exec unqStr_proc_2e [], 'x' +go + +-- double-quoted/bracketed datatype: +set quoted_identifier off +go +exec unqStr_proc_2g +go +set quoted_identifier on +go +exec unqStr_proc_2h +go + +exec unqStr_proc_2i +go +set quoted_identifier off go -- should print 'dbo': exec unqStr_proc_3 go -create function unqStr_func_2(@par1 varchar(20) = abc) -returns varchar(20) -as -begin -return @par1 -end -go -- should reflect column value: -select dbo.unqStr_func_2(abc) from unqStr_tab +select dbo.unqStr_func_2(aBc) from unqStr_tab go -- should print 'dbo', since 'user' is a function call select dbo.unqStr_func_2(user) go --- should print 'abc': +-- should print 'aBc': declare @v varchar(20) execute @v = unqStr_func_2 select @v go -- this test will fail since 'default' arguments are not yet supported in Babelfish (BABEL-335) --- once supported, should print 'abc': +-- once supported, should print 'aBc': select dbo.unqStr_func_2(default) go -- should print value of @@language: -create procedure unqStr_proc_4 @p1 varchar(20) = @@language as select @p1 -go exec unqStr_proc_4 go -- as a default, @p2 should take value of @p1. -- however this does not work in Babelfish, both before and after -- the fix for unquoted strings in BABEL-334 -create procedure unqStr_proc_5 @p1 varchar(20), @p2 varchar(20) = @p1 -as select @p2 -go exec unqStr_proc_5 'this is @p1' go --- should raise error: -create procedure unqStr_proc_6 @p0 varchar(20) = @@myvar -as select @p1 -go - --- variable as column default is invalid syntax in SQL Server, but works in Babelfish, which is harmless --- in Babelfish this only works for a global (@@) variable, not for a local variable -create function unqStr_func_3 (@p1 varchar(20) = @@language) returns varchar(20) -as -begin -return @p1 -end +exec unqStr_proc_7 go -- this will not work until 'default' arguments are supported (BABEL-335) @@ -190,81 +273,35 @@ exec @v = unqStr_func_3 select @v go --- invalid in SQL Server, should also fail in Babelfish: -create function unqStr_func_4 (@p1 varchar(20), @p2 varchar(20) = @p1) returns varchar(20) -as -begin -return @p2 -end -go - -- psql -- PG test cases -- call T-SQL proc from PG, T-SQL semantics still apply --- should print 'abc': +-- should print 'aBc': -- NOTE: test is disabled as it crashes the JDBC test framework with message 'Received resultset tuples, but no field structure for them' --call master_dbo.unqStr_proc_2(); --go -- PG procedure/function calls should not be affected by the T-SQL fix -- for unquoted strings in BABEL-334 -CREATE OR REPLACE PROCEDURE unqStr_pgproc_1(par1 varchar(20)) -LANGUAGE plpgsql -AS $$ -begin -raise notice 'arg=[%]', par1; -end -$$; -go --- should print 'abc': -CALL unqStr_pgproc_1('abc'); + +-- should print 'aBc': +CALL unqStr_pgproc_1('aBc'); go -- should raise an error about column not found: -CALL unqStr_pgproc_1(abc); +CALL unqStr_pgproc_1(aBc); go -CREATE function unqStr_pgfunc_1(par1 name) -returns varchar(20) -LANGUAGE plpgsql -AS $$ -begin -return par1; -end -$$; -go --- should print 'abc': -select unqStr_pgfunc_1('abc'); +-- should print 'aBc': +select unqStr_pgfunc_1('aBc'); go -- should print column value: -select unqStr_pgfunc_1(abc) from master_dbo.unqStr_tab; +select unqStr_pgfunc_1(aBc) from master_dbo.unqStr_tab; go -- should raise an error about column not found: -select unqStr_pgfunc_1(abc); +select unqStr_pgfunc_1(aBc); go -- should print 'jdbc_user', since 'user' is a function call: select unqStr_pgfunc_1(user); go - --- should fail since PG does not support unquoted string defaults: -CREATE OR REPLACE PROCEDURE unqStr_pgproc_2(par1 varchar(20) = abc) -LANGUAGE plpgsql -AS $$ -begin -raise notice 'arg=[%]', par1; -end -$$; -go - --- should fail since PG does not support unquoted string defaults: -CREATE function unqStr_pgfunc_2(par1 varchar(20) = abc) -returns varchar(20) -LANGUAGE plpgsql -AS $$ -begin -return par1; -end -$$; -go - diff --git a/test/JDBC/upgrade/latest/schedule b/test/JDBC/upgrade/latest/schedule index 42143cbd27..8b078d6380 100644 --- a/test/JDBC/upgrade/latest/schedule +++ b/test/JDBC/upgrade/latest/schedule @@ -473,3 +473,4 @@ sys_asymmetric_keys sys_certificates sys_database_permissions BABEL-4279 +BABEL-4484