|  | %{ | 
|  |  | 
|  | /* | 
|  | * Implementation of the Microsoft Installer (msi.dll) | 
|  | * | 
|  | * Copyright 2002-2004 Mike McCormack for CodeWeavers | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "query.h" | 
|  | #include "wine/list.h" | 
|  | #include "wine/debug.h" | 
|  | #include "wine/unicode.h" | 
|  |  | 
|  | #define YYLEX_PARAM info | 
|  | #define YYPARSE_PARAM info | 
|  |  | 
|  | static int sql_error(const char *str); | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(msi); | 
|  |  | 
|  | typedef struct tag_SQL_input | 
|  | { | 
|  | MSIDATABASE *db; | 
|  | LPCWSTR command; | 
|  | DWORD n, len; | 
|  | UINT r; | 
|  | MSIVIEW **view;  /* View structure for the resulting query.  This value | 
|  | * tracks the view currently being created so we can free | 
|  | * this view on syntax error. | 
|  | */ | 
|  | struct list *mem; | 
|  | } SQL_input; | 
|  |  | 
|  | static UINT SQL_getstring( void *info, const struct sql_str *strdata, LPWSTR *str ); | 
|  | static INT SQL_getint( void *info ); | 
|  | static int sql_lex( void *SQL_lval, SQL_input *info ); | 
|  |  | 
|  | static LPWSTR parser_add_table( void *info, LPCWSTR list, LPCWSTR table ); | 
|  | static void *parser_alloc( void *info, unsigned int sz ); | 
|  | static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ); | 
|  |  | 
|  | static BOOL SQL_MarkPrimaryKeys( column_info **cols, column_info *keys); | 
|  |  | 
|  | static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ); | 
|  | static struct expr * EXPR_unary( void *info, struct expr *l, UINT op ); | 
|  | static struct expr * EXPR_column( void *info, const column_info *column ); | 
|  | static struct expr * EXPR_ival( void *info, int val ); | 
|  | static struct expr * EXPR_sval( void *info, const struct sql_str *str ); | 
|  | static struct expr * EXPR_wildcard( void *info ); | 
|  |  | 
|  | #define PARSER_BUBBLE_UP_VIEW( sql, result, current_view ) \ | 
|  | *sql->view = current_view; \ | 
|  | result = current_view | 
|  |  | 
|  | %} | 
|  |  | 
|  | %pure-parser | 
|  |  | 
|  | %union | 
|  | { | 
|  | struct sql_str str; | 
|  | LPWSTR string; | 
|  | column_info *column_list; | 
|  | MSIVIEW *query; | 
|  | struct expr *expr; | 
|  | USHORT column_type; | 
|  | int integer; | 
|  | } | 
|  |  | 
|  | %token TK_ALTER TK_AND TK_BY TK_CHAR TK_COMMA TK_CREATE TK_DELETE TK_DROP | 
|  | %token TK_DISTINCT TK_DOT TK_EQ TK_FREE TK_FROM TK_GE TK_GT TK_HOLD TK_ADD | 
|  | %token <str> TK_ID | 
|  | %token TK_ILLEGAL TK_INSERT TK_INT | 
|  | %token <str> TK_INTEGER | 
|  | %token TK_INTO TK_IS TK_KEY TK_LE TK_LONG TK_LONGCHAR TK_LP TK_LT | 
|  | %token TK_LOCALIZABLE TK_MINUS TK_NE TK_NOT TK_NULL | 
|  | %token TK_OBJECT TK_OR TK_ORDER TK_PRIMARY TK_RP | 
|  | %token TK_SELECT TK_SET TK_SHORT TK_SPACE TK_STAR | 
|  | %token <str> TK_STRING | 
|  | %token TK_TABLE TK_TEMPORARY TK_UPDATE TK_VALUES TK_WHERE TK_WILDCARD | 
|  |  | 
|  | /* | 
|  | * These are extra tokens used by the lexer but never seen by the | 
|  | * parser.  We put them in a rule so that the parser generator will | 
|  | * add them to the parse.h output file. | 
|  | * | 
|  | */ | 
|  | %nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION | 
|  | COLUMN AGG_FUNCTION. | 
|  |  | 
|  | %type <string> table tablelist id string | 
|  | %type <column_list> selcollist collist selcolumn column column_and_type column_def table_def | 
|  | %type <column_list> column_assignment update_assign_list constlist | 
|  | %type <query> query from selectfrom unorderdfrom | 
|  | %type <query> oneupdate onedelete oneselect onequery onecreate oneinsert onealter onedrop | 
|  | %type <expr> expr val column_val const_val | 
|  | %type <column_type> column_type data_type data_type_l data_count | 
|  | %type <integer> number alterop | 
|  |  | 
|  | %left TK_OR | 
|  | %left TK_AND | 
|  | %left TK_NOT | 
|  | %left TK_EQ TK_NE TK_LT TK_GT TK_LE TK_GE TK_LIKE | 
|  | %right TK_NEGATION | 
|  |  | 
|  | %% | 
|  |  | 
|  | query: | 
|  | onequery | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | *sql->view = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | onequery: | 
|  | oneselect | 
|  | | onecreate | 
|  | | oneinsert | 
|  | | oneupdate | 
|  | | onedelete | 
|  | | onealter | 
|  | | onedrop | 
|  | ; | 
|  |  | 
|  | oneinsert: | 
|  | TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP | 
|  | { | 
|  | SQL_input *sql = (SQL_input*) info; | 
|  | MSIVIEW *insert = NULL; | 
|  |  | 
|  | INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE ); | 
|  | if( !insert ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$,  insert ); | 
|  | } | 
|  | | TK_INSERT TK_INTO table TK_LP collist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMPORARY | 
|  | { | 
|  | SQL_input *sql = (SQL_input*) info; | 
|  | MSIVIEW *insert = NULL; | 
|  |  | 
|  | INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE ); | 
|  | if( !insert ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$,  insert ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | onecreate: | 
|  | TK_CREATE TK_TABLE table TK_LP table_def TK_RP | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW *create = NULL; | 
|  | UINT r; | 
|  |  | 
|  | if( !$5 ) | 
|  | YYABORT; | 
|  | r = CREATE_CreateView( sql->db, &create, $3, $5, FALSE ); | 
|  | if( !create ) | 
|  | { | 
|  | sql->r = r; | 
|  | YYABORT; | 
|  | } | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$,  create ); | 
|  | } | 
|  | | TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW *create = NULL; | 
|  |  | 
|  | if( !$5 ) | 
|  | YYABORT; | 
|  | CREATE_CreateView( sql->db, &create, $3, $5, TRUE ); | 
|  | if( !create ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$,  create ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | oneupdate: | 
|  | TK_UPDATE table TK_SET update_assign_list TK_WHERE expr | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW *update = NULL; | 
|  |  | 
|  | UPDATE_CreateView( sql->db, &update, $2, $4, $6 ); | 
|  | if( !update ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$,  update ); | 
|  | } | 
|  | | TK_UPDATE table TK_SET update_assign_list | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW *update = NULL; | 
|  |  | 
|  | UPDATE_CreateView( sql->db, &update, $2, $4, NULL ); | 
|  | if( !update ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$,  update ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | onedelete: | 
|  | TK_DELETE from | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW *delete = NULL; | 
|  |  | 
|  | DELETE_CreateView( sql->db, &delete, $2 ); | 
|  | if( !delete ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, delete ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | onealter: | 
|  | TK_ALTER TK_TABLE table alterop | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW *alter = NULL; | 
|  |  | 
|  | ALTER_CreateView( sql->db, &alter, $3, NULL, $4 ); | 
|  | if( !alter ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, alter ); | 
|  | } | 
|  | | TK_ALTER TK_TABLE table TK_ADD column_and_type | 
|  | { | 
|  | SQL_input *sql = (SQL_input *)info; | 
|  | MSIVIEW *alter = NULL; | 
|  |  | 
|  | ALTER_CreateView( sql->db, &alter, $3, $5, 0 ); | 
|  | if (!alter) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, alter ); | 
|  | } | 
|  | | TK_ALTER TK_TABLE table TK_ADD column_and_type TK_HOLD | 
|  | { | 
|  | SQL_input *sql = (SQL_input *)info; | 
|  | MSIVIEW *alter = NULL; | 
|  |  | 
|  | ALTER_CreateView( sql->db, &alter, $3, $5, 1 ); | 
|  | if (!alter) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, alter ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | alterop: | 
|  | TK_HOLD | 
|  | { | 
|  | $$ = 1; | 
|  | } | 
|  | | TK_FREE | 
|  | { | 
|  | $$ = -1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | onedrop: | 
|  | TK_DROP TK_TABLE table | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW* drop = NULL; | 
|  | UINT r; | 
|  |  | 
|  | r = DROP_CreateView( sql->db, &drop, $3 ); | 
|  | if( r != ERROR_SUCCESS || !$$ ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, drop ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | table_def: | 
|  | column_def TK_PRIMARY TK_KEY collist | 
|  | { | 
|  | if( SQL_MarkPrimaryKeys( &$1, $4 ) ) | 
|  | $$ = $1; | 
|  | else | 
|  | $$ = NULL; | 
|  | } | 
|  | ; | 
|  |  | 
|  | column_def: | 
|  | column_def TK_COMMA column_and_type | 
|  | { | 
|  | column_info *ci; | 
|  |  | 
|  | for( ci = $1; ci->next; ci = ci->next ) | 
|  | ; | 
|  |  | 
|  | ci->next = $3; | 
|  | $$ = $1; | 
|  | } | 
|  | | column_and_type | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | column_and_type: | 
|  | column column_type | 
|  | { | 
|  | $$ = $1; | 
|  | $$->type = ($2 | MSITYPE_VALID); | 
|  | $$->temporary = $2 & MSITYPE_TEMPORARY ? TRUE : FALSE; | 
|  | } | 
|  | ; | 
|  |  | 
|  | column_type: | 
|  | data_type_l | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | data_type_l TK_LOCALIZABLE | 
|  | { | 
|  | $$ = $1 | MSITYPE_LOCALIZABLE; | 
|  | } | 
|  | | data_type_l TK_TEMPORARY | 
|  | { | 
|  | $$ = $1 | MSITYPE_TEMPORARY; | 
|  | } | 
|  | ; | 
|  |  | 
|  | data_type_l: | 
|  | data_type | 
|  | { | 
|  | $$ |= MSITYPE_NULLABLE; | 
|  | } | 
|  | | data_type TK_NOT TK_NULL | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | data_type: | 
|  | TK_CHAR | 
|  | { | 
|  | $$ = MSITYPE_STRING | 1; | 
|  | } | 
|  | | TK_CHAR TK_LP data_count TK_RP | 
|  | { | 
|  | $$ = MSITYPE_STRING | 0x400 | $3; | 
|  | } | 
|  | | TK_LONGCHAR | 
|  | { | 
|  | $$ = MSITYPE_STRING | 0x400; | 
|  | } | 
|  | | TK_SHORT | 
|  | { | 
|  | $$ = 2 | 0x400; | 
|  | } | 
|  | | TK_INT | 
|  | { | 
|  | $$ = 2 | 0x400; | 
|  | } | 
|  | | TK_LONG | 
|  | { | 
|  | $$ = 4; | 
|  | } | 
|  | | TK_OBJECT | 
|  | { | 
|  | $$ = MSITYPE_STRING | MSITYPE_VALID; | 
|  | } | 
|  | ; | 
|  |  | 
|  | data_count: | 
|  | number | 
|  | { | 
|  | if( ( $1 > 255 ) || ( $1 < 0 ) ) | 
|  | YYABORT; | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | oneselect: | 
|  | TK_SELECT selectfrom | 
|  | { | 
|  | $$ = $2; | 
|  | } | 
|  | | TK_SELECT TK_DISTINCT selectfrom | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW* distinct = NULL; | 
|  | UINT r; | 
|  |  | 
|  | r = DISTINCT_CreateView( sql->db, &distinct, $3 ); | 
|  | if (r != ERROR_SUCCESS) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, distinct ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | selectfrom: | 
|  | selcollist from | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW* select = NULL; | 
|  | UINT r; | 
|  |  | 
|  | if( $1 ) | 
|  | { | 
|  | r = SELECT_CreateView( sql->db, &select, $2, $1 ); | 
|  | if (r != ERROR_SUCCESS) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, select ); | 
|  | } | 
|  | else | 
|  | $$ = $2; | 
|  | } | 
|  | ; | 
|  |  | 
|  | selcollist: | 
|  | selcolumn | 
|  | | selcolumn TK_COMMA selcollist | 
|  | { | 
|  | $1->next = $3; | 
|  | } | 
|  | | TK_STAR | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | ; | 
|  |  | 
|  | collist: | 
|  | column | 
|  | | column TK_COMMA collist | 
|  | { | 
|  | $1->next = $3; | 
|  | } | 
|  | | TK_STAR | 
|  | { | 
|  | $$ = NULL; | 
|  | } | 
|  | ; | 
|  |  | 
|  | from: | 
|  | TK_FROM table | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW* table = NULL; | 
|  | UINT r; | 
|  |  | 
|  | r = TABLE_CreateView( sql->db, $2, &table ); | 
|  | if( r != ERROR_SUCCESS || !$$ ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, table ); | 
|  | } | 
|  | | unorderdfrom TK_ORDER TK_BY collist | 
|  | { | 
|  | UINT r; | 
|  |  | 
|  | if( $4 ) | 
|  | { | 
|  | r = $1->ops->sort( $1, $4 ); | 
|  | if ( r != ERROR_SUCCESS) | 
|  | YYABORT; | 
|  | } | 
|  |  | 
|  | $$ = $1; | 
|  | } | 
|  | | unorderdfrom | 
|  | ; | 
|  |  | 
|  | unorderdfrom: | 
|  | TK_FROM tablelist | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW* where = NULL; | 
|  | UINT r; | 
|  |  | 
|  | r = WHERE_CreateView( sql->db, &where, $2, NULL ); | 
|  | if( r != ERROR_SUCCESS ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, where ); | 
|  | } | 
|  | | TK_FROM tablelist TK_WHERE expr | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | MSIVIEW* where = NULL; | 
|  | UINT r; | 
|  |  | 
|  | r = WHERE_CreateView( sql->db, &where, $2, $4 ); | 
|  | if( r != ERROR_SUCCESS ) | 
|  | YYABORT; | 
|  |  | 
|  | PARSER_BUBBLE_UP_VIEW( sql, $$, where ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | tablelist: | 
|  | table | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | | table TK_COMMA tablelist | 
|  | { | 
|  | $$ = parser_add_table( info, $3, $1 ); | 
|  | if (!$$) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | expr: | 
|  | TK_LP expr TK_RP | 
|  | { | 
|  | $$ = $2; | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | expr TK_AND expr | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_AND, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | expr TK_OR expr | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_OR, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_EQ val | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_EQ, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_GT val | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_GT, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_LT val | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_LT, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_LE val | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_LE, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_GE val | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_GE, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_NE val | 
|  | { | 
|  | $$ = EXPR_complex( info, $1, OP_NE, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_IS TK_NULL | 
|  | { | 
|  | $$ = EXPR_unary( info, $1, OP_ISNULL ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | column_val TK_IS TK_NOT TK_NULL | 
|  | { | 
|  | $$ = EXPR_unary( info, $1, OP_NOTNULL ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | val: | 
|  | column_val | 
|  | | const_val | 
|  | ; | 
|  |  | 
|  | constlist: | 
|  | const_val | 
|  | { | 
|  | $$ = parser_alloc_column( info, NULL, NULL ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | $$->val = $1; | 
|  | } | 
|  | | const_val TK_COMMA constlist | 
|  | { | 
|  | $$ = parser_alloc_column( info, NULL, NULL ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | $$->val = $1; | 
|  | $$->next = $3; | 
|  | } | 
|  | ; | 
|  |  | 
|  | update_assign_list: | 
|  | column_assignment | 
|  | | column_assignment TK_COMMA update_assign_list | 
|  | { | 
|  | $$ = $1; | 
|  | $$->next = $3; | 
|  | } | 
|  | ; | 
|  |  | 
|  | column_assignment: | 
|  | column TK_EQ const_val | 
|  | { | 
|  | $$ = $1; | 
|  | $$->val = $3; | 
|  | } | 
|  | ; | 
|  |  | 
|  | const_val: | 
|  | number | 
|  | { | 
|  | $$ = EXPR_ival( info, $1 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | TK_MINUS number %prec TK_NEGATION | 
|  | { | 
|  | $$ = EXPR_ival( info, -$2 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | TK_STRING | 
|  | { | 
|  | $$ = EXPR_sval( info, &$1 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | TK_WILDCARD | 
|  | { | 
|  | $$ = EXPR_wildcard( info ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | column_val: | 
|  | column | 
|  | { | 
|  | $$ = EXPR_column( info, $1 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | column: | 
|  | table TK_DOT id | 
|  | { | 
|  | $$ = parser_alloc_column( info, $1, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | id | 
|  | { | 
|  | $$ = parser_alloc_column( info, NULL, $1 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | selcolumn: | 
|  | table TK_DOT id | 
|  | { | 
|  | $$ = parser_alloc_column( info, $1, $3 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | id | 
|  | { | 
|  | $$ = parser_alloc_column( info, NULL, $1 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | | string | 
|  | { | 
|  | $$ = parser_alloc_column( info, NULL, $1 ); | 
|  | if( !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | table: | 
|  | id | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | id: | 
|  | TK_ID | 
|  | { | 
|  | if ( SQL_getstring( info, &$1, &$$ ) != ERROR_SUCCESS || !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | string: | 
|  | TK_STRING | 
|  | { | 
|  | if ( SQL_getstring( info, &$1, &$$ ) != ERROR_SUCCESS || !$$ ) | 
|  | YYABORT; | 
|  | } | 
|  | ; | 
|  |  | 
|  | number: | 
|  | TK_INTEGER | 
|  | { | 
|  | $$ = SQL_getint( info ); | 
|  | } | 
|  | ; | 
|  |  | 
|  | %% | 
|  |  | 
|  | static LPWSTR parser_add_table( void *info, LPCWSTR list, LPCWSTR table ) | 
|  | { | 
|  | static const WCHAR space[] = {' ',0}; | 
|  | DWORD len = strlenW( list ) + strlenW( table ) + 2; | 
|  | LPWSTR ret; | 
|  |  | 
|  | ret = parser_alloc( info, len * sizeof(WCHAR) ); | 
|  | if( ret ) | 
|  | { | 
|  | strcpyW( ret, list ); | 
|  | strcatW( ret, space ); | 
|  | strcatW( ret, table ); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void *parser_alloc( void *info, unsigned int sz ) | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | struct list *mem; | 
|  |  | 
|  | mem = msi_alloc( sizeof (struct list) + sz ); | 
|  | list_add_tail( sql->mem, mem ); | 
|  | return &mem[1]; | 
|  | } | 
|  |  | 
|  | static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ) | 
|  | { | 
|  | column_info *col; | 
|  |  | 
|  | col = parser_alloc( info, sizeof (*col) ); | 
|  | if( col ) | 
|  | { | 
|  | col->table = table; | 
|  | col->column = column; | 
|  | col->val = NULL; | 
|  | col->type = 0; | 
|  | col->next = NULL; | 
|  | } | 
|  |  | 
|  | return col; | 
|  | } | 
|  |  | 
|  | static int sql_lex( void *SQL_lval, SQL_input *sql ) | 
|  | { | 
|  | int token, skip; | 
|  | struct sql_str * str = SQL_lval; | 
|  |  | 
|  | do | 
|  | { | 
|  | sql->n += sql->len; | 
|  | if( ! sql->command[sql->n] ) | 
|  | return 0;  /* end of input */ | 
|  |  | 
|  | /* TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); */ | 
|  | sql->len = sqliteGetToken( &sql->command[sql->n], &token, &skip ); | 
|  | if( sql->len==0 ) | 
|  | break; | 
|  | str->data = &sql->command[sql->n]; | 
|  | str->len = sql->len; | 
|  | sql->n += skip; | 
|  | } | 
|  | while( token == TK_SPACE ); | 
|  |  | 
|  | /* TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); */ | 
|  |  | 
|  | return token; | 
|  | } | 
|  |  | 
|  | UINT SQL_getstring( void *info, const struct sql_str *strdata, LPWSTR *str ) | 
|  | { | 
|  | LPCWSTR p = strdata->data; | 
|  | UINT len = strdata->len; | 
|  |  | 
|  | /* match quotes */ | 
|  | if( ( (p[0]=='`') && (p[len-1]!='`') ) || | 
|  | ( (p[0]=='\'') && (p[len-1]!='\'') ) ) | 
|  | return ERROR_FUNCTION_FAILED; | 
|  |  | 
|  | /* if there's quotes, remove them */ | 
|  | if( ( (p[0]=='`') && (p[len-1]=='`') ) || | 
|  | ( (p[0]=='\'') && (p[len-1]=='\'') ) ) | 
|  | { | 
|  | p++; | 
|  | len -= 2; | 
|  | } | 
|  | *str = parser_alloc( info, (len + 1)*sizeof(WCHAR) ); | 
|  | if( !*str ) | 
|  | return ERROR_OUTOFMEMORY; | 
|  | memcpy( *str, p, len*sizeof(WCHAR) ); | 
|  | (*str)[len]=0; | 
|  |  | 
|  | return ERROR_SUCCESS; | 
|  | } | 
|  |  | 
|  | INT SQL_getint( void *info ) | 
|  | { | 
|  | SQL_input* sql = (SQL_input*) info; | 
|  | LPCWSTR p = &sql->command[sql->n]; | 
|  | INT i, r = 0; | 
|  |  | 
|  | for( i=0; i<sql->len; i++ ) | 
|  | { | 
|  | if( '0' > p[i] || '9' < p[i] ) | 
|  | { | 
|  | ERR("should only be numbers here!\n"); | 
|  | break; | 
|  | } | 
|  | r = (p[i]-'0') + r*10; | 
|  | } | 
|  |  | 
|  | return r; | 
|  | } | 
|  |  | 
|  | static int sql_error( const char *str ) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct expr * EXPR_wildcard( void *info ) | 
|  | { | 
|  | struct expr *e = parser_alloc( info, sizeof *e ); | 
|  | if( e ) | 
|  | { | 
|  | e->type = EXPR_WILDCARD; | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ) | 
|  | { | 
|  | struct expr *e = parser_alloc( info, sizeof *e ); | 
|  | if( e ) | 
|  | { | 
|  | e->type = EXPR_COMPLEX; | 
|  | e->u.expr.left = l; | 
|  | e->u.expr.op = op; | 
|  | e->u.expr.right = r; | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | static struct expr * EXPR_unary( void *info, struct expr *l, UINT op ) | 
|  | { | 
|  | struct expr *e = parser_alloc( info, sizeof *e ); | 
|  | if( e ) | 
|  | { | 
|  | e->type = EXPR_UNARY; | 
|  | e->u.expr.left = l; | 
|  | e->u.expr.op = op; | 
|  | e->u.expr.right = NULL; | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | static struct expr * EXPR_column( void *info, const column_info *column ) | 
|  | { | 
|  | struct expr *e = parser_alloc( info, sizeof *e ); | 
|  | if( e ) | 
|  | { | 
|  | e->type = EXPR_COLUMN; | 
|  | e->u.column.unparsed.column = column->column; | 
|  | e->u.column.unparsed.table = column->table; | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | static struct expr * EXPR_ival( void *info, int val ) | 
|  | { | 
|  | struct expr *e = parser_alloc( info, sizeof *e ); | 
|  | if( e ) | 
|  | { | 
|  | e->type = EXPR_IVAL; | 
|  | e->u.ival = val; | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | static struct expr * EXPR_sval( void *info, const struct sql_str *str ) | 
|  | { | 
|  | struct expr *e = parser_alloc( info, sizeof *e ); | 
|  | if( e ) | 
|  | { | 
|  | e->type = EXPR_SVAL; | 
|  | if( SQL_getstring( info, str, (LPWSTR *)&e->u.sval ) != ERROR_SUCCESS ) | 
|  | return NULL; /* e will be freed by query destructor */ | 
|  | } | 
|  | return e; | 
|  | } | 
|  |  | 
|  | static void swap_columns( column_info **cols, column_info *A, int idx ) | 
|  | { | 
|  | column_info *preA = NULL, *preB = NULL, *B, *ptr; | 
|  | int i = 0; | 
|  |  | 
|  | B = NULL; | 
|  | ptr = *cols; | 
|  | while( ptr ) | 
|  | { | 
|  | if( i++ == idx ) | 
|  | B = ptr; | 
|  | else if( !B ) | 
|  | preB = ptr; | 
|  |  | 
|  | if( ptr->next == A ) | 
|  | preA = ptr; | 
|  |  | 
|  | ptr = ptr->next; | 
|  | } | 
|  |  | 
|  | if( preB ) preB->next = A; | 
|  | if( preA ) preA->next = B; | 
|  | ptr = A->next; | 
|  | A->next = B->next; | 
|  | B->next = ptr; | 
|  | if( idx == 0 ) | 
|  | *cols = A; | 
|  | } | 
|  |  | 
|  | static BOOL SQL_MarkPrimaryKeys( column_info **cols, | 
|  | column_info *keys ) | 
|  | { | 
|  | column_info *k; | 
|  | BOOL found = TRUE; | 
|  | int count; | 
|  |  | 
|  | for( k = keys, count = 0; k && found; k = k->next, count++ ) | 
|  | { | 
|  | column_info *c; | 
|  | int idx; | 
|  |  | 
|  | found = FALSE; | 
|  | for( c = *cols, idx = 0; c && !found; c = c->next, idx++ ) | 
|  | { | 
|  | if( strcmpW( k->column, c->column ) ) | 
|  | continue; | 
|  | c->type |= MSITYPE_KEY; | 
|  | found = TRUE; | 
|  | if (idx != count) | 
|  | swap_columns( cols, c, count ); | 
|  | } | 
|  | } | 
|  |  | 
|  | return found; | 
|  | } | 
|  |  | 
|  | UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview, | 
|  | struct list *mem ) | 
|  | { | 
|  | SQL_input sql; | 
|  | int r; | 
|  |  | 
|  | *phview = NULL; | 
|  |  | 
|  | sql.db = db; | 
|  | sql.command = command; | 
|  | sql.n = 0; | 
|  | sql.len = 0; | 
|  | sql.r = ERROR_BAD_QUERY_SYNTAX; | 
|  | sql.view = phview; | 
|  | sql.mem = mem; | 
|  |  | 
|  | r = sql_parse(&sql); | 
|  |  | 
|  | TRACE("Parse returned %d\n", r); | 
|  | if( r ) | 
|  | { | 
|  | if (*sql.view) | 
|  | { | 
|  | (*sql.view)->ops->delete(*sql.view); | 
|  | *sql.view = NULL; | 
|  | } | 
|  | return sql.r; | 
|  | } | 
|  |  | 
|  | return ERROR_SUCCESS; | 
|  | } |