File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\server\pg\PgServer.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.server.pg;
8
9 import java.io.IOException;
10 import java.net.ServerSocket;
11 import java.net.Socket;
12 import java.net.UnknownHostException;
13 import java.sql.Connection;
14 import java.sql.PreparedStatement;
15 import java.sql.ResultSet;
16 import java.sql.SQLException;
17 import java.sql.Timestamp;
18 import java.sql.Types;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.Set;
22 import org.h2.engine.Constants;
23 import org.h2.message.DbException;
24 import org.h2.server.Service;
25 import org.h2.util.NetUtils;
26 import org.h2.util.New;
27 import org.h2.util.Tool;
28
29 /**
30 * This class implements a subset of the PostgreSQL protocol as described here:
31 * http://developer.postgresql.org/pgdocs/postgres/protocol.html
32 * The PostgreSQL catalog is described here:
33 * http://www.postgresql.org/docs/7.4/static/catalogs.html
34 *
35 * @author Thomas Mueller
36 * @author Sergi Vladykin 2009-07-03 (convertType)
37 */
38 public class PgServer implements Service {
39
40 /**
41 * The default port to use for the PG server.
42 * This value is also in the documentation and in the Server javadoc.
43 */
44 public static final int DEFAULT_PORT = 5435;
45
46 /**
47 * The VARCHAR type.
48 */
49 public static final int PG_TYPE_VARCHAR = 1043;
50
51 /**
52 * The integer array type (for the column pg_index.indkey).
53 */
54 public static final int PG_TYPE_INT2VECTOR = 22;
55
56 private static final int PG_TYPE_BOOL = 16;
57 private static final int PG_TYPE_BYTEA = 17;
58 private static final int PG_TYPE_BPCHAR = 1042;
59 private static final int PG_TYPE_INT8 = 20;
60 private static final int PG_TYPE_INT2 = 21;
61 private static final int PG_TYPE_INT4 = 23;
62 private static final int PG_TYPE_TEXT = 25;
63 private static final int PG_TYPE_OID = 26;
64 private static final int PG_TYPE_FLOAT4 = 700;
65 private static final int PG_TYPE_FLOAT8 = 701;
66 private static final int PG_TYPE_UNKNOWN = 705;
67 private static final int PG_TYPE_TEXTARRAY = 1009;
68 private static final int PG_TYPE_DATE = 1082;
69 private static final int PG_TYPE_TIME = 1083;
70 private static final int PG_TYPE_TIMESTAMP_NO_TMZONE = 1114;
71 private static final int PG_TYPE_NUMERIC = 1700;
72
73 private final HashSet<Integer> typeSet = New.hashSet();
74
75 private int port = PgServer.DEFAULT_PORT;
76 private boolean portIsSet;
77 private boolean stop;
78 private boolean trace;
79 private ServerSocket serverSocket;
80 private final Set<PgServerThread> running = Collections.synchronizedSet(new HashSet<PgServerThread>());
81 private String baseDir;
82 private boolean allowOthers;
83 private boolean isDaemon;
84 private boolean ifExists;
85
86 public void init(String... args) {
87 port = DEFAULT_PORT;
88 for (int i = 0; args != null && i < args.length; i++) {
89 String a = args[i];
90 if (Tool.isOption(a, "-trace")) {
91 trace = true;
92 } else if (Tool.isOption(a, "-pgPort")) {
93 port = Integer.decode(args[++i]);
94 portIsSet = true;
95 } else if (Tool.isOption(a, "-baseDir")) {
96 baseDir = args[++i];
97 } else if (Tool.isOption(a, "-pgAllowOthers")) {
98 allowOthers = true;
99 } else if (Tool.isOption(a, "-pgDaemon")) {
100 isDaemon = true;
101 } else if (Tool.isOption(a, "-ifExists")) {
102 ifExists = true;
103 }
104 }
105 org.h2.Driver.load();
106 // int testing;
107 // trace = true;
108 }
109
110 boolean getTrace() {
111 return trace;
112 }
113
114 /**
115 * Print a message if the trace flag is enabled.
116 *
117 * @param s the message
118 */
119 void trace(String s) {
120 if (trace) {
121 System.out.println(s);
122 }
123 }
124
125 /**
126 * Remove a thread from the list.
127 *
128 * @param t the thread to remove
129 */
130 synchronized void remove(PgServerThread t) {
131 running.remove(t);
132 }
133
134 /**
135 * Print the stack trace if the trace flag is enabled.
136 *
137 * @param e the exception
138 */
139 void traceError(Exception e) {
140 if (trace) {
141 e.printStackTrace();
142 }
143 }
144
145 public String getURL() {
146 return "pg://" + NetUtils.getLocalAddress() + ":" + port;
147 }
148
149 public int getPort() {
150 return port;
151 }
152
153 private boolean allow(Socket socket) {
154 if (allowOthers) {
155 return true;
156 }
157 try {
158 return NetUtils.isLocalAddress(socket);
159 } catch (UnknownHostException e) {
160 traceError(e);
161 return false;
162 }
163 }
164
165 public void start() {
166 try {
167 serverSocket = NetUtils.createServerSocket(port, false);
168 } catch (DbException e) {
169 if (!portIsSet) {
170 serverSocket = NetUtils.createServerSocket(0, false);
171 } else {
172 throw e;
173 }
174 }
175 port = serverSocket.getLocalPort();
176 }
177
178 public void listen() {
179 String threadName = Thread.currentThread().getName();
180 try {
181 while (!stop) {
182 Socket s = serverSocket.accept();
183 if (!allow(s)) {
184 trace("Connection not allowed");
185 s.close();
186 } else {
187 PgServerThread c = new PgServerThread(s, this);
188 running.add(c);
189 c.setProcessId(running.size());
190 Thread thread = new Thread(c, threadName+" thread");
191 thread.setDaemon(isDaemon);
192 c.setThread(thread);
193 thread.start();
194 }
195 }
196 } catch (Exception e) {
197 if (!stop) {
198 e.printStackTrace();
199 }
200 }
201 }
202
203 public void stop() {
204 // TODO server: combine with tcp server
205 if (!stop) {
206 stop = true;
207 if (serverSocket != null) {
208 try {
209 serverSocket.close();
210 } catch (IOException e) {
211 // TODO log exception
212 e.printStackTrace();
213 }
214 serverSocket = null;
215 }
216 }
217 // TODO server: using a boolean 'now' argument? a timeout?
218 for (PgServerThread c : New.arrayList(running)) {
219 c.close();
220 try {
221 Thread t = c.getThread();
222 if (t != null) {
223 t.join(100);
224 }
225 } catch (Exception e) {
226 // TODO log exception
227 e.printStackTrace();
228 }
229 }
230 }
231
232 public boolean isRunning(boolean traceError) {
233 if (serverSocket == null) {
234 return false;
235 }
236 try {
237 Socket s = NetUtils.createLoopbackSocket(serverSocket.getLocalPort(), false);
238 s.close();
239 return true;
240 } catch (Exception e) {
241 if (traceError) {
242 traceError(e);
243 }
244 return false;
245 }
246 }
247
248 String getBaseDir() {
249 return baseDir;
250 }
251
252 public boolean getAllowOthers() {
253 return allowOthers;
254 }
255
256 public String getType() {
257 return "PG";
258 }
259
260 public String getName() {
261 return "H2 PG Server";
262 }
263
264 boolean getIfExists() {
265 return ifExists;
266 }
267
268 /**
269 * The Java implementation of the PostgreSQL function pg_get_indexdef. The
270 * method is used to get CREATE INDEX command for an index, or the column
271 * definition of one column in the index.
272 *
273 * @param conn the connection
274 * @param indexId the index id
275 * @param ordinalPosition the ordinal position (null if the SQL statement
276 * should be returned)
277 * @param pretty this flag is ignored
278 * @return the SQL statement or the column name
279 */
280 public static String getIndexColumn(Connection conn, int indexId, Integer ordinalPosition, Boolean pretty)
281 throws SQLException {
282 if (ordinalPosition == null || ordinalPosition.intValue() == 0) {
283 PreparedStatement prep = conn.prepareStatement("select sql from information_schema.indexes where id=?");
284 prep.setInt(1, indexId);
285 ResultSet rs = prep.executeQuery();
286 if (rs.next()) {
287 return rs.getString(1);
288 }
289 return "";
290 }
291 PreparedStatement prep = conn.prepareStatement(
292 "select column_name from information_schema.indexes where id=? and ordinal_position=?");
293 prep.setInt(1, indexId);
294 prep.setInt(2, ordinalPosition.intValue());
295 ResultSet rs = prep.executeQuery();
296 if (rs.next()) {
297 return rs.getString(1);
298 }
299 return "";
300 }
301
302 /**
303 * Get the name of the current schema.
304 * This method is called by the database.
305 *
306 * @param conn the connection
307 * @return the schema name
308 */
309 public static String getCurrentSchema(Connection conn) throws SQLException {
310 ResultSet rs = conn.createStatement().executeQuery("call schema()");
311 rs.next();
312 return rs.getString(1);
313 }
314
315 /**
316 * Get the OID of an object.
317 * This method is called by the database.
318 *
319 * @param conn the connection
320 * @param tableName the table name
321 * @return the oid
322 */
323 public static int getOid(Connection conn, String tableName) throws SQLException {
324 if (tableName.startsWith("\"") && tableName.endsWith("\"")) {
325 tableName = tableName.substring(1, tableName.length() - 1);
326 }
327 PreparedStatement prep = conn.prepareStatement("select oid from pg_class where relName = ?");
328 prep.setString(1, tableName);
329 ResultSet rs = prep.executeQuery();
330 if (!rs.next()) {
331 return 0;
332 }
333 return rs.getInt(1);
334 }
335
336 /**
337 * Get the name of this encoding code.
338 * This method is called by the database.
339 *
340 * @param code the encoding code
341 * @return the encoding name
342 */
343 public static String getEncodingName(int code) {
344 switch (code) {
345 case 0:
346 return "SQL_ASCII";
347 case 6:
348 return "UTF8";
349 case 8:
350 return "LATIN1";
351 default:
352 return code < 40 ? "UTF8" : "";
353 }
354 }
355
356 /**
357 * Get the version. This method must return PostgreSQL to keep some clients
358 * happy. This method is called by the database.
359 *
360 * @return the server name and version
361 */
362 public static String getVersion() {
363 return "PostgreSQL 8.1.4 server protocol using H2 " + Constants.getFullVersion();
364 }
365
366 /**
367 * Get the current system time.
368 * This method is called by the database.
369 *
370 * @return the current system time
371 */
372 public static Timestamp getStartTime() {
373 return new Timestamp(System.currentTimeMillis());
374 }
375
376 /**
377 * Get the user name for this id.
378 * This method is called by the database.
379 *
380 * @param conn the connection
381 * @param id the user id
382 * @return the user name
383 */
384 public static String getUserById(Connection conn, int id) throws SQLException {
385 PreparedStatement prep = conn.prepareStatement("SELECT NAME FROM INFORMATION_SCHEMA.USERS WHERE ID=?");
386 prep.setInt(1, id);
387 ResultSet rs = prep.executeQuery();
388 if (rs.next()) {
389 return rs.getString(1);
390 }
391 return null;
392 }
393
394 /**
395 * Check if the this session has the given database privilege.
396 * This method is called by the database.
397 *
398 * @param id the session id
399 * @param privilege the privilege to check
400 * @return true
401 */
402 public static boolean hasDatabasePrivilege(int id, String privilege) {
403 return true;
404 }
405
406 /**
407 * Check if the current session has access to this table.
408 * This method is called by the database.
409 *
410 * @param table the table name
411 * @param privilege the privilege to check
412 * @return true
413 */
414 public static boolean hasTablePrivilege(String table, String privilege) {
415 return true;
416 }
417
418 /**
419 * Get the current transaction id.
420 * This method is called by the database.
421 *
422 * @param table the table name
423 * @param id the id
424 * @return 1
425 */
426 public static int getCurrentTid(String table, String id) {
427 return 1;
428 }
429
430 /**
431 * Convert the SQL type to a PostgreSQL type
432 *
433 * @param type the SQL type
434 * @return the PostgreSQL type
435 */
436 public static int convertType(final int type) {
437 switch (type) {
438 case Types.BOOLEAN:
439 return PG_TYPE_BOOL;
440 case Types.VARCHAR:
441 return PG_TYPE_VARCHAR;
442 case Types.CLOB:
443 return PG_TYPE_TEXT;
444 case Types.CHAR:
445 return PG_TYPE_BPCHAR;
446 case Types.SMALLINT:
447 return PG_TYPE_INT2;
448 case Types.INTEGER:
449 return PG_TYPE_INT4;
450 case Types.BIGINT:
451 return PG_TYPE_INT8;
452 case Types.DECIMAL:
453 return PG_TYPE_NUMERIC;
454 case Types.REAL:
455 return PG_TYPE_FLOAT4;
456 case Types.DOUBLE:
457 return PG_TYPE_FLOAT8;
458 case Types.TIME:
459 return PG_TYPE_TIME;
460 case Types.DATE:
461 return PG_TYPE_DATE;
462 case Types.TIMESTAMP:
463 return PG_TYPE_TIMESTAMP_NO_TMZONE;
464 case Types.VARBINARY:
465 return PG_TYPE_BYTEA;
466 case Types.BLOB:
467 return PG_TYPE_OID;
468 case Types.ARRAY:
469 return PG_TYPE_TEXTARRAY;
470 default:
471 return PG_TYPE_UNKNOWN;
472 }
473 }
474
475 /**
476 * Get the type hash set.
477 *
478 * @return the type set
479 */
480 HashSet<Integer> getTypeSet() {
481 return typeSet;
482 }
483
484 /**
485 * Check whether a data type is supported.
486 * A warning is logged if not.
487 *
488 * @param type the type
489 */
490 void checkType(int type) {
491 if (!typeSet.contains(type)) {
492 trace("Unsupported type: " + type);
493 }
494 }
495
496 public boolean isDaemon() {
497 return isDaemon;
498 }
499
500 }
501