1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.skife.jdbi.v2;
18
19 import org.antlr.runtime.ANTLRStringStream;
20 import org.antlr.runtime.Token;
21 import org.skife.jdbi.v2.exceptions.UnableToCreateStatementException;
22 import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException;
23 import org.skife.jdbi.v2.tweak.Argument;
24 import org.skife.jdbi.v2.tweak.RewrittenStatement;
25 import org.skife.jdbi.v2.tweak.StatementRewriter;
26 import org.skife.jdbi.rewriter.colon.ColonStatementLexer;
27
28 import static org.skife.jdbi.rewriter.colon.ColonStatementLexer.*;
29
30 import java.sql.PreparedStatement;
31 import java.sql.SQLException;
32 import java.util.ArrayList;
33 import java.util.List;
34
35
36
37
38
39
40 public class ColonPrefixNamedParamStatementRewriter implements StatementRewriter
41 {
42
43
44
45
46
47
48
49
50
51
52 public RewrittenStatement rewrite(String sql, Binding params, StatementContext ctx)
53 {
54 StringBuilder b = new StringBuilder();
55 ParsedStatement stmt = new ParsedStatement();
56 ColonStatementLexer lexer = new ColonStatementLexer(new ANTLRStringStream(sql));
57 try {
58 Token t = lexer.nextToken();
59 while (t.getType() != ColonStatementLexer.EOF) {
60 switch (t.getType()) {
61 case LITERAL:
62 b.append(t.getText());
63 break;
64 case NAMED_PARAM:
65 stmt.addNamedParamAt(t.getText().substring(1, t.getText().length()));
66 b.append("?");
67 break;
68 case QUOTED_TEXT:
69 b.append(t.getText());
70 break;
71 case DOUBLE_QUOTED_TEXT:
72 b.append(t.getText());
73 break;
74 case POSITIONAL_PARAM:
75 b.append("?");
76 stmt.addPositionalParamAt();
77 break;
78 }
79 t = lexer.nextToken();
80 }
81 }
82 catch (IllegalArgumentException e) {
83 throw new UnableToCreateStatementException("Exception parsing for named parameter replacement", e, ctx);
84 }
85
86 return new MyRewrittenStatement(b.toString(), stmt, ctx);
87 }
88
89 private static class MyRewrittenStatement implements RewrittenStatement
90 {
91 private final String sql;
92 private final ParsedStatement stmt;
93 private final StatementContext context;
94
95 public MyRewrittenStatement(String sql, ParsedStatement stmt, StatementContext ctx)
96 {
97 this.context = ctx;
98 this.sql = sql;
99 this.stmt = stmt;
100 }
101
102 public void bind(Binding params, PreparedStatement statement) throws SQLException
103 {
104 if (stmt.positionalOnly) {
105
106 boolean finished = false;
107 for (int i = 0; !finished; ++i) {
108 final Argument a = params.forPosition(i);
109 if (a != null) {
110 try {
111 a.apply(i + 1, statement, this.context);
112 }
113 catch (SQLException e) {
114 throw new UnableToExecuteStatementException(
115 String.format("Excpetion while binding positional param at (0 based) position %d",
116 i), e, context);
117 }
118 }
119 else {
120 finished = true;
121 }
122 }
123 }
124 else {
125
126 int i = 0;
127 for (String named_param : stmt.params) {
128 if ("*".equals(named_param)) continue;
129 Argument a = params.forName(named_param);
130 if (a == null) {
131 a = params.forPosition(i);
132 }
133
134 if (a == null) {
135 String msg = String.format("Unable to execute, no named parameter matches " +
136 "\"%s\" and no positional param for place %d (which is %d in " +
137 "the JDBC 'start at 1' scheme) has been set.",
138 named_param, i, i + 1);
139 throw new UnableToExecuteStatementException(msg, context);
140 }
141
142 try {
143 a.apply(i + 1, statement, this.context);
144 }
145 catch (SQLException e) {
146 throw new UnableToCreateStatementException(String.format("Exception while binding '%s'",
147 named_param), e, context);
148 }
149 i++;
150 }
151 }
152 }
153
154 public String getSql()
155 {
156 return sql;
157 }
158 }
159
160 private static class ParsedStatement
161 {
162
163 private boolean positionalOnly = true;
164 private List<String> params = new ArrayList<String>();
165
166 public void addNamedParamAt(String name)
167 {
168 positionalOnly = false;
169 params.add(name);
170 }
171
172 public void addPositionalParamAt()
173 {
174 params.add("*");
175 }
176 }
177 }