2018-03-14 11:27:27 +08:00
|
|
|
/* $%BEGINLICENSE%$
|
|
|
|
Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
|
|
|
|
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 the Free Software Foundation; version 2 of the
|
|
|
|
License.
|
|
|
|
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
|
|
02110-1301 USA
|
|
|
|
|
|
|
|
$%ENDLICENSE%$ */
|
|
|
|
|
2018-03-06 14:00:39 +08:00
|
|
|
#include "sql-expression.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
#include "myparser.y.h"
|
|
|
|
|
|
|
|
char *sql_token_dup(sql_token_t token)
|
|
|
|
{
|
|
|
|
if (token.n == 0)
|
|
|
|
return NULL;
|
|
|
|
char *s = g_malloc0(token.n + 1);
|
|
|
|
memcpy(s, token.z, token.n);
|
|
|
|
sql_string_dequote(s);
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int64_t sql_token_to_int(sql_token_t token)
|
|
|
|
{
|
|
|
|
/* TODO: HEX */
|
|
|
|
int64_t value = 0;
|
|
|
|
int sign = 1;
|
|
|
|
const char *c = token.z;
|
|
|
|
int i = 0;
|
|
|
|
if (*c == '+' || *c == '-' ) {
|
|
|
|
if (*c == '-' ) sign = -1;
|
|
|
|
c++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
while (isdigit(*c) && i++ < token.n) {
|
|
|
|
value *= 10;
|
|
|
|
value += (int) (*c - '0');
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
return (value *sign);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_string_dequote(char *z)
|
|
|
|
{
|
|
|
|
int quote;
|
|
|
|
int i, j;
|
|
|
|
if (z == 0) return;
|
|
|
|
quote = z[0];
|
|
|
|
switch(quote) {
|
|
|
|
case '\'': break;
|
|
|
|
case '"': break;
|
|
|
|
case '`': break; /* For MySQL compatibility */
|
|
|
|
case '[': quote = ']'; break; /* For MS SqlServer compatibility */
|
|
|
|
default: return;
|
|
|
|
}
|
|
|
|
for (i = 1, j = 0; z[i]; i++) {
|
|
|
|
if (z[i] == quote) {
|
|
|
|
if (z[i+1] == quote) { /* quote escape */
|
|
|
|
z[j++] = quote;
|
|
|
|
i++;
|
|
|
|
} else {
|
|
|
|
z[j++] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (z[i] == '\\') { /* slash escape */
|
|
|
|
i++;
|
|
|
|
z[j++] = z[i];
|
|
|
|
} else {
|
|
|
|
z[j++] = z[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_expr_t *sql_expr_new(int op, const sql_token_t *token)
|
|
|
|
{
|
|
|
|
int extra = 0;
|
|
|
|
if (token && op != TK_INTEGER) {
|
|
|
|
extra = token->n + 1;
|
|
|
|
}
|
|
|
|
sql_expr_t *expr = g_malloc0(sizeof(sql_expr_t) + extra);
|
|
|
|
if (expr) {
|
|
|
|
expr->op = op;
|
|
|
|
if (token) {
|
|
|
|
if (extra == 0) {
|
|
|
|
expr->num_value = sql_token_to_int(*token);
|
|
|
|
} else {
|
|
|
|
expr->token_text = (char *)&expr[1];
|
|
|
|
assert(token != NULL);
|
|
|
|
if (token->n) {
|
|
|
|
strncpy(expr->token_text, token->z, token->n);
|
|
|
|
if (op == TK_STRING || op == TK_ID) {
|
|
|
|
sql_string_dequote(expr->token_text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
expr->start = token->z;
|
|
|
|
expr->end = &token->z[token->n];
|
|
|
|
}
|
|
|
|
expr->height = 1;
|
|
|
|
expr->var_scope = SCOPE_SESSION;
|
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Only duplicate the root node
|
|
|
|
*/
|
|
|
|
sql_expr_t *sql_expr_dup(const sql_expr_t *p)
|
|
|
|
{
|
|
|
|
int extra = 0;
|
|
|
|
if (p->token_text) {
|
|
|
|
extra = strlen(p->token_text) + 1;
|
|
|
|
}
|
|
|
|
int size = sizeof(sql_expr_t) + extra;
|
|
|
|
sql_expr_t *expr = g_malloc0(size);
|
|
|
|
if (expr) {
|
|
|
|
memcpy(expr, p, size);
|
|
|
|
if (p->op == TK_DOT) {
|
|
|
|
expr->left = sql_expr_dup(p->left);
|
|
|
|
expr->right = sql_expr_dup(p->right);
|
|
|
|
} else {
|
|
|
|
expr->left = 0;
|
|
|
|
expr->right = 0;
|
|
|
|
}
|
|
|
|
expr->list = 0;
|
|
|
|
expr->select = 0;
|
|
|
|
expr->alias = 0;
|
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void height_of_expr(sql_expr_t *p, int *height)
|
|
|
|
{
|
|
|
|
if (p) {
|
|
|
|
if (p->height > *height) {
|
|
|
|
*height = p->height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void expr_set_height(sql_expr_t *p)
|
|
|
|
{
|
|
|
|
int height = 0;
|
|
|
|
height_of_expr(p->left, &height);
|
|
|
|
height_of_expr(p->right, &height);
|
|
|
|
p->height = height + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_expr_attach_subtrees(sql_expr_t *root, sql_expr_t *left, sql_expr_t *right)
|
|
|
|
{
|
|
|
|
if (root == NULL) {
|
|
|
|
sql_expr_free(left);
|
|
|
|
sql_expr_free(right);
|
|
|
|
} else {
|
|
|
|
if (left) {
|
|
|
|
root->left = left;
|
|
|
|
root->start = left->start;
|
|
|
|
}
|
|
|
|
if (right) {
|
|
|
|
root->right = right;
|
|
|
|
root->end = right->end;
|
|
|
|
}
|
|
|
|
expr_set_height(root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_expr_free(void *p)
|
|
|
|
{
|
|
|
|
if (p) {
|
|
|
|
sql_expr_t *exp = (sql_expr_t *)p;
|
|
|
|
if (exp->left)
|
|
|
|
sql_expr_free(exp->left);
|
|
|
|
if (exp->right)
|
|
|
|
sql_expr_free(exp->right);
|
|
|
|
if (exp->list)
|
|
|
|
g_ptr_array_free(exp->list, TRUE);
|
|
|
|
if (exp->select)
|
|
|
|
sql_select_free(exp->select);
|
|
|
|
if (exp->alias)
|
|
|
|
g_free(exp->alias);
|
|
|
|
g_free(exp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean sql_expr_get_int(sql_expr_t *p, gint64 *value)
|
|
|
|
{
|
|
|
|
gboolean rc = FALSE;
|
|
|
|
if (p == NULL)
|
|
|
|
return FALSE;
|
|
|
|
switch (p->op) {
|
|
|
|
case TK_INTEGER:
|
|
|
|
if (value) {
|
|
|
|
*value = p->num_value;
|
|
|
|
}
|
|
|
|
rc = TRUE;
|
|
|
|
break;
|
|
|
|
case TK_UMINUS:
|
|
|
|
rc = sql_expr_get_int(p->left, value);
|
|
|
|
*value = -(*value);
|
|
|
|
break;
|
|
|
|
case TK_UPLUS:
|
|
|
|
rc = sql_expr_get_int(p->left, value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = FALSE;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean sql_expr_is_boolean(sql_expr_t *p, gboolean *value)
|
|
|
|
{
|
|
|
|
gint64 int_val = -1;
|
|
|
|
if (sql_expr_get_int(p, &int_val)) {
|
|
|
|
*value = (int_val != 0);
|
|
|
|
return TRUE;
|
|
|
|
} else if (sql_expr_is_id(p, "off") || sql_expr_is_id(p, "false")) {
|
|
|
|
*value = FALSE;
|
|
|
|
return TRUE;
|
|
|
|
} else if (sql_expr_is_id(p, "on") || sql_expr_is_id(p, "true")) {
|
|
|
|
*value = TRUE;
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param name NULL or expected token name
|
|
|
|
* @return
|
|
|
|
* if name is NULL:
|
|
|
|
* return whether p is of type TK_ID
|
|
|
|
* if name not NULL:
|
|
|
|
* return TRUE only if p is TK_ID and has same name
|
|
|
|
*/
|
|
|
|
gboolean sql_expr_is_id(const sql_expr_t *p, const char *name)
|
|
|
|
{
|
|
|
|
const char *id = sql_expr_id(p);
|
|
|
|
if (name == NULL) {
|
|
|
|
return id != NULL;
|
|
|
|
} else {
|
|
|
|
if (id) {
|
|
|
|
return strcasecmp(id, name) == 0;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean sql_expr_is_function(const sql_expr_t *p, const char *name)
|
|
|
|
{
|
|
|
|
const char *func = NULL;
|
|
|
|
if (p && p->op == TK_FUNCTION) {
|
|
|
|
func = p->token_text;
|
|
|
|
}
|
|
|
|
if (name == NULL) {
|
|
|
|
return func != NULL;
|
|
|
|
} else {
|
|
|
|
if (func) {
|
|
|
|
return strcasecmp(func, name) == 0;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* prefix and suffix is NULLable
|
|
|
|
* @return
|
|
|
|
* if prefix or suffix is NULL, return true if the expression is of type:
|
|
|
|
* TK_DOT
|
|
|
|
* |--TK_ID
|
|
|
|
* |--TK_ID
|
|
|
|
*
|
|
|
|
* if prefix or suffix not NULL, return ture only if expr is "preifx.suffix"
|
|
|
|
*/
|
|
|
|
gboolean sql_expr_is_dotted_name(const sql_expr_t *p, const char *prefix, const char *suffix)
|
|
|
|
{
|
|
|
|
if (p && p->op == TK_DOT) {
|
|
|
|
return sql_expr_is_id(p->left, prefix)
|
|
|
|
&& sql_expr_is_id(p->right, suffix);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean sql_expr_is_field_name(const sql_expr_t *p)
|
|
|
|
{
|
|
|
|
if (p) {
|
|
|
|
if (p->op == TK_ID) {
|
|
|
|
return TRUE;
|
|
|
|
} else if (p->op == TK_DOT) { /* qualified name */
|
|
|
|
if (p->right->op == TK_DOT) { /* db.table.col */
|
|
|
|
return sql_expr_is_id(p->left,0)
|
|
|
|
&& sql_expr_is_dotted_name(p->right,0,0);
|
|
|
|
} else { /* table.col */
|
|
|
|
return sql_expr_is_dotted_name(p,0,0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_expr_list_t *sql_expr_list_append(sql_expr_list_t *list, sql_expr_t *expr)
|
|
|
|
{
|
|
|
|
if (expr == NULL)
|
|
|
|
return list;
|
|
|
|
if (list == NULL) {
|
|
|
|
list = g_ptr_array_new_with_free_func(sql_expr_free);
|
|
|
|
}
|
|
|
|
g_ptr_array_add(list, expr);
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_expr_t *sql_expr_list_find(sql_expr_list_t *list, const char *name)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
for (i = 0; i < list->len; ++i) {
|
|
|
|
sql_expr_t *col = g_ptr_array_index(list, i);
|
|
|
|
if (sql_expr_is_id(col, name)) {
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_expr_t *sql_expr_list_find_fullname(sql_expr_list_t *list, const sql_expr_t *expr)
|
|
|
|
{
|
|
|
|
if (sql_expr_is_dotted_name(expr, NULL, NULL)) {
|
|
|
|
const char *prefix = expr->left->token_text;
|
|
|
|
const char *suffix = expr->right->token_text;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < list->len; ++i) {
|
|
|
|
sql_expr_t *col = g_ptr_array_index(list, i);
|
|
|
|
if (sql_expr_is_dotted_name(col, prefix, suffix)) {
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sql_expr_list_find_aggregate(sql_expr_list_t *list)
|
|
|
|
{
|
|
|
|
int i, index = 0;
|
|
|
|
enum sql_func_type_t type;
|
|
|
|
for (i = 0; i < list->len; ++i) {
|
|
|
|
sql_expr_t *p = g_ptr_array_index(list, i);
|
|
|
|
if (p->op == TK_FUNCTION) {
|
|
|
|
type = sql_func_type(p->token_text);
|
|
|
|
if (type != FT_UNKNOWN) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sql_expr_list_find_aggregates(sql_expr_list_t *list, GROUP_AGGR *aggr_array)
|
|
|
|
{
|
|
|
|
int i, index = 0;
|
|
|
|
enum sql_func_type_t type;
|
|
|
|
for (i = 0; i < list->len; ++i) {
|
|
|
|
sql_expr_t *p = g_ptr_array_index(list, i);
|
|
|
|
if (p->op == TK_FUNCTION) {
|
|
|
|
type = sql_func_type(p->token_text);
|
|
|
|
if (type != FT_UNKNOWN) {
|
|
|
|
if (index < MAX_AGGR_FUNS) {
|
|
|
|
aggr_array[index].pos = i;
|
|
|
|
aggr_array[index].fun_type = type;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_expr_list_free(sql_expr_list_t *list)
|
|
|
|
{
|
|
|
|
if (list)
|
|
|
|
g_ptr_array_free(list, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
enum sql_func_type_t sql_func_type(const char *s)
|
|
|
|
{
|
|
|
|
if (strncasecmp(s, "count", 5) == 0) return FT_COUNT;
|
|
|
|
else if (strncasecmp(s, "sum", 3) == 0) return FT_SUM;
|
|
|
|
else if (strncasecmp(s, "avg", 3) == 0) return FT_AVG;
|
|
|
|
else if (strncasecmp(s, "max", 3) == 0) return FT_MAX;
|
|
|
|
else if (strncasecmp(s, "min", 3) == 0) return FT_MIN;
|
|
|
|
else return FT_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_column_t *sql_column_new()
|
|
|
|
{
|
|
|
|
return g_new0(struct sql_column_t, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_column_free(void *p)
|
|
|
|
{
|
|
|
|
if (!p)
|
|
|
|
return;
|
|
|
|
sql_column_t *col = (sql_column_t *)p;
|
|
|
|
if (col->expr)
|
|
|
|
sql_expr_free(col->expr);
|
|
|
|
if (col->alias)
|
|
|
|
g_free(col->alias);
|
|
|
|
if (col->type)
|
|
|
|
g_free(col->type);
|
|
|
|
g_free(col);
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_column_list_t *sql_column_list_append(sql_column_list_t *list, sql_column_t *col)
|
|
|
|
{
|
|
|
|
if (col == NULL)
|
|
|
|
return list;
|
|
|
|
if (list == NULL) {
|
|
|
|
list = g_ptr_array_new_with_free_func(sql_column_free);
|
|
|
|
}
|
|
|
|
g_ptr_array_add(list, col);
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_column_list_free(sql_column_list_t *list)
|
|
|
|
{
|
|
|
|
if (list)
|
|
|
|
g_ptr_array_free(list, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_select_t *sql_select_new()
|
|
|
|
{
|
|
|
|
sql_select_t *p = g_new0(sql_select_t, 1);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_select_free(sql_select_t *p)
|
|
|
|
{
|
|
|
|
if (!p)
|
|
|
|
return;
|
|
|
|
if (p->columns) /* The fields of the result */
|
|
|
|
sql_expr_list_free(p->columns);
|
|
|
|
if (p->from_src) /* The FROM clause */
|
|
|
|
sql_src_list_free(p->from_src);
|
|
|
|
if (p->where_clause) /* The WHERE clause */
|
|
|
|
sql_expr_free(p->where_clause);
|
|
|
|
if (p->groupby_clause) /* The GROUP BY clause */
|
|
|
|
sql_expr_list_free(p->groupby_clause);
|
|
|
|
if (p->having_clause) /* The HAVING clause */
|
|
|
|
sql_expr_free(p->having_clause);
|
|
|
|
if (p->orderby_clause) /* The ORDER BY clause */
|
|
|
|
sql_expr_list_free(p->orderby_clause);
|
|
|
|
if (p->prior) /* Prior select in a compound select statement */
|
|
|
|
sql_select_free(p->prior);
|
|
|
|
/* sql_select_t *pNext; Next select to the left in a compound */
|
|
|
|
if (p->limit) /* LIMIT expression. NULL means not used. */
|
|
|
|
sql_expr_free(p->limit);
|
|
|
|
if (p->offset) /* OFFSET expression. NULL means not used. */
|
|
|
|
sql_expr_free(p->offset);
|
|
|
|
g_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_delete_t *sql_delete_new()
|
|
|
|
{
|
|
|
|
sql_delete_t *p = g_new0(sql_delete_t, 1);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_delete_free(sql_delete_t *p)
|
|
|
|
{
|
|
|
|
if (!p)
|
|
|
|
return;
|
|
|
|
if (p->from_src) /* The FROM clause */
|
|
|
|
sql_src_list_free(p->from_src);
|
|
|
|
if (p->where_clause) /* The WHERE clause */
|
|
|
|
sql_expr_free(p->where_clause);
|
|
|
|
if (p->orderby_clause)
|
|
|
|
sql_expr_list_free(p->orderby_clause); /* The ORDER BY clause */
|
|
|
|
if (p->limit) /* LIMIT expression. NULL means not used. */
|
|
|
|
sql_expr_free(p->limit);
|
|
|
|
if (p->offset) /* OFFSET expression. NULL means not used. */
|
|
|
|
sql_expr_free(p->offset);
|
|
|
|
g_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_update_t *sql_update_new()
|
|
|
|
{
|
|
|
|
sql_update_t *p = g_new0(sql_update_t, 1);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_update_free(sql_update_t *p)
|
|
|
|
{
|
|
|
|
if (!p)
|
|
|
|
return;
|
|
|
|
if (p->table)
|
|
|
|
sql_src_list_free(p->table);
|
|
|
|
if (p->set_list)
|
|
|
|
sql_expr_list_free(p->set_list);
|
|
|
|
if (p->where_clause) /* The WHERE clause */
|
|
|
|
sql_expr_free(p->where_clause);
|
|
|
|
if (p->orderby_clause)
|
|
|
|
sql_expr_list_free(p->orderby_clause); /* The ORDER BY clause */
|
|
|
|
if (p->limit) /* LIMIT expression. NULL means not used. */
|
|
|
|
sql_expr_free(p->limit);
|
|
|
|
if (p->offset) /* OFFSET expression. NULL means not used. */
|
|
|
|
sql_expr_free(p->offset);
|
|
|
|
g_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_insert_t *sql_insert_new()
|
|
|
|
{
|
|
|
|
sql_insert_t *p = g_new0(sql_insert_t, 1);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void sql_insert_free(sql_insert_t *p)
|
|
|
|
{
|
|
|
|
if (!p)
|
|
|
|
return;
|
|
|
|
if (p->table)
|
|
|
|
sql_src_list_free(p->table);
|
|
|
|
if (p->sel_val)
|
|
|
|
sql_select_free(p->sel_val);
|
|
|
|
if (p->columns)
|
|
|
|
sql_id_list_free(p->columns);
|
|
|
|
g_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_src_item_free(void *p)
|
|
|
|
{
|
|
|
|
if (!p)
|
|
|
|
return;
|
|
|
|
struct sql_src_item_t *item = (struct sql_src_item_t *)p;
|
|
|
|
if (item->table_name)
|
|
|
|
g_free(item->table_name);
|
|
|
|
if (item->table_alias)
|
|
|
|
g_free(item->table_alias);
|
|
|
|
if (item->dbname)
|
|
|
|
g_free(item->dbname);
|
|
|
|
if (item->select)
|
|
|
|
sql_select_free(item->select);
|
|
|
|
if (item->on_clause)
|
|
|
|
sql_expr_free(item->on_clause);
|
|
|
|
if (item->pUsing)
|
|
|
|
sql_id_list_free(item->pUsing);
|
|
|
|
if (item->func_arg)
|
|
|
|
sql_expr_list_free(item->func_arg);
|
|
|
|
g_free(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_src_list_t *sql_src_list_append(sql_src_list_t *p, sql_token_t *tname,
|
|
|
|
sql_token_t *dbname, sql_token_t *alias, sql_select_t *subquery,
|
|
|
|
sql_expr_t *on_clause, sql_id_list_t *using_clause)
|
|
|
|
{
|
|
|
|
struct sql_src_item_t *item = g_new0(sql_src_item_t, 1);
|
|
|
|
if (item) {
|
|
|
|
item->table_name = tname ? sql_token_dup(*tname) : NULL;
|
|
|
|
item->table_alias = alias ? sql_token_dup(*alias) : NULL;
|
|
|
|
item->dbname = dbname ? sql_token_dup(*dbname) : NULL;
|
|
|
|
item->select = subquery;
|
|
|
|
item->on_clause = on_clause;
|
|
|
|
item->pUsing = using_clause;
|
|
|
|
}
|
|
|
|
if (!p) {
|
|
|
|
p = g_ptr_array_new_with_free_func(sql_src_item_free);
|
|
|
|
}
|
|
|
|
g_ptr_array_add(p, item);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_src_list_free(sql_src_list_t *p)
|
|
|
|
{
|
|
|
|
if (p)
|
|
|
|
g_ptr_array_free(p, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
sql_id_list_t *sql_id_list_append(sql_id_list_t *p, sql_token_t *id_name)
|
|
|
|
{
|
|
|
|
if (!p) {
|
|
|
|
p = g_ptr_array_new_with_free_func(g_free);
|
|
|
|
}
|
|
|
|
if (id_name)
|
|
|
|
g_ptr_array_add(p, sql_token_dup(*id_name));
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_id_list_free(sql_id_list_t *p)
|
|
|
|
{
|
|
|
|
if (p)
|
|
|
|
g_ptr_array_free(p, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *sql_get_token_name(int op)
|
|
|
|
{
|
|
|
|
static struct token_list_s{
|
|
|
|
int code;
|
|
|
|
char *name;
|
|
|
|
} token_list[] = {
|
|
|
|
{TK_SEMI ,"TK_SEMI "},
|
|
|
|
{TK_CREATE ,"TK_CREATE "},
|
|
|
|
{TK_TABLE ,"TK_TABLE "},
|
|
|
|
{TK_IF ,"TK_IF "},
|
|
|
|
{TK_NOT ,"TK_NOT "},
|
|
|
|
{TK_EXISTS ,"TK_EXISTS "},
|
|
|
|
{TK_TEMP ,"TK_TEMP "},
|
|
|
|
{TK_LP ,"TK_LP "},
|
|
|
|
{TK_RP ,"TK_RP "},
|
|
|
|
{TK_AS ,"TK_AS "},
|
|
|
|
{TK_WITHOUT ,"TK_WITHOUT "},
|
|
|
|
{TK_COMMA ,"TK_COMMA "},
|
|
|
|
{TK_OR ,"TK_OR "},
|
|
|
|
{TK_AND ,"TK_AND "},
|
|
|
|
{TK_IS ,"TK_IS "},
|
|
|
|
{TK_MATCH ,"TK_MATCH "},
|
|
|
|
{TK_LIKE_KW ,"TK_LIKE_KW "},
|
|
|
|
{TK_BETWEEN ,"TK_BETWEEN "},
|
|
|
|
{TK_IN ,"TK_IN "},
|
|
|
|
/* {TK_ISNULL ,"TK_ISNULL "},*/
|
|
|
|
/* {TK_NOTNULL ,"TK_NOTNULL "},*/
|
|
|
|
{TK_NE ,"TK_NE "},
|
|
|
|
{TK_EQ ,"TK_EQ "},
|
|
|
|
{TK_GT ,"TK_GT "},
|
|
|
|
{TK_LE ,"TK_LE "},
|
|
|
|
{TK_LT ,"TK_LT "},
|
|
|
|
{TK_GE ,"TK_GE "},
|
|
|
|
{TK_ESCAPE ,"TK_ESCAPE "},
|
|
|
|
{TK_BITAND ,"TK_BITAND "},
|
|
|
|
{TK_BITOR ,"TK_BITOR "},
|
|
|
|
{TK_LSHIFT ,"TK_LSHIFT "},
|
|
|
|
{TK_RSHIFT ,"TK_RSHIFT "},
|
|
|
|
{TK_PLUS ,"TK_PLUS "},
|
|
|
|
{TK_MINUS ,"TK_MINUS "},
|
|
|
|
{TK_STAR ,"TK_STAR "},
|
|
|
|
{TK_SLASH ,"TK_SLASH "},
|
|
|
|
{TK_REM ,"TK_REM "},
|
|
|
|
{TK_CONCAT ,"TK_CONCAT "},
|
|
|
|
{TK_COLLATE ,"TK_COLLATE "},
|
|
|
|
{TK_BITNOT ,"TK_BITNOT "},
|
|
|
|
{TK_ID ,"TK_ID "},
|
|
|
|
{TK_ABORT ,"TK_ABORT "},
|
|
|
|
{TK_ACTION ,"TK_ACTION "},
|
|
|
|
{TK_AFTER ,"TK_AFTER "},
|
|
|
|
{TK_ANALYZE ,"TK_ANALYZE "},
|
|
|
|
{TK_ASC ,"TK_ASC "},
|
|
|
|
{TK_ATTACH ,"TK_ATTACH "},
|
|
|
|
{TK_BEFORE ,"TK_BEFORE "},
|
|
|
|
{TK_BEGIN ,"TK_BEGIN "},
|
|
|
|
{TK_BY ,"TK_BY "},
|
|
|
|
{TK_CASCADE ,"TK_CASCADE "},
|
|
|
|
{TK_CAST ,"TK_CAST "},
|
|
|
|
{TK_COLUMNKW ,"TK_COLUMNKW "},
|
|
|
|
{TK_CONFLICT ,"TK_CONFLICT "},
|
|
|
|
{TK_DATABASE ,"TK_DATABASE "},
|
|
|
|
{TK_DESC ,"TK_DESC "},
|
|
|
|
{TK_DETACH ,"TK_DETACH "},
|
|
|
|
{TK_EACH ,"TK_EACH "},
|
|
|
|
{TK_END ,"TK_END "},
|
|
|
|
{TK_FAIL ,"TK_FAIL "},
|
|
|
|
{TK_FOR ,"TK_FOR "},
|
|
|
|
{TK_IGNORE ,"TK_IGNORE "},
|
|
|
|
{TK_INITIALLY ,"TK_INITIALLY "},
|
|
|
|
{TK_INSTEAD ,"TK_INSTEAD "},
|
|
|
|
{TK_NO ,"TK_NO "},
|
|
|
|
{TK_PLAN ,"TK_PLAN "},
|
|
|
|
{TK_QUERY ,"TK_QUERY "},
|
|
|
|
{TK_KEY ,"TK_KEY "},
|
|
|
|
{TK_OF ,"TK_OF "},
|
|
|
|
{TK_TO,"TK_TO"},
|
|
|
|
{TK_OFFSET ,"TK_OFFSET "},
|
|
|
|
{TK_PRAGMA ,"TK_PRAGMA "},
|
|
|
|
{TK_RAISE ,"TK_RAISE "},
|
|
|
|
{TK_RECURSIVE ,"TK_RECURSIVE "},
|
|
|
|
{TK_RELEASE ,"TK_RELEASE "},
|
|
|
|
{TK_REPLACE ,"TK_REPLACE "},
|
|
|
|
{TK_RESTRICT ,"TK_RESTRICT "},
|
|
|
|
{TK_ROW ,"TK_ROW "},
|
|
|
|
{TK_TRANSACTION ,"TK_TRANSACTION"},
|
|
|
|
{TK_START ,"TK_START"},
|
|
|
|
{TK_COMMIT ,"TK_COMMIT"},
|
|
|
|
{TK_ROLLBACK ,"TK_ROLLBACK "},
|
|
|
|
{TK_SAVEPOINT ,"TK_SAVEPOINT "},
|
|
|
|
{TK_TRIGGER ,"TK_TRIGGER "},
|
|
|
|
{TK_VACUUM ,"TK_VACUUM "},
|
|
|
|
{TK_VIEW ,"TK_VIEW "},
|
|
|
|
{TK_VIRTUAL ,"TK_VIRTUAL "},
|
|
|
|
{TK_WITH ,"TK_WITH "},
|
|
|
|
{TK_RENAME ,"TK_RENAME "},
|
|
|
|
{TK_ANY ,"TK_ANY "},
|
|
|
|
{TK_STRING ,"TK_STRING "},
|
|
|
|
{TK_JOIN_KW ,"TK_JOIN_KW "},
|
|
|
|
{TK_INTEGER ,"TK_INTEGER "},
|
|
|
|
{TK_FLOAT ,"TK_FLOAT "},
|
|
|
|
{TK_CONSTRAINT ,"TK_CONSTRAINT"},
|
|
|
|
{TK_DEFAULT ,"TK_DEFAULT "},
|
|
|
|
{TK_CHECK ,"TK_CHECK "},
|
|
|
|
{TK_AUTO_INCREMENT ,"TK_AUTO_INCREMENT "},
|
|
|
|
{TK_PRIMARY ,"TK_PRIMARY "},
|
|
|
|
{TK_UNIQUE ,"TK_UNIQUE "},
|
|
|
|
{TK_FOREIGN ,"TK_FOREIGN "},
|
|
|
|
{TK_DROP ,"TK_DROP "},
|
|
|
|
{TK_SELECT ,"TK_SELECT "},
|
|
|
|
{TK_VALUES ,"TK_VALUES "},
|
|
|
|
{TK_DISTINCT ,"TK_DISTINCT "},
|
|
|
|
{TK_DOT ,"TK_DOT "},
|
|
|
|
{TK_FROM ,"TK_FROM "},
|
|
|
|
{TK_JOIN ,"TK_JOIN "},
|
|
|
|
{TK_ON ,"TK_ON "},
|
|
|
|
{TK_USING ,"TK_USING "},
|
|
|
|
{TK_ORDER ,"TK_ORDER "},
|
|
|
|
{TK_GROUP ,"TK_GROUP "},
|
|
|
|
{TK_HAVING ,"TK_HAVING "},
|
|
|
|
{TK_LIMIT ,"TK_LIMIT "},
|
|
|
|
{TK_DELETE ,"TK_DELETE "},
|
|
|
|
{TK_WHERE ,"TK_WHERE "},
|
|
|
|
{TK_UPDATE ,"TK_UPDATE "},
|
|
|
|
{TK_SET ,"TK_SET "},
|
|
|
|
{TK_INTO ,"TK_INTO "},
|
|
|
|
{TK_INSERT ,"TK_INSERT "},
|
|
|
|
{TK_NULL ,"TK_NULL "},
|
|
|
|
{TK_BLOB ,"TK_BLOB "},
|
|
|
|
{TK_CASE ,"TK_CASE "},
|
|
|
|
{TK_WHEN ,"TK_WHEN "},
|
|
|
|
{TK_THEN ,"TK_THEN "},
|
|
|
|
{TK_ELSE ,"TK_ELSE "}
|
|
|
|
};
|
|
|
|
int len = sizeof(token_list)/sizeof(struct token_list_s);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < len; ++i)
|
|
|
|
{
|
|
|
|
if (token_list[i].code == op)
|
|
|
|
return token_list[i].name;
|
|
|
|
}
|
|
|
|
return "NotFound";
|
|
|
|
}
|
|
|
|
|
|
|
|
/* print the syntax tree, used for debug */
|
|
|
|
void sql_expr_print(sql_expr_t *p, int depth)
|
|
|
|
{
|
|
|
|
if (p) {
|
|
|
|
int INDENT = 2;
|
|
|
|
static const char *spaces = " ";
|
|
|
|
printf("%.*s", depth *INDENT, spaces);
|
|
|
|
printf("%s ", sql_get_token_name(p->op));
|
|
|
|
if (p->op != TK_INTEGER && p->token_text)
|
|
|
|
printf("%s\n", p->token_text);
|
|
|
|
else if (p->op == TK_INTEGER)
|
|
|
|
printf("%" PRIu64 "\n", p->num_value);
|
|
|
|
else
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
if (p->left)
|
|
|
|
sql_expr_print(p->left, depth+1);
|
|
|
|
else if (p->right)
|
|
|
|
printf("%.*s[nul]\n", (depth+1)*INDENT, spaces);
|
|
|
|
else
|
|
|
|
;
|
|
|
|
if (p->right)
|
|
|
|
sql_expr_print(p->right, depth+1);
|
|
|
|
else if (p->left)
|
|
|
|
printf("%.*s[nul]\n", (depth+1)*INDENT, spaces);
|
|
|
|
else
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sql_statement_free(void *clause, sql_stmt_type_t stmt_type)
|
|
|
|
{
|
|
|
|
if (!clause)
|
|
|
|
return;
|
|
|
|
switch(stmt_type) {
|
|
|
|
case STMT_SELECT:
|
|
|
|
sql_select_free(clause);
|
|
|
|
break;
|
|
|
|
case STMT_UPDATE:
|
|
|
|
sql_update_free(clause);
|
|
|
|
break;
|
|
|
|
case STMT_INSERT:
|
|
|
|
sql_insert_free(clause);
|
|
|
|
break;
|
|
|
|
case STMT_DELETE:
|
|
|
|
sql_delete_free(clause);
|
|
|
|
break;
|
|
|
|
case STMT_SHOW:
|
|
|
|
break;
|
|
|
|
case STMT_SHOW_COLUMNS:
|
|
|
|
case STMT_SHOW_CREATE:
|
|
|
|
case STMT_EXPLAIN_TABLE:
|
|
|
|
sql_src_list_free(clause);
|
|
|
|
break;
|
|
|
|
case STMT_SET:
|
|
|
|
sql_expr_list_free(clause);
|
|
|
|
break;
|
|
|
|
case STMT_SET_TRANSACTION:
|
|
|
|
case STMT_SET_NAMES:
|
|
|
|
case STMT_USE:
|
|
|
|
case STMT_SAVEPOINT:
|
|
|
|
g_free(clause);
|
|
|
|
break;
|
|
|
|
case STMT_START:
|
|
|
|
case STMT_COMMIT:
|
|
|
|
case STMT_ROLLBACK:
|
|
|
|
case STMT_COMMON_DDL:
|
|
|
|
break;
|
|
|
|
case STMT_CALL:
|
|
|
|
sql_expr_free(clause);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_warning(G_STRLOC ":not supported clause type, caution mem leak");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean sql_is_quoted_string(const char *s)
|
|
|
|
{
|
|
|
|
if (!s)
|
|
|
|
return FALSE;
|
|
|
|
int len = strlen(s);
|
|
|
|
if (len < 2)
|
|
|
|
return FALSE;
|
|
|
|
if (s[0] != s[len-1])
|
|
|
|
return FALSE;
|
|
|
|
return s[0] == '\'' || s[0] == '"' || s[0] == '`';
|
|
|
|
}
|
|
|
|
|
|
|
|
int sql_join_type(sql_token_t kw)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
char *name;
|
|
|
|
uint8_t code;
|
|
|
|
} _map[] = {
|
|
|
|
{"INNER", JT_INNER},
|
|
|
|
{"CROSS", JT_CROSS},
|
|
|
|
{"NATURAL", JT_NATURAL},
|
|
|
|
{"LEFT", JT_LEFT },
|
|
|
|
{"RIGHT", JT_RIGHT},
|
|
|
|
{"OUTER", JT_OUTER},
|
|
|
|
};
|
|
|
|
int ret = JT_ERROR;
|
|
|
|
char *kw_str = sql_token_dup(kw);
|
|
|
|
int i = 0;
|
|
|
|
while (i < 6) {
|
|
|
|
if (strcasecmp(_map[i].name, kw_str) == 0) {
|
|
|
|
ret = _map[i].code;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
g_free(kw_str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* !! support only ID, INTGER and STRING
|
|
|
|
*/
|
|
|
|
gboolean sql_expr_equals(const sql_expr_t *p1, const sql_expr_t *p2)
|
|
|
|
{
|
|
|
|
if (p1 && p2 && p1->op == p2->op) {
|
|
|
|
if (p1->op == TK_ID || p1->op == TK_STRING) {
|
|
|
|
return strcmp(p1->token_text, p2->token_text) == 0;
|
|
|
|
} else if (p1->op == TK_INTEGER) {
|
|
|
|
return p1->num_value == p2->num_value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|