1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.command.dml; |
7 | |
8 | import java.io.BufferedInputStream; |
9 | import java.io.BufferedOutputStream; |
10 | import java.io.IOException; |
11 | import java.io.InputStream; |
12 | import java.io.OutputStream; |
13 | |
14 | import org.h2.api.ErrorCode; |
15 | import org.h2.api.JavaObjectSerializer; |
16 | import org.h2.command.Prepared; |
17 | import org.h2.engine.Constants; |
18 | import org.h2.engine.Database; |
19 | import org.h2.engine.Session; |
20 | import org.h2.engine.SysProperties; |
21 | import org.h2.expression.Expression; |
22 | import org.h2.message.DbException; |
23 | import org.h2.security.SHA256; |
24 | import org.h2.store.DataHandler; |
25 | import org.h2.store.FileStore; |
26 | import org.h2.store.FileStoreInputStream; |
27 | import org.h2.store.FileStoreOutputStream; |
28 | import org.h2.store.LobStorageBackend; |
29 | import org.h2.store.fs.FileUtils; |
30 | import org.h2.tools.CompressTool; |
31 | import org.h2.util.IOUtils; |
32 | import org.h2.util.SmallLRUCache; |
33 | import org.h2.util.TempFileDeleter; |
34 | |
35 | /** |
36 | * This class is the base for RunScriptCommand and ScriptCommand. |
37 | */ |
38 | abstract class ScriptBase extends Prepared implements DataHandler { |
39 | |
40 | /** |
41 | * The default name of the script file if .zip compression is used. |
42 | */ |
43 | private static final String SCRIPT_SQL = "script.sql"; |
44 | |
45 | /** |
46 | * The output stream. |
47 | */ |
48 | protected OutputStream out; |
49 | |
50 | /** |
51 | * The input stream. |
52 | */ |
53 | protected InputStream in; |
54 | |
55 | /** |
56 | * The file name (if set). |
57 | */ |
58 | private Expression fileNameExpr; |
59 | |
60 | private Expression password; |
61 | |
62 | private String fileName; |
63 | |
64 | private String cipher; |
65 | private FileStore store; |
66 | private String compressionAlgorithm; |
67 | |
68 | ScriptBase(Session session) { |
69 | super(session); |
70 | } |
71 | |
72 | public void setCipher(String c) { |
73 | cipher = c; |
74 | } |
75 | |
76 | private boolean isEncrypted() { |
77 | return cipher != null; |
78 | } |
79 | |
80 | public void setPassword(Expression password) { |
81 | this.password = password; |
82 | } |
83 | |
84 | public void setFileNameExpr(Expression file) { |
85 | this.fileNameExpr = file; |
86 | } |
87 | |
88 | protected String getFileName() { |
89 | if (fileNameExpr != null && fileName == null) { |
90 | fileName = fileNameExpr.optimize(session).getValue(session).getString(); |
91 | if (fileName == null || fileName.trim().length() == 0) { |
92 | fileName = "script.sql"; |
93 | } |
94 | fileName = SysProperties.getScriptDirectory() + fileName; |
95 | } |
96 | return fileName; |
97 | } |
98 | |
99 | @Override |
100 | public boolean isTransactional() { |
101 | return false; |
102 | } |
103 | |
104 | /** |
105 | * Delete the target file. |
106 | */ |
107 | void deleteStore() { |
108 | String file = getFileName(); |
109 | if (file != null) { |
110 | FileUtils.delete(file); |
111 | } |
112 | } |
113 | |
114 | private void initStore() { |
115 | Database db = session.getDatabase(); |
116 | byte[] key = null; |
117 | if (cipher != null && password != null) { |
118 | char[] pass = password.optimize(session). |
119 | getValue(session).getString().toCharArray(); |
120 | key = SHA256.getKeyPasswordHash("script", pass); |
121 | } |
122 | String file = getFileName(); |
123 | store = FileStore.open(db, file, "rw", cipher, key); |
124 | store.setCheckedWriting(false); |
125 | store.init(); |
126 | } |
127 | |
128 | /** |
129 | * Open the output stream. |
130 | */ |
131 | void openOutput() { |
132 | String file = getFileName(); |
133 | if (file == null) { |
134 | return; |
135 | } |
136 | if (isEncrypted()) { |
137 | initStore(); |
138 | out = new FileStoreOutputStream(store, this, compressionAlgorithm); |
139 | // always use a big buffer, otherwise end-of-block is written a lot |
140 | out = new BufferedOutputStream(out, Constants.IO_BUFFER_SIZE_COMPRESS); |
141 | } else { |
142 | OutputStream o; |
143 | try { |
144 | o = FileUtils.newOutputStream(file, false); |
145 | } catch (IOException e) { |
146 | throw DbException.convertIOException(e, null); |
147 | } |
148 | out = new BufferedOutputStream(o, Constants.IO_BUFFER_SIZE); |
149 | out = CompressTool.wrapOutputStream(out, compressionAlgorithm, SCRIPT_SQL); |
150 | } |
151 | } |
152 | |
153 | /** |
154 | * Open the input stream. |
155 | */ |
156 | void openInput() { |
157 | String file = getFileName(); |
158 | if (file == null) { |
159 | return; |
160 | } |
161 | if (isEncrypted()) { |
162 | initStore(); |
163 | in = new FileStoreInputStream(store, this, compressionAlgorithm != null, false); |
164 | } else { |
165 | InputStream inStream; |
166 | try { |
167 | inStream = FileUtils.newInputStream(file); |
168 | } catch (IOException e) { |
169 | throw DbException.convertIOException(e, file); |
170 | } |
171 | in = new BufferedInputStream(inStream, Constants.IO_BUFFER_SIZE); |
172 | in = CompressTool.wrapInputStream(in, compressionAlgorithm, SCRIPT_SQL); |
173 | if (in == null) { |
174 | throw DbException.get(ErrorCode.FILE_NOT_FOUND_1, SCRIPT_SQL + " in " + file); |
175 | } |
176 | } |
177 | } |
178 | |
179 | /** |
180 | * Close input and output streams. |
181 | */ |
182 | void closeIO() { |
183 | IOUtils.closeSilently(out); |
184 | out = null; |
185 | IOUtils.closeSilently(in); |
186 | in = null; |
187 | if (store != null) { |
188 | store.closeSilently(); |
189 | store = null; |
190 | } |
191 | } |
192 | |
193 | @Override |
194 | public boolean needRecompile() { |
195 | return false; |
196 | } |
197 | |
198 | @Override |
199 | public String getDatabasePath() { |
200 | return null; |
201 | } |
202 | |
203 | @Override |
204 | public FileStore openFile(String name, String mode, boolean mustExist) { |
205 | return null; |
206 | } |
207 | |
208 | @Override |
209 | public void checkPowerOff() { |
210 | session.getDatabase().checkPowerOff(); |
211 | } |
212 | |
213 | @Override |
214 | public void checkWritingAllowed() { |
215 | session.getDatabase().checkWritingAllowed(); |
216 | } |
217 | |
218 | @Override |
219 | public int getMaxLengthInplaceLob() { |
220 | return session.getDatabase().getMaxLengthInplaceLob(); |
221 | } |
222 | |
223 | @Override |
224 | public TempFileDeleter getTempFileDeleter() { |
225 | return session.getDatabase().getTempFileDeleter(); |
226 | } |
227 | |
228 | @Override |
229 | public String getLobCompressionAlgorithm(int type) { |
230 | return session.getDatabase().getLobCompressionAlgorithm(type); |
231 | } |
232 | |
233 | public void setCompressionAlgorithm(String algorithm) { |
234 | this.compressionAlgorithm = algorithm; |
235 | } |
236 | |
237 | @Override |
238 | public Object getLobSyncObject() { |
239 | return this; |
240 | } |
241 | |
242 | @Override |
243 | public SmallLRUCache<String, String[]> getLobFileListCache() { |
244 | return null; |
245 | } |
246 | |
247 | @Override |
248 | public LobStorageBackend getLobStorage() { |
249 | return null; |
250 | } |
251 | |
252 | @Override |
253 | public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, |
254 | int off, int length) { |
255 | throw DbException.throwInternalError(); |
256 | } |
257 | |
258 | @Override |
259 | public JavaObjectSerializer getJavaObjectSerializer() { |
260 | return session.getDataHandler().getJavaObjectSerializer(); |
261 | } |
262 | } |