@ -56,7 +56,7 @@ static Tuplestorestate *get_crosstab_tuplestore(char *sql,
bool randomAccess ) ;
bool randomAccess ) ;
static void validateConnectbyTupleDesc ( TupleDesc tupdesc , bool show_branch , bool show_serial ) ;
static void validateConnectbyTupleDesc ( TupleDesc tupdesc , bool show_branch , bool show_serial ) ;
static bool compatCrosstabTupleDescs ( TupleDesc tupdesc1 , TupleDesc tupdesc2 ) ;
static bool compatCrosstabTupleDescs ( TupleDesc tupdesc1 , TupleDesc tupdesc2 ) ;
static bool compatConnectbyTupleDescs ( TupleDesc tupdesc1 , TupleDesc tupdesc2 ) ;
static void compatConnectbyTupleDescs ( TupleDesc tupdesc1 , TupleDesc tupdesc2 ) ;
static void get_normal_pair ( float8 * x1 , float8 * x2 ) ;
static void get_normal_pair ( float8 * x1 , float8 * x2 ) ;
static Tuplestorestate * connectby ( char * relname ,
static Tuplestorestate * connectby ( char * relname ,
char * key_fld ,
char * key_fld ,
@ -70,7 +70,7 @@ static Tuplestorestate *connectby(char *relname,
MemoryContext per_query_ctx ,
MemoryContext per_query_ctx ,
bool randomAccess ,
bool randomAccess ,
AttInMetadata * attinmeta ) ;
AttInMetadata * attinmeta ) ;
static Tuplestorestate * build_tuplestore_recursively ( char * key_fld ,
static void build_tuplestore_recursively ( char * key_fld ,
char * parent_key_fld ,
char * parent_key_fld ,
char * relname ,
char * relname ,
char * orderby_fld ,
char * orderby_fld ,
@ -1180,7 +1180,7 @@ connectby(char *relname,
MemoryContextSwitchTo ( oldcontext ) ;
MemoryContextSwitchTo ( oldcontext ) ;
/* now go get the whole tree */
/* now go get the whole tree */
tupstore = build_tuplestore_recursively ( key_fld ,
build_tuplestore_recursively ( key_fld ,
parent_key_fld ,
parent_key_fld ,
relname ,
relname ,
orderby_fld ,
orderby_fld ,
@ -1201,7 +1201,7 @@ connectby(char *relname,
return tupstore ;
return tupstore ;
}
}
static Tuplestorestate *
static void
build_tuplestore_recursively ( char * key_fld ,
build_tuplestore_recursively ( char * key_fld ,
char * parent_key_fld ,
char * parent_key_fld ,
char * relname ,
char * relname ,
@ -1232,7 +1232,7 @@ build_tuplestore_recursively(char *key_fld,
HeapTuple tuple ;
HeapTuple tuple ;
if ( max_depth > 0 & & level > max_depth )
if ( max_depth > 0 & & level > max_depth )
return tupstore ;
return ;
initStringInfo ( & sql ) ;
initStringInfo ( & sql ) ;
@ -1318,22 +1318,11 @@ build_tuplestore_recursively(char *key_fld,
StringInfoData chk_branchstr ;
StringInfoData chk_branchstr ;
StringInfoData chk_current_key ;
StringInfoData chk_current_key ;
/* First time through, do a little more setup */
if ( level = = 0 )
{
/*
/*
* Check that return tupdesc is compatible with the one we got
* Check that return tupdesc is compatible with the one we got from
* from the query , but only at level 0 - - no need to check more
* the query .
* than once
*/
*/
compatConnectbyTupleDescs ( tupdesc , spi_tupdesc ) ;
if ( ! compatConnectbyTupleDescs ( tupdesc , spi_tupdesc ) )
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " invalid return type " ) ,
errdetail ( " Return and SQL tuple descriptions are " \
" incompatible. " ) ) ) ;
}
initStringInfo ( & branchstr ) ;
initStringInfo ( & branchstr ) ;
initStringInfo ( & chk_branchstr ) ;
initStringInfo ( & chk_branchstr ) ;
@ -1348,24 +1337,31 @@ build_tuplestore_recursively(char *key_fld,
/* get the next sql result tuple */
/* get the next sql result tuple */
spi_tuple = tuptable - > vals [ i ] ;
spi_tuple = tuptable - > vals [ i ] ;
/* get the current key and parent */
/* get the current key (might be NULL) */
current_key = SPI_getvalue ( spi_tuple , spi_tupdesc , 1 ) ;
current_key = SPI_getvalue ( spi_tuple , spi_tupdesc , 1 ) ;
appendStringInfo ( & chk_current_key , " %s%s%s " , branch_delim , current_key , branch_delim ) ;
current_key_parent = pstrdup ( SPI_getvalue ( spi_tuple , spi_tupdesc , 2 ) ) ;
/* get the parent key (might be NULL) */
current_key_parent = SPI_getvalue ( spi_tuple , spi_tupdesc , 2 ) ;
/* get the current level */
/* get the current level */
sprintf ( current_level , " %d " , level ) ;
sprintf ( current_level , " %d " , level ) ;
/* check to see if this key is also an ancestor */
/* check to see if this key is also an ancestor */
if ( current_key )
{
appendStringInfo ( & chk_current_key , " %s%s%s " ,
branch_delim , current_key , branch_delim ) ;
if ( strstr ( chk_branchstr . data , chk_current_key . data ) )
if ( strstr ( chk_branchstr . data , chk_current_key . data ) )
elog ( ERROR , " infinite recursion detected " ) ;
elog ( ERROR , " infinite recursion detected " ) ;
}
/* OK, extend the branch */
/* OK, extend the branch */
if ( current_key )
appendStringInfo ( & branchstr , " %s%s " , branch_delim , current_key ) ;
appendStringInfo ( & branchstr , " %s%s " , branch_delim , current_key ) ;
current_branch = branchstr . data ;
current_branch = branchstr . data ;
/* build a tuple */
/* build a tuple */
values [ 0 ] = pstrdup ( current_key ) ;
values [ 0 ] = current_key ;
values [ 1 ] = current_key_parent ;
values [ 1 ] = current_key_parent ;
values [ 2 ] = current_level ;
values [ 2 ] = current_level ;
if ( show_branch )
if ( show_branch )
@ -1381,21 +1377,19 @@ build_tuplestore_recursively(char *key_fld,
tuple = BuildTupleFromCStrings ( attinmeta , values ) ;
tuple = BuildTupleFromCStrings ( attinmeta , values ) ;
xpfree ( current_key ) ;
xpfree ( current_key_parent ) ;
/* store the tuple for later use */
/* store the tuple for later use */
tuplestore_puttuple ( tupstore , tuple ) ;
tuplestore_puttuple ( tupstore , tuple ) ;
heap_freetuple ( tuple ) ;
heap_freetuple ( tuple ) ;
/* recurse using current_key_parent as the new start_with */
/* recurse using current_key as the new start_with */
tupstore = build_tuplestore_recursively ( key_fld ,
if ( current_key )
build_tuplestore_recursively ( key_fld ,
parent_key_fld ,
parent_key_fld ,
relname ,
relname ,
orderby_fld ,
orderby_fld ,
branch_delim ,
branch_delim ,
values [ 0 ] ,
current_key ,
current_branch ,
current_branch ,
level + 1 ,
level + 1 ,
serial ,
serial ,
@ -1406,6 +1400,9 @@ build_tuplestore_recursively(char *key_fld,
attinmeta ,
attinmeta ,
tupstore ) ;
tupstore ) ;
xpfree ( current_key ) ;
xpfree ( current_key_parent ) ;
/* reset branch for next pass */
/* reset branch for next pass */
resetStringInfo ( & branchstr ) ;
resetStringInfo ( & branchstr ) ;
resetStringInfo ( & chk_branchstr ) ;
resetStringInfo ( & chk_branchstr ) ;
@ -1416,8 +1413,6 @@ build_tuplestore_recursively(char *key_fld,
xpfree ( chk_branchstr . data ) ;
xpfree ( chk_branchstr . data ) ;
xpfree ( chk_current_key . data ) ;
xpfree ( chk_current_key . data ) ;
}
}
return tupstore ;
}
}
/*
/*
@ -1490,34 +1485,25 @@ validateConnectbyTupleDesc(TupleDesc tupdesc, bool show_branch, bool show_serial
/*
/*
* Check if spi sql tupdesc and return tupdesc are compatible
* Check if spi sql tupdesc and return tupdesc are compatible
*/
*/
static bool
static void
compatConnectbyTupleDescs ( TupleDesc ret_tupdesc , TupleDesc sql_tupdesc )
compatConnectbyTupleDescs ( TupleDesc ret_tupdesc , TupleDesc sql_tupdesc )
{
{
Oid ret_atttypid ;
/*
Oid sql_atttypid ;
* Result must have at least 2 columns .
*/
/* check the key_fld types match */
if ( sql_tupdesc - > natts < 2 )
ret_atttypid = ret_tupdesc - > attrs [ 0 ] - > atttypid ;
sql_atttypid = sql_tupdesc - > attrs [ 0 ] - > atttypid ;
if ( ret_atttypid ! = sql_atttypid )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " invalid return type " ) ,
errmsg ( " invalid return type " ) ,
errdetail ( " SQL key field datatype does " \
errdetail ( " Query must return at least two columns. " ) ) ) ;
" not match return key field datatype. " ) ) ) ;
/* check the parent_key_fld types match */
/*
ret_atttypid = ret_tupdesc - > attrs [ 1 ] - > atttypid ;
* We have failed to check datatype match since 2003 , so we don ' t do that
sql_atttypid = sql_tupdesc - > attrs [ 1 ] - > atttypid ;
* here . The call will work as long as the datatypes are I / O
if ( ret_atttypid ! = sql_atttypid )
* representation compatible .
ereport ( ERROR ,
*/
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " invalid return type " ) ,
errdetail ( " SQL parent key field datatype does " \
" not match return parent key field datatype. " ) ) ) ;
/* OK, the two tupdescs are compatible for our purposes */
/* OK, the two tupdescs are compatible for our purposes */
return true ;
}
}
/*
/*