You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnosygnu_xowa/140_dbs/src/gplx/dbs/sqls/Sql_qry_wtr_ansi.java

301 lines
11 KiB

/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.dbs.sqls; import gplx.*; import gplx.dbs.*;
import gplx.core.strings.*; import gplx.core.criterias.*;
import gplx.dbs.qrys.*;
public class Sql_qry_wtr_ansi implements Sql_qry_wtr {
private final String_bldr sb = String_bldr_.new_();
public boolean prepare = false;
public String Xto_str(Db_qry cmd, boolean prepare) {
synchronized (sb) {
this.prepare = prepare;
switch (cmd.Tid()) {
case Db_qry_.Tid_insert: return Bld_qry_insert((Db_qry_insert)cmd);
case Db_qry_.Tid_delete: return Bld_qry_delete((Db_qry_delete)cmd);
case Db_qry_.Tid_update: return Bld_qry_update((Db_qry_update)cmd);
case Db_qry_.Tid_select_in_tbl:
case Db_qry_.Tid_select: return Bld_qry_select((Db_qry__select_cmd)cmd);
case Db_qry_.Tid_sql: return ((Db_qry_sql)cmd).Xto_sql();
default: throw Err_.new_unhandled(cmd.Tid());
}
}
}
private String Bld_qry_delete(Db_qry_delete cmd) {
sb.Add_many("DELETE FROM ", cmd.Base_table());
Bld_where(sb, cmd.Where());
return sb.To_str_and_clear();
}
private String Bld_qry_insert(Db_qry_insert cmd) {
if (cmd.Select() != null) {
sb.Add_many("INSERT INTO ", cmd.Base_table(), " (");
for (int i = 0; i < cmd.Cols().Count(); i++) {
Sql_select_fld_base fld = cmd.Cols().Get_at(i);
sb.Add(fld.Alias());
sb.Add(i == cmd.Cols().Count() - 1 ? ") " : ", ");
}
sb.Add(Bld_qry_select(cmd.Select()));
return sb.To_str_and_clear();
}
int arg_count = cmd.Args().Count(); if (arg_count == 0) throw Err_.new_wo_type("Db_qry_insert has no columns", "base_table", cmd.Base_table());
int last = arg_count - 1;
sb.Add_many("INSERT INTO ", cmd.Base_table(), " (");
for (int i = 0; i < arg_count; i++) {
KeyVal pair = cmd.Args().Get_at(i);
this.Xto_sql_col(sb, pair.Key_as_obj());
sb.Add(i == last ? ")" : ", ");
}
sb.Add(" VALUES (");
for (int i = 0; i < arg_count; i++) {
KeyVal pair = cmd.Args().Get_at(i);
Db_arg arg = (Db_arg)pair.Val();
this.Bld_val(sb, arg);
sb.Add(i == last ? ")" : ", ");
}
return sb.To_str_and_clear();
}
private String Bld_qry_update(Db_qry_update cmd) {
int arg_count = cmd.Args().Count(); if (arg_count == 0) throw Err_.new_wo_type("Db_qry_update has no columns", "base_table", cmd.Base_table());
sb.Add_many("UPDATE ", cmd.Base_table(), " SET ");
for (int i = 0; i < arg_count; i++) {
KeyVal pair = cmd.Args().Get_at(i);
if (i > 0) sb.Add(", ");
this.Xto_sql_col(sb, pair.Key_as_obj());
sb.Add("=");
this.Bld_val(sb, (Db_arg)pair.Val());
}
Bld_where(sb, cmd.Where());
return sb.To_str_and_clear();
}
private String Bld_qry_select(Db_qry__select_cmd cmd) {
sb.Add("SELECT ");
if (cmd.Cols().Distinct()) sb.Add("DISTINCT ");
Sql_select_fld_list flds = cmd.Cols().Flds();
if (flds.Count() == 0) sb.Add("*");
for (int i = 0; i < flds.Count(); i++) {
Sql_select_fld_base fld = (Sql_select_fld_base)flds.Get_at(i);
if (i > 0) sb.Add(", ");
this.Xto_sql_col(sb, fld.XtoSql());
}
Bld_clause_from(sb, cmd.From());
Bld_indexed_by(sb, cmd.Indexed_by());
Bld_where(sb, cmd.Where());
Bld_select_group_by(sb, cmd.GroupBy());
Bld_select_order_by(sb, cmd.OrderBy());
Bld_select_limit(sb, cmd.Limit());
return sb.To_str_and_clear();
}
private void Bld_select_group_by(String_bldr sb, Sql_group_by groupBy) {
if (groupBy == null) return;
sb.Add(" GROUP BY ");
for (int i = 0; i < groupBy.Flds().Count(); i++) {
String item = (String)groupBy.Flds().Get_at(i);
if (i > 0) sb.Add(", ");
sb.Add(item);
}
}
private void Bld_select_order_by(String_bldr sb, Sql_order_by orderBy) {
if (orderBy == null) return;
sb.Add(" ORDER BY ");
for (int i = 0; i < orderBy.Flds().Count(); i++) {
Sql_order_by_itm item = (Sql_order_by_itm)orderBy.Flds().Get_at(i);
if (i > 0) sb.Add(", ");
sb.Add(item.XtoSql());
}
}
private void Bld_select_limit(String_bldr sb, int limit) {
if (limit == Db_qry__select_cmd.Limit_disabled) return;
sb.Add(" LIMIT ").Add(limit);
}
private void Bld_clause_from(String_bldr sb, Sql_from from) {
for (Object tblObj : from.Tbls()) {
Sql_tbl_src tbl = (Sql_tbl_src)tblObj;
sb.Add_many
( " ", String_.Upper(tbl.JoinType().Name()), " ", tbl.TblName(), String_.FormatOrEmptyStrIfNull(" {0}", tbl.Alias())
);
String tblAliasForJoin = tbl.Alias() == null ? tbl.TblName() : tbl.Alias();
for (int i = 0; i < tbl.JoinLinks().Count(); i++) {
Sql_join_itm joinLink = (Sql_join_itm)tbl.JoinLinks().Get_at(i);
String conjunction = i == 0 ? " ON " : " AND ";
sb.Add_many(conjunction, joinLink.SrcTbl(), ".", joinLink.SrcFld(), "=", tblAliasForJoin, ".", joinLink.TrgFldOrSrcFld());
}
}
}
private void Bld_indexed_by(String_bldr sb, String idx_name) {
if (idx_name == null) return;
sb.Add(" INDEXED BY ").Add(idx_name);
}
private void Xto_sql_col(String_bldr sb, Object obj) {
if (obj == null) throw Err_.new_null();
sb.Add_obj(obj); // FIXME: options for bracketing; ex: [name]
}
public void Bld_val(String_bldr sb, Db_arg arg) {
if (prepare) {
sb.Add("?");
return;
}
Object val = arg.Val();
if (val == null) {
sb.Add("NULL");
return;
}
Class<?> val_type = val.getClass();
if (val_type == Bool_.Cls_ref_type)
sb.Add_obj(Bool_.To_int(Bool_.cast(val))); // NOTE: save boolean to 0 or 1, b/c (a) db may not support bit datatype (sqllite) and (b) avoid i18n issues with "true"/"false"
else if
( val_type == Byte_.Cls_ref_type || val_type == Short_.Cls_ref_type
|| val_type == Int_.Cls_ref_type || val_type == Long_.Cls_ref_type
|| val_type == Float_.Cls_ref_type || val_type == Double_.Cls_ref_type
)
sb.Add(Object_.Xto_str_strict_or_null(val));
else if (val_type == DateAdp.class)
Bld_val_date(sb, arg, (DateAdp)val);
else if (val_type == Decimal_adp.class) {
Decimal_adp valDecimal = (Decimal_adp)val;
sb.Add(valDecimal.To_str());
}
else {
String valString = Object_.Xto_str_strict_or_null(val);
Bld_val_str(sb, arg, valString);
}
}
@gplx.Virtual public void Bld_val_str(String_bldr sb, Db_arg arg, String s) {
sb.Add_many("'", String_.Replace(s, "'", "''"), "'"); // stupid escaping of '
}
@gplx.Virtual public void Bld_val_date(String_bldr sb, Db_arg arg, DateAdp s) {
sb.Add_many("'", s.XtoStr_gplx_long(), "'");
}
public void Bld_where(String_bldr sb, Criteria crt) {
if (crt == null) return;
if (crt.Tid() == Criteria_.Tid_wrapper) {
Criteria_fld crt_fld = (Criteria_fld)crt;
Criteria crt_inner = crt_fld.Crt();
switch (crt_inner.Tid()) {
case Criteria_.Tid_const:
case Criteria_.Tid_not:
case Criteria_.Tid_and:
case Criteria_.Tid_or: crt = crt_inner; break;
default: break;
}
}
if (crt.Tid() == Criteria_.Tid_const) return;
sb.Add(" WHERE ");
this.Bld_where_val(sb, crt);
}
public void Bld_where_val(String_bldr sb, Criteria crt) {
if (crt == null) return; // handle empty crt; EX: SELECT * FROM tbl;
Criteria_bool_base crt_bool = Criteria_bool_base.as_(crt);
if (crt_bool != null) {
sb.Add("(");
Bld_where_val(sb, crt_bool.Lhs());
sb.Add_many(" ", crt_bool.Op_literal(), " ");
Bld_where_val(sb, crt_bool.Rhs());
sb.Add(")");
return;
}
if (crt.Tid() == Criteria_.Tid_db_obj_ary) {
Append_db_obj_ary(sb, (Db_obj_ary_crt)crt);
}
else {
Criteria_fld leaf = Criteria_fld.as_(crt); if (leaf == null) throw Err_.new_invalid_op(crt.To_str());
sb.Add(leaf.Key());
Bld_where_crt(sb, leaf.Crt());
}
}
private void Bld_where_crt(String_bldr sb, Criteria crt) {
switch (crt.Tid()) {
case Criteria_.Tid_eq: Bld_where_eq(sb, Criteria_eq.as_(crt)); break;
case Criteria_.Tid_comp: Bld_where_comp(sb, Criteria_comp.as_(crt)); break;
case Criteria_.Tid_between: Bld_where_between(sb, Criteria_between.as_(crt)); break;
case Criteria_.Tid_in: Bld_where_in(sb, Criteria_in.as_(crt)); break;
case Criteria_.Tid_like: Bld_where_like(sb, Criteria_like.as_(crt)); break;
case Criteria_.Tid_iomatch: Bld_where_iomatch(sb, Criteria_ioMatch.as_(crt)); break;
default: throw Err_.new_unhandled(crt);
}
}
private void Bld_where_eq(String_bldr sb, Criteria_eq crt) {
sb.Add(crt.Negated() ? "!=" : "=");
this.Bld_val(sb, Wrap(crt.Val()));
}
private void Bld_where_comp(String_bldr sb, Criteria_comp crt) {
sb.Add_many(crt.XtoSymbol());
this.Bld_val(sb, Wrap(crt.Val()));
}
private void Bld_where_between(String_bldr sb, Criteria_between crt) {
sb.Add(crt.Negated() ? " NOT BETWEEN " : " BETWEEN ");
this.Bld_val(sb, Wrap(crt.Lhs()));
sb.Add(" AND ");
this.Bld_val(sb, Wrap(crt.Rhs()));
}
private void Bld_where_like(String_bldr sb, Criteria_like crt) {
sb.Add(crt.Negated() ? " NOT LIKE " : " LIKE ");
this.Bld_val(sb, Wrap(crt.Pattern().Raw()));
sb.Add_fmt(" ESCAPE '{0}'", crt.Pattern().Escape());
}
private void Bld_where_in(String_bldr sb, Criteria_in crt) {
sb.Add(crt.Negated() ? " NOT IN (" : " IN (");
Object[] crt_vals = crt.Val_as_obj_ary();
int len = crt_vals.length;
int last = len - 1;
for (int i = 0; i < len; i++) {
Object val = crt_vals[i];
this.Bld_val(sb, Wrap(val));
sb.Add(i == last ? ")" : ", ");
}
}
private void Bld_where_iomatch(String_bldr sb, Criteria_ioMatch crt) {
sb.Add(crt.Negated() ? " NOT IOMATCH " : " IOMATCH ");
this.Bld_val(sb, Wrap(crt.Pattern().Raw()));
}
public void Append_db_obj_ary(String_bldr sb, Db_obj_ary_crt crt) {
Object[][] ary = crt.Vals();
int ary_len = ary.length;
Db_fld[] flds = crt.Flds();
for (int i = 0; i < ary_len; i++) {
Object[] itm = (Object[])ary[i];
int itm_len = itm.length;
if (i != 0) sb.Add(" OR ");
sb.Add("(");
for (int j = 0; j < itm_len; j++) {
if (j != 0) sb.Add(" AND ");
Db_fld fld = flds[j];
Object val = itm[j];
boolean quote = false;
switch (fld.Type_tid()) {
case Type_adp_.Tid__str:
case Type_adp_.Tid__char:
case Type_adp_.Tid__date:
quote = true;
break;
}
sb.Add(fld.Name());
sb.Add("=");
if (quote) sb.Add("'");
sb.Add(Object_.Xto_str_strict_or_empty(val));
if (quote) sb.Add("'");
}
sb.Add(")");
}
}
private Db_arg Wrap(Object val) {return new Db_arg("unknown", val);}
}
class Sql_qry_wtr_ansi_escape_backslash extends Sql_qry_wtr_ansi { @Override public void Bld_val_str(String_bldr sb, Db_arg arg, String s) {
if (String_.Has(s, "\\")) s = String_.Replace(s, "\\", "\\\\");
super.Bld_val_str(sb, arg, s);
}
}