View Javadoc

1   /*
2    * SymmetricDS is an open source database synchronization solution.
3    *   
4    * Copyright (C) Chris Henson <chenson42@users.sourceforge.net>
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 3 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, see
18   * <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.jumpmind.symmetric.db.hsqldb;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.ddlutils.model.Table;
26  import org.jumpmind.symmetric.db.AbstractDbDialect;
27  import org.jumpmind.symmetric.db.BinaryEncoding;
28  import org.jumpmind.symmetric.db.IDbDialect;
29  import org.jumpmind.symmetric.model.Trigger;
30  import org.jumpmind.symmetric.model.TriggerHistory;
31  
32  public class HsqlDbDialect extends AbstractDbDialect implements IDbDialect {
33  
34      static final Log logger = LogFactory.getLog(HsqlDbDialect.class);
35  
36      public static String DUAL_TABLE = "DUAL";
37  
38      private boolean initializeDatabase;
39  
40      private boolean charFieldTrimmed = false;
41  
42      private static boolean hsqldbInitialized = false;
43  
44      ThreadLocal<Boolean> syncEnabled = new ThreadLocal<Boolean>() {
45          @Override
46          protected Boolean initialValue() {
47              return Boolean.TRUE;
48          }
49  
50      };
51  
52      ThreadLocal<String> syncNodeDisabled = new ThreadLocal<String>() {
53          @Override
54          protected String initialValue() {
55              return null;
56          }
57      };
58  
59      protected void initForSpecificDialect() {
60          if (initializeDatabase) {
61              if (!hsqldbInitialized) {
62                  jdbcTemplate.update("SET WRITE_DELAY 100 MILLIS");
63                  jdbcTemplate.update("SET PROPERTY \"hsqldb.default_table_type\" 'cached'");
64                  jdbcTemplate.update("SET PROPERTY \"sql.enforce_strict_size\" true");
65                  Runtime.getRuntime().addShutdownHook(new Thread() {
66  
67                      @Override
68                      public void run() {
69                          jdbcTemplate.update("SHUTDOWN");
70                      }
71                  });
72                  hsqldbInitialized = true;
73              }
74          }
75  
76          createDummyDualTable();
77  
78          charFieldTrimmed = jdbcTemplate
79                  .queryForInt("select count(*) from INFORMATION_SCHEMA.SYSTEM_PROPERTIES where property_name='sql.enforce_strict_size' and property_value='true'") == 1;
80  
81          if (jdbcTemplate
82                  .queryForInt("select count(*) from INFORMATION_SCHEMA.SYSTEM_ALIASES where ALIAS='BASE64_ENCODE'") == 0) {
83              jdbcTemplate
84                      .update("CREATE ALIAS BASE64_ENCODE for \"org.jumpmind.symmetric.db.hsqldb.HsqlDbFunctions.encodeBase64\"");
85          }
86      }
87  
88      /***
89       * This is for use in the java triggers so we can create a virtual table w/
90       * old and new columns values to bump SQL expressions up against.
91       */
92      private void createDummyDualTable() {
93          Table table = getMetaDataFor(null, null, DUAL_TABLE, false);
94          if (table == null) {
95              jdbcTemplate.update("CREATE MEMORY TABLE " + DUAL_TABLE + "(DUMMY VARCHAR(1))");
96              jdbcTemplate.update("INSERT INTO " + DUAL_TABLE + " VALUES(NULL)");
97              jdbcTemplate.update("SET TABLE " + DUAL_TABLE + " READONLY TRUE");
98          }
99  
100     }
101 
102     protected boolean doesTriggerExistOnPlatform(String catalogName, String schema, String tableName, String triggerName) {
103         schema = schema == null ? (getDefaultSchema() == null ? null : getDefaultSchema()) : schema;
104         return jdbcTemplate.queryForInt(
105                 "select count(*) from INFORMATION_SCHEMA.SYSTEM_TRIGGERS where trigger_name = ?",
106                 new Object[] { triggerName }) > 0;
107     }
108 
109     public void removeTrigger(String schemaName, String triggerName, TriggerHistory hist) {
110         schemaName = schemaName == null ? "" : (schemaName + ".");
111         triggerName = schemaName + triggerName;
112         try {
113             jdbcTemplate.update(new String("drop trigger " + triggerName + "_" + getEngineName() + "_" + hist.getTriggerHistoryId()).toUpperCase());
114         } catch (Exception e) {
115             logger.warn("Error removing " + triggerName + ": " + e.getMessage());
116         }
117     }
118 
119     public void removeTrigger(String catalogName, String schemaName, String triggerName, String tableName, TriggerHistory oldHistory) {
120         removeTrigger(schemaName, triggerName, oldHistory);
121     }
122 
123     public boolean isBlobSyncSupported() {
124         return true;
125     }
126 
127     public boolean isClobSyncSupported() {
128         return true;
129     }
130 
131     public boolean isSyncEnabled() {
132         return syncEnabled.get();
133     }
134 
135     public String getSyncNodeDisabled() {
136         return syncNodeDisabled.get();
137     }
138 
139     public void disableSyncTriggers(String nodeId) {
140         syncEnabled.set(Boolean.FALSE);
141         syncNodeDisabled.set(nodeId);
142     }
143 
144     public void enableSyncTriggers() {
145         syncEnabled.set(Boolean.TRUE);
146         syncNodeDisabled.set(null);
147     }
148 
149     public String getSyncTriggersExpression() {
150         return "1 = 1";
151     }
152 
153     /***
154      * This is not used by the HSQLDB Java triggers
155      */
156     public String getTransactionTriggerExpression(Trigger trigger) {
157         return "not used";
158     }
159 
160     public String getSelectLastInsertIdSql(String sequenceName) {
161         return "call IDENTITY()";
162     }
163 
164     @Override
165     public BinaryEncoding getBinaryEncoding() {
166         return BinaryEncoding.BASE64;
167     }
168 
169     public boolean isCharSpacePadded() {
170         return charFieldTrimmed;
171     }
172 
173     public boolean isCharSpaceTrimmed() {
174         return false;
175     }
176 
177     public boolean isEmptyStringNulled() {
178         return false;
179     }
180 
181     public boolean storesUpperCaseNamesInCatalog() {
182         return true;
183     }
184 
185     public boolean supportsGetGeneratedKeys() {
186         return false;
187     }
188 
189     protected boolean allowsNullForIdentityColumn() {
190         return false;
191     }
192 
193     public void purge() {
194     }
195 
196     public String getDefaultCatalog() {
197         return null;
198     }
199 
200     public String getDefaultSchema() {
201         return null;
202     }
203 
204     public void setInitializeDatabase(boolean initializeDatabase) {
205         this.initializeDatabase = initializeDatabase;
206     }
207 }