OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 144 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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