1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.millscript.millscript.functions;
23
24 import org.millscript.millscript.alert.Alerts;
25 import org.millscript.millscript.datatypes.DatabaseSource;
26 import org.millscript.millscript.vm.Machine;
27
28 import java.sql.Connection;
29 import java.sql.PreparedStatement;
30 import java.sql.SQLException;
31
32 /**
33 * Thsi class implements a millscript function to execute an SQL update through
34 * the JDBC interface. The update is done using a prepared statement, which
35 * should improve efficiency for multiple updates. The generated function
36 * requires at least one argument, which will be the DataSource for the update.
37 * Any subsequent arguments are treated as parameters for the prepared
38 * statement.
39 * <pre>
40 * f( datasource, arg1, arg2, arg3, ... )
41 * </pre>
42 * The function returns the number of rows changed.
43 *
44 * @since 9.6.4
45 */
46 public class JdbcExecuteUpdateFunction extends Function {
47
48 /**
49 * The SQL update statement this function will prepare and execute.
50 */
51 private final String sql;
52
53 /**
54 * The Connection, cached when the function is first appplied, or the
55 * DataSource changes.
56 */
57 private Connection cachedConn;
58
59 /**
60 * The PreparedStatement, cached when the function is first applied, or the
61 * DataSource changes.
62 */
63 private PreparedStatement cachedPrep;
64
65 /**
66 * Construct a new function which will execute the specifed SQL as a
67 * prepared statement.
68 *
69 * @param sqlString the string containing SQL to prepare and execute
70 */
71 public JdbcExecuteUpdateFunction( final String sqlString ) {
72 this.sql = sqlString;
73 }
74
75 /**
76 * @see org.millscript.millscript.functions.Function#apply(org.millscript.millscript.vm.Machine, int)
77 */
78 @Override
79 public void apply( final Machine mc, final int nargs ) {
80
81
82 this.checkNargsGT( mc, 1, nargs );
83
84
85 final Object[] prepArgs = mc.popArgsArray( nargs - 1 );
86
87
88 final DatabaseSource source = mc.popDatabaseSource();
89
90 try {
91
92
93 Connection conn = source.getConnection();
94
95
96
97 if ( conn != cachedConn ) {
98
99 if ( cachedPrep != null ) {
100 cachedPrep.close();
101 }
102
103
104 cachedPrep = conn.prepareStatement( sql );
105
106
107 cachedConn = conn;
108 }
109
110
111
112 for ( int i = 0; i < prepArgs.length; i++ ) {
113 this.cachedPrep.setObject( i + 1, prepArgs[ i ] );
114 }
115
116
117 int rowsChanged = this.cachedPrep.executeUpdate();
118
119
120 mc.pushObject( new Integer( rowsChanged ) );
121
122
123 this.cachedPrep.clearParameters();
124
125 } catch ( SQLException ex ) {
126
127
128
129
130 throw(
131 Alerts.eval(
132 "SQL update failure",
133 ex.getMessage()
134 ).culprit( "SQL", sql ).mishap()
135 );
136 }
137
138 }
139
140 /**
141 * Returns a string representation of this function. It shows that it
142 * requires one or more arguments.
143 *
144 * @return a string representation of this function
145 */
146 @Override
147 public String toString() {
148 String name = getName();
149 return (
150 name == null ?
151 "<function (1+)>" :
152 "<function " + name + "(1+)>"
153 );
154 }
155
156 }