Parsing complex arithmetic operations.

pull/17/head
Todd Persen 2013-10-08 15:50:59 -04:00
parent 10f571263c
commit 51893ebd37
10 changed files with 1225 additions and 1387 deletions

View File

@ -1,3 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
yacc -t -d query.yacc && lex -i query.lex export PATH=/usr/local/Cellar/bison/3.0/bin:/usr/local/Cellar/flex/2.5.37/bin:$PATH
bison -t -d query.yacc -o y.tab.c --defines=y.tab.h && flex -o lex.yy.c -i query.lex

View File

@ -32,8 +32,12 @@ free_value(value *value)
void void
free_expression(expression *expr) free_expression(expression *expr)
{ {
free_value(expr->left); if (expr->op == 0) {
if (expr->right) free_value(expr->right); free_value((value*)expr->left);
} else {
free_expression((expression*) expr->left);
free_expression(expr->right);
}
free(expr); free(expr);
} }

View File

@ -1,5 +1,6 @@
#line 2 "lex.yy.c"
#line 3 "lex.yy.c" #line 4 "lex.yy.c"
#define YY_INT_ALIGNED short int #define YY_INT_ALIGNED short int
@ -8,7 +9,7 @@
#define FLEX_SCANNER #define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5 #define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 35 #define YY_FLEX_SUBMINOR_VERSION 37
#if YY_FLEX_SUBMINOR_VERSION > 0 #if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA #define FLEX_BETA
#endif #endif
@ -158,15 +159,7 @@ typedef void* yyscan_t;
/* Size of default input buffer. */ /* Size of default input buffer. */
#ifndef YY_BUF_SIZE #ifndef YY_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k.
* Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
* Ditto for the __ia64__ case accordingly.
*/
#define YY_BUF_SIZE 32768
#else
#define YY_BUF_SIZE 16384 #define YY_BUF_SIZE 16384
#endif /* __ia64__ */
#endif #endif
/* The state buf must be large enough to hold one state per character in the main buffer. /* The state buf must be large enough to hold one state per character in the main buffer.
@ -178,6 +171,11 @@ typedef void* yyscan_t;
typedef struct yy_buffer_state *YY_BUFFER_STATE; typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif #endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_CONTINUE_SCAN 0
#define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_END_OF_FILE 1
#define EOB_ACT_LAST_MATCH 2 #define EOB_ACT_LAST_MATCH 2
@ -200,11 +198,6 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE;
#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE #ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state struct yy_buffer_state
@ -222,7 +215,7 @@ struct yy_buffer_state
/* Number of characters read into yy_ch_buf, not including EOB /* Number of characters read into yy_ch_buf, not including EOB
* characters. * characters.
*/ */
int yy_n_chars; yy_size_t yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it, /* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to * and can realloc() it to grow it, and should free() it to
@ -301,7 +294,7 @@ static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
void *yyalloc (yy_size_t ,yyscan_t yyscanner ); void *yyalloc (yy_size_t ,yyscan_t yyscanner );
void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
@ -331,7 +324,7 @@ void yyfree (void * ,yyscan_t yyscanner );
#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
#define yywrap(n) 1 #define yywrap(yyscanner) 1
#define YY_SKIP_YYWRAP #define YY_SKIP_YYWRAP
typedef unsigned char YY_CHAR; typedef unsigned char YY_CHAR;
@ -570,7 +563,7 @@ static yyconst flex_int16_t yy_chk[458] =
yylloc_param->last_column = yycolumn+yyleng-1; \ yylloc_param->last_column = yycolumn+yyleng-1; \
yycolumn += yyleng; \ yycolumn += yyleng; \
} while(0); } while(0);
#line 574 "lex.yy.c" #line 567 "lex.yy.c"
#define INITIAL 0 #define INITIAL 0
@ -599,8 +592,8 @@ struct yyguts_t
size_t yy_buffer_stack_max; /**< capacity of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */
YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
char yy_hold_char; char yy_hold_char;
int yy_n_chars; yy_size_t yy_n_chars;
int yyleng_r; yy_size_t yyleng_r;
char *yy_c_buf_p; char *yy_c_buf_p;
int yy_init; int yy_init;
int yy_start; int yy_start;
@ -657,7 +650,7 @@ FILE *yyget_out (yyscan_t yyscanner );
void yyset_out (FILE * out_str ,yyscan_t yyscanner ); void yyset_out (FILE * out_str ,yyscan_t yyscanner );
int yyget_leng (yyscan_t yyscanner ); yy_size_t yyget_leng (yyscan_t yyscanner );
char *yyget_text (yyscan_t yyscanner ); char *yyget_text (yyscan_t yyscanner );
@ -665,6 +658,10 @@ int yyget_lineno (yyscan_t yyscanner );
void yyset_lineno (int line_number ,yyscan_t yyscanner ); void yyset_lineno (int line_number ,yyscan_t yyscanner );
int yyget_column (yyscan_t yyscanner );
void yyset_column (int column_no ,yyscan_t yyscanner );
YYSTYPE * yyget_lval (yyscan_t yyscanner ); YYSTYPE * yyget_lval (yyscan_t yyscanner );
void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
@ -707,12 +704,7 @@ static int input (yyscan_t yyscanner );
/* Amount of stuff to slurp up with each read. */ /* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE #ifndef YY_READ_BUF_SIZE
#ifdef __ia64__
/* On IA-64, the buffer size is 16k, not 8k */
#define YY_READ_BUF_SIZE 16384
#else
#define YY_READ_BUF_SIZE 8192 #define YY_READ_BUF_SIZE 8192
#endif /* __ia64__ */
#endif #endif
/* Copy whatever the last rule matched to the standard output. */ /* Copy whatever the last rule matched to the standard output. */
@ -819,7 +811,7 @@ YY_DECL
#line 21 "query.lex" #line 21 "query.lex"
#line 823 "lex.yy.c" #line 815 "lex.yy.c"
yylval = yylval_param; yylval = yylval_param;
@ -1059,7 +1051,7 @@ YY_RULE_SETUP
#line 61 "query.lex" #line 61 "query.lex"
ECHO; ECHO;
YY_BREAK YY_BREAK
#line 1063 "lex.yy.c" #line 1055 "lex.yy.c"
case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(INITIAL):
yyterminate(); yyterminate();
@ -1246,21 +1238,21 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
else else
{ {
int num_to_read = yy_size_t num_to_read =
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
while ( num_to_read <= 0 ) while ( num_to_read <= 0 )
{ /* Not enough room in the buffer - grow it. */ { /* Not enough room in the buffer - grow it. */
/* just a shorter name for the current buffer */ /* just a shorter name for the current buffer */
YY_BUFFER_STATE b = YY_CURRENT_BUFFER; YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
int yy_c_buf_p_offset = int yy_c_buf_p_offset =
(int) (yyg->yy_c_buf_p - b->yy_ch_buf); (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
if ( b->yy_is_our_buffer ) if ( b->yy_is_our_buffer )
{ {
int new_size = b->yy_buf_size * 2; yy_size_t new_size = b->yy_buf_size * 2;
if ( new_size <= 0 ) if ( new_size <= 0 )
b->yy_buf_size += b->yy_buf_size / 8; b->yy_buf_size += b->yy_buf_size / 8;
@ -1291,7 +1283,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
/* Read in more data. */ /* Read in more data. */
YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
yyg->yy_n_chars, (size_t) num_to_read ); yyg->yy_n_chars, num_to_read );
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
} }
@ -1388,6 +1380,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
yy_is_jam = (yy_current_state == 72); yy_is_jam = (yy_current_state == 72);
(void)yyg;
return yy_is_jam ? 0 : yy_current_state; return yy_is_jam ? 0 : yy_current_state;
} }
@ -1404,7 +1397,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
{ /* need to shift things up to make room */ { /* need to shift things up to make room */
/* +2 for EOB chars. */ /* +2 for EOB chars. */
register int number_to_move = yyg->yy_n_chars + 2; register yy_size_t number_to_move = yyg->yy_n_chars + 2;
register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
register char *source = register char *source =
@ -1454,7 +1447,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
else else
{ /* need more input */ { /* need more input */
int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
++yyg->yy_c_buf_p; ++yyg->yy_c_buf_p;
switch ( yy_get_next_buffer( yyscanner ) ) switch ( yy_get_next_buffer( yyscanner ) )
@ -1618,10 +1611,6 @@ static void yy_load_buffer_state (yyscan_t yyscanner)
yyfree((void *) b ,yyscanner ); yyfree((void *) b ,yyscanner );
} }
#ifndef __cplusplus
extern int isatty (int );
#endif /* __cplusplus */
/* Initializes or reinitializes a buffer. /* Initializes or reinitializes a buffer.
* This function is sometimes called more than once on the same buffer, * This function is sometimes called more than once on the same buffer,
* such as during a yyrestart() or at EOF. * such as during a yyrestart() or at EOF.
@ -1738,7 +1727,7 @@ void yypop_buffer_state (yyscan_t yyscanner)
*/ */
static void yyensure_buffer_stack (yyscan_t yyscanner) static void yyensure_buffer_stack (yyscan_t yyscanner)
{ {
int num_to_alloc; yy_size_t num_to_alloc;
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
if (!yyg->yy_buffer_stack) { if (!yyg->yy_buffer_stack) {
@ -1836,7 +1825,7 @@ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
* @param yyscanner The scanner object. * @param yyscanner The scanner object.
* @return the newly allocated buffer state object. * @return the newly allocated buffer state object.
*/ */
YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
{ {
YY_BUFFER_STATE b; YY_BUFFER_STATE b;
char *buf; char *buf;
@ -1951,7 +1940,7 @@ FILE *yyget_out (yyscan_t yyscanner)
/** Get the length of the current token. /** Get the length of the current token.
* @param yyscanner The scanner object. * @param yyscanner The scanner object.
*/ */
int yyget_leng (yyscan_t yyscanner) yy_size_t yyget_leng (yyscan_t yyscanner)
{ {
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
return yyleng; return yyleng;
@ -1987,7 +1976,7 @@ void yyset_lineno (int line_number , yyscan_t yyscanner)
/* lineno is only valid if an input buffer exists. */ /* lineno is only valid if an input buffer exists. */
if (! YY_CURRENT_BUFFER ) if (! YY_CURRENT_BUFFER )
yy_fatal_error( "yyset_lineno called with no buffer" , yyscanner); YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
yylineno = line_number; yylineno = line_number;
} }
@ -2002,7 +1991,7 @@ void yyset_column (int column_no , yyscan_t yyscanner)
/* column is only valid if an input buffer exists. */ /* column is only valid if an input buffer exists. */
if (! YY_CURRENT_BUFFER ) if (! YY_CURRENT_BUFFER )
yy_fatal_error( "yyset_column called with no buffer" , yyscanner); YY_FATAL_ERROR( "yyset_column called with no buffer" );
yycolumn = column_no; yycolumn = column_no;
} }

View File

@ -25,9 +25,9 @@ func (self *Value) IsFunctionCall() bool {
} }
type Expression struct { type Expression struct {
Left *Value Left interface{}
Operation byte Operation byte
Right *Value Right *Expression
} }
type BoolExpression struct { type BoolExpression struct {
@ -45,6 +45,20 @@ type WhereCondition struct {
Right *WhereCondition Right *WhereCondition
} }
func (self *WhereCondition) GetBoolExpression() (*BoolExpression, bool) {
if self.isBooleanExpression {
return self.Left.(*BoolExpression), true
}
return nil, false
}
func (self *WhereCondition) GetLeftWhereCondition() (*WhereCondition, bool) {
if !self.isBooleanExpression {
return self.Left.(*WhereCondition), true
}
return nil, false
}
type Query struct { type Query struct {
q C.query q C.query
closed bool closed bool
@ -53,12 +67,6 @@ type Query struct {
groupByClause GroupByClause groupByClause GroupByClause
Limit int Limit int
} }
func (self *WhereCondition) GetBoolExpression() (*BoolExpression, bool) {
if self.isBooleanExpression {
return self.Left.(*BoolExpression), true
}
return nil, false
}
func (self *Query) GetColumnNames() []*Value { func (self *Query) GetColumnNames() []*Value {
if self.ColumnNames != nil { if self.ColumnNames != nil {
@ -73,9 +81,16 @@ func (self *Query) GetFromClause() *Value {
return GetValue(self.q.f) return GetValue(self.q.f)
} }
func (self *Expression) GetSimpleValue() (*Value, bool) { func (self *Expression) GetLeftValue() (*Value, bool) {
if self.Operation == '\000' { if self.Operation == 0 {
return self.Left, true return self.Left.(*Value), true
}
return nil, false
}
func (self *Expression) GetLeftExpression() (*Expression, bool) {
if self.Operation != 0 {
return self.Left.(*Expression), true
} }
return nil, false return nil, false
} }
@ -128,10 +143,14 @@ func GetValue(value *C.value) *Value {
func GetExpression(expr *C.expression) *Expression { func GetExpression(expr *C.expression) *Expression {
expression := &Expression{} expression := &Expression{}
expression.Left = GetValue(expr.left) if expr.op == 0 {
if expr.op != '\000' { expression.Left = GetValue((*C.value)(expr.left))
expression.Operation = byte(expr.op) expression.Operation = byte(expr.op)
expression.Right = GetValue(expr.right) expression.Right = nil
} else {
expression.Left = GetExpression((*C.expression)(expr.left))
expression.Operation = byte(expr.op)
expression.Right = GetExpression((*C.expression)(unsafe.Pointer(expr.right)))
} }
return expression return expression

View File

@ -3,6 +3,7 @@ package query
import ( import (
. "launchpad.net/gocheck" . "launchpad.net/gocheck"
"testing" "testing"
"fmt"
) )
// Hook up gocheck into the gotest runner. // Hook up gocheck into the gotest runner.
@ -21,296 +22,363 @@ func ToValueArray(strings ...string) (values []*Value) {
return return
} }
func (self *QueryParserSuite) TestParseBasicSelectQuery(c *C) { // func (self *QueryParserSuite) TestParseBasicSelectQuery(c *C) {
q, err := ParseQuery("select value from t where c == '5';") // q, err := ParseQuery("select value from t where c == '5';")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.Limit, Equals, 0)
// c.Assert(q.GetColumnNames(), DeepEquals, ToValueArray("value"))
// w := q.GetWhereCondition()
// c.Assert(q.GetFromClause().Name, Equals, "t")
// boolExpression, ok := w.GetBoolExpression()
// c.Assert(ok, Equals, true)
// leftExpression := boolExpression.Left
// rightExpression := boolExpression.Right
// leftValue, ok := leftExpression.GetLeftValue() // simple value is an expression with one value, e.g. it doesn't combine value using arithmetic operations
// rightValue, ok := rightExpression.GetLeftValue()
// c.Assert(leftValue.Name, Equals, "c")
// c.Assert(boolExpression.Operation, Equals, "==")
// c.Assert(rightValue.Name, Equals, "5")
// }
// func (self *QueryParserSuite) TestParseSelectWithoutWhereClause(c *C) {
// q, err := ParseQuery("select value, time from t;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetColumnNames(), DeepEquals, ToValueArray("value", "time"))
// c.Assert(q.GetFromClause().Name, Equals, "t")
// c.Assert(q.GetWhereCondition(), IsNil)
// }
// func (self *QueryParserSuite) TestParseSelectWithUpperCase(c *C) {
// q, err := ParseQuery("SELECT VALUE, TIME FROM t WHERE C == '5';")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetColumnNames(), DeepEquals, ToValueArray("VALUE", "TIME"))
// w := q.GetWhereCondition()
// c.Assert(q.GetFromClause().Name, Equals, "t")
// boolExpression, ok := w.GetBoolExpression()
// c.Assert(ok, Equals, true)
// leftExpression := boolExpression.Left
// rightExpression := boolExpression.Right
// leftValue, ok := leftExpression.GetLeftValue()
// c.Assert(ok, Equals, true)
// rightValue, ok := rightExpression.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(leftValue.Name, Equals, "C")
// c.Assert(rightValue.Name, Equals, "5")
// }
// func (self *QueryParserSuite) TestParseSelectWithMultipleColumns(c *C) {
// q, err := ParseQuery("select value, time from t;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().Name, Equals, "t")
// }
// func (self *QueryParserSuite) TestParseSelectWithInequality(c *C) {
// q, err := ParseQuery("select value, time from t where c < 5;")
// defer q.Close()
// c.Assert(err, IsNil)
// w := q.GetWhereCondition()
// c.Assert(q.GetFromClause().Name, Equals, "t")
// boolExpression, ok := w.GetBoolExpression()
// c.Assert(ok, Equals, true)
// leftExpression := boolExpression.Left
// rightExpression := boolExpression.Right
// leftValue, ok := leftExpression.GetLeftValue()
// c.Assert(ok, Equals, true)
// rightValue, ok := rightExpression.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(leftValue.Name, Equals, "c")
// c.Assert(boolExpression.Operation, Equals, "<")
// c.Assert(rightValue.Name, Equals, "5")
// }
// func (self *QueryParserSuite) TestParseSelectWithTimeCondition(c *C) {
// q, err := ParseQuery("select value, time from t where time > now() - 1d;")
// defer q.Close()
// c.Assert(err, IsNil)
// w := q.GetWhereCondition()
// c.Assert(q.GetFromClause().Name, Equals, "t")
// boolExpression, ok := w.GetBoolExpression()
// c.Assert(ok, Equals, true)
// leftExpression := boolExpression.Left
// leftValue, ok := leftExpression.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(leftValue.Name, Equals, "time")
// rightExpression := boolExpression.Right
// funCallExpr, ok := rightExpression.GetLeftExpression()
// c.Assert(ok, Equals, true)
// funCall, ok := funCallExpr.GetLeftValue()
// c.Assert(ok, Equals, true)
// oneDay, ok := rightExpression.Right.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(funCall.IsFunctionCall(), Equals, true)
// c.Assert(funCall.Name, Equals, "now")
// c.Assert(oneDay.IsFunctionCall(), Equals, false)
// c.Assert(oneDay.Name, Equals, "1d")
// c.Assert(rightExpression.Operation, Equals, byte('-'))
// }
// func (self *QueryParserSuite) TestParseSelectWithAnd(c *C) {
// q, err := ParseQuery("select value from cpu.idle where time>now()-7d and time<now()-6d;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().Name, Equals, "cpu.idle")
// w := q.GetWhereCondition()
// c.Assert(w.Operation, Equals, "AND")
// // leftBoolExpression = 'time > now() - 7d'
// leftWhereCondition, ok := w.GetLeftWhereCondition()
// c.Assert(ok, Equals, true)
// leftBoolExpression, ok := leftWhereCondition.GetBoolExpression()
// c.Assert(ok, Equals, true)
// // rightBoolExpression = 'time < now() - 6d'
// rightBoolExpression, ok := w.Right.GetBoolExpression()
// c.Assert(ok, Equals, true)
// c.Assert(leftBoolExpression.Left.Left, DeepEquals, &Value{"time", nil})
// expr, ok := leftBoolExpression.Right.GetLeftExpression()
// c.Assert(ok, Equals, true)
// value, ok := expr.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(value, DeepEquals, &Value{"now", []*Value{}})
// value, ok = leftBoolExpression.Right.Right.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(value, DeepEquals, &Value{"7d", nil})
// c.Assert(leftBoolExpression.Operation, Equals, ">")
// c.Assert(rightBoolExpression.Left.Left, DeepEquals, &Value{"time", nil})
// expr, ok = rightBoolExpression.Right.GetLeftExpression()
// c.Assert(ok, Equals, true)
// value, ok = expr.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(value, DeepEquals, &Value{"now", []*Value{}})
// value, ok = rightBoolExpression.Right.Right.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(value, DeepEquals, &Value{"6d", nil})
// c.Assert(rightBoolExpression.Operation, Equals, "<")
// }
// func (self *QueryParserSuite) TestParseSelectWithGroupBy(c *C) {
// q, err := ParseQuery("select count(*) from users.events group_by user_email,time(1h) where time>now()-1d;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().Name, Equals, "users.events")
// c.Assert(q.GetColumnNames(), HasLen, 1)
// column := q.GetColumnNames()[0]
// c.Assert(column.IsFunctionCall(), Equals, true)
// c.Assert(column.Name, Equals, "count")
// c.Assert(column.Elems, HasLen, 1)
// c.Assert(column.Elems[0].IsFunctionCall(), Equals, false)
// c.Assert(column.Elems[0].Name, Equals, "*")
// groupBy := q.GetGroupByClause()
// c.Assert(groupBy, HasLen, 2)
// c.Assert(groupBy[0].IsFunctionCall(), Equals, false)
// c.Assert(groupBy[0].Name, Equals, "user_email")
// c.Assert(groupBy[1].IsFunctionCall(), Equals, true)
// c.Assert(groupBy[1].Name, Equals, "time")
// c.Assert(groupBy[1].Elems, HasLen, 1)
// c.Assert(groupBy[1].Elems[0].Name, Equals, "1h")
// }
// func (self *QueryParserSuite) TestParseFromWithNestedFunctions(c *C) {
// q, err := ParseQuery("select top(10, count(*)) from users.events;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetColumnNames(), HasLen, 1)
// column := q.GetColumnNames()[0]
// c.Assert(column.IsFunctionCall(), Equals, true)
// c.Assert(column.Name, Equals, "top")
// c.Assert(column.Elems, HasLen, 2)
// c.Assert(column.Elems[0].IsFunctionCall(), Equals, false)
// c.Assert(column.Elems[0].Name, Equals, "10")
// c.Assert(column.Elems[1].IsFunctionCall(), Equals, true)
// c.Assert(column.Elems[1].Name, Equals, "count")
// c.Assert(column.Elems[1].Elems, HasLen, 1)
// c.Assert(column.Elems[1].Elems[0].IsFunctionCall(), Equals, false)
// c.Assert(column.Elems[1].Elems[0].Name, Equals, "*")
// }
// func (self *QueryParserSuite) TestParseWhereClausePrecedence(c *C) {
// q, err := ParseQuery("select value from cpu.idle where value > 90 and time > now() - 1d or value > 80 and time > now() - 1w;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().Name, Equals, "cpu.idle")
// whereCondition := q.GetWhereCondition()
// c.Assert(whereCondition.Operation, Equals, "OR")
// leftCondition, ok := whereCondition.Left.(*WhereCondition)
// c.Assert(ok, Equals, true)
// c.Assert(leftCondition.Operation, Equals, "AND")
// condition, ok := leftCondition.GetLeftWhereCondition()
// c.Assert(ok, Equals, true)
// leftExpression, ok := condition.GetBoolExpression()
// c.Assert(ok, Equals, true)
// c.Assert(leftExpression.Operation, Equals, ">")
// c.Assert(leftExpression.Left.Left, DeepEquals, &Value{"value", nil})
// c.Assert(leftExpression.Right.Left, DeepEquals, &Value{"90", nil})
// rightExpression, ok := leftCondition.Right.GetBoolExpression()
// c.Assert(ok, Equals, true)
// c.Assert(rightExpression.Operation, Equals, ">")
// c.Assert(rightExpression.Left.Left, DeepEquals, &Value{"time", nil})
// expr, ok := rightExpression.Right.GetLeftExpression()
// value, ok := expr.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(value, DeepEquals, &Value{"now", []*Value{}})
// value, ok = rightExpression.Right.Right.GetLeftValue()
// c.Assert(ok, Equals, true)
// c.Assert(value, DeepEquals, &Value{"1d", nil})
// }
// func (self *QueryParserSuite) TestParseWhereClauseParantheses(c *C) {
// q, err := ParseQuery("select value from cpu.idle where value > 90 and (time > now() - 1d or value > 80) and time < now() - 1w;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().Name, Equals, "cpu.idle")
// whereCondition := q.GetWhereCondition()
// first := whereCondition.Left.(*WhereCondition).Left.(*WhereCondition).Left.(*BoolExpression)
// second := whereCondition.Left.(*WhereCondition).Right
// third := whereCondition.Right.Left.(*BoolExpression)
// c.Assert(first.Operation, Equals, ">")
// c.Assert(second.Operation, Equals, "OR")
// c.Assert(third.Operation, Equals, "<")
// }
// func (self *QueryParserSuite) TestParseSelectWithLast(c *C) {
// q, err := ParseQuery("select value from t last 10;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.Limit, Equals, -10)
// q, err = ParseQuery("select value from t first 10;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.Limit, Equals, 10)
// }
// func (self *QueryParserSuite) TestParseFromWithNestedFunctions2(c *C) {
// q, err := ParseQuery("select count(distinct(email)) from user.events where time>now()-1d group_by time(15m);")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetColumnNames(), HasLen, 1)
// column := q.GetColumnNames()[0]
// c.Assert(column.IsFunctionCall(), Equals, true)
// c.Assert(column.Name, Equals, "count")
// c.Assert(column.Elems, HasLen, 1)
// c.Assert(column.Elems[0].IsFunctionCall(), Equals, true)
// c.Assert(column.Elems[0].Name, Equals, "distinct")
// c.Assert(column.Elems[0].Elems, HasLen, 1)
// c.Assert(column.Elems[0].Elems[0].Name, Equals, "email")
// c.Assert(q.GetGroupByClause(), HasLen, 1)
// c.Assert(q.GetGroupByClause()[0], DeepEquals, &Value{
// Name: "time",
// Elems: []*Value{&Value{"15m", nil}},
// })
// }
// func (self *QueryParserSuite) TestParseFromWithMergedTable(c *C) {
// q, err := ParseQuery("select count(*) from merge(newsletter.signups,user.signups) where time>now()-1d;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().IsFunctionCall(), Equals, true)
// c.Assert(q.GetFromClause().Name, Equals, "merge")
// c.Assert(q.GetFromClause().Elems, HasLen, 2)
// c.Assert(q.GetFromClause().Elems[0].Name, Equals, "newsletter.signups")
// c.Assert(q.GetFromClause().Elems[1].Name, Equals, "user.signups")
// }
// func (self *QueryParserSuite) TestParseFromWithJoinedTable(c *C) {
// q, err := ParseQuery("select max(t1.value, t2.value) from inner_join(newsletter.signups, t1, user.signups, t2) where time>now()-1d;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().IsFunctionCall(), Equals, true)
// c.Assert(q.GetFromClause().Name, Equals, "inner_join")
// c.Assert(q.GetFromClause().Elems, HasLen, 4)
// c.Assert(q.GetFromClause().Elems[0].Name, Equals, "newsletter.signups")
// c.Assert(q.GetFromClause().Elems[1].Name, Equals, "t1")
// c.Assert(q.GetFromClause().Elems[2].Name, Equals, "user.signups")
// c.Assert(q.GetFromClause().Elems[3].Name, Equals, "t2")
// }
// func (self *QueryParserSuite) TestParseSelectWithRegexCondition(c *C) {
// q, err := ParseQuery("select email from users.events where email ~= /gmail\\.com/i and time>now()-2d;")
// defer q.Close()
// c.Assert(err, IsNil)
// w := q.GetWhereCondition()
// regexExpression := w.Left.(*WhereCondition).Left.(*BoolExpression)
// c.Assert(regexExpression.Left.Left, DeepEquals, &Value{"email", nil})
// c.Assert(regexExpression.Operation, Equals, "~=")
// c.Assert(regexExpression.Right.Left, DeepEquals, &Value{"/gmail\\.com/i", nil})
// }
// func (self *QueryParserSuite) TestParseSelectWithRegexTables(c *C) {
// q, err := ParseQuery("select email from users.* where time>now()-2d;")
// defer q.Close()
// c.Assert(err, IsNil)
// c.Assert(q.GetFromClause().Name, Equals, "users.*")
// }
func (self *QueryParserSuite) TestParseSelectWithComplexArithmeticOperations(c *C) {
q, err := ParseQuery("select value from cpu.idle where 30 < value * 1 / 3 ;")
defer q.Close() defer q.Close()
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(q.Limit, Equals, 0) c.Assert(q.GetFromClause().Name, Equals, "cpu.idle")
c.Assert(q.GetColumnNames(), DeepEquals, ToValueArray("value")) boolExpression, ok := q.GetWhereCondition().GetBoolExpression()
w := q.GetWhereCondition()
c.Assert(q.GetFromClause().Name, Equals, "t")
boolExpression, ok := w.GetBoolExpression()
c.Assert(ok, Equals, true) c.Assert(ok, Equals, true)
leftExpression := boolExpression.Left c.Assert(boolExpression.Left.Left, DeepEquals, &Value{"30", nil})
rightExpression := boolExpression.Right
leftValue, ok := leftExpression.GetSimpleValue() // simple value is an expression with one value, e.g. it doesn't combine value using arithmetic operations
rightValue, ok := rightExpression.GetSimpleValue()
c.Assert(leftValue.Name, Equals, "c") // value * 1 / 3
c.Assert(boolExpression.Operation, Equals, "==") rightExpression := boolExpression.Right.Right
c.Assert(rightValue.Name, Equals, "5")
}
func (self *QueryParserSuite) TestParseSelectWithoutWhereClause(c *C) { // value * 1
q, err := ParseQuery("select value, time from t;") left, ok := rightExpression.GetLeftExpression()
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetColumnNames(), DeepEquals, ToValueArray("value", "time"))
c.Assert(q.GetFromClause().Name, Equals, "t")
c.Assert(q.GetWhereCondition(), IsNil)
}
func (self *QueryParserSuite) TestParseSelectWithUpperCase(c *C) {
q, err := ParseQuery("SELECT VALUE, TIME FROM t WHERE C == '5';")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetColumnNames(), DeepEquals, ToValueArray("VALUE", "TIME"))
w := q.GetWhereCondition()
c.Assert(q.GetFromClause().Name, Equals, "t")
boolExpression := w.Left.(*BoolExpression)
leftExpression := boolExpression.Left
rightExpression := boolExpression.Right
leftValue := leftExpression.Left.Name
rightValue := rightExpression.Left.Name
c.Assert(leftValue, Equals, "C")
c.Assert(rightValue, Equals, "5")
}
func (self *QueryParserSuite) TestParseSelectWithMultipleColumns(c *C) {
q, err := ParseQuery("select value, time from t;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetFromClause().Name, Equals, "t")
}
func (self *QueryParserSuite) TestParseSelectWithInequality(c *C) {
q, err := ParseQuery("select value, time from t where c < 5;")
defer q.Close()
c.Assert(err, IsNil)
w := q.GetWhereCondition()
c.Assert(q.GetFromClause().Name, Equals, "t")
boolExpression := w.Left.(*BoolExpression)
leftExpression := boolExpression.Left
rightExpression := boolExpression.Right
leftValue := leftExpression.Left.Name
rightValue := rightExpression.Left.Name
c.Assert(leftValue, Equals, "c")
c.Assert(boolExpression.Operation, Equals, "<")
c.Assert(rightValue, Equals, "5")
}
func (self *QueryParserSuite) TestParseSelectWithTimeCondition(c *C) {
q, err := ParseQuery("select value, time from t where time > now() - 1d;")
defer q.Close()
c.Assert(err, IsNil)
w := q.GetWhereCondition()
boolExpression := w.Left.(*BoolExpression)
leftExpression := boolExpression.Left
rightExpression := boolExpression.Right
leftValue := leftExpression.Left.Name
funCall := rightExpression.Left
oneDay := rightExpression.Right
c.Assert(q.GetFromClause().Name, Equals, "t")
c.Assert(leftValue, Equals, "time")
c.Assert(funCall.IsFunctionCall(), Equals, true)
c.Assert(funCall.Name, Equals, "now")
c.Assert(oneDay.IsFunctionCall(), Equals, false)
c.Assert(oneDay.Name, Equals, "1d")
c.Assert(rightExpression.Operation, Equals, byte('-'))
}
func (self *QueryParserSuite) TestParseSelectWithAnd(c *C) {
q, err := ParseQuery("select value from cpu.idle where time>now()-7d and time<now()-6d;")
defer q.Close()
c.Assert(err, IsNil)
w := q.GetWhereCondition()
c.Assert(q.GetFromClause().Name, Equals, "cpu.idle")
leftBoolExpression := w.Left.(*WhereCondition).Left.(*BoolExpression)
c.Assert(w.Operation, Equals, "AND")
rightBoolExpression := w.Right.Left.(*BoolExpression)
c.Assert(leftBoolExpression.Left.Left.Name, Equals, "time")
c.Assert(leftBoolExpression.Left.Left.IsFunctionCall(), Equals, false)
c.Assert(leftBoolExpression.Right.Left.Name, Equals, "now")
c.Assert(leftBoolExpression.Right.Left.IsFunctionCall(), Equals, true)
c.Assert(leftBoolExpression.Right.Right.Name, Equals, "7d")
c.Assert(leftBoolExpression.Right.Right.IsFunctionCall(), Equals, false)
c.Assert(leftBoolExpression.Operation, Equals, ">")
c.Assert(rightBoolExpression.Left.Left.Name, Equals, "time")
c.Assert(rightBoolExpression.Left.Left.IsFunctionCall(), Equals, false)
c.Assert(rightBoolExpression.Right.Left.Name, Equals, "now")
c.Assert(rightBoolExpression.Right.Left.IsFunctionCall(), Equals, true)
c.Assert(rightBoolExpression.Right.Right.Name, Equals, "6d")
c.Assert(rightBoolExpression.Right.Right.IsFunctionCall(), Equals, false)
c.Assert(rightBoolExpression.Operation, Equals, "<")
}
func (self *QueryParserSuite) TestParseSelectWithGroupBy(c *C) {
q, err := ParseQuery("select count(*) from users.events group_by user_email,time(1h) where time>now()-1d;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetColumnNames(), HasLen, 1)
column := q.GetColumnNames()[0]
c.Assert(column.IsFunctionCall(), Equals, true)
c.Assert(column.Name, Equals, "count")
c.Assert(column.Elems, HasLen, 1)
c.Assert(column.Elems[0].IsFunctionCall(), Equals, false)
c.Assert(column.Elems[0].Name, Equals, "*")
groupBy := q.GetGroupByClause()
c.Assert(groupBy, HasLen, 2)
c.Assert(groupBy[0].IsFunctionCall(), Equals, false)
c.Assert(groupBy[0].Name, Equals, "user_email")
c.Assert(groupBy[1].IsFunctionCall(), Equals, true)
c.Assert(groupBy[1].Name, Equals, "time")
c.Assert(groupBy[1].Elems, HasLen, 1)
c.Assert(groupBy[1].Elems[0].Name, Equals, "1h")
c.Assert(q.GetFromClause().Name, Equals, "users.events")
}
func (self *QueryParserSuite) TestParseFromWithNestedFunctions(c *C) {
q, err := ParseQuery("select top(10, count(*)) from users.events;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetColumnNames(), HasLen, 1)
column := q.GetColumnNames()[0]
c.Assert(column.IsFunctionCall(), Equals, true)
c.Assert(column.Name, Equals, "top")
c.Assert(column.Elems, HasLen, 2)
c.Assert(column.Elems[0].IsFunctionCall(), Equals, false)
c.Assert(column.Elems[0].Name, Equals, "10")
c.Assert(column.Elems[1].IsFunctionCall(), Equals, true)
c.Assert(column.Elems[1].Name, Equals, "count")
c.Assert(column.Elems[1].Elems, HasLen, 1)
c.Assert(column.Elems[1].Elems[0].IsFunctionCall(), Equals, false)
c.Assert(column.Elems[1].Elems[0].Name, Equals, "*")
}
func (self *QueryParserSuite) TestParseWhereClausePrecedence(c *C) {
q, err := ParseQuery("select value from cpu.idle where value > 90 and time > now() - 1d or value > 80 and time > now() - 1w;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetFromClause().Name, Equals, "cpu.idle")
whereCondition := q.GetWhereCondition()
c.Assert(whereCondition.Operation, Equals, "OR")
leftCondition, ok := whereCondition.Left.(*WhereCondition)
c.Assert(ok, Equals, true) c.Assert(ok, Equals, true)
c.Assert(leftCondition.Operation, Equals, "AND") fmt.Printf("left: %#v\n", left.Left)
c.Assert(left.Operation, Equals, "*")
leftExpression := leftCondition.Left.(*WhereCondition).Left.(*BoolExpression) _value, _ := left.GetLeftExpression()
c.Assert(leftExpression.Operation, Equals, ">") value, _ := _value.GetLeftValue()
c.Assert(leftExpression.Left.Left.Name, Equals, "value") c.Assert(value.Name, Equals, "value")
c.Assert(leftExpression.Right.Left.Name, Equals, "90") one, _ := left.Right.GetLeftValue()
c.Assert(one.Name, Equals, "1")
rightExpression := leftCondition.Right.Left.(*BoolExpression) // right = '3'
c.Assert(rightExpression.Operation, Equals, ">")
c.Assert(rightExpression.Left.Left.Name, Equals, "time")
c.Assert(rightExpression.Right.Left.Name, Equals, "now")
c.Assert(rightExpression.Right.Right.Name, Equals, "1d")
}
func (self *QueryParserSuite) TestParseWhereClauseParantheses(c *C) {
q, err := ParseQuery("select value from cpu.idle where value > 90 and (time > now() - 1d or value > 80) and time < now() - 1w;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetFromClause().Name, Equals, "cpu.idle")
whereCondition := q.GetWhereCondition()
first := whereCondition.Left.(*WhereCondition).Left.(*WhereCondition).Left.(*BoolExpression)
second := whereCondition.Left.(*WhereCondition).Right
third := whereCondition.Right.Left.(*BoolExpression)
c.Assert(first.Operation, Equals, ">")
c.Assert(second.Operation, Equals, "OR")
c.Assert(third.Operation, Equals, "<")
}
func (self *QueryParserSuite) TestParseSelectWithLast(c *C) {
q, err := ParseQuery("select value from t last 10;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.Limit, Equals, -10)
q, err = ParseQuery("select value from t first 10;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.Limit, Equals, 10)
}
func (self *QueryParserSuite) TestParseFromWithNestedFunctions2(c *C) {
q, err := ParseQuery("select count(distinct(email)) from user.events where time>now()-1d group_by time(15m);")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetColumnNames(), HasLen, 1)
column := q.GetColumnNames()[0]
c.Assert(column.IsFunctionCall(), Equals, true)
c.Assert(column.Name, Equals, "count")
c.Assert(column.Elems, HasLen, 1)
c.Assert(column.Elems[0].IsFunctionCall(), Equals, true)
c.Assert(column.Elems[0].Name, Equals, "distinct")
c.Assert(column.Elems[0].Elems, HasLen, 1)
c.Assert(column.Elems[0].Elems[0].Name, Equals, "email")
c.Assert(q.GetGroupByClause(), HasLen, 1)
c.Assert(q.GetGroupByClause()[0], DeepEquals, &Value{
Name: "time",
Elems: []*Value{&Value{"15m", nil}},
})
}
func (self *QueryParserSuite) TestParseFromWithMergedTable(c *C) {
q, err := ParseQuery("select count(*) from merge(newsletter.signups,user.signups) where time>now()-1d;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetFromClause().IsFunctionCall(), Equals, true)
c.Assert(q.GetFromClause().Name, Equals, "merge")
c.Assert(q.GetFromClause().Elems, HasLen, 2)
c.Assert(q.GetFromClause().Elems[0].Name, Equals, "newsletter.signups")
c.Assert(q.GetFromClause().Elems[1].Name, Equals, "user.signups")
}
func (self *QueryParserSuite) TestParseFromWithJoinedTable(c *C) {
q, err := ParseQuery("select max(t1.value, t2.value) from inner_join(newsletter.signups, t1, user.signups, t2) where time>now()-1d;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetFromClause().IsFunctionCall(), Equals, true)
c.Assert(q.GetFromClause().Name, Equals, "inner_join")
c.Assert(q.GetFromClause().Elems, HasLen, 4)
c.Assert(q.GetFromClause().Elems[0].Name, Equals, "newsletter.signups")
c.Assert(q.GetFromClause().Elems[1].Name, Equals, "t1")
c.Assert(q.GetFromClause().Elems[2].Name, Equals, "user.signups")
c.Assert(q.GetFromClause().Elems[3].Name, Equals, "t2")
}
func (self *QueryParserSuite) TestParseSelectWithRegexCondition(c *C) {
q, err := ParseQuery("select email from users.events where email ~= /gmail\\.com/i and time>now()-2d;")
defer q.Close()
c.Assert(err, IsNil)
w := q.GetWhereCondition()
regexExpression := w.Left.(*WhereCondition).Left.(*BoolExpression)
c.Assert(regexExpression.Left.Left.Name, Equals, "email")
c.Assert(regexExpression.Operation, Equals, "~=")
c.Assert(regexExpression.Right.Left.Name, Equals, "/gmail\\.com/i")
}
func (self *QueryParserSuite) TestParseSelectWithRegexTables(c *C) {
q, err := ParseQuery("select email from users.* where time>now()-2d;")
defer q.Close()
c.Assert(err, IsNil)
c.Assert(q.GetFromClause().Name, Equals, "users.*")
} }
// TODO: // TODO:

View File

@ -11,7 +11,6 @@
%union { %union {
char character; char character;
char* string; char* string;
array* arr;
int integer; int integer;
condition* condition; condition* condition;
bool_expression* bool_expression; bool_expression* bool_expression;
@ -65,7 +64,6 @@
// destructors are used to free up memory in case of an error // destructors are used to free up memory in case of an error
%destructor { free_value($$); } <v> %destructor { free_value($$); } <v>
%destructor { if ($$) free_condition($$); } <condition> %destructor { if ($$) free_condition($$); } <condition>
%destructor { free_array($$); } <arr>
%destructor { free($$); } <string> %destructor { free($$); } <string>
%destructor { free_expression($$); } <expression> %destructor { free_expression($$); } <expression>
%destructor { if ($$) free_value_array($$); } <value_array> %destructor { if ($$) free_value_array($$); } <value_array>
@ -210,9 +208,15 @@ EXPRESSION:
$$->right = NULL; $$->right = NULL;
} }
| |
VALUE ARITHMETIC_OPERATION VALUE '(' EXPRESSION ')'
{
$$ = $2;
}
|
EXPRESSION ARITHMETIC_OPERATION EXPRESSION
{ {
$$ = malloc(sizeof(expression)); $$ = malloc(sizeof(expression));
printf("operation: %c\n", $2);
$$->left = $1; $$->left = $1;
$$->op = $2; $$->op = $2;
$$->right = $3; $$->right = $3;
@ -313,7 +317,7 @@ parse_query(char *const query_s)
{ {
query q; query q;
q.error = NULL; q.error = NULL;
/* yydebug = 1; */ yydebug = 1;
void *scanner; void *scanner;
yylex_init(&scanner); yylex_init(&scanner);
void *buffer = yy_scan_string(query_s, scanner); void *buffer = yy_scan_string(query_s, scanner);

View File

@ -20,10 +20,10 @@ typedef struct value_t {
value_array *args; value_array *args;
} value; } value;
typedef struct { typedef struct expression_t {
value *left; void *left; /* this can be a *value or *expression */
char op; /* +, -, *, / or \0 if there's no right operand */ char op; /* +, -, *, / or \0 if there's no right operand */
value *right; struct expression_t *right;
} expression; } expression;
typedef struct { typedef struct {

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/* A Bison parser, made by GNU Bison 2.5. */ /* A Bison parser, made by GNU Bison 3.0. */
/* Bison interface for Yacc-like parsers in C /* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -30,13 +30,21 @@
This special exception was added by the Free Software Foundation in This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */ version 2.2 of Bison. */
#ifndef YY_YY_Y_TAB_H_INCLUDED
# define YY_YY_Y_TAB_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 1
#endif
#if YYDEBUG
extern int yydebug;
#endif
/* Tokens. */ /* Token type. */
#ifndef YYTOKENTYPE #ifndef YYTOKENTYPE
# define YYTOKENTYPE # define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers enum yytokentype
know about them. */ {
enum yytokentype {
SELECT = 258, SELECT = 258,
FROM = 259, FROM = 259,
WHERE = 260, WHERE = 260,
@ -51,49 +59,24 @@
REGEX_STRING = 269, REGEX_STRING = 269,
OR = 270, OR = 270,
AND = 271, AND = 271,
OPERATION_GE = 272, OPERATION_EQUAL = 272,
OPERATION_LE = 273, OPERATION_NE = 273,
OPERATION_LT = 274, OPERATION_GT = 274,
OPERATION_GT = 275, OPERATION_LT = 275,
OPERATION_NE = 276, OPERATION_LE = 276,
OPERATION_EQUAL = 277 OPERATION_GE = 277
}; };
#endif #endif
/* Tokens. */
#define SELECT 258
#define FROM 259
#define WHERE 260
#define EQUAL 261
#define GROUP_BY 262
#define FIRST 263
#define LAST 264
#define STRING_VALUE 265
#define INT_VALUE 266
#define NAME 267
#define REGEX_OP 268
#define REGEX_STRING 269
#define OR 270
#define AND 271
#define OPERATION_GE 272
#define OPERATION_LE 273
#define OPERATION_LT 274
#define OPERATION_GT 275
#define OPERATION_NE 276
#define OPERATION_EQUAL 277
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE typedef union YYSTYPE YYSTYPE;
union YYSTYPE
{ {
#line 11 "query.yacc" /* yacc.c:1915 */
/* Line 2068 of yacc.c */
#line 11 "query.yacc"
char character; char character;
char* string; char* string;
array* arr;
int integer; int integer;
condition* condition; condition* condition;
bool_expression* bool_expression; bool_expression* bool_expression;
@ -101,30 +84,28 @@ typedef union YYSTYPE
value_array* value_array; value_array* value_array;
value* v; value* v;
#line 88 "y.tab.h" /* yacc.c:1915 */
};
/* Line 2068 of yacc.c */
#line 108 "y.tab.h"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_DECLARED 1
#endif #endif
/* Location type. */
#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
typedef struct YYLTYPE typedef struct YYLTYPE YYLTYPE;
struct YYLTYPE
{ {
int first_line; int first_line;
int first_column; int first_column;
int last_line; int last_line;
int last_column; int last_column;
} YYLTYPE; };
# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
# define YYLTYPE_IS_DECLARED 1 # define YYLTYPE_IS_DECLARED 1
# define YYLTYPE_IS_TRIVIAL 1 # define YYLTYPE_IS_TRIVIAL 1
#endif #endif
int yyparse (query *q, void *scanner);
#endif /* !YY_YY_Y_TAB_H_INCLUDED */

View File

@ -5,15 +5,15 @@ set -e
cd `dirname $0` cd `dirname $0`
. exports.sh . exports.sh
pushd src/query
./build_parser.sh
if [ "x`uname`" == "xLinux" ]; then if [ "x`uname`" == "xLinux" ]; then
pushd src/query
./build_parser.sh
if ! ./test_memory_leaks.sh; then if ! ./test_memory_leaks.sh; then
echo "ERROR: memory leak detected" echo "ERROR: memory leak detected"
exit 1 exit 1
fi fi
popd
fi fi
popd
go get launchpad.net/gocheck go get launchpad.net/gocheck