001/*
002 * Copyright (C) 2014 Jörg Prante
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.xbib.elasticsearch.plugin.jdbc.util;
017
018import org.elasticsearch.common.io.Streams;
019import org.elasticsearch.common.xcontent.support.XContentMapValues;
020
021import java.io.FileInputStream;
022import java.io.IOException;
023import java.io.InputStreamReader;
024import java.io.Reader;
025import java.util.Arrays;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029import java.util.regex.Pattern;
030
031import static org.elasticsearch.common.collect.Lists.newLinkedList;
032import static org.elasticsearch.common.collect.Maps.newHashMap;
033
034/**
035 * The SQL command
036 */
037public class SQLCommand {
038
039    private String sql;
040
041    private static final Pattern STATEMENT_PATTERN = Pattern.compile("^\\s*(update|insert)", Pattern.CASE_INSENSITIVE);
042
043    private List<Object> params = newLinkedList();
044
045    private Map<String, Object> register = newHashMap();
046
047    private boolean callable;
048
049    public SQLCommand setSQL(String sql) throws IOException {
050        if (sql.endsWith(".sql")) {
051            Reader r = new InputStreamReader(new FileInputStream(sql), "UTF-8");
052            sql = Streams.copyToString(r);
053            r.close();
054        }
055        this.sql = sql;
056        return this;
057    }
058
059    public String getSQL() {
060        return sql;
061    }
062
063    public SQLCommand setParameters(List<Object> params) {
064        this.params = params;
065        return this;
066    }
067
068    public List<Object> getParameters() {
069        return params;
070    }
071
072    public SQLCommand setCallable(boolean callable) {
073        this.callable = callable;
074        return this;
075    }
076
077    public boolean isCallable() {
078        return callable;
079    }
080
081    public boolean isQuery() {
082        if (sql == null) {
083            throw new IllegalArgumentException("no SQL found");
084        }
085        if (STATEMENT_PATTERN.matcher(sql).find()) {
086            return false;
087        }
088        int p1 = sql.toLowerCase().indexOf("select");
089        if (p1 < 0) {
090            return false;
091        }
092        int p2 = sql.toLowerCase().indexOf("update");
093        if (p2 < 0) {
094            return true;
095        }
096        int p3 = sql.toLowerCase().indexOf("insert");
097        return p3 < 0 || p1 < p2 && p1 < p3;
098    }
099
100    /**
101     * A register is for parameters of a callable statement.
102     *
103     * @param register a map for registering parameters
104     */
105    public void setRegister(Map<String, Object> register) {
106        this.register = register;
107    }
108
109    /**
110     * Get the parameters of a callable statement
111     *
112     * @return the register map
113     */
114    public Map<String, Object> getRegister() {
115        return register;
116    }
117
118    @SuppressWarnings({"unchecked"})
119    public static List<SQLCommand> parse(Map<String, Object> settings) {
120        List<SQLCommand> sql = new LinkedList<SQLCommand>();
121        if (!XContentMapValues.isArray(settings.get("sql"))) {
122            settings.put("sql", Arrays.asList(settings.get("sql")));
123        }
124        List<Object> list = (List<Object>) settings.get("sql");
125        for (Object entry : list) {
126            SQLCommand command = new SQLCommand();
127            try {
128                if (entry instanceof Map) {
129                    Map<String, Object> m = (Map<String, Object>) entry;
130                    if (m.containsKey("statement")) {
131                        command.setSQL((String) m.get("statement"));
132                    }
133                    if (m.containsKey("parameter")) {
134                        command.setParameters(XContentMapValues.extractRawValues("parameter", m));
135                    }
136                    if (m.containsKey("callable")) {
137                        command.setCallable(XContentMapValues.nodeBooleanValue(m.get("callable")));
138                    }
139                    if (m.containsKey("register")) {
140                        command.setRegister(XContentMapValues.nodeMapValue(m.get("register"), null));
141                    }
142                } else if (entry instanceof String) {
143                    command.setSQL((String) entry);
144                }
145                sql.add(command);
146            } catch (IOException e) {
147                throw new IllegalArgumentException("SQL command not found", e);
148            }
149        }
150        return sql;
151    }
152
153    public String toString() {
154        return "statement=" + sql + " parameter=" + params + " callable=" + callable;
155    }
156
157}