cetus/lib/sql-expression.c

884 lines
24 KiB
C
Raw Normal View History

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;
}