mirror of
https://github.com/reactos/wine.git
synced 2024-12-04 09:53:54 +00:00
cc366e1282
This fixes the bug where multiple columns of the same name, but different tables are members of a join. Any attempt to refer to these columns will resolve to the first available column with that name, irregardless of any tablename modifier.
975 lines
21 KiB
Plaintext
975 lines
21 KiB
Plaintext
%{
|
|
|
|
/*
|
|
* 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 */
|
|
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 );
|
|
|
|
%}
|
|
|
|
%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
|
|
%type <column_list> selcollist column column_and_type column_def table_def
|
|
%type <column_list> column_assignment update_assign_list constlist
|
|
%type <query> query from fromtable selectfrom unorderedsel
|
|
%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
|
|
|
|
/* Reference: http://mates.ms.mff.cuni.cz/oracle/doc/ora815nt/server.815/a67779/operator.htm */
|
|
%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 selcollist 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;
|
|
$$ = insert;
|
|
}
|
|
| TK_INSERT TK_INTO table TK_LP selcollist 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;
|
|
$$ = 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;
|
|
}
|
|
$$ = 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;
|
|
$$ = 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;
|
|
$$ = 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;
|
|
$$ = update;
|
|
}
|
|
;
|
|
|
|
onedelete:
|
|
TK_DELETE from
|
|
{
|
|
SQL_input* sql = (SQL_input*) info;
|
|
MSIVIEW *delete = NULL;
|
|
|
|
DELETE_CreateView( sql->db, &delete, $2 );
|
|
if( !delete )
|
|
YYABORT;
|
|
$$ = 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;
|
|
$$ = 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;
|
|
$$ = 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;
|
|
$$ = alter;
|
|
}
|
|
;
|
|
|
|
alterop:
|
|
TK_HOLD
|
|
{
|
|
$$ = 1;
|
|
}
|
|
| TK_FREE
|
|
{
|
|
$$ = -1;
|
|
}
|
|
;
|
|
|
|
onedrop:
|
|
TK_DROP TK_TABLE table
|
|
{
|
|
SQL_input* sql = (SQL_input*) info;
|
|
UINT r;
|
|
|
|
$$ = NULL;
|
|
r = DROP_CreateView( sql->db, &$$, $3 );
|
|
if( r != ERROR_SUCCESS || !$$ )
|
|
YYABORT;
|
|
}
|
|
;
|
|
|
|
table_def:
|
|
column_def TK_PRIMARY TK_KEY selcollist
|
|
{
|
|
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:
|
|
unorderedsel TK_ORDER TK_BY selcollist
|
|
{
|
|
UINT r;
|
|
|
|
if( $4 )
|
|
{
|
|
r = $1->ops->sort( $1, $4 );
|
|
if ( r != ERROR_SUCCESS)
|
|
YYABORT;
|
|
}
|
|
|
|
$$ = $1;
|
|
}
|
|
| unorderedsel
|
|
;
|
|
|
|
unorderedsel:
|
|
TK_SELECT selectfrom
|
|
{
|
|
$$ = $2;
|
|
}
|
|
| TK_SELECT TK_DISTINCT selectfrom
|
|
{
|
|
SQL_input* sql = (SQL_input*) info;
|
|
UINT r;
|
|
|
|
$$ = NULL;
|
|
r = DISTINCT_CreateView( sql->db, &$$, $3 );
|
|
if (r != ERROR_SUCCESS)
|
|
{
|
|
$3->ops->delete($3);
|
|
YYABORT;
|
|
}
|
|
}
|
|
;
|
|
|
|
selectfrom:
|
|
selcollist from
|
|
{
|
|
SQL_input* sql = (SQL_input*) info;
|
|
UINT r;
|
|
|
|
$$ = NULL;
|
|
if( $1 )
|
|
{
|
|
r = SELECT_CreateView( sql->db, &$$, $2, $1 );
|
|
if (r != ERROR_SUCCESS)
|
|
{
|
|
$2->ops->delete($2);
|
|
YYABORT;
|
|
}
|
|
}
|
|
else
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
selcollist:
|
|
column
|
|
| column TK_COMMA selcollist
|
|
{
|
|
$1->next = $3;
|
|
}
|
|
| TK_STAR
|
|
{
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
from:
|
|
fromtable
|
|
| fromtable TK_WHERE expr
|
|
{
|
|
SQL_input* sql = (SQL_input*) info;
|
|
UINT r;
|
|
|
|
$$ = NULL;
|
|
r = WHERE_CreateView( sql->db, &$$, $1, $3 );
|
|
if( r != ERROR_SUCCESS )
|
|
{
|
|
$1->ops->delete( $1 );
|
|
YYABORT;
|
|
}
|
|
}
|
|
;
|
|
|
|
fromtable:
|
|
TK_FROM table
|
|
{
|
|
SQL_input* sql = (SQL_input*) info;
|
|
UINT r;
|
|
|
|
$$ = NULL;
|
|
r = TABLE_CreateView( sql->db, $2, &$$ );
|
|
if( r != ERROR_SUCCESS || !$$ )
|
|
YYABORT;
|
|
}
|
|
| TK_FROM tablelist
|
|
{
|
|
SQL_input* sql = (SQL_input*) info;
|
|
UINT r;
|
|
|
|
r = JOIN_CreateView( sql->db, &$$, $2 );
|
|
if( r != ERROR_SUCCESS )
|
|
YYABORT;
|
|
}
|
|
;
|
|
|
|
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;
|
|
}
|
|
;
|
|
|
|
table:
|
|
id
|
|
{
|
|
$$ = $1;
|
|
}
|
|
;
|
|
|
|
id:
|
|
TK_ID
|
|
{
|
|
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;
|
|
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 );
|
|
if( sql->len==0 )
|
|
break;
|
|
str->data = &sql->command[sql->n];
|
|
str->len = sql->len;
|
|
}
|
|
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.column = column->column;
|
|
e->u.column.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( lstrcmpW( 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;
|
|
}
|