Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support TSQL syntax for geospatial functions #2024

Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1abfca4
Support TSQL syntax for geospatial functions
Nov 16, 2023
449cd2b
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Nov 16, 2023
96922ed
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Nov 16, 2023
25ea50f
Fix github action failures
Nov 16, 2023
11c5377
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Nov 17, 2023
c77d348
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Nov 18, 2023
afc89da
Added support for STDistance, STAsText, STAsBinary functions
Nov 18, 2023
0837436
Fixed query bug
Nov 22, 2023
b49ee8d
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Nov 22, 2023
7e0296f
Added comments and code cleanup
Nov 22, 2023
aa8753a
Resolved Comments
Nov 24, 2023
dd138be
Merge branch 'babelfish-for-postgresql:BABEL_3_X_DEV' into Jira-babel…
Anikait143 Nov 24, 2023
bb745d3
Retrigger tests
Nov 24, 2023
4991d0f
Resolved comments
Nov 24, 2023
64a4249
Fixed Github Action Failures
Nov 24, 2023
68884dc
Resolved Comments
Nov 24, 2023
6c8083c
Reverted Invalid db check removal
Nov 27, 2023
a693b83
Resolved Github Action failures
Nov 27, 2023
559d684
Added JDBC test Cases
Nov 28, 2023
0d57316
Changed hook name
Nov 28, 2023
3719f72
Added JDBC test cases
Nov 28, 2023
3823b13
Retrigger tests
Nov 28, 2023
7e5436f
Added Test Cases
Nov 29, 2023
156af76
Fixed action failures
Nov 29, 2023
a067ed6
Fixed failures
Nov 29, 2023
832598c
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Nov 30, 2023
69643cf
Resloved Comments
Nov 30, 2023
4238e12
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Nov 30, 2023
143119d
Fix Action Failures
Nov 30, 2023
5321bc7
Added support for View, Function, Procedure for Geospatial functions
Dec 1, 2023
073ffd0
Fix github action failures
Dec 1, 2023
03c3384
Merge branch 'babelfish-for-postgresql:BABEL_3_X_DEV' into Jira-babel…
Anikait143 Dec 1, 2023
5ef63d9
Merge remote-tracking branch 'upstream/BABEL_3_X_DEV' into Jira-babel…
Jan 18, 2024
4cbf1e3
Disabled Analyser changes
Jan 18, 2024
d521181
Supported Geospatial TSQL syntax with Parser Changes
Jan 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions contrib/babelfishpg_tsql/src/multidb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1277,12 +1277,6 @@ get_physical_schema_name_by_mode(char *db_name, const char *schema_name, Migrati

snprintf(result, (MAX_BBF_NAMEDATALEND), "%s_%s", db_name, name);
}
else if (!DbidIsValid(get_db_id(db_name)))
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database \"%s\" does not exist. Make sure that the name is entered correctly.", db_name)));
}
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
else
{
/* all schema names are not prepended with db name on single-db */
Expand Down
219 changes: 219 additions & 0 deletions contrib/babelfishpg_tsql/src/pl_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
Expand All @@ -44,6 +45,8 @@
#include "codegen.h"
#include "iterative_exec.h"
#include "multidb.h"
#include "session.h"
#include "catalog.h"

/* ----------
* Our own local and global variables
Expand Down Expand Up @@ -104,6 +107,12 @@ static const ExceptionLabelMap exception_label_map[] = {

static int cur_handle_id = 1;

/*
* Global variable used to prevent recurring check for TSQL functional call for Geospatial types
* Eg: table.col.stx (here stx will be considered as a Geospatial function)
*/
static bool is_geospatial_function = false;

/* ----------
* static prototypes
* ----------
Expand All @@ -119,6 +128,7 @@ static void add_dummy_return(PLtsql_function *function);
static void add_decl_table(PLtsql_function *function, int tbl_dno, char *tbl_typ);
static Node *pltsql_pre_column_ref(ParseState *pstate, ColumnRef *cref);
static Node *pltsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var);
static List *pltsql_post_func_ref(ParseState *pstate, FuncCall *fn, List *fargs);
static void pltsql_post_expand_star(ParseState *pstate, ColumnRef *cref, List *l);
static Node *pltsql_param_ref(ParseState *pstate, ParamRef *pref);
static Node *resolve_column_ref(ParseState *pstate, PLtsql_expr *expr,
Expand All @@ -142,6 +152,9 @@ static void pltsql_HashTableInsert(PLtsql_function *function,
PLtsql_func_hashkey *func_key);
static void pltsql_HashTableDelete(PLtsql_function *function);
static void delete_function(PLtsql_function *func);
static Node *resolve_geospatial_col_ref(ParseState *pstate, ColumnRef *cref);
static List *resolve_geospatial_func_ref(ParseState *pstate, FuncCall *fn, List *fargs);
static char *extract_logical_schema_name(char *db_name, char *schema_name, MigrationMode mode);

extern Portal ActivePortal;
extern bool pltsql_function_parse_error_transpose(const char *prosrc);
Expand Down Expand Up @@ -1479,6 +1492,7 @@ pltsql_parser_setup(struct ParseState *pstate, PLtsql_expr *expr)
{
pstate->p_pre_columnref_hook = pltsql_pre_column_ref;
pstate->p_post_columnref_hook = pltsql_post_column_ref;
pstate->p_post_funcref_hook = pltsql_post_func_ref;
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
pstate->p_post_expand_star_hook = pltsql_post_expand_star;
pstate->p_paramref_hook = pltsql_param_ref;
/* no need to use p_coerce_param_hook */
Expand Down Expand Up @@ -1507,6 +1521,7 @@ pltsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
{
PLtsql_expr *expr = (PLtsql_expr *) pstate->p_ref_hook_state;
Node *myvar;
Node *geovar;
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved

if (expr->func->resolve_option == PLTSQL_RESOLVE_VARIABLE)
return NULL; /* we already found there's no match */
Expand Down Expand Up @@ -1539,6 +1554,16 @@ pltsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
errdetail("It could refer to either a PL/tsql variable or a table column."),
parser_errposition(pstate, cref->location)));
}
/*
* We check if the current colRef represents a geospatial function
* Eg: SELECT col.STX from t1;
* where col is a column of table t1
*/
else if (var == NULL)
{
if ((geovar = resolve_geospatial_col_ref(pstate, cref)) != NULL)
return geovar;
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
}

return myvar;
}
Expand Down Expand Up @@ -1642,6 +1667,200 @@ pltsql_param_ref(ParseState *pstate, ParamRef *pref)
return make_datum_param(expr, nse->itemno, pref->location);
}

/*
* pltsql_post_func_ref parser callback for FuncRefs to check and modify Geospatial function call
* We are returning a List * of function args because in the cases we are concerned with geospatial types
* we are just trying to modify the arg list for functions, which is then passed to ParseFuncOrColumn in transformFuncCall.
*/
static List *
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
pltsql_post_func_ref(ParseState *pstate, FuncCall *fn, List *fargs)
{
List *ret = resolve_geospatial_func_ref(pstate, fn, fargs);
if (ret)
return ret;

return fargs;
}

/*
* This function reverts schema name, which is already rewritten by rewrite_column_refs or rewrite_plain_name functions
* This is required so that parser identifies the name for Geospatial Function call
* Eg: select t2.geom.STX from t2; (where 'geom' is geometry column in table t2)
* In this Case, 't2.geom.STX' will rewritten as 'master_t2.geom.STX'
* because t2 is considered as Schema name and not table name because of 3 part call. So, we have to resolve it
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
*/
static char *
extract_logical_schema_name(char *db_name, char *physical_schema_name, MigrationMode mode)
{

if (SINGLE_DB == mode)
{
if ((strlen(db_name) == 6 && (strncmp(db_name, "master", 6) == 0)) ||
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
(strlen(db_name) == 6 && (strncmp(db_name, "tempdb", 6) == 0)) ||
(strlen(db_name) == 4 && (strncmp(db_name, "msdb", 4) == 0)))
{
return physical_schema_name + strlen(db_name) + 1;
}
else
{
/* all schema names are not prepended with db name on single-db */
return physical_schema_name;
}
}
else
{
return physical_schema_name + strlen(db_name) + 1;
}

return physical_schema_name;
}

/*
* This function identifies and modifies geospatial function call for
* STX, STY, LAT, LONG functions
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
*/
static Node *
resolve_geospatial_col_ref(ParseState *pstate, ColumnRef *cref)
{
Node *last_field; /* represents the geospatial function name */
Node *col;

Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
/* if there is only one field then it cannot be a geospatial function call */
if (list_length(cref->fields) <= 1){
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
is_geospatial_function = false; /* resetting the global variable */
return NULL;
}

last_field = (Node *) llast(cref->fields);
/*
* checking if last field matches any of the geospatial functions - stx, sty, lat, long
* and also checking if is_geospatial_function variable is false to avoid entering the loop if it is a column rather than function
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
*/
if (IsA(last_field, String) && !is_geospatial_function &&
(pg_strcasecmp(strVal(last_field), "stx") == 0 ||
pg_strcasecmp(strVal(last_field), "sty") == 0 ||
pg_strcasecmp(strVal(last_field), "lat") == 0 ||
pg_strcasecmp(strVal(last_field), "long") == 0))
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
{
FuncExpr *funcexpr = makeNode(FuncExpr); /* func expression to modify colref to funcref */
char *name = strVal(last_field);
Oid funcid;
Oid dataTypeId;
ColumnRef *colref = makeNode(ColumnRef); /* new colref to avoid making changes to existing structure */

colref->fields = list_copy(cref->fields);
/* if length is 3 then colref would have been modified by rewrite_column_refs function */
if(list_length(colref->fields) == 3)
{
Node *schema = (Node *) linitial(colref->fields);
char *cur_db = get_cur_db_name();
String *new_schema;
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved

if (!is_shared_schema(strVal(schema)))
{
new_schema = makeString(extract_logical_schema_name(cur_db, strVal(schema), get_migration_mode()));
colref->fields = list_delete_first(colref->fields);
colref->fields = lcons(new_schema, colref->fields);
}
}
colref->fields = list_delete_last(colref->fields); /* deletes the function name and check if the remaining colref is a valid column reference */
is_geospatial_function = true;
PG_TRY();
{
col = transformExpr(pstate, (Node *) colref, pstate->p_expr_kind); /* Recursive call to check for valid column reference */
}
PG_CATCH();
{
is_geospatial_function = false; /* resetting the global variable */
return NULL; /* if not a valid column ref */
}
PG_END_TRY();

is_geospatial_function = false; /* resetting the global variable */
dataTypeId = ((Var *)col)->vartype;
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
/* Modifying colref to funcref since a valid Geospatial function call is identified */
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
funcid = LookupFuncName(list_make2(makeString("sys"), makeString(name)), 1, &dataTypeId, false);
funcexpr = makeFuncExpr(funcid, get_func_rettype(funcid), list_make1(col), 0, 0, COERCE_EXPLICIT_CALL);
funcexpr->location = cref->location;

return (Node *) funcexpr;
}

is_geospatial_function = false; /* resetting the global variable */

return NULL;
}

/*
* This function identifies and modifies geospatial function call for
* STDistance(), STAsText(), STAsBinary() functions
*/
static List *resolve_geospatial_func_ref(ParseState *pstate, FuncCall *fn, List *fargs)
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
{
Node *fname; /* represents the geospatial function name */
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved

if(list_length(fn->funcname) > 1 && list_length(fn->args) <= 1
&& fn->agg_order == NULL && fn->agg_filter == NULL
&& !fn->agg_within_group && !fn->agg_star
&& !fn->agg_distinct && !fn->func_variadic)
{
Node *col;
bool flag = true;
fname = (Node *) llast(fn->funcname);
/*
* checking if last field matches any of the geospatial functions - stdistance, stastext, stasbinary
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
* and also checking if is_geospatial_function variable is false to avoid entering the loop if it is not a function
*/
if (IsA(fname, String) && !is_geospatial_function &&
((pg_strcasecmp(strVal(fname), "stdistance") == 0 && list_length(fn->args) == 1) ||
(pg_strcasecmp(strVal(fname), "stastext") == 0 && list_length(fn->args) == 0) ||
(pg_strcasecmp(strVal(fname), "stasbinary") == 0 && list_length(fn->args) == 0)))
{
ColumnRef *cref = makeNode(ColumnRef); /* new colref to avoid making changes to existing structure */
cref->fields = list_copy(fn->funcname);
cref->fields = list_delete_last(cref->fields); /* deletes the function name and check if the remaining colref is a valid column reference */

/* if length is 1 then colref would have been modified by rewrite_plain_name function */
if(list_length(cref->fields) == 1)
{
Node *schema = (Node *) linitial(cref->fields);
char *cur_db = get_cur_db_name();
String *new_schema;

if (!is_shared_schema(strVal(schema)))
{
new_schema = makeString(extract_logical_schema_name(cur_db, strVal(schema), get_migration_mode()));
cref->fields = list_delete_first(cref->fields);
cref->fields = lcons(new_schema, cref->fields);
}
}
is_geospatial_function = true;
PG_TRY();
{
col = transformExpr(pstate, (Node *) cref, pstate->p_expr_kind); /* Recursive call to check for valid column reference */
}
PG_CATCH();
{
flag = false; /* if not a valid column ref. Eg: table.col.stx (where col is not a coulmn of table) */
Anikait143 marked this conversation as resolved.
Show resolved Hide resolved
}
PG_END_TRY();

/* Modifying funcref since a valid Geospatial function call is identified */
if(flag)
{
is_geospatial_function = false; /* resetting the global variable */
fn->args = lcons(cref, fn->args);
fargs = lcons(col, fargs);
fn->funcname = list_delete_first(fn->funcname);
return fargs;
}
}
}
is_geospatial_function = false; /* resetting the global variable */

return NULL;
}

/*
* resolve_column_ref attempt to resolve a ColumnRef as a pltsql var
*
Expand Down
Loading
Loading