/* $%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%$ */ #include "sql-expression.h" #include #include #include #include #include #include #include #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; } void sql_expr_get_dotted_names(const sql_expr_t *p, char *db, int db_len, char *table, int tb_len, char *col, int col_len) { if (p && p->op == TK_DOT) { const sql_expr_t *dot = p; if (p->right->op == TK_DOT) { /* db.table.col */ if (db) { strncpy(db, p->left->token_text, db_len); } dot = p->right; } if (table && dot->left->token_text) { strncpy(table, dot->left->token_text, tb_len); } if (col && dot->right->token_text) { strncpy(col, dot->right->token_text, col_len); } } } 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; 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_t * 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); if (p->update_list) sql_expr_list_free(p->update_list); 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; }