1 |
/*
|
1 |
/*
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
3 |
*
|
3 |
*
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
4 |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
|
5 |
*
|
5 |
*
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
9 |
* language governing permissions and limitations under the License.
|
9 |
* language governing permissions and limitations under the License.
|
10 |
*
|
10 |
*
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
12 |
*/
|
12 |
*/
|
13 |
|
13 |
|
14 |
package org.openconcerto.sql.model;
|
14 |
package org.openconcerto.sql.model;
|
15 |
|
15 |
|
16 |
import org.openconcerto.sql.model.SQLField.Properties;
|
16 |
import org.openconcerto.sql.model.SQLField.Properties;
|
17 |
import org.openconcerto.sql.model.graph.Link.Rule;
|
17 |
import org.openconcerto.sql.model.graph.Link.Rule;
|
18 |
import org.openconcerto.sql.model.graph.TablesMap;
|
18 |
import org.openconcerto.sql.model.graph.TablesMap;
|
19 |
import org.openconcerto.sql.utils.ChangeTable.ClauseType;
|
19 |
import org.openconcerto.sql.utils.ChangeTable.ClauseType;
|
20 |
import org.openconcerto.sql.utils.ChangeTable.DeferredClause;
|
20 |
import org.openconcerto.sql.utils.ChangeTable.DeferredClause;
|
21 |
import org.openconcerto.sql.utils.SQLUtils;
|
21 |
import org.openconcerto.sql.utils.SQLUtils;
|
22 |
import org.openconcerto.utils.CollectionUtils;
|
22 |
import org.openconcerto.utils.CollectionUtils;
|
23 |
import org.openconcerto.utils.FileUtils;
|
23 |
import org.openconcerto.utils.FileUtils;
|
24 |
import org.openconcerto.utils.ListMap;
|
24 |
import org.openconcerto.utils.ListMap;
|
25 |
import org.openconcerto.utils.ProcessStreams;
|
25 |
import org.openconcerto.utils.ProcessStreams;
|
26 |
import org.openconcerto.utils.RTInterruptedException;
|
26 |
import org.openconcerto.utils.RTInterruptedException;
|
27 |
import org.openconcerto.utils.StringUtils;
|
27 |
import org.openconcerto.utils.StringUtils;
|
28 |
import org.openconcerto.utils.Tuple2;
|
28 |
import org.openconcerto.utils.Tuple2;
|
29 |
import org.openconcerto.utils.cc.ITransformer;
|
29 |
import org.openconcerto.utils.cc.ITransformer;
|
- |
|
30 |
import org.openconcerto.xml.XMLCodecUtils;
|
30 |
|
31 |
|
- |
|
32 |
import java.beans.DefaultPersistenceDelegate;
|
31 |
import java.io.BufferedReader;
|
33 |
import java.io.BufferedReader;
|
32 |
import java.io.BufferedWriter;
|
34 |
import java.io.BufferedWriter;
|
33 |
import java.io.File;
|
35 |
import java.io.File;
|
34 |
import java.io.FileInputStream;
|
36 |
import java.io.FileInputStream;
|
35 |
import java.io.FileOutputStream;
|
37 |
import java.io.FileOutputStream;
|
36 |
import java.io.IOException;
|
38 |
import java.io.IOException;
|
37 |
import java.io.InputStreamReader;
|
39 |
import java.io.InputStreamReader;
|
38 |
import java.io.OutputStreamWriter;
|
40 |
import java.io.OutputStreamWriter;
|
39 |
import java.math.BigDecimal;
|
41 |
import java.math.BigDecimal;
|
40 |
import java.nio.charset.Charset;
|
42 |
import java.nio.charset.Charset;
|
41 |
import java.sql.Blob;
|
43 |
import java.sql.Blob;
|
42 |
import java.sql.Clob;
|
44 |
import java.sql.Clob;
|
43 |
import java.sql.SQLException;
|
45 |
import java.sql.SQLException;
|
44 |
import java.sql.Timestamp;
|
46 |
import java.sql.Timestamp;
|
45 |
import java.sql.Types;
|
47 |
import java.sql.Types;
|
46 |
import java.util.Arrays;
|
48 |
import java.util.Arrays;
|
47 |
import java.util.BitSet;
|
49 |
import java.util.BitSet;
|
48 |
import java.util.IdentityHashMap;
|
50 |
import java.util.IdentityHashMap;
|
49 |
import java.util.List;
|
51 |
import java.util.List;
|
50 |
import java.util.Map;
|
52 |
import java.util.Map;
|
51 |
import java.util.Set;
|
53 |
import java.util.Set;
|
52 |
import java.util.regex.Pattern;
|
54 |
import java.util.regex.Pattern;
|
53 |
|
55 |
|
54 |
class SQLSyntaxMS extends SQLSyntax {
|
56 |
public class SQLSyntaxMS extends SQLSyntax {
|
55 |
|
57 |
|
56 |
static private final IdentityHashMap<String, String> DATE_SPECS;
|
58 |
static private final IdentityHashMap<String, String> DATE_SPECS;
|
57 |
|
59 |
|
58 |
static {
|
60 |
static {
|
59 |
DATE_SPECS = new IdentityHashMap<String, String>();
|
61 |
DATE_SPECS = new IdentityHashMap<String, String>();
|
60 |
DATE_SPECS.put(DateProp.YEAR, "yyyy");
|
62 |
DATE_SPECS.put(DateProp.YEAR, "yyyy");
|
61 |
DATE_SPECS.put(DateProp.MONTH_NAME, "MMMM");
|
63 |
DATE_SPECS.put(DateProp.MONTH_NAME, "MMMM");
|
62 |
DATE_SPECS.put(DateProp.MONTH_NUMBER, "MM");
|
64 |
DATE_SPECS.put(DateProp.MONTH_NUMBER, "MM");
|
63 |
DATE_SPECS.put(DateProp.DAY_IN_MONTH, "dd");
|
65 |
DATE_SPECS.put(DateProp.DAY_IN_MONTH, "dd");
|
64 |
DATE_SPECS.put(DateProp.DAY_NAME_IN_WEEK, "dddd");
|
66 |
DATE_SPECS.put(DateProp.DAY_NAME_IN_WEEK, "dddd");
|
65 |
DATE_SPECS.put(DateProp.HOUR, "HH");
|
67 |
DATE_SPECS.put(DateProp.HOUR, "HH");
|
66 |
DATE_SPECS.put(DateProp.MINUTE, "mm");
|
68 |
DATE_SPECS.put(DateProp.MINUTE, "mm");
|
67 |
DATE_SPECS.put(DateProp.SECOND, "ss");
|
69 |
DATE_SPECS.put(DateProp.SECOND, "ss");
|
68 |
DATE_SPECS.put(DateProp.MICROSECOND, "ffffff");
|
70 |
DATE_SPECS.put(DateProp.MICROSECOND, "ffffff");
|
- |
|
71 |
|
- |
|
72 |
XMLCodecUtils.register(SQLSyntaxMS.class, new DefaultPersistenceDelegate(new String[] {}));
|
69 |
}
|
73 |
}
|
70 |
|
74 |
|
71 |
SQLSyntaxMS() {
|
75 |
public SQLSyntaxMS() {
|
72 |
super(SQLSystem.MSSQL, DATE_SPECS);
|
76 |
super(SQLSystem.MSSQL, DATE_SPECS);
|
73 |
this.typeNames.addAll(Boolean.class, "bit");
|
77 |
this.typeNames.addAll(Boolean.class, "bit");
|
74 |
// tinyint is unsigned
|
78 |
// tinyint is unsigned
|
75 |
this.typeNames.addAll(Short.class, "smallint", "tinyint");
|
79 |
this.typeNames.addAll(Short.class, "smallint", "tinyint");
|
76 |
this.typeNames.addAll(Integer.class, "int");
|
80 |
this.typeNames.addAll(Integer.class, "int");
|
77 |
this.typeNames.addAll(Long.class, "bigint");
|
81 |
this.typeNames.addAll(Long.class, "bigint");
|
78 |
this.typeNames.addAll(BigDecimal.class, "decimal", "numeric", "smallmoney", "money");
|
82 |
this.typeNames.addAll(BigDecimal.class, "decimal", "numeric", "smallmoney", "money");
|
79 |
this.typeNames.addAll(Float.class, "real");
|
83 |
this.typeNames.addAll(Float.class, "real");
|
80 |
this.typeNames.addAll(Double.class, "double precision", "float");
|
84 |
this.typeNames.addAll(Double.class, "double precision", "float");
|
81 |
this.typeNames.addAll(Timestamp.class, "datetime2", "datetime", "smalldatetime");
|
85 |
this.typeNames.addAll(Timestamp.class, "datetime2", "datetime", "smalldatetime");
|
82 |
this.typeNames.addAll(java.sql.Date.class, "date");
|
86 |
this.typeNames.addAll(java.sql.Date.class, "date");
|
83 |
this.typeNames.addAll(java.sql.Time.class, "time");
|
87 |
this.typeNames.addAll(java.sql.Time.class, "time");
|
84 |
this.typeNames.addAll(Blob.class, "image",
|
88 |
this.typeNames.addAll(Blob.class, "image",
|
85 |
// byte[]
|
89 |
// byte[]
|
86 |
"varbinary", "binary");
|
90 |
"varbinary", "binary");
|
87 |
this.typeNames.addAll(Clob.class, "text", "ntext", "unitext");
|
91 |
this.typeNames.addAll(Clob.class, "text", "ntext", "unitext");
|
88 |
this.typeNames.addAll(String.class, "char", "varchar", "nchar", "nvarchar", "unichar", "univarchar");
|
92 |
this.typeNames.addAll(String.class, "char", "varchar", "nchar", "nvarchar", "unichar", "univarchar");
|
89 |
}
|
93 |
}
|
90 |
|
94 |
|
91 |
@Override
|
95 |
@Override
|
92 |
public final String quoteString(String s) {
|
96 |
public final String quoteString(String s) {
|
93 |
final String res = super.quoteString(s);
|
97 |
final String res = super.quoteString(s);
|
94 |
if (s == null)
|
98 |
if (s == null)
|
95 |
return res;
|
99 |
return res;
|
96 |
// only use escape form if needed (=> equals with other systems most of the time)
|
100 |
// only use escape form if needed (=> equals with other systems most of the time)
|
97 |
boolean simpleASCII = true;
|
101 |
boolean simpleASCII = true;
|
98 |
final int l = s.length();
|
102 |
final int l = s.length();
|
99 |
for (int i = 0; simpleASCII && i < l; i++) {
|
103 |
for (int i = 0; simpleASCII && i < l; i++) {
|
100 |
final char c = s.charAt(i);
|
104 |
final char c = s.charAt(i);
|
101 |
simpleASCII = c <= 0xFF;
|
105 |
simpleASCII = c <= 0xFF;
|
102 |
}
|
106 |
}
|
103 |
// see http://msdn.microsoft.com/fr-fr/library/ms191200(v=sql.105).aspx
|
107 |
// see http://msdn.microsoft.com/fr-fr/library/ms191200(v=sql.105).aspx
|
104 |
return simpleASCII ? res : "N" + res;
|
108 |
return simpleASCII ? res : "N" + res;
|
105 |
}
|
109 |
}
|
106 |
|
110 |
|
107 |
@Override
|
111 |
@Override
|
108 |
public int getMaximumIdentifierLength() {
|
112 |
public int getMaximumIdentifierLength() {
|
109 |
// https://msdn.microsoft.com/en-us/library/ms143432.aspx
|
113 |
// https://msdn.microsoft.com/en-us/library/ms143432.aspx
|
110 |
return 128;
|
114 |
return 128;
|
111 |
}
|
115 |
}
|
112 |
|
116 |
|
113 |
@Override
|
117 |
@Override
|
114 |
public String getInitSystemRoot() {
|
118 |
public String getInitSystemRoot() {
|
115 |
final String sql;
|
119 |
final String sql;
|
116 |
try {
|
120 |
try {
|
117 |
final String fileContent = FileUtils.readUTF8(SQLSyntaxPG.class.getResourceAsStream("mssql-functions.sql"));
|
121 |
final String fileContent = FileUtils.readUTF8(SQLSyntaxPG.class.getResourceAsStream("mssql-functions.sql"));
|
118 |
sql = fileContent.replace("${rootName}", SQLBase.quoteIdentifier("dbo"));
|
122 |
sql = fileContent.replace("${rootName}", SQLBase.quoteIdentifier("dbo"));
|
119 |
} catch (IOException e) {
|
123 |
} catch (IOException e) {
|
120 |
throw new IllegalStateException("cannot read functions", e);
|
124 |
throw new IllegalStateException("cannot read functions", e);
|
121 |
}
|
125 |
}
|
122 |
return sql;
|
126 |
return sql;
|
123 |
}
|
127 |
}
|
124 |
|
128 |
|
125 |
@Override
|
129 |
@Override
|
126 |
public boolean isAuto(SQLField f) {
|
130 |
public boolean isAuto(SQLField f) {
|
127 |
return f.getType().getJavaType() == Integer.class && "YES".equals(f.getMetadata("IS_AUTOINCREMENT"));
|
131 |
return f.getType().getJavaType() == Integer.class && "YES".equals(f.getMetadata("IS_AUTOINCREMENT"));
|
128 |
}
|
132 |
}
|
129 |
|
133 |
|
130 |
@Override
|
134 |
@Override
|
131 |
public String getAuto() {
|
135 |
public String getAuto() {
|
132 |
return " int IDENTITY";
|
136 |
return " int IDENTITY";
|
133 |
}
|
137 |
}
|
134 |
|
138 |
|
135 |
@Override
|
139 |
@Override
|
136 |
public int getMaximumVarCharLength() {
|
140 |
public int getMaximumVarCharLength() {
|
137 |
// http://msdn.microsoft.com/en-us/library/ms176089(v=sql.105).aspx
|
141 |
// http://msdn.microsoft.com/en-us/library/ms176089(v=sql.105).aspx
|
138 |
return 8000;
|
142 |
return 8000;
|
139 |
}
|
143 |
}
|
140 |
|
144 |
|
141 |
@Override
|
145 |
@Override
|
142 |
public String transfDefaultJDBC2SQL(SQLField f) {
|
146 |
public String transfDefaultJDBC2SQL(SQLField f) {
|
143 |
final Object def = f.getDefaultValue();
|
147 |
final Object def = f.getDefaultValue();
|
144 |
if (def == null)
|
148 |
if (def == null)
|
145 |
return null;
|
149 |
return null;
|
146 |
|
150 |
|
147 |
// remove parentheses from ((1))
|
151 |
// remove parentheses from ((1))
|
148 |
String stringDef = def.toString();
|
152 |
String stringDef = def.toString();
|
149 |
while (stringDef.charAt(0) == '(' && stringDef.charAt(stringDef.length() - 1) == ')')
|
153 |
while (stringDef.charAt(0) == '(' && stringDef.charAt(stringDef.length() - 1) == ')')
|
150 |
stringDef = stringDef.substring(1, stringDef.length() - 1);
|
154 |
stringDef = stringDef.substring(1, stringDef.length() - 1);
|
151 |
|
155 |
|
152 |
if (f.getType().getJavaType() == Boolean.class) {
|
156 |
if (f.getType().getJavaType() == Boolean.class) {
|
153 |
return stringDef.equals("'true'") ? "true" : "false";
|
157 |
return stringDef.equals("'true'") ? "true" : "false";
|
154 |
} else {
|
158 |
} else {
|
155 |
return stringDef;
|
159 |
return stringDef;
|
156 |
}
|
160 |
}
|
157 |
}
|
161 |
}
|
158 |
|
162 |
|
159 |
@Override
|
163 |
@Override
|
160 |
protected String transfDefault(SQLField f, String castless) {
|
164 |
protected String transfDefault(SQLField f, String castless) {
|
161 |
if (castless != null && f.getType().getJavaType() == Boolean.class) {
|
165 |
if (castless != null && f.getType().getJavaType() == Boolean.class) {
|
162 |
// yes MS has no true/false keywords
|
166 |
// yes MS has no true/false keywords
|
163 |
return castless.equals("TRUE") ? "'true'" : "'false'";
|
167 |
return castless.equals("TRUE") ? "'true'" : "'false'";
|
164 |
} else
|
168 |
} else
|
165 |
return castless;
|
169 |
return castless;
|
166 |
}
|
170 |
}
|
167 |
|
171 |
|
168 |
@Override
|
172 |
@Override
|
169 |
protected String getRuleSQL(final Rule r) {
|
173 |
protected String getRuleSQL(final Rule r) {
|
170 |
// MSSQL doesn't support RESTRICT
|
174 |
// MSSQL doesn't support RESTRICT
|
171 |
return (r.equals(Rule.RESTRICT) ? Rule.NO_ACTION : r).asString();
|
175 |
return (r.equals(Rule.RESTRICT) ? Rule.NO_ACTION : r).asString();
|
172 |
}
|
176 |
}
|
173 |
|
177 |
|
174 |
@Override
|
178 |
@Override
|
175 |
public String disableFKChecks(DBRoot b) {
|
179 |
public String disableFKChecks(DBRoot b) {
|
176 |
return fkChecks(b, false);
|
180 |
return fkChecks(b, false);
|
177 |
}
|
181 |
}
|
178 |
|
182 |
|
179 |
private String fkChecks(final DBRoot b, final boolean enable) {
|
183 |
private String fkChecks(final DBRoot b, final boolean enable) {
|
180 |
final String s = enable ? "with check check constraint all" : "nocheck constraint all";
|
184 |
final String s = enable ? "with check check constraint all" : "nocheck constraint all";
|
181 |
return "exec sp_MSforeachtable @command1 = 'ALTER TABLE ? " + s + "' , @whereand = " +
|
185 |
return "exec sp_MSforeachtable @command1 = 'ALTER TABLE ? " + s + "' , @whereand = " +
|
182 |
//
|
186 |
//
|
183 |
quoteString("and schema_id = SCHEMA_ID( " + quoteString(b.getName()) + " )");
|
187 |
quoteString("and schema_id = SCHEMA_ID( " + quoteString(b.getName()) + " )");
|
184 |
}
|
188 |
}
|
185 |
|
189 |
|
186 |
@Override
|
190 |
@Override
|
187 |
public String enableFKChecks(DBRoot b) {
|
191 |
public String enableFKChecks(DBRoot b) {
|
188 |
return fkChecks(b, true);
|
192 |
return fkChecks(b, true);
|
189 |
}
|
193 |
}
|
190 |
|
194 |
|
191 |
@Override
|
195 |
@Override
|
192 |
public List<Map<String, Object>> getIndexInfo(SQLTable t) throws SQLException {
|
196 |
public List<Map<String, Object>> getIndexInfo(SQLTable t) throws SQLException {
|
193 |
final String query = "SELECT NULL AS \"TABLE_CAT\", schema_name(t.schema_id) as \"TABLE_SCHEM\", t.name as \"TABLE_NAME\",\n" +
|
197 |
final String query = "SELECT NULL AS \"TABLE_CAT\", schema_name(t.schema_id) as \"TABLE_SCHEM\", t.name as \"TABLE_NAME\",\n" +
|
194 |
//
|
198 |
//
|
195 |
"~idx.is_unique as \"NON_UNIQUE\", NULL AS \"INDEX_QUALIFIER\", idx.name as \"INDEX_NAME\", NULL as \"TYPE\",\n" +
|
199 |
"~idx.is_unique as \"NON_UNIQUE\", NULL AS \"INDEX_QUALIFIER\", idx.name as \"INDEX_NAME\", NULL as \"TYPE\",\n" +
|
196 |
//
|
200 |
//
|
197 |
"indexCols.key_ordinal as \"ORDINAL_POSITION\", cols.name as \"COLUMN_NAME\",\n" +
|
201 |
"indexCols.key_ordinal as \"ORDINAL_POSITION\", cols.name as \"COLUMN_NAME\",\n" +
|
198 |
//
|
202 |
//
|
199 |
"case when indexCols.is_descending_key = 1 then 'D' else 'A' end as \"ASC_OR_DESC\", null as \"CARDINALITY\", null as \"PAGES\",\n" +
|
203 |
"case when indexCols.is_descending_key = 1 then 'D' else 'A' end as \"ASC_OR_DESC\", null as \"CARDINALITY\", null as \"PAGES\",\n" +
|
200 |
//
|
204 |
//
|
201 |
"filter_definition as \"FILTER_CONDITION\"\n" +
|
205 |
"filter_definition as \"FILTER_CONDITION\"\n" +
|
202 |
//
|
206 |
//
|
203 |
" FROM [test].[sys].[objects] t\n" +
|
207 |
" FROM [test].[sys].[objects] t\n" +
|
204 |
//
|
208 |
//
|
205 |
" join [test].[sys].[indexes] idx on idx.object_id = t.object_id\n" +
|
209 |
" join [test].[sys].[indexes] idx on idx.object_id = t.object_id\n" +
|
206 |
//
|
210 |
//
|
207 |
" join [test].[sys].[index_columns] indexCols on idx.index_id = indexCols.index_id and idx.object_id = indexCols.object_id\n" +
|
211 |
" join [test].[sys].[index_columns] indexCols on idx.index_id = indexCols.index_id and idx.object_id = indexCols.object_id\n" +
|
208 |
//
|
212 |
//
|
209 |
" join [test].[sys].[columns] cols on t.object_id = cols.object_id and cols.column_id = indexCols.column_id \n" +
|
213 |
" join [test].[sys].[columns] cols on t.object_id = cols.object_id and cols.column_id = indexCols.column_id \n" +
|
210 |
//
|
214 |
//
|
211 |
" where schema_name(t.schema_id) = " + quoteString(t.getSchema().getName()) + " and t.name = " + quoteString(t.getName()) + "\n"
|
215 |
" where schema_name(t.schema_id) = " + quoteString(t.getSchema().getName()) + " and t.name = " + quoteString(t.getName()) + "\n"
|
212 |
//
|
216 |
//
|
213 |
+ "ORDER BY \"NON_UNIQUE\", \"TYPE\", \"INDEX_NAME\", \"ORDINAL_POSITION\";";
|
217 |
+ "ORDER BY \"NON_UNIQUE\", \"TYPE\", \"INDEX_NAME\", \"ORDINAL_POSITION\";";
|
214 |
// don't cache since we don't listen on system tables
|
218 |
// don't cache since we don't listen on system tables
|
215 |
return (List<Map<String, Object>>) t.getDBSystemRoot().getDataSource().execute(query, new IResultSetHandler(SQLDataSource.MAP_LIST_HANDLER, false));
|
219 |
return (List<Map<String, Object>>) t.getDBSystemRoot().getDataSource().execute(query, new IResultSetHandler(SQLDataSource.MAP_LIST_HANDLER, false));
|
216 |
}
|
220 |
}
|
217 |
|
221 |
|
218 |
@SuppressWarnings("unchecked")
|
222 |
@SuppressWarnings("unchecked")
|
219 |
@Override
|
223 |
@Override
|
220 |
public Map<String, Object> normalizeIndexInfo(final Map m) {
|
224 |
public Map<String, Object> normalizeIndexInfo(final Map m) {
|
221 |
// genuine MS driver
|
225 |
// genuine MS driver
|
222 |
if (getSystem().getJDBCName().equals("sqlserver"))
|
226 |
if (getSystem().getJDBCName().equals("sqlserver"))
|
223 |
m.put("NON_UNIQUE", ((Number) m.get("NON_UNIQUE")).intValue() != 0);
|
227 |
m.put("NON_UNIQUE", ((Number) m.get("NON_UNIQUE")).intValue() != 0);
|
224 |
return m;
|
228 |
return m;
|
225 |
}
|
229 |
}
|
226 |
|
230 |
|
227 |
@Override
|
231 |
@Override
|
228 |
public String getDropIndex(String name, SQLName tableName) {
|
232 |
public String getDropIndex(String name, SQLName tableName) {
|
229 |
return "DROP INDEX " + SQLBase.quoteIdentifier(name) + " on " + tableName.quote() + ";";
|
233 |
return "DROP INDEX " + SQLBase.quoteIdentifier(name) + " on " + tableName.quote() + ";";
|
230 |
}
|
234 |
}
|
231 |
|
235 |
|
232 |
@Override
|
236 |
@Override
|
233 |
public boolean isUniqueException(SQLException exn) {
|
237 |
public boolean isUniqueException(SQLException exn) {
|
234 |
return SQLUtils.findWithSQLState(exn).getErrorCode() == 2601;
|
238 |
return SQLUtils.findWithSQLState(exn).getErrorCode() == 2601;
|
235 |
}
|
239 |
}
|
236 |
|
240 |
|
237 |
@Override
|
241 |
@Override
|
238 |
public boolean isDeadLockException(SQLException exn) {
|
242 |
public boolean isDeadLockException(SQLException exn) {
|
239 |
return SQLUtils.findWithSQLState(exn).getErrorCode() == 1205;
|
243 |
return SQLUtils.findWithSQLState(exn).getErrorCode() == 1205;
|
240 |
}
|
244 |
}
|
241 |
|
245 |
|
242 |
@Override
|
246 |
@Override
|
- |
|
247 |
public boolean isTableNotFoundException(Exception exn) {
|
- |
|
248 |
// TODO
|
- |
|
249 |
throw new UnsupportedOperationException();
|
- |
|
250 |
}
|
- |
|
251 |
|
- |
|
252 |
@Override
|
- |
|
253 |
public String getSetLockTimeoutQuery(int millis) {
|
- |
|
254 |
return "SET LOCK_TIMEOUT " + millis;
|
- |
|
255 |
}
|
- |
|
256 |
|
- |
|
257 |
@Override
|
- |
|
258 |
public String getShowLockTimeoutQuery() {
|
- |
|
259 |
return "SELECT @@LOCK_TIMEOUT";
|
- |
|
260 |
}
|
- |
|
261 |
|
- |
|
262 |
@Override
|
243 |
public Map<ClauseType, List<String>> getAlterField(SQLField f, Set<Properties> toAlter, String type, String defaultVal, Boolean nullable) {
|
263 |
public Map<ClauseType, List<String>> getAlterField(SQLField f, Set<Properties> toAlter, String type, String defaultVal, Boolean nullable) {
|
244 |
final ListMap<ClauseType, String> res = new ListMap<ClauseType, String>();
|
264 |
final ListMap<ClauseType, String> res = new ListMap<ClauseType, String>();
|
245 |
if (toAlter.contains(Properties.TYPE) || toAlter.contains(Properties.NULLABLE)) {
|
265 |
if (toAlter.contains(Properties.TYPE) || toAlter.contains(Properties.NULLABLE)) {
|
246 |
final String newType = toAlter.contains(Properties.TYPE) ? type : getType(f);
|
266 |
final String newType = toAlter.contains(Properties.TYPE) ? type : getType(f);
|
247 |
final boolean newNullable = toAlter.contains(Properties.NULLABLE) ? nullable : getNullable(f);
|
267 |
final boolean newNullable = toAlter.contains(Properties.NULLABLE) ? nullable : getNullable(f);
|
248 |
res.add(ClauseType.ALTER_COL, "ALTER COLUMN " + f.getQuotedName() + " " + getFieldDecl(newType, null, newNullable));
|
268 |
res.add(ClauseType.ALTER_COL, "ALTER COLUMN " + f.getQuotedName() + " " + getFieldDecl(newType, null, newNullable));
|
249 |
}
|
269 |
}
|
250 |
if (toAlter.contains(Properties.DEFAULT)) {
|
270 |
if (toAlter.contains(Properties.DEFAULT)) {
|
251 |
final Constraint existingConstraint = f.getTable().getConstraint(ConstraintType.DEFAULT, Arrays.asList(f.getName()));
|
271 |
final Constraint existingConstraint = f.getTable().getConstraint(ConstraintType.DEFAULT, Arrays.asList(f.getName()));
|
252 |
if (existingConstraint != null) {
|
272 |
if (existingConstraint != null) {
|
253 |
res.add(ClauseType.DROP_CONSTRAINT, "DROP CONSTRAINT " + SQLBase.quoteIdentifier(existingConstraint.getName()));
|
273 |
res.add(ClauseType.DROP_CONSTRAINT, "DROP CONSTRAINT " + SQLBase.quoteIdentifier(existingConstraint.getName()));
|
254 |
}
|
274 |
}
|
255 |
if (defaultVal != null) {
|
275 |
if (defaultVal != null) {
|
256 |
res.add(ClauseType.ADD_CONSTRAINT, "ADD DEFAULT " + defaultVal + " FOR " + f.getQuotedName());
|
276 |
res.add(ClauseType.ADD_CONSTRAINT, "ADD DEFAULT " + defaultVal + " FOR " + f.getQuotedName());
|
257 |
}
|
277 |
}
|
258 |
}
|
278 |
}
|
259 |
return res;
|
279 |
return res;
|
260 |
}
|
280 |
}
|
261 |
|
281 |
|
262 |
@Override
|
282 |
@Override
|
263 |
public String getRenameTable(SQLName table, String newName) {
|
283 |
public String getRenameTable(SQLName table, String newName) {
|
264 |
return "sp_rename " + SQLBase.quoteStringStd(table.quote()) + ", " + SQLBase.quoteStringStd(newName);
|
284 |
return "sp_rename " + SQLBase.quoteStringStd(table.quote()) + ", " + SQLBase.quoteStringStd(newName);
|
265 |
}
|
285 |
}
|
266 |
|
286 |
|
267 |
@Override
|
287 |
@Override
|
268 |
public String getDropTable(SQLName name, boolean ifExists, boolean restrict) {
|
288 |
public String getDropTable(SQLName name, boolean ifExists, boolean restrict) {
|
269 |
// doesn't support cascade
|
289 |
// doesn't support cascade
|
270 |
if (!restrict)
|
290 |
if (!restrict)
|
271 |
return null;
|
291 |
return null;
|
272 |
if (!ifExists) {
|
292 |
if (!ifExists) {
|
273 |
return super.getDropTable(name, ifExists, restrict);
|
293 |
return super.getDropTable(name, ifExists, restrict);
|
274 |
} else {
|
294 |
} else {
|
275 |
final String quoted = name.quote();
|
295 |
final String quoted = name.quote();
|
276 |
return "IF OBJECT_ID(" + SQLBase.quoteStringStd(quoted) + ", 'U') IS NOT NULL DROP TABLE " + quoted;
|
296 |
return "IF OBJECT_ID(" + SQLBase.quoteStringStd(quoted) + ", 'U') IS NOT NULL DROP TABLE " + quoted;
|
277 |
}
|
297 |
}
|
278 |
}
|
298 |
}
|
279 |
|
299 |
|
280 |
@Override
|
300 |
@Override
|
281 |
public String getDropRoot(String name) {
|
301 |
public String getDropRoot(String name) {
|
282 |
// Only works if getInitSystemRoot() was executed
|
302 |
// Only works if getInitSystemRoot() was executed
|
283 |
// http://ranjithk.com/2010/01/31/script-to-drop-all-objects-of-a-schema/
|
303 |
// http://ranjithk.com/2010/01/31/script-to-drop-all-objects-of-a-schema/
|
284 |
return "exec CleanUpSchema " + SQLBase.quoteStringStd(name) + ", 'w' ;";
|
304 |
return "exec CleanUpSchema " + SQLBase.quoteStringStd(name) + ", 'w' ;";
|
285 |
}
|
305 |
}
|
286 |
|
306 |
|
287 |
@Override
|
307 |
@Override
|
288 |
public String getCreateRoot(String name) {
|
308 |
public String getCreateRoot(String name) {
|
289 |
return "CREATE SCHEMA " + SQLBase.quoteIdentifier(name) + " ;";
|
309 |
return "CREATE SCHEMA " + SQLBase.quoteIdentifier(name) + " ;";
|
290 |
}
|
310 |
}
|
291 |
|
311 |
|
292 |
@Override
|
312 |
@Override
|
293 |
protected Tuple2<Boolean, String> getCast() {
|
313 |
protected Tuple2<Boolean, String> getCast() {
|
294 |
return null;
|
314 |
return null;
|
295 |
}
|
315 |
}
|
296 |
|
316 |
|
297 |
@Override
|
317 |
@Override
|
298 |
public void _loadData(final File f, final SQLTable t) throws IOException {
|
318 |
public void _loadData(final File f, final SQLTable t) throws IOException {
|
299 |
final String data = FileUtils.readUTF8(f);
|
319 |
final String data = FileUtils.readUTF8(f);
|
300 |
final File temp = File.createTempFile(FileUtils.sanitize("mssql_loadData_" + t.getName()), ".txt");
|
320 |
final File temp = File.createTempFile(FileUtils.sanitize("mssql_loadData_" + t.getName()), ".txt");
|
301 |
|
321 |
|
302 |
// no we cant't use UTF16 since Java write BE and MS ignores the BOM, always using LE.
|
322 |
// no we cant't use UTF16 since Java write BE and MS ignores the BOM, always using LE.
|
303 |
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(temp), Charset.forName("x-UTF-16LE-BOM")));
|
323 |
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(temp), Charset.forName("x-UTF-16LE-BOM")));
|
304 |
|
324 |
|
305 |
final List<SQLField> fields = t.getOrderedFields();
|
325 |
final List<SQLField> fields = t.getOrderedFields();
|
306 |
final int fieldsCount = fields.size();
|
326 |
final int fieldsCount = fields.size();
|
307 |
final BitSet booleanFields = new BitSet(fieldsCount);
|
327 |
final BitSet booleanFields = new BitSet(fieldsCount);
|
308 |
int fieldIndex = 0;
|
328 |
int fieldIndex = 0;
|
309 |
for (final SQLField field : fields) {
|
329 |
for (final SQLField field : fields) {
|
310 |
final int type = field.getType().getType();
|
330 |
final int type = field.getType().getType();
|
311 |
booleanFields.set(fieldIndex++, type == Types.BOOLEAN || type == Types.BIT);
|
331 |
booleanFields.set(fieldIndex++, type == Types.BOOLEAN || type == Types.BIT);
|
312 |
}
|
332 |
}
|
313 |
fieldIndex = 0;
|
333 |
fieldIndex = 0;
|
314 |
|
334 |
|
315 |
try {
|
335 |
try {
|
316 |
// skip fields names
|
336 |
// skip fields names
|
317 |
int i = data.indexOf('\n') + 1;
|
337 |
int i = data.indexOf('\n') + 1;
|
318 |
while (i < data.length()) {
|
338 |
while (i < data.length()) {
|
319 |
final String twoChars = i + 2 <= data.length() ? data.substring(i, i + 2) : null;
|
339 |
final String twoChars = i + 2 <= data.length() ? data.substring(i, i + 2) : null;
|
320 |
if ("\\N".equals(twoChars)) {
|
340 |
if ("\\N".equals(twoChars)) {
|
321 |
i += 2;
|
341 |
i += 2;
|
322 |
} else if ("\"\"".equals(twoChars)) {
|
342 |
} else if ("\"\"".equals(twoChars)) {
|
323 |
writer.write("\0");
|
343 |
writer.write("\0");
|
324 |
i += 2;
|
344 |
i += 2;
|
325 |
} else {
|
345 |
} else {
|
326 |
final Tuple2<String, Integer> unDoubleQuote = StringUtils.unDoubleQuote(data, i);
|
346 |
final Tuple2<String, Integer> unDoubleQuote = StringUtils.unDoubleQuote(data, i);
|
327 |
String unquoted = unDoubleQuote.get0();
|
347 |
String unquoted = unDoubleQuote.get0();
|
328 |
if (booleanFields.get(fieldIndex)) {
|
348 |
if (booleanFields.get(fieldIndex)) {
|
329 |
if (unquoted.equalsIgnoreCase("false")) {
|
349 |
if (unquoted.equalsIgnoreCase("false")) {
|
330 |
unquoted = "0";
|
350 |
unquoted = "0";
|
331 |
} else if (unquoted.equalsIgnoreCase("true")) {
|
351 |
} else if (unquoted.equalsIgnoreCase("true")) {
|
332 |
unquoted = "1";
|
352 |
unquoted = "1";
|
333 |
}
|
353 |
}
|
334 |
}
|
354 |
}
|
335 |
writer.write(unquoted);
|
355 |
writer.write(unquoted);
|
336 |
i = unDoubleQuote.get1();
|
356 |
i = unDoubleQuote.get1();
|
337 |
}
|
357 |
}
|
338 |
fieldIndex++;
|
358 |
fieldIndex++;
|
339 |
if (i < data.length()) {
|
359 |
if (i < data.length()) {
|
340 |
final char c = data.charAt(i);
|
360 |
final char c = data.charAt(i);
|
341 |
if (c == ',') {
|
361 |
if (c == ',') {
|
342 |
writer.write(FIELD_DELIM);
|
362 |
writer.write(FIELD_DELIM);
|
343 |
i++;
|
363 |
i++;
|
344 |
} else if (c == '\n') {
|
364 |
} else if (c == '\n') {
|
345 |
writer.write(ROW_DELIM);
|
365 |
writer.write(ROW_DELIM);
|
346 |
i++;
|
366 |
i++;
|
347 |
if (fieldIndex != fieldsCount)
|
367 |
if (fieldIndex != fieldsCount)
|
348 |
throw new IOException("Expected " + fieldsCount + " fields but got : " + fieldIndex);
|
368 |
throw new IOException("Expected " + fieldsCount + " fields but got : " + fieldIndex);
|
349 |
fieldIndex = 0;
|
369 |
fieldIndex = 0;
|
350 |
} else {
|
370 |
} else {
|
351 |
throw new IOException("Unexpected character after field : " + c);
|
371 |
throw new IOException("Unexpected character after field : " + c);
|
352 |
}
|
372 |
}
|
353 |
}
|
373 |
}
|
354 |
}
|
374 |
}
|
355 |
if (fieldIndex != 0 && fieldIndex != fieldsCount)
|
375 |
if (fieldIndex != 0 && fieldIndex != fieldsCount)
|
356 |
throw new IOException("Expected " + fieldsCount + " fields but got : " + fieldIndex);
|
376 |
throw new IOException("Expected " + fieldsCount + " fields but got : " + fieldIndex);
|
357 |
} finally {
|
377 |
} finally {
|
358 |
writer.close();
|
378 |
writer.close();
|
359 |
}
|
379 |
}
|
360 |
|
380 |
|
361 |
execute_bcp(t, false, temp);
|
381 |
execute_bcp(t, false, temp);
|
362 |
temp.delete();
|
382 |
temp.delete();
|
363 |
|
383 |
|
364 |
// MAYBE when on localhost, remove the bcp requirement (OTOH bcp should already be
|
384 |
// MAYBE when on localhost, remove the bcp requirement (OTOH bcp should already be
|
365 |
// installed, just perhaps not in the path)
|
385 |
// installed, just perhaps not in the path)
|
366 |
// checkServerLocalhost(t);
|
386 |
// checkServerLocalhost(t);
|
367 |
// "bulk insert " + t.getSQL() + " from " + b.quoteString(temp.getAbsolutePath()) +
|
387 |
// "bulk insert " + t.getSQL() + " from " + b.quoteString(temp.getAbsolutePath()) +
|
368 |
// " with ( DATAFILETYPE='widechar', FIELDTERMINATOR = " + b.quoteString(FIELD_DELIM)
|
388 |
// " with ( DATAFILETYPE='widechar', FIELDTERMINATOR = " + b.quoteString(FIELD_DELIM)
|
369 |
// + ", ROWTERMINATOR= " + b.quoteString(ROW_DELIM) +
|
389 |
// + ", ROWTERMINATOR= " + b.quoteString(ROW_DELIM) +
|
370 |
// ", FIRSTROW=1, KEEPIDENTITY, KEEPNULLS ) ;"
|
390 |
// ", FIRSTROW=1, KEEPIDENTITY, KEEPNULLS ) ;"
|
371 |
}
|
391 |
}
|
372 |
|
392 |
|
373 |
private static final String FIELD_DELIM = "<|!!|>";
|
393 |
private static final String FIELD_DELIM = "<|!!|>";
|
374 |
private static final String ROW_DELIM = "...#~\n~#...";
|
394 |
private static final String ROW_DELIM = "...#~\n~#...";
|
375 |
|
395 |
|
376 |
protected void execute_bcp(final SQLTable t, final boolean dump, final File f) throws IOException {
|
396 |
protected void execute_bcp(final SQLTable t, final boolean dump, final File f) throws IOException {
|
377 |
final ProcessBuilder pb = new ProcessBuilder("bcp");
|
397 |
final ProcessBuilder pb = new ProcessBuilder("bcp");
|
378 |
pb.command().add(t.getSQLName().quote());
|
398 |
pb.command().add(t.getSQLName().quote());
|
379 |
pb.command().add(dump ? "out" : "in");
|
399 |
pb.command().add(dump ? "out" : "in");
|
380 |
pb.command().add(f.getAbsolutePath());
|
400 |
pb.command().add(f.getAbsolutePath());
|
381 |
// UTF-16LE with a BOM
|
401 |
// UTF-16LE with a BOM
|
382 |
pb.command().add("-w");
|
402 |
pb.command().add("-w");
|
383 |
pb.command().add("-t" + FIELD_DELIM);
|
403 |
pb.command().add("-t" + FIELD_DELIM);
|
384 |
pb.command().add("-r" + ROW_DELIM);
|
404 |
pb.command().add("-r" + ROW_DELIM);
|
385 |
// needed if table name is a keyword (e.g. RIGHT)
|
405 |
// needed if table name is a keyword (e.g. RIGHT)
|
386 |
pb.command().add("-q");
|
406 |
pb.command().add("-q");
|
387 |
pb.command().add("-S" + t.getServer().getName());
|
407 |
pb.command().add("-S" + t.getServer().getName());
|
388 |
pb.command().add("-U" + t.getDBSystemRoot().getDataSource().getUsername());
|
408 |
pb.command().add("-U" + t.getDBSystemRoot().getDataSource().getUsername());
|
389 |
pb.command().add("-P" + t.getDBSystemRoot().getDataSource().getPassword());
|
409 |
pb.command().add("-P" + t.getDBSystemRoot().getDataSource().getPassword());
|
390 |
if (!dump) {
|
410 |
if (!dump) {
|
391 |
// retain null
|
411 |
// retain null
|
392 |
pb.command().add("-k");
|
412 |
pb.command().add("-k");
|
393 |
// keep identity
|
413 |
// keep identity
|
394 |
pb.command().add("-E");
|
414 |
pb.command().add("-E");
|
395 |
}
|
415 |
}
|
396 |
|
416 |
|
397 |
final Process p = ProcessStreams.redirect(pb).start();
|
417 |
final Process p = ProcessStreams.redirect(pb).start();
|
398 |
try {
|
418 |
try {
|
399 |
final int returnCode = p.waitFor();
|
419 |
final int returnCode = p.waitFor();
|
400 |
if (returnCode != 0)
|
420 |
if (returnCode != 0)
|
401 |
throw new IOException("Did not finish correctly : " + returnCode + "\n" + pb.command());
|
421 |
throw new IOException("Did not finish correctly : " + returnCode + "\n" + pb.command());
|
402 |
} catch (InterruptedException e) {
|
422 |
} catch (InterruptedException e) {
|
403 |
throw new RTInterruptedException(e);
|
423 |
throw new RTInterruptedException(e);
|
404 |
}
|
424 |
}
|
405 |
}
|
425 |
}
|
406 |
|
426 |
|
407 |
// For bcp : http://www.microsoft.com/en-us/download/details.aspx?id=16978
|
427 |
// For bcp : http://www.microsoft.com/en-us/download/details.aspx?id=16978
|
408 |
@Override
|
428 |
@Override
|
409 |
protected void _storeData(final SQLTable t, final File f) throws IOException {
|
429 |
protected void _storeData(final SQLTable t, final File f) throws IOException {
|
410 |
final File tmpFile = File.createTempFile(FileUtils.sanitize("mssql_dump_" + t.getName()), ".dat");
|
430 |
final File tmpFile = File.createTempFile(FileUtils.sanitize("mssql_dump_" + t.getName()), ".dat");
|
411 |
execute_bcp(t, true, tmpFile);
|
431 |
execute_bcp(t, true, tmpFile);
|
412 |
final int readerBufferSize = 32768;
|
432 |
final int readerBufferSize = 32768;
|
413 |
final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(tmpFile), StringUtils.UTF16), readerBufferSize);
|
433 |
final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(tmpFile), StringUtils.UTF16), readerBufferSize);
|
414 |
final List<SQLField> orderedFields = t.getOrderedFields();
|
434 |
final List<SQLField> orderedFields = t.getOrderedFields();
|
415 |
final int fieldsCount = orderedFields.size();
|
435 |
final int fieldsCount = orderedFields.size();
|
416 |
final String cols = CollectionUtils.join(orderedFields, ",", new ITransformer<SQLField, String>() {
|
436 |
final String cols = CollectionUtils.join(orderedFields, ",", new ITransformer<SQLField, String>() {
|
417 |
@Override
|
437 |
@Override
|
418 |
public String transformChecked(SQLField input) {
|
438 |
public String transformChecked(SQLField input) {
|
419 |
return SQLBase.quoteIdentifier(input.getName());
|
439 |
return SQLBase.quoteIdentifier(input.getName());
|
420 |
}
|
440 |
}
|
421 |
});
|
441 |
});
|
422 |
final FileOutputStream outs = new FileOutputStream(f);
|
442 |
final FileOutputStream outs = new FileOutputStream(f);
|
423 |
BufferedWriter writer = null;
|
443 |
BufferedWriter writer = null;
|
424 |
try {
|
444 |
try {
|
425 |
writer = new BufferedWriter(new OutputStreamWriter(outs, StringUtils.UTF8));
|
445 |
writer = new BufferedWriter(new OutputStreamWriter(outs, StringUtils.UTF8));
|
426 |
writer.write(cols);
|
446 |
writer.write(cols);
|
427 |
writer.write('\n');
|
447 |
writer.write('\n');
|
428 |
final StringBuilder sb = new StringBuilder(readerBufferSize * 2);
|
448 |
final StringBuilder sb = new StringBuilder(readerBufferSize * 2);
|
429 |
String row = readUntil(reader, sb, ROW_DELIM);
|
449 |
String row = readUntil(reader, sb, ROW_DELIM);
|
430 |
final Pattern fieldPattern = Pattern.compile(FIELD_DELIM, Pattern.LITERAL);
|
450 |
final Pattern fieldPattern = Pattern.compile(FIELD_DELIM, Pattern.LITERAL);
|
431 |
while (row != null) {
|
451 |
while (row != null) {
|
432 |
if (row.length() > 0) {
|
452 |
if (row.length() > 0) {
|
433 |
// -1 to have every (even empty) field
|
453 |
// -1 to have every (even empty) field
|
434 |
final String[] fields = fieldPattern.split(row, -1);
|
454 |
final String[] fields = fieldPattern.split(row, -1);
|
435 |
if (fields.length != fieldsCount)
|
455 |
if (fields.length != fieldsCount)
|
436 |
throw new IOException("Invalid fields count, expected " + fieldsCount + " but was " + fields.length + "\n" + row);
|
456 |
throw new IOException("Invalid fields count, expected " + fieldsCount + " but was " + fields.length + "\n" + row);
|
437 |
int i = 0;
|
457 |
int i = 0;
|
438 |
for (final String field : fields) {
|
458 |
for (final String field : fields) {
|
439 |
final String quoted;
|
459 |
final String quoted;
|
440 |
if (field.length() == 0) {
|
460 |
if (field.length() == 0) {
|
441 |
quoted = "\\N";
|
461 |
quoted = "\\N";
|
442 |
} else if (field.equals("\0")) {
|
462 |
} else if (field.equals("\0")) {
|
443 |
quoted = "\"\"";
|
463 |
quoted = "\"\"";
|
444 |
} else {
|
464 |
} else {
|
445 |
quoted = StringUtils.doubleQuote(field);
|
465 |
quoted = StringUtils.doubleQuote(field);
|
446 |
}
|
466 |
}
|
447 |
writer.write(quoted);
|
467 |
writer.write(quoted);
|
448 |
if (++i < fieldsCount)
|
468 |
if (++i < fieldsCount)
|
449 |
writer.write(',');
|
469 |
writer.write(',');
|
450 |
}
|
470 |
}
|
451 |
writer.write('\n');
|
471 |
writer.write('\n');
|
452 |
}
|
472 |
}
|
453 |
row = readUntil(reader, sb, ROW_DELIM);
|
473 |
row = readUntil(reader, sb, ROW_DELIM);
|
454 |
}
|
474 |
}
|
455 |
} finally {
|
475 |
} finally {
|
456 |
tmpFile.delete();
|
476 |
tmpFile.delete();
|
457 |
if (writer != null)
|
477 |
if (writer != null)
|
458 |
writer.close();
|
478 |
writer.close();
|
459 |
else
|
479 |
else
|
460 |
outs.close();
|
480 |
outs.close();
|
461 |
reader.close();
|
481 |
reader.close();
|
462 |
}
|
482 |
}
|
463 |
}
|
483 |
}
|
464 |
|
484 |
|
465 |
private String readUntil(BufferedReader reader, StringBuilder sb, String rowDelim) throws IOException {
|
485 |
private String readUntil(BufferedReader reader, StringBuilder sb, String rowDelim) throws IOException {
|
466 |
if (sb.capacity() == 0)
|
486 |
if (sb.capacity() == 0)
|
467 |
return null;
|
487 |
return null;
|
468 |
final int existing = sb.indexOf(rowDelim);
|
488 |
final int existing = sb.indexOf(rowDelim);
|
469 |
if (existing >= 0) {
|
489 |
if (existing >= 0) {
|
470 |
final String res = sb.substring(0, existing);
|
490 |
final String res = sb.substring(0, existing);
|
471 |
sb.delete(0, existing + rowDelim.length());
|
491 |
sb.delete(0, existing + rowDelim.length());
|
472 |
return res;
|
492 |
return res;
|
473 |
} else {
|
493 |
} else {
|
474 |
final char[] buffer = new char[sb.capacity() / 3];
|
494 |
final char[] buffer = new char[sb.capacity() / 3];
|
475 |
final int readCount = reader.read(buffer);
|
495 |
final int readCount = reader.read(buffer);
|
476 |
if (readCount <= 0) {
|
496 |
if (readCount <= 0) {
|
477 |
final String res = sb.toString();
|
497 |
final String res = sb.toString();
|
478 |
sb.setLength(0);
|
498 |
sb.setLength(0);
|
479 |
sb.trimToSize();
|
499 |
sb.trimToSize();
|
480 |
assert sb.capacity() == 0;
|
500 |
assert sb.capacity() == 0;
|
481 |
return res;
|
501 |
return res;
|
482 |
} else {
|
502 |
} else {
|
483 |
sb.append(buffer, 0, readCount);
|
503 |
sb.append(buffer, 0, readCount);
|
484 |
return readUntil(reader, sb, rowDelim);
|
504 |
return readUntil(reader, sb, rowDelim);
|
485 |
}
|
505 |
}
|
486 |
}
|
506 |
}
|
487 |
}
|
507 |
}
|
488 |
|
508 |
|
489 |
@Override
|
509 |
@Override
|
490 |
public boolean supportMultiAlterClause() {
|
510 |
public boolean supportMultiAlterClause() {
|
491 |
// support multiple if you omit the "add" : ALTER TABLE t add f1 int, f2 bit
|
511 |
// support multiple if you omit the "add" : ALTER TABLE t add f1 int, f2 bit
|
492 |
return false;
|
512 |
return false;
|
493 |
}
|
513 |
}
|
494 |
|
514 |
|
495 |
@Override
|
515 |
@Override
|
496 |
public String getNullIsDataComparison(String x, boolean eq, String y) {
|
516 |
public String getNullIsDataComparison(String x, boolean eq, String y) {
|
497 |
final String nullSafe = x + " = " + y + " or ( " + x + " is null and " + y + " is null)";
|
517 |
final String nullSafe = x + " = " + y + " or ( " + x + " is null and " + y + " is null)";
|
498 |
if (eq)
|
518 |
if (eq)
|
499 |
return nullSafe;
|
519 |
return nullSafe;
|
500 |
else
|
520 |
else
|
501 |
return x + " <> " + y + " or (" + x + " is null and " + y + " is not null) " + " or (" + x + " is not null and " + y + " is null) ";
|
521 |
return x + " <> " + y + " or (" + x + " is null and " + y + " is not null) " + " or (" + x + " is not null and " + y + " is null) ";
|
502 |
}
|
522 |
}
|
503 |
|
523 |
|
504 |
@Override
|
524 |
@Override
|
505 |
public String getFunctionQuery(SQLBase b, Set<String> schemas) {
|
525 |
public String getFunctionQuery(SQLBase b, Set<String> schemas) {
|
506 |
return " select name, schema_name(schema_id) as \"schema\", cast(OBJECT_DEFINITION(object_id) as varchar(4096)) as \"src\"\n"
|
526 |
return " select name, schema_name(schema_id) as \"schema\", cast(OBJECT_DEFINITION(object_id) as varchar(4096)) as \"src\"\n"
|
507 |
//
|
527 |
//
|
508 |
+ " FROM " + new SQLName(b.getName(), "sys", "objects") + "\n"
|
528 |
+ " FROM " + new SQLName(b.getName(), "sys", "objects") + "\n"
|
509 |
// scalar, inline table-valued, table-valued
|
529 |
// scalar, inline table-valued, table-valued
|
510 |
+ " where type IN ('FN', 'IF', 'TF') and SCHEMA_NAME( schema_id ) in (" + quoteStrings(schemas) + ") ";
|
530 |
+ " where type IN ('FN', 'IF', 'TF') and SCHEMA_NAME( schema_id ) in (" + quoteStrings(schemas) + ") ";
|
511 |
}
|
531 |
}
|
512 |
|
532 |
|
513 |
@Override
|
533 |
@Override
|
514 |
public String getTriggerQuery(SQLBase b, TablesMap tables) {
|
534 |
public String getTriggerQuery(SQLBase b, TablesMap tables) {
|
515 |
// for some reason OBJECT_DEFINITION always returns null
|
535 |
// for some reason OBJECT_DEFINITION always returns null
|
516 |
return "SELECT trig.name as \"TRIGGER_NAME\", SCHEMA_NAME( tabl.schema_id ) as \"TABLE_SCHEMA\", tabl.name as \"TABLE_NAME\", null as \"ACTION\", cast(OBJECT_DEFINITION(trig.object_id) as varchar(4096)) as \"SQL\"\n"
|
536 |
return "SELECT trig.name as \"TRIGGER_NAME\", SCHEMA_NAME( tabl.schema_id ) as \"TABLE_SCHEMA\", tabl.name as \"TABLE_NAME\", null as \"ACTION\", cast(OBJECT_DEFINITION(trig.object_id) as varchar(4096)) as \"SQL\"\n"
|
517 |
//
|
537 |
//
|
518 |
+ "FROM " + new SQLName(b.getName(), "sys", "triggers") + " trig\n"
|
538 |
+ "FROM " + new SQLName(b.getName(), "sys", "triggers") + " trig\n"
|
519 |
//
|
539 |
//
|
520 |
+ "join " + new SQLName(b.getName(), "sys", "objects") + " tabl on trig.parent_id = tabl.object_id\n"
|
540 |
+ "join " + new SQLName(b.getName(), "sys", "objects") + " tabl on trig.parent_id = tabl.object_id\n"
|
521 |
// requested tables
|
541 |
// requested tables
|
522 |
+ getTablesMapJoin(tables, "SCHEMA_NAME( tabl.schema_id )", "tabl.name");
|
542 |
+ getTablesMapJoin(tables, "SCHEMA_NAME( tabl.schema_id )", "tabl.name");
|
523 |
}
|
543 |
}
|
524 |
|
544 |
|
525 |
@Override
|
545 |
@Override
|
526 |
public String getDropTrigger(Trigger t) {
|
546 |
public String getDropTrigger(Trigger t) {
|
527 |
return "DROP TRIGGER " + new SQLName(t.getTable().getSchema().getName(), t.getName()).quote();
|
547 |
return "DROP TRIGGER " + new SQLName(t.getTable().getSchema().getName(), t.getName()).quote();
|
528 |
}
|
548 |
}
|
529 |
|
549 |
|
530 |
@Override
|
550 |
@Override
|
531 |
public String getColumnsQuery(SQLBase b, TablesMap tables) {
|
551 |
public String getColumnsQuery(SQLBase b, TablesMap tables) {
|
532 |
return "SELECT TABLE_SCHEMA as \"" + INFO_SCHEMA_NAMES_KEYS.get(0) + "\", TABLE_NAME as \"" + INFO_SCHEMA_NAMES_KEYS.get(1) + "\", COLUMN_NAME as \"" + INFO_SCHEMA_NAMES_KEYS.get(2)
|
552 |
return "SELECT TABLE_SCHEMA as \"" + INFO_SCHEMA_NAMES_KEYS.get(0) + "\", TABLE_NAME as \"" + INFO_SCHEMA_NAMES_KEYS.get(1) + "\", COLUMN_NAME as \"" + INFO_SCHEMA_NAMES_KEYS.get(2)
|
533 |
+ "\" , CHARACTER_SET_NAME as \"CHARACTER_SET_NAME\", COLLATION_NAME as \"COLLATION_NAME\" from INFORMATION_SCHEMA.COLUMNS\n" +
|
553 |
+ "\" , CHARACTER_SET_NAME as \"CHARACTER_SET_NAME\", COLLATION_NAME as \"COLLATION_NAME\" from INFORMATION_SCHEMA.COLUMNS\n" +
|
534 |
// requested tables
|
554 |
// requested tables
|
535 |
getTablesMapJoin(tables, "TABLE_SCHEMA", "TABLE_NAME");
|
555 |
getTablesMapJoin(tables, "TABLE_SCHEMA", "TABLE_NAME");
|
536 |
}
|
556 |
}
|
537 |
|
557 |
|
538 |
@Override
|
558 |
@Override
|
539 |
public List<Map<String, Object>> getConstraints(SQLBase b, TablesMap tables) throws SQLException {
|
559 |
public List<Map<String, Object>> getConstraints(SQLBase b, TablesMap tables) throws SQLException {
|
540 |
final String where = getTablesMapJoin(tables, "SCHEMA_NAME(t.schema_id)", "t.name");
|
560 |
final String where = getTablesMapJoin(tables, "SCHEMA_NAME(t.schema_id)", "t.name");
|
541 |
final String sel = "SELECT SCHEMA_NAME(t.schema_id) AS \"TABLE_SCHEMA\", t.name AS \"TABLE_NAME\", k.name AS \"CONSTRAINT_NAME\", case k.type when 'UQ' then 'UNIQUE' when 'PK' then 'PRIMARY KEY' end as \"CONSTRAINT_TYPE\", col_name(c.object_id, c.column_id) AS \"COLUMN_NAME\", c.key_ordinal AS \"ORDINAL_POSITION\", null AS [DEFINITION]\n"
|
561 |
final String sel = "SELECT SCHEMA_NAME(t.schema_id) AS \"TABLE_SCHEMA\", t.name AS \"TABLE_NAME\", k.name AS \"CONSTRAINT_NAME\", case k.type when 'UQ' then 'UNIQUE' when 'PK' then 'PRIMARY KEY' end as \"CONSTRAINT_TYPE\", col_name(c.object_id, c.column_id) AS \"COLUMN_NAME\", c.key_ordinal AS \"ORDINAL_POSITION\", null AS [DEFINITION]\n"
|
542 |
+ "FROM sys.key_constraints k\n"
|
562 |
+ "FROM sys.key_constraints k\n"
|
543 |
//
|
563 |
//
|
544 |
+ "JOIN sys.index_columns c ON c.object_id = k.parent_object_id AND c.index_id = k.unique_index_id\n"
|
564 |
+ "JOIN sys.index_columns c ON c.object_id = k.parent_object_id AND c.index_id = k.unique_index_id\n"
|
545 |
//
|
565 |
//
|
546 |
+ "JOIN sys.tables t ON t.object_id = k.parent_object_id\n" + where + "\nUNION ALL\n"
|
566 |
+ "JOIN sys.tables t ON t.object_id = k.parent_object_id\n" + where + "\nUNION ALL\n"
|
547 |
//
|
567 |
//
|
548 |
+ "SELECT SCHEMA_NAME(t.schema_id) AS \"TABLE_SCHEMA\", t.name AS \"TABLE_NAME\", k.name AS \"CONSTRAINT_NAME\", 'CHECK' as \"CONSTRAINT_TYPE\", col.name AS \"COLUMN_NAME\", 1 AS \"ORDINAL_POSITION\", k.[definition] AS [DEFINITION]\n"
|
568 |
+ "SELECT SCHEMA_NAME(t.schema_id) AS \"TABLE_SCHEMA\", t.name AS \"TABLE_NAME\", k.name AS \"CONSTRAINT_NAME\", 'CHECK' as \"CONSTRAINT_TYPE\", col.name AS \"COLUMN_NAME\", 1 AS \"ORDINAL_POSITION\", k.[definition] AS [DEFINITION]\n"
|
549 |
+ "FROM sys.check_constraints k\n"
|
569 |
+ "FROM sys.check_constraints k\n"
|
550 |
//
|
570 |
//
|
551 |
+ "join sys.tables t on k.parent_object_id = t.object_id\n"
|
571 |
+ "join sys.tables t on k.parent_object_id = t.object_id\n"
|
552 |
//
|
572 |
//
|
553 |
+ "left join sys.columns col on k.parent_column_id = col.column_id and col.object_id = t.object_id\n"
|
573 |
+ "left join sys.columns col on k.parent_column_id = col.column_id and col.object_id = t.object_id\n"
|
554 |
//
|
574 |
//
|
555 |
+ where + "\nUNION ALL\n"
|
575 |
+ where + "\nUNION ALL\n"
|
556 |
//
|
576 |
//
|
557 |
+ "SELECT SCHEMA_NAME(t.schema_id) AS [TABLE_SCHEMA], t.name AS [TABLE_NAME], k.name AS [CONSTRAINT_NAME], 'DEFAULT' as [CONSTRAINT_TYPE], col.name AS [COLUMN_NAME], 1 AS [ORDINAL_POSITION], k.[definition] AS [DEFINITION]\n"
|
577 |
+ "SELECT SCHEMA_NAME(t.schema_id) AS [TABLE_SCHEMA], t.name AS [TABLE_NAME], k.name AS [CONSTRAINT_NAME], 'DEFAULT' as [CONSTRAINT_TYPE], col.name AS [COLUMN_NAME], 1 AS [ORDINAL_POSITION], k.[definition] AS [DEFINITION]\n"
|
558 |
+ "FROM sys.[default_constraints] k\n"
|
578 |
+ "FROM sys.[default_constraints] k\n"
|
559 |
//
|
579 |
//
|
560 |
+ "JOIN sys.tables t ON t.object_id = k.parent_object_id\n"
|
580 |
+ "JOIN sys.tables t ON t.object_id = k.parent_object_id\n"
|
561 |
//
|
581 |
//
|
562 |
+ "left join sys.columns col on k.parent_column_id = col.column_id and col.object_id = t.object_id\n"
|
582 |
+ "left join sys.columns col on k.parent_column_id = col.column_id and col.object_id = t.object_id\n"
|
563 |
//
|
583 |
//
|
564 |
+ where;
|
584 |
+ where;
|
565 |
// don't cache since we don't listen on system tables
|
585 |
// don't cache since we don't listen on system tables
|
566 |
@SuppressWarnings("unchecked")
|
586 |
@SuppressWarnings("unchecked")
|
567 |
final List<Map<String, Object>> res = (List<Map<String, Object>>) b.getDBSystemRoot().getDataSource().execute(sel, new IResultSetHandler(SQLDataSource.MAP_LIST_HANDLER, false));
|
587 |
final List<Map<String, Object>> res = (List<Map<String, Object>>) b.getDBSystemRoot().getDataSource().execute(sel, new IResultSetHandler(SQLDataSource.MAP_LIST_HANDLER, false));
|
568 |
SQLSyntaxMySQL.mergeColumnNames(res);
|
588 |
SQLSyntaxMySQL.mergeColumnNames(res);
|
569 |
return res;
|
589 |
return res;
|
570 |
}
|
590 |
}
|
571 |
|
591 |
|
572 |
@Override
|
592 |
@Override
|
573 |
public DeferredClause getSetTableComment(String comment) {
|
593 |
public DeferredClause getSetTableComment(String comment) {
|
574 |
return null;
|
594 |
return null;
|
575 |
}
|
595 |
}
|
576 |
|
596 |
|
577 |
@Override
|
597 |
@Override
|
578 |
public String getConcatOp() {
|
598 |
public String getConcatOp() {
|
579 |
return "+";
|
599 |
return "+";
|
580 |
}
|
600 |
}
|
581 |
|
601 |
|
582 |
@Override
|
602 |
@Override
|
583 |
public String getRegexpOp(boolean negation) {
|
603 |
public String getRegexpOp(boolean negation) {
|
584 |
// MS needs either the CLR : http://msdn.microsoft.com/en-us/magazine/cc163473.aspx
|
604 |
// MS needs either the CLR : http://msdn.microsoft.com/en-us/magazine/cc163473.aspx
|
585 |
// or http://www.codeproject.com/KB/database/xp_pcre.aspx
|
605 |
// or http://www.codeproject.com/KB/database/xp_pcre.aspx
|
586 |
return null;
|
606 |
return null;
|
587 |
}
|
607 |
}
|
588 |
|
608 |
|
589 |
@Override
|
609 |
@Override
|
590 |
public String getDayOfWeek(String sqlTS) {
|
610 |
public String getDayOfWeek(String sqlTS) {
|
591 |
return "SELECT DATEPART(dw, " + sqlTS + ")";
|
611 |
return "SELECT DATEPART(dw, " + sqlTS + ")";
|
592 |
}
|
612 |
}
|
593 |
|
613 |
|
594 |
@Override
|
614 |
@Override
|
595 |
public String getMonth(String sqlTS) {
|
615 |
public String getMonth(String sqlTS) {
|
596 |
return "SELECT DATEPART(month, " + sqlTS + ")";
|
616 |
return "SELECT DATEPART(month, " + sqlTS + ")";
|
597 |
}
|
617 |
}
|
598 |
|
618 |
|
599 |
@Override
|
619 |
@Override
|
600 |
public String getFormatTimestamp(String sqlTS, boolean basic) {
|
620 |
public String getFormatTimestamp(String sqlTS, boolean basic) {
|
601 |
final String extended = "CONVERT(nvarchar(30), " + sqlTS + ", 126) + '000'";
|
621 |
final String extended = "CONVERT(nvarchar(30), " + sqlTS + ", 126) + '000'";
|
602 |
if (basic) {
|
622 |
if (basic) {
|
603 |
return "replace( replace( " + extended + ", '-', ''), ':' , '' )";
|
623 |
return "replace( replace( " + extended + ", '-', ''), ':' , '' )";
|
604 |
} else {
|
624 |
} else {
|
605 |
return extended;
|
625 |
return extended;
|
606 |
}
|
626 |
}
|
607 |
}
|
627 |
}
|
608 |
|
628 |
|
609 |
@Override
|
629 |
@Override
|
610 |
public String getFormatTimestamp(String sqlTS, String nativeFormat) {
|
630 |
public String getFormatTimestamp(String sqlTS, String nativeFormat) {
|
611 |
return "FORMAT(" + sqlTS + ", " + nativeFormat + ")";
|
631 |
return "FORMAT(" + sqlTS + ", " + nativeFormat + ")";
|
612 |
}
|
632 |
}
|
613 |
|
633 |
|
614 |
@Override
|
634 |
@Override
|
615 |
public String quoteForTimestampFormat(String text) {
|
635 |
public String quoteForTimestampFormat(String text) {
|
616 |
return StringUtils.doubleQuote(text);
|
636 |
return StringUtils.doubleQuote(text);
|
617 |
}
|
637 |
}
|
- |
|
638 |
|
- |
|
639 |
@Override
|
- |
|
640 |
public String getSessionIDExpression() {
|
- |
|
641 |
return "@@SPID";
|
- |
|
642 |
}
|
- |
|
643 |
|
- |
|
644 |
@Override
|
- |
|
645 |
public String getSessionsQuery(final DBSystemRoot sysRoot, final boolean includeSelf) {
|
- |
|
646 |
final String allRows = "SELECT ps.spid as \"ID\", cmd as \"QUERY\", users.name as \"USER_NAME\" FROM sys.sysprocesses ps\n"
|
- |
|
647 |
//
|
- |
|
648 |
+ "JOIN sys.sysdatabases db on db.dbid = ps.dbid\nJOIN sys.sysusers users on users.uid = ps.uid\\n"
|
- |
|
649 |
//
|
- |
|
650 |
+ " WHERE db.name=" + quoteString(sysRoot.getName());
|
- |
|
651 |
if (includeSelf)
|
- |
|
652 |
return allRows;
|
- |
|
653 |
return allRows + " and ps.spid != " + this.getSessionIDExpression();
|
- |
|
654 |
}
|
- |
|
655 |
|
- |
|
656 |
@Override
|
- |
|
657 |
public String getVersionFunction() {
|
- |
|
658 |
// if this doesn't work, try SERVERPROPERTY('ProductVersion')
|
- |
|
659 |
return "@@VERSION";
|
- |
|
660 |
}
|
618 |
}
|
661 |
}
|