Skip to content

Commit

Permalink
Support View usage for PIVOT operator
Browse files Browse the repository at this point in the history
Support create/select/drop view on stmt with pivot operator

We switched to deparsed psql-text approach for VIEW/JOIN/CTE, all
pivot metadata will be passed to bbf_pivot function arguments, so we
removed all funcCall/funcExpr context field related code.

Task: BABEL-4673
Signed-off-by: Yanjie Xu <[email protected]>
  • Loading branch information
RIC06X committed Sep 25, 2024
1 parent 51a2529 commit 3124362
Show file tree
Hide file tree
Showing 22 changed files with 6,097 additions and 283 deletions.
137 changes: 26 additions & 111 deletions contrib/babelfishpg_tsql/runtime/functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,12 @@ void *get_language(void);
void *get_host_id(void);

Datum datepart_internal(char *field , Timestamp timestamp , float8 df_tz, bool general_integer_datatype);
int SPI_execute_raw_parsetree(RawStmt *parsetree, const char *sourcetext, bool read_only, long tcount);
static HTAB *load_categories_hash(RawStmt *cats_sql, const char *sourcetext, MemoryContext per_query_ctx);
static Tuplestorestate *get_bbf_pivot_tuplestore(RawStmt *sql,
const char *sourcetext,
const char *funcName,
HTAB *bbf_pivot_hash,
TupleDesc tupdesc,
bool randomAccess);
static HTAB *load_categories_hash(const char *sourcetext, MemoryContext per_query_ctx);
static Tuplestorestate *get_bbf_pivot_tuplestore(const char *sourcetext,
const char *funcName,
HTAB *bbf_pivot_hash,
TupleDesc tupdesc,
bool randomAccess);

extern bool canCommitTransaction(void);
extern bool is_ms_shipped(char *object_name, int type, Oid schema_id);
Expand Down Expand Up @@ -4360,68 +4358,6 @@ objectproperty_internal(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}

/*
* We transformed tsql pivot stmt to 3 parsetree. The outer parsetree is a wrapper stmt
* while the other two are helper stmts. Since postgres does not natively support execute
* raw parsetree, and we can only get raw parsetree after the analyzer, we created this
* SPI function to help execute raw parsetree.
*/
int
SPI_execute_raw_parsetree(RawStmt *parsetree, const char * sourcetext, bool read_only, long tcount)
{
_SPI_plan plan;
int ret = 0;
List *plancache_list;
CachedPlanSource *plansource;
int prev_sql_dialect;

if (parsetree == NULL || tcount < 0)
return SPI_ERROR_ARGUMENT;

/*
* set sql_dialect to tsql, which is needed for raw parsetree parsing
* and processing
*/
prev_sql_dialect = sql_dialect;
sql_dialect = SQL_DIALECT_TSQL;

memset(&plan, 0, sizeof(_SPI_plan));
plan.magic = _SPI_PLAN_MAGIC;
plan.parse_mode = RAW_PARSE_DEFAULT;
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;

/*
* Construct plancache entries, but don't do parse analysis yet.
*/
plancache_list = NIL;

/*
* there are some parsetree node copied from the orginial parsetree,
* and the node's location is fixed with the sourcetext. So we are
* passing the orginal sourcetext to the following function to prevent
* analyzing error.
*/
plansource = CreateOneShotCachedPlan(parsetree,
sourcetext,
CreateCommandTag(parsetree->stmt));

plancache_list = lappend(plancache_list, plansource);
plan.plancache_list = plancache_list;
plan.oneshot = true;
PG_TRY();
{
ret = SPI_execute_plan_with_paramlist(&plan, NULL, read_only, tcount);
}
PG_FINALLY();
{
/* reset sql_dialect */
sql_dialect = prev_sql_dialect;
}
PG_END_TRY();

return ret;
}

PG_FUNCTION_INFO_V1(bbf_pivot);
Datum
bbf_pivot(PG_FUNCTION_ARGS)
Expand All @@ -4431,13 +4367,9 @@ bbf_pivot(PG_FUNCTION_ARGS)
MemoryContext per_query_ctx;
MemoryContext oldcontext;
HTAB *bbf_pivot_hash;
RawStmt *bbf_pivot_src_sql;
RawStmt *bbf_pivot_cat_sql;
List *pivot_parsetree;
List *pivot_extrainfo;
char *query_string;
char *src_sql_string;
char *cat_sql_string;
char *funcName;
Node *node;

/* check to see if caller supports us returning a tuplestore */
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
Expand All @@ -4450,33 +4382,19 @@ bbf_pivot(PG_FUNCTION_ARGS)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("materialize mode required, but it is not allowed in this context")));

if (fcinfo->context == NULL || !IsA(fcinfo->context, List) || list_length((List *) fcinfo->context) != 3)
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("Babelfish PIVOT is not properly initialized.")));
src_sql_string = text_to_cstring(PG_GETARG_TEXT_PP(0));
cat_sql_string = text_to_cstring(PG_GETARG_TEXT_PP(1));
funcName = text_to_cstring(PG_GETARG_TEXT_PP(2));

node = list_nth((List *)fcinfo->context, 0);
if (!IsA(node, List)
|| !IsA(list_nth((List *)node, 0), String)
|| strcmp(((String *)list_nth((List *)node, 0))->sval, "bbf_pivot_func") != 0)
/* check if babelfish pivot metadata is complete */
if (src_sql_string == NULL || cat_sql_string == NULL || funcName == NULL
|| strlen(src_sql_string) == 0 || strlen(src_sql_string) == 0 || strlen(funcName) == 0)
{
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("Babelfish PIVOT is not properly initialized.")));
}
pivot_parsetree = (List *) list_nth((List *) fcinfo->context, 1);
pivot_extrainfo = (List *) list_nth((List *) fcinfo->context, 2);

if (!IsA(pivot_parsetree, List) || !IsA(pivot_extrainfo, List))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("Babelfish PIVOT is not properly initialized.")));

bbf_pivot_src_sql = (RawStmt *) list_nth(pivot_parsetree, 0);
bbf_pivot_cat_sql = (RawStmt *) list_nth(pivot_parsetree, 1);
query_string = ((String *) list_nth(pivot_extrainfo, 0))->sval;
funcName = ((String *) list_nth(pivot_extrainfo, 1))->sval;


per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx);

Expand All @@ -4497,14 +4415,13 @@ bbf_pivot(PG_FUNCTION_ARGS)
"bbf_pivot function are not compatible")));

/* load up the categories hash table */
bbf_pivot_hash = load_categories_hash(bbf_pivot_cat_sql, query_string, per_query_ctx);
bbf_pivot_hash = load_categories_hash(cat_sql_string, per_query_ctx);

/* let the caller know we're sending back a tuplestore */
rsinfo->returnMode = SFRM_Materialize;

/* now go build it */
rsinfo->setResult = get_bbf_pivot_tuplestore(bbf_pivot_src_sql,
query_string,
rsinfo->setResult = get_bbf_pivot_tuplestore(src_sql_string,
funcName,
bbf_pivot_hash,
tupdesc,
Expand All @@ -4527,7 +4444,8 @@ bbf_pivot(PG_FUNCTION_ARGS)
* load up the categories hash table
*/
static HTAB *
load_categories_hash(RawStmt *cats_sql, const char * sourcetext, MemoryContext per_query_ctx)
load_categories_hash(const char *sourcetext,
MemoryContext per_query_ctx)
{
HTAB *bbf_pivot_hash;
HASHCTL ctl;
Expand Down Expand Up @@ -4555,7 +4473,7 @@ load_categories_hash(RawStmt *cats_sql, const char * sourcetext, MemoryContext p
elog(ERROR, "load_categories_hash: SPI_connect returned %d", ret);

/* Retrieve the category name rows */
ret = SPI_execute_raw_parsetree(cats_sql, sourcetext, true, 0);
ret = SPI_execute(sourcetext, true, 0);
tuple_processed = SPI_processed;

/* Check for qualifying tuples */
Expand Down Expand Up @@ -4614,18 +4532,15 @@ load_categories_hash(RawStmt *cats_sql, const char * sourcetext, MemoryContext p
return bbf_pivot_hash;
}



/*
* create and populate the bbf_pivot tuplestore
*/
static Tuplestorestate *
get_bbf_pivot_tuplestore(RawStmt *sql,
const char *sourcetext,
const char *funcName,
HTAB *bbf_pivot_hash,
TupleDesc tupdesc,
bool randomAccess)
get_bbf_pivot_tuplestore(const char *sourcetext,
const char *funcName,
HTAB *bbf_pivot_hash,
TupleDesc tupdesc,
bool randomAccess)
{
Tuplestorestate *tupstore;
int num_categories = hash_get_num_entries(bbf_pivot_hash);
Expand All @@ -4644,7 +4559,7 @@ get_bbf_pivot_tuplestore(RawStmt *sql,
elog(ERROR, "get_bbf_pivot_tuplestore: SPI_connect returned %d", ret);

/* Now retrieve the bbf_pivot source rows */
ret = SPI_execute_raw_parsetree(sql, sourcetext, true, 0);
ret = SPI_execute(sourcetext, true, 0);
tuple_processed = SPI_processed;

/* Check for qualifying tuples */
Expand Down
2 changes: 1 addition & 1 deletion contrib/babelfishpg_tsql/sql/sys_functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5170,7 +5170,7 @@ END;
$body$
LANGUAGE plpgsql STABLE;
CREATE OR REPLACE FUNCTION sys.bbf_pivot()
CREATE OR REPLACE FUNCTION sys.bbf_pivot(IN src_sql TEXT, IN cat_sql TEXT, IN agg_func TEXT)
RETURNS setof record
AS 'babelfishpg_tsql', 'bbf_pivot'
LANGUAGE C STABLE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ LANGUAGE plpgsql;
* final behaviour.
*/

ALTER FUNCTION sys.bbf_pivot() RENAME TO bbf_pivot_deprecated_in_4_4_0;
CALL sys.babelfish_drop_deprecated_object('function', 'sys', 'bbf_pivot_deprecated_in_4_4_0');

CREATE OR REPLACE FUNCTION sys.bbf_pivot(IN src_sql TEXT, IN cat_sql TEXT, IN agg_func TEXT)
RETURNS setof record
AS 'babelfishpg_tsql', 'bbf_pivot'
LANGUAGE C STABLE;

-- Assigning dbo role to the db_owner login
DO $$
DECLARE
Expand Down
94 changes: 47 additions & 47 deletions contrib/babelfishpg_tsql/src/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ extern Datum pltsql_exec_tsql_cast_value(Datum value, bool *isnull,
static void is_function_pg_stat_valid(FunctionCallInfo fcinfo,
PgStat_FunctionCallUsage *fcu,
char prokind, bool finalize);
static void pass_pivot_data_to_fcinfo(FunctionCallInfo fcinfo, Expr *expr);
static AclResult pltsql_ExecFuncProc_AclCheck(Oid funcid);

/*****************************************
Expand Down Expand Up @@ -244,7 +243,6 @@ static drop_relation_refcnt_hook_type prev_drop_relation_refcnt_hook = NULL;
static set_local_schema_for_func_hook_type prev_set_local_schema_for_func_hook = NULL;
static bbf_get_sysadmin_oid_hook_type prev_bbf_get_sysadmin_oid_hook = NULL;
static transform_pivot_clause_hook_type pre_transform_pivot_clause_hook = NULL;
static pass_pivot_data_to_fcinfo_hook_type pre_pass_pivot_data_to_fcinfo_hook = NULL;
static called_from_tsql_insert_exec_hook_type pre_called_from_tsql_insert_exec_hook = NULL;
static called_for_tsql_itvf_func_hook_type prev_called_for_tsql_itvf_func_hook = NULL;
static exec_tsql_cast_value_hook_type pre_exec_tsql_cast_value_hook = NULL;
Expand Down Expand Up @@ -414,9 +412,6 @@ InstallExtendedHooks(void)
pre_transform_pivot_clause_hook = transform_pivot_clause_hook;
transform_pivot_clause_hook = transform_pivot_clause;

pre_pass_pivot_data_to_fcinfo_hook = pass_pivot_data_to_fcinfo_hook;
pass_pivot_data_to_fcinfo_hook = pass_pivot_data_to_fcinfo;

prev_optimize_explicit_cast_hook = optimize_explicit_cast_hook;
optimize_explicit_cast_hook = optimize_explicit_cast;

Expand Down Expand Up @@ -4711,6 +4706,19 @@ make_restarget_from_cstr_list(List * l)
return tempResTarget;
}

static A_Const *
makeStringConst(char *str, int location)
{
A_Const *node;

node = makeNode(A_Const);
node->val.sval.type = T_String;
node->val.sval.sval = str;

node->location = location;
return node;
}

static void
transform_pivot_clause(ParseState *pstate, SelectStmt *stmt)
{
Expand All @@ -4721,22 +4729,28 @@ transform_pivot_clause(ParseState *pstate, SelectStmt *stmt)
List *src_sql_groupbylist;
List *src_sql_sortbylist;
List *src_sql_fromClause_copy;
List *pivot_context_list;
char *pivot_colstr;
char *value_colstr;
String *funcName;
ColumnRef *value_col;
TargetEntry *aggfunc_te;
RangeFunction *wrapperSelect_RangeFunction;
SelectStmt *pivot_src_sql;
RawStmt *s_sql;
RawStmt *c_sql;
FuncCall *pivot_func;
WithClause *with_clause;

RawStmt *src_sql_rawstmt;
RawStmt *cat_sql_rawstmt;
Query *src_sql_query;
Query *cat_sql_query;
char *src_sql_string;
char *cat_sql_string;

if (sql_dialect != SQL_DIALECT_TSQL)
return;

/* initialize all lists */
temp_src_targetlist = NIL;
new_src_sql_targetist = NIL;
new_pivot_aliaslist = NIL;
src_sql_groupbylist = NIL;
Expand Down Expand Up @@ -4870,53 +4884,39 @@ transform_pivot_clause(ParseState *pstate, SelectStmt *stmt)

wrapperSelect_RangeFunction->coldeflist = new_pivot_aliaslist;

src_sql_rawstmt = makeNode(RawStmt);
cat_sql_rawstmt = makeNode(RawStmt);

s_sql = makeNode(RawStmt);
c_sql = makeNode(RawStmt);
s_sql->stmt = (Node *) pivot_src_sql;
s_sql->stmt_location = 0;
s_sql->stmt_len = 0;
src_sql_rawstmt->stmt = (Node *) pivot_src_sql;
src_sql_rawstmt->stmt_location = 0;
src_sql_rawstmt->stmt_len = 0;

c_sql->stmt = (Node *) stmt->catSql;
c_sql->stmt_location = 0;
c_sql->stmt_len = 0;
cat_sql_rawstmt->stmt = (Node *) stmt->catSql;
cat_sql_rawstmt->stmt_location = 0;
cat_sql_rawstmt->stmt_len = 0;

pivot_context_list = list_make3(list_make1(makeString("bbf_pivot_func")),
list_make2((Node *) copyObject(s_sql),
(Node *) copyObject(c_sql)
),
list_make2(makeString(pstrdup(pstate->p_sourcetext)),
makeString(pstrdup(funcName->sval))
)
);
/* get psql-text of src_sql and cat_sql */
src_sql_query = parse_analyze_fixedparams((RawStmt *) copyObject(src_sql_rawstmt),
pstrdup(pstate->p_sourcetext),
NULL, 0, NULL);
src_sql_string = pg_get_querydef(src_sql_query, true);

cat_sql_query = parse_analyze_fixedparams((RawStmt *) copyObject(cat_sql_rawstmt),
pstrdup(pstate->p_sourcetext),
NULL, 0, NULL);
cat_sql_string = pg_get_querydef(cat_sql_query, true);

/* Store pivot information in FuncCall to live through parser analyzer */
pivot_func = makeFuncCall(list_make2(makeString("sys"), makeString("bbf_pivot")), NIL, COERCE_EXPLICIT_CALL, -1);
pivot_func->context = (Node *) pivot_context_list;
pivot_func = makeFuncCall(list_make2(makeString("sys"), makeString("bbf_pivot")),
list_make3((Node *) makeStringConst(src_sql_string, -1),
(Node *) makeStringConst(cat_sql_string, -1),
(Node *) makeStringConst(pstrdup(funcName->sval), -1)
),
COERCE_EXPLICIT_CALL,
-1);
wrapperSelect_RangeFunction->functions = list_make1(list_make2((Node *) pivot_func, NIL));
}

static void
pass_pivot_data_to_fcinfo(FunctionCallInfo fcinfo, Expr *expr)
{
/* if current FuncExpr is a bbf_pivot function, we set the fcinfo context to pivot data */
if (sql_dialect != SQL_DIALECT_TSQL)
return;

if (IsA(expr, FuncExpr)
&& ((FuncExpr*) expr)->context != NULL
&& (IsA(((FuncExpr*) expr)->context, List)))
{
Node *node;
node = list_nth((List *)((FuncExpr*) expr)->context, 0);
if (IsA(node, List)
&& IsA(list_nth((List *)node, 0), String)
&& strcmp(((String *)list_nth((List *)node, 0))->sval, "bbf_pivot_func") == 0)
{
fcinfo->context = ((FuncExpr*) expr)->context;
}
}
}

static Node* optimize_explicit_cast(ParseState *pstate, Node *node)
{
Expand Down
Loading

0 comments on commit 3124362

Please sign in to comment.