mirror of
https://gitee.com/wangbin579/cetus.git
synced 2024-11-30 02:47:35 +08:00
457 lines
13 KiB
C
457 lines
13 KiB
C
/* $%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-construction.h"
|
|
#include <inttypes.h>
|
|
|
|
#include "myparser.y.h"
|
|
|
|
static void sql_expr_traverse(GString *s, sql_expr_t *expr);
|
|
|
|
static void
|
|
string_append_quoted(GString *s, const char *p, char quote)
|
|
{
|
|
g_string_append_c(s, quote);
|
|
for (; *p != '\0'; ++p) {
|
|
if (*p == quote) { /* escape quote inside string */
|
|
g_string_append_c(s, '\\');
|
|
}
|
|
g_string_append_c(s, *p);
|
|
}
|
|
g_string_append_c(s, quote);
|
|
}
|
|
|
|
/* sql construction */
|
|
static void
|
|
sql_append_expr(GString *s, sql_expr_t *p)
|
|
{
|
|
switch (p->op) {
|
|
case TK_ID:
|
|
g_string_append(s, " ");
|
|
g_string_append(s, p->token_text);
|
|
break;
|
|
case TK_EQ:
|
|
g_string_append(s, "=");
|
|
break;
|
|
case TK_LT:
|
|
g_string_append(s, "<");
|
|
break;
|
|
case TK_GT:
|
|
g_string_append(s, ">");
|
|
break;
|
|
case TK_LE:
|
|
g_string_append(s, "<=");
|
|
break;
|
|
case TK_GE:
|
|
g_string_append(s, ">=");
|
|
break;
|
|
case TK_NE:
|
|
g_string_append(s, "<>");
|
|
break;
|
|
case TK_AND:
|
|
g_string_append(s, " AND ");
|
|
break;
|
|
case TK_OR:
|
|
g_string_append(s, " OR ");
|
|
break;
|
|
case TK_DOT:
|
|
g_string_append(s, " ");
|
|
g_string_append(s, p->left->token_text);
|
|
g_string_append(s, ".");
|
|
g_string_append(s, p->right->token_text);
|
|
break;
|
|
case TK_UPLUS:
|
|
case TK_UMINUS:
|
|
case TK_INTEGER:{
|
|
char valstr[32] = { 0 };
|
|
char *pstr = valstr;
|
|
if (p->op == TK_UMINUS) {
|
|
*pstr = '-';
|
|
++pstr;
|
|
}
|
|
sprintf(pstr, "%" PRIu64, p->num_value);
|
|
g_string_append(s, valstr);
|
|
break;
|
|
}
|
|
case TK_STRING:
|
|
if (sql_is_quoted_string(p->token_text)) { /* TODO: dequote all */
|
|
g_string_append(s, " ");
|
|
g_string_append(s, p->token_text);
|
|
} else {
|
|
g_string_append_c(s, ' ');
|
|
string_append_quoted(s, p->token_text, '\'');
|
|
}
|
|
break;
|
|
case TK_FUNCTION:{
|
|
g_string_append(s, " ");
|
|
g_string_append(s, p->token_text);
|
|
g_string_append(s, "(");
|
|
sql_expr_list_t *args = p->list;
|
|
if (args) {
|
|
int i = 0;
|
|
for (i = 0; i < args->len; ++i) {
|
|
sql_expr_t *arg = g_ptr_array_index(args, i);
|
|
sql_expr_traverse(s, arg);
|
|
if (i < args->len - 1) {
|
|
g_string_append(s, ",");
|
|
}
|
|
}
|
|
}
|
|
g_string_append(s, ")");
|
|
break;
|
|
}
|
|
case TK_BETWEEN:{
|
|
sql_append_expr(s, p->left);
|
|
g_string_append(s, " BETWEEN ");
|
|
sql_expr_list_t *args = p->list;
|
|
if (args && args->len == 2) {
|
|
sql_expr_t *low = g_ptr_array_index(args, 0);
|
|
sql_expr_t *high = g_ptr_array_index(args, 1);
|
|
sql_expr_traverse(s, low);
|
|
g_string_append(s, " AND ");
|
|
sql_expr_traverse(s, high);
|
|
}
|
|
break;
|
|
}
|
|
case TK_IN:{
|
|
sql_append_expr(s, p->left);
|
|
g_string_append(s, " IN (");
|
|
if (p->list) {
|
|
sql_expr_list_t *args = p->list;
|
|
int i;
|
|
for (i = 0; args && i < args->len; ++i) {
|
|
sql_expr_t *arg = g_ptr_array_index(args, i);
|
|
sql_append_expr(s, arg);
|
|
if (i < args->len - 1) {
|
|
g_string_append_c(s, ',');
|
|
}
|
|
}
|
|
} else if (p->select) {
|
|
GString *sel = sql_construct_select(p->select);
|
|
if (sel) {
|
|
g_string_append(s, sel->str);
|
|
g_string_free(sel, TRUE);
|
|
}
|
|
}
|
|
g_string_append(s, ")");
|
|
break;
|
|
}
|
|
case TK_EXISTS:{
|
|
g_string_append(s, " EXISTS (");
|
|
GString *sel = sql_construct_select(p->select);
|
|
if (sel) {
|
|
g_string_append(s, sel->str);
|
|
g_string_free(sel, TRUE);
|
|
}
|
|
g_string_append(s, ")");
|
|
break;
|
|
}
|
|
case TK_LIKE_KW:
|
|
if (p->list) {
|
|
sql_expr_list_t *args = p->list;
|
|
if (args->len > 0) {
|
|
sql_expr_t *arg = g_ptr_array_index(args, 0);
|
|
sql_expr_traverse(s, arg);
|
|
}
|
|
g_string_append(s, " LIKE ");
|
|
if (args->len > 1) {
|
|
sql_expr_t *arg = g_ptr_array_index(args, 1);
|
|
sql_append_expr(s, arg);
|
|
}
|
|
if (args->len > 2) {
|
|
sql_expr_t *arg = g_ptr_array_index(args, 2);
|
|
g_string_append(s, " ESCAPE ");
|
|
sql_append_expr(s, arg);
|
|
}
|
|
}
|
|
break;
|
|
case TK_NOT:
|
|
g_string_append(s, " NOT(");
|
|
sql_expr_traverse(s, p->left);
|
|
g_string_append_c(s, ')');
|
|
break;
|
|
case TK_SELECT:{ /* subselect as an expression */
|
|
g_string_append(s, "(");
|
|
GString *sel = sql_construct_select(p->select);
|
|
if (sel) {
|
|
g_string_append(s, sel->str);
|
|
g_string_free(sel, TRUE);
|
|
}
|
|
g_string_append(s, ")");
|
|
break;
|
|
}
|
|
case TK_IS:
|
|
g_string_append(s, " IS ");
|
|
break;
|
|
case TK_ISNOT:
|
|
g_string_append(s, " IS NOT ");
|
|
break;
|
|
case TK_PLUS:
|
|
g_string_append_c(s, '+');
|
|
break;
|
|
case TK_MINUS:
|
|
g_string_append_c(s, '-');
|
|
break;
|
|
default:
|
|
g_string_append(s, " ");
|
|
g_string_append(s, p->token_text);
|
|
}
|
|
}
|
|
|
|
/* these expr will be processed as leaf node */
|
|
static gboolean
|
|
sql_expr_is_leaf_node(sql_expr_t *expr)
|
|
{
|
|
return (expr->op == TK_DOT && expr->left && expr->right) /* db.table */
|
|
||(expr->op == TK_UMINUS && expr->left) /* -3 */
|
|
||(expr->op == TK_UPLUS && expr->left) /* +4 */
|
|
||(expr->op == TK_BETWEEN)
|
|
|| (expr->op == TK_NOT)
|
|
|| (expr->op == TK_EXISTS)
|
|
|| (expr->op == TK_IN);
|
|
}
|
|
|
|
static void
|
|
sql_expr_traverse(GString *s, sql_expr_t *expr)
|
|
{
|
|
if (!expr)
|
|
return;
|
|
if (sql_expr_is_leaf_node(expr)) {
|
|
sql_append_expr(s, expr);
|
|
return;
|
|
}
|
|
if (expr->left) {
|
|
gboolean parenth = (expr->op == TK_AND && expr->left->op == TK_OR);
|
|
if (parenth)
|
|
g_string_append_c(s, '(');
|
|
|
|
sql_expr_traverse(s, expr->left);
|
|
if (parenth)
|
|
g_string_append_c(s, ')');
|
|
}
|
|
|
|
sql_append_expr(s, expr);
|
|
|
|
if (expr->right) {
|
|
gboolean parenth = (expr->op == TK_AND && expr->right->op == TK_OR);
|
|
if (parenth)
|
|
g_string_append_c(s, '(');
|
|
|
|
sql_expr_traverse(s, expr->right);
|
|
if (parenth)
|
|
g_string_append_c(s, ')');
|
|
}
|
|
}
|
|
|
|
static void
|
|
sql_construct_join(GString *s, int flag)
|
|
{
|
|
if (flag == JT_INNER) {
|
|
g_string_append(s, " JOIN ");
|
|
return;
|
|
}
|
|
if (flag & JT_INNER) {
|
|
g_string_append(s, "INNER ");
|
|
}
|
|
if (flag & JT_CROSS) {
|
|
g_string_append(s, "CROSS ");
|
|
}
|
|
if (flag & JT_NATURAL) {
|
|
g_string_append(s, "NATURAL ");
|
|
}
|
|
if (flag & JT_LEFT) {
|
|
g_string_append(s, "LEFT ");
|
|
}
|
|
if (flag & JT_RIGHT) {
|
|
g_string_append(s, "RIGHT ");
|
|
}
|
|
if (flag & JT_OUTER) {
|
|
g_string_append(s, "OUTER ");
|
|
}
|
|
g_string_append(s, "JOIN ");
|
|
}
|
|
|
|
static inline void
|
|
append_sql_expr(GString *s, sql_expr_t *expr)
|
|
{
|
|
g_string_append_len(s, expr->start, expr->end - expr->start);
|
|
}
|
|
|
|
GString *
|
|
sql_construct_select(sql_select_t *select)
|
|
{
|
|
int i = 0;
|
|
GString *s = g_string_new(NULL);
|
|
g_string_append(s, "SELECT ");
|
|
if (select->columns) {
|
|
if (select->flags & SF_DISTINCT) {
|
|
g_string_append(s, "DISTINCT ");
|
|
}
|
|
for (i = 0; i < select->columns->len; ++i) {
|
|
sql_expr_t *expr = g_ptr_array_index(select->columns, i);
|
|
append_sql_expr(s, expr);
|
|
if (expr->alias) {
|
|
g_string_append(s, " AS ");
|
|
g_string_append(s, expr->alias);
|
|
}
|
|
if (i != select->columns->len - 1)
|
|
g_string_append(s, ",");
|
|
}
|
|
}
|
|
if (select->from_src) {
|
|
g_string_append(s, " FROM ");
|
|
for (i = 0; i < select->from_src->len; ++i) {
|
|
sql_src_item_t *src = g_ptr_array_index(select->from_src, i);
|
|
if (src->table_name) {
|
|
if (src->dbname) {
|
|
g_string_append(s, src->dbname);
|
|
g_string_append(s, ".");
|
|
}
|
|
g_string_append(s, src->table_name);
|
|
g_string_append(s, " ");
|
|
} else if (src->select) {
|
|
GString *sub = sql_construct_select(src->select);
|
|
g_string_append_c(s, '(');
|
|
g_string_append_len(s, sub->str, sub->len);
|
|
g_string_append_c(s, ')');
|
|
g_string_free(sub, TRUE);
|
|
}
|
|
|
|
if (src->table_alias) {
|
|
g_string_append(s, " AS ");
|
|
g_string_append(s, src->table_alias);
|
|
g_string_append(s, " ");
|
|
}
|
|
if (src->on_clause) {
|
|
g_string_append(s, " ON ");
|
|
append_sql_expr(s, src->on_clause);
|
|
g_string_append_c(s, ' ');
|
|
}
|
|
if (src->jointype != 0) {
|
|
sql_construct_join(s, src->jointype);
|
|
}
|
|
}
|
|
}
|
|
if (select->where_clause) {
|
|
g_string_append(s, " WHERE ");
|
|
append_sql_expr(s, select->where_clause);
|
|
}
|
|
if (select->groupby_clause) {
|
|
sql_expr_list_t *groupby = select->groupby_clause;
|
|
g_string_append(s, " GROUP BY ");
|
|
for (i = 0; i < groupby->len; ++i) {
|
|
sql_expr_t *expr = g_ptr_array_index(groupby, i);
|
|
append_sql_expr(s, expr);
|
|
if (i < groupby->len - 1) {
|
|
g_string_append(s, ",");
|
|
}
|
|
}
|
|
}
|
|
if (select->having_clause) {
|
|
g_string_append(s, " HAVING ");
|
|
append_sql_expr(s, select->having_clause);
|
|
}
|
|
if (select->orderby_clause) {
|
|
sql_expr_list_t *orderby = select->orderby_clause;
|
|
g_string_append(s, " ORDER BY ");
|
|
for (i = 0; i < orderby->len; ++i) {
|
|
sql_column_t *col = g_ptr_array_index(orderby, i);
|
|
|
|
/* this might be duped from column, @see sql_modify_orderby
|
|
not using append_sql_expr() to reserve possible alias */
|
|
sql_expr_t *expr = col->expr;
|
|
sql_expr_traverse(s, expr);
|
|
if (col->sort_order && col->sort_order == SQL_SO_DESC) {
|
|
g_string_append(s, " DESC ");
|
|
}
|
|
if (i < orderby->len - 1) {
|
|
g_string_append(s, ",");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* don't use append_sql_expr() for LIMIT/OFFSET, the expression has changed */
|
|
if (select->limit) {
|
|
g_string_append(s, " LIMIT ");
|
|
sql_expr_traverse(s, select->limit);
|
|
}
|
|
if (select->offset) {
|
|
g_string_append(s, " OFFSET ");
|
|
sql_expr_traverse(s, select->offset);
|
|
}
|
|
if (select->lock_read) {
|
|
g_string_append(s, " FOR UPDATE");
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/* format as " expr1,expr2,expr3 "*/
|
|
void
|
|
sql_append_expr_list(GString *s, sql_expr_list_t *exprlist)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < exprlist->len; ++i) {
|
|
sql_expr_t *expr = g_ptr_array_index(exprlist, i);
|
|
append_sql_expr(s, expr);
|
|
if (i != exprlist->len - 1) {
|
|
g_string_append_c(s, ',');
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
sql_construct_insert(GString *s, sql_insert_t *p)
|
|
{
|
|
g_string_append(s, "INSERT INTO ");
|
|
if (p->table && p->table->len > 0) {
|
|
sql_src_item_t *src = g_ptr_array_index(p->table, 0);
|
|
if (src->dbname) {
|
|
g_string_append(s, src->dbname);
|
|
g_string_append_c(s, '.');
|
|
}
|
|
g_string_append(s, src->table_name);
|
|
g_string_append_c(s, ' ');
|
|
}
|
|
if (p->columns && p->columns->len > 0) {
|
|
g_string_append_len(s, p->columns_start,
|
|
p->columns_end - p->columns_start);
|
|
g_string_append_c(s, ' ');
|
|
}
|
|
if (p->sel_val) {
|
|
if (p->sel_val->from_src) {
|
|
/* select as values */
|
|
GString *select = sql_construct_select(p->sel_val);
|
|
g_string_append(s, select->str);
|
|
g_string_free(select, TRUE);
|
|
} else {
|
|
/* expression values */
|
|
g_string_append(s, "VALUES");
|
|
sql_select_t *values = p->sel_val;
|
|
for (; values; values = values->prior) {
|
|
sql_expr_list_t *cols = values->columns;
|
|
g_string_append_c(s, '(');
|
|
sql_append_expr_list(s, cols);
|
|
g_string_append(s, "),");
|
|
}
|
|
s->str[s->len - 1] = ' '; /* no comma at the end */
|
|
}
|
|
}
|
|
}
|