Hi, I have a code in which I translate some code from sqlf to sql, but
when it comes to yy_parse the server crashes, I have no idea why,
because it works fine in other situations.
This is the code (the problem is in parse_sqlf, when I call sqlf_yyparse):
#include "postgres.h"
#include "gram.h"
#include "utils/builtins.h"
#include "funcapi.h"
#include "executor/spi.h"
#include "access/heapam.h"
#include "fmgr.h"
#include "miscadmin.h"
extern Datum sqlf(PG_FUNCTION_ARGS);
char *parse_sqlf();
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(sqlf);
Datum
sqlf(PG_FUNCTION_ARGS)
{ char *query = text_to_cstring(PG_GETARG_TEXT_PP(0)); char *sql; ReturnSetInfo
*rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; Tuplestorestate *tupstore; TupleDesc tupdesc; int
call_cntr; int max_calls; AttInMetadata *attinmeta; SPITupleTable
*spi_tuptable; TupleDesc spi_tupdesc; bool firstpass; char *lastrowid; int
i; int num_categories; MemoryContext per_query_ctx; MemoryContext
oldcontext; int ret; int proc;
sql=(char *)palloc(strlen(query)*sizeof(char *));
sql=parse_sqlf(query);
/* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valuedfunction called in context
that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \
"allowed in this context")));
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
/* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) /* internal error */ elog(ERROR,
"SPI_connectreturned %d", ret);
/* Retrieve the desired rows */ ret = SPI_execute(sql, true, 0); proc = SPI_processed;
/* If no qualifying tuples, fall out early */ if (ret != SPI_OK_SELECT || proc <= 0) { SPI_finish();
rsinfo->isDone = ExprEndResult; PG_RETURN_NULL(); }
spi_tuptable = SPI_tuptable; spi_tupdesc = spi_tuptable->tupdesc;
/* get a tuple descriptor for our result type */ switch (get_call_result_type(fcinfo, NULL, &tupdesc)) {
case TYPEFUNC_COMPOSITE: /* success */ break; case TYPEFUNC_RECORD:
/* failed to determine actual type of RECORD */ ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called
in context " "that cannot accept type record"))); break;
default: /* result type isn't composite */ elog(ERROR, "return type must be a
rowtype"); break; }
/* * switch to long-lived memory context */ oldcontext = MemoryContextSwitchTo(per_query_ctx);
/* make sure we have a persistent copy of the result tupdesc */ tupdesc = CreateTupleDescCopy(tupdesc);
/* initialize our tuplestore in long-lived context */ tupstore =
tuplestore_begin_heap(rsinfo->allowedModes&
SFRM_Materialize_Random, false, work_mem);
MemoryContextSwitchTo(oldcontext);
/* * Generate attribute metadata needed later to produce tuples from raw C * strings */ attinmeta =
TupleDescGetAttInMetadata(tupdesc);
/* total number of tuples to be examined */ max_calls = proc;
/* the return tuple always must have 1 rowid + num_categories columns */ num_categories = tupdesc->natts;
firstpass = true; lastrowid = NULL;
for (call_cntr = 0; call_cntr < max_calls; call_cntr++) { char **values; HeapTuple
spi_tuple; HeapTuple tuple;
/* allocate and zero space */ values = (char **) palloc0((1 + num_categories) * sizeof(char *));
/* get the next sql result tuple */ spi_tuple = spi_tuptable->vals[call_cntr];
/* * now loop through the sql results and assign each value in sequence * to the next category
*/ for (i = 0; i < num_categories; i++) { /* see if we've gone too far already */
if (call_cntr >= max_calls) break;
values[i] = SPI_getvalue(spi_tuple, spi_tupdesc, i+1); }
/* build the tuple */ tuple = BuildTupleFromCStrings(attinmeta, values);
/* switch to appropriate context while storing the tuple */ oldcontext =
MemoryContextSwitchTo(per_query_ctx); tuplestore_puttuple(tupstore, tuple);
MemoryContextSwitchTo(oldcontext);
heap_freetuple(tuple);
/* Clean up */ for (i = 0; i < num_categories + 1; i++) if (values[i] != NULL)
pfree(values[i]); pfree(values);
}
/* let the caller know we're sending back a tuplestore */ rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult= tupstore; rsinfo->setDesc = tupdesc;
/* release SPI related resources (and return to caller's context) */ SPI_finish();
pfree(sql); return (Datum) 0;
}
char *parse_sqlf(const char *query){ void *result;
yy_scan_string(query);
sqlf_yyparse(&result);
return (char *)result;
}
Bison code:
%{ #include <stdio.h> #include <string.h> #include <stdlib.h>
#include "postgres.h" #include "parsing.h"
#define YYDEBUG 1 #define QUERY_LENGTH 4 #define YYPARSE_PARAM result /* need this to pass a pointer (void
*) to yyparse */
int sqlf_yyparse(void *result);
int real_length; char *field; char *fuzzy_query[QUERY_LENGTH];
%}
%name-prefix="sqlf_yy"
%union { int integer; char *text;
}
%token CREATE FUZZY PREDICATE ON AS COMMA DOTDOT LEFTP RIGHTP INFINIT DROP EQUAL SELECT WHERE FROM AND OR ORDER
BYASC DESC WITH CALIBRATION
%token <text> PARAMETER
%type <text> Param Param_select Param_from List_where List_order SelectStmt
%%
query: /* empty string */ | query command
;
command: '\n' | CreateFuzzyPredStmt | DropFuzzyPredStmt | SelectStmt {
int i;
*((void **)result) = fuzzy_query[real_length-1];
for (i=0;i<real_length;i++) pfree(fuzzy_query[i]); } | error '\n' {
yyerrok;}
;
CreateFuzzyPredStmt: CREATE FUZZY PREDICATE Param ON Param DOTDOT Param AS
LEFTP Param COMMA Param COMMA Param COMMA Param RIGHTP {
create_fuzzy_pred($4,$6,$8,$11,$13,$15,$17); } | CREATE FUZZY PREDICATE Param ON Param
DOTDOTParam AS
LEFTP INFINIT COMMA INFINIT COMMA Param COMMA Param RIGHTP {
create_fuzzy_pred($4,$6,$8,"INFINIT","INFINIT",$15,$17); } | CREATE FUZZY PREDICATE Param
ONParam DOTDOT Param AS
LEFTP Param COMMA Param COMMA INFINIT COMMA INFINIT RIGHTP {
create_fuzzy_pred($4,$6,$8,$11,$13,"INFINIT","INFINIT"); }
;
DropFuzzyPredStmt: DROP FUZZY PREDICATE Param { drop_fuzzy_pred($4); }
;
/**************SELECT STATEMENT**********************************/
/*
[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] * | expression [ [ AS ] output_name ] [, ...] [ FROM
from_item[, ...] ] [ WHERE condition ] [ GROUP BY expression [, ...] ] [ HAVING condition [, ...] ] [ WINDOW
window_nameAS ( window_definition ) [, ...] ] [ { UNION | INTERSECT | EXCEPT } [ ALL ] select ] [ ORDER BY
expression[ ASC | DESC | USING operator ] [ NULLS {
FIRST | LAST } ] [, ...] ] [ LIMIT { count | ALL } ] [ OFFSET start [ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [
count] { ROW | ROWS } ONLY ] [ FOR { UPDATE | SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ]
where from_item can be one of:
[ ONLY ] table_name [ * ] [ [ AS ] alias [ ( column_alias [, ...] ) ] ] ( select ) [ AS ] alias [ ( column_alias
[,...] ) ] with_query_name [ [ AS ] alias [ ( column_alias [, ...] ) ] ] function_name ( [ argument [, ...] ] ) [
AS] alias [ (
column_alias [, ...] | column_definition [, ...] ) ] function_name ( [ argument [, ...] ] ) AS ( column_definition [,
...]) from_item [ NATURAL ] join_type from_item [ ON join_condition |
USING ( join_column [, ...] ) ]
and with_query is:
with_query_name [ ( column_name [, ...] ) ] AS ( select )
WITH CALIBRATION
*/
/* Missing pretty much everything, it parses the basic select */
SelectStmt: SELECT Param_select FROM Param_from { fuzzy_query[0]=(char
*)palloc(sizeof(char)*(strlen($2)+strlen($4)+20));
snprintf(fuzzy_query[0],(strlen($2)+strlen($4)+20),"SELECT %s FROM
%s",$2,$4); $$=fuzzy_query[0]; real_length=1; } | SelectStmt
WHEREList_where { fuzzy_query[1]=(char
*)palloc(sizeof(char)*(strlen($1)+strlen($3)+20));
snprintf(fuzzy_query[1],(strlen($1)+strlen($3)+20),"%s
WHERE %s",$1,$3); $$=fuzzy_query[1]; real_length=2; } |
SelectStmtORDER BY List_order { fuzzy_query[2]=(char
*)palloc(sizeof(char)*(strlen($1)+strlen($4)+20));
snprintf(fuzzy_query[2],(strlen($1)+strlen($4)+20),"%s
ORDER BY %s",$1,$4); $$=fuzzy_query[2]; real_length=3; } |
SelectStmtWITH CALIBRATION Param { fuzzy_query[3]=(char
*)palloc(sizeof(char)*(strlen($1)+strlen($4)+20));
snprintf(fuzzy_query[3],(strlen($1)+strlen($4)+20),"%s
WITH CALIBRATION %s",$1,$4); $$=fuzzy_query[3]; real_length=4; }
;
Param: PARAMETER { $$ = $1; };
Param_select: Param { $$ = $1; } | Param_select COMMA Param { strcat($$,", ");
strcat($$,$3); } | Param_select AS Param { strcat($$," AS ");
strcat($$,$3); } | Param_select Param { strcat($$," "); strcat($$,$2);
}
;
Param_from: Param { $$ = $1;} | Param_from COMMA Param { strcat($$,", ");
strcat($$,$3); } | Param_from AS Param { strcat($$," AS ");
strcat($$,$3); } | Param_from Param { strcat($$," "); strcat($$,$2);
}
;
List_where: Param { $$=$1; field=$1; } | LEFTP Param {
strcat($$," ("); strcat($$,$2); field=$2; } | List_where EQUAL
Param{ int len; char *str_result; len=strlen(field)+strlen($3)+15;//15 is the
lengthof
"%s > %f AND %s < %f" str_result=(char *)palloc(sizeof(char)*(len*2));
$$=translate_fuzzy_preds(str_result,field,$3); pfree(str_result); } | List_where AND
Param{ strcat($$," AND "); strcat($$,$3); field=$3; } |
List_whereRIGHTP AND Param { strcat($$,") AND "); strcat($$,$4); field=$4;
} | List_where OR Param { strcat($$," OR "); strcat($$,$3);
field=$3; } | List_where RIGHTP OR Param { strcat($$,") OR ");
strcat($$,$4); field=$4; }
;
List_order: Param { $$=$1; } | List_order COMMA Param { strcat($$,", ");
strcat($$,$3); } | List_order ASC { strcat($$," ASC"); strcat($$,$1);
} | List_order DESC { strcat($$," DESC"); strcat($$,$1); }
;
%%
void yyerror (char *s) {elog (ERROR, "%s\n", s);}
#include "scan.c"