1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "DatabaseMetaData.hxx"
21 #include "Util.hxx"
22 
23 #include <ibase.h>
24 #include <rtl/ustrbuf.hxx>
25 #include <sal/log.hxx>
26 #include <FDatabaseMetaDataResultSet.hxx>
27 
28 #include <com/sun/star/sdbc/ColumnSearch.hpp>
29 #include <com/sun/star/sdbc/ColumnValue.hpp>
30 #include <com/sun/star/sdbc/DataType.hpp>
31 #include <com/sun/star/sdbc/IndexType.hpp>
32 #include <com/sun/star/sdbc/ResultSetType.hpp>
33 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
34 #include <com/sun/star/sdbc/SQLException.hpp>
35 #include <com/sun/star/sdbc/TransactionIsolation.hpp>
36 #include <com/sun/star/sdbc/XRow.hpp>
37 #include <com/sun/star/sdbc/KeyRule.hpp>
38 #include <com/sun/star/sdbc/Deferrability.hpp>
39 
40 using namespace connectivity::firebird;
41 using namespace com::sun::star;
42 using namespace com::sun::star::uno;
43 using namespace com::sun::star::lang;
44 using namespace com::sun::star::beans;
45 using namespace com::sun::star::sdbc;
46 
47 ODatabaseMetaData::ODatabaseMetaData(Connection* _pCon)
48 : m_pConnection(_pCon)
49 {
50     SAL_WARN_IF(!m_pConnection.is(), "connectivity.firebird",
51             "ODatabaseMetaData::ODatabaseMetaData: No connection set!");
52 }
53 
54 ODatabaseMetaData::~ODatabaseMetaData()
55 {
56 }
57 
58 //----- Catalog Info -- UNSUPPORTED -------------------------------------------
59 OUString SAL_CALL ODatabaseMetaData::getCatalogSeparator()
60 {
61     return OUString();
62 }
63 
64 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCatalogNameLength()
65 {
66     return -1;
67 }
68 
69 OUString SAL_CALL ODatabaseMetaData::getCatalogTerm()
70 {
71     return OUString();
72 }
73 
74 sal_Bool SAL_CALL ODatabaseMetaData::isCatalogAtStart()
75 {
76     return false;
77 }
78 
79 sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInTableDefinitions()
80 {
81     return false;
82 }
83 
84 sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInIndexDefinitions()
85 {
86     return false;
87 }
88 
89 sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInDataManipulation(  )
90 {
91     return false;
92 }
93 
94 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCatalogs()
95 {
96     OSL_FAIL("Not implemented yet!");
97     // TODO implement
98     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eCatalogs);
99 }
100 
101 sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInProcedureCalls()
102 {
103     return false;
104 }
105 
106 sal_Bool SAL_CALL ODatabaseMetaData::supportsCatalogsInPrivilegeDefinitions()
107 {
108     return false;
109 }
110 
111 //----- Schema Info -- UNSUPPORTED --------------------------------------------
112 sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInProcedureCalls()
113 {
114     return false;
115 }
116 
117 sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInPrivilegeDefinitions()
118 {
119     return false;
120 }
121 
122 sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInDataManipulation()
123 {
124     return false;
125 }
126 
127 sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInIndexDefinitions()
128 {
129     return false;
130 }
131 
132 sal_Bool SAL_CALL ODatabaseMetaData::supportsSchemasInTableDefinitions()
133 {
134     return false;
135 }
136 
137 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxSchemaNameLength()
138 {
139     return -1;
140 }
141 
142 OUString SAL_CALL ODatabaseMetaData::getSchemaTerm()
143 {
144     return OUString();
145 }
146 
147 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getSchemas()
148 {
149     OSL_FAIL("Not implemented yet!");
150     // TODO implement
151     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eSchemas);
152 }
153 
154 //----- Max Sizes/Lengths -----------------------------------------------------
155 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxBinaryLiteralLength()
156 {
157     return 32767;
158 }
159 
160 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxRowSize()
161 {
162     return 32767;
163 }
164 
165 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCharLiteralLength()
166 {
167     return 32767;
168 }
169 
170 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnNameLength()
171 {
172     return 31;
173 }
174 
175 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInIndex()
176 {
177     // TODO: No idea.
178     // See: http://www.firebirdsql.org/en/firebird-technical-specifications/
179     return 16;
180 }
181 
182 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxCursorNameLength()
183 {
184     return 32;
185 }
186 
187 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxConnections()
188 {
189     return 100; // Arbitrary
190 }
191 
192 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInTable()
193 {
194     // May however be smaller.
195     // See: http://www.firebirdsql.org/en/firebird-technical-specifications/
196     return 32767;
197 }
198 
199 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatementLength()
200 {
201     return 32767;
202 }
203 
204 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTableNameLength()
205 {
206     return 31;
207 }
208 
209 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxTablesInSelect(  )
210 {
211     return 0; // 0 means no limit
212 }
213 
214 
215 sal_Bool SAL_CALL ODatabaseMetaData::doesMaxRowSizeIncludeBlobs(  )
216 {
217     return false;
218 }
219 
220 // ---- Identifiers -----------------------------------------------------------
221 // Only quoted identifiers are case sensitive, unquoted are case insensitive
222 OUString SAL_CALL ODatabaseMetaData::getIdentifierQuoteString()
223 {
224     OUString aVal('"');
225     return aVal;
226 }
227 
228 sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseQuotedIdentifiers(  )
229 {
230     return true;
231 }
232 
233 sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseQuotedIdentifiers()
234 {
235     return false;
236 }
237 
238 sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseQuotedIdentifiers()
239 {
240     // TODO: confirm this -- the documentation is highly ambiguous
241     // However it seems this should be true as quoted identifiers ARE
242     // stored mixed case.
243     return true;
244 }
245 
246 sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseQuotedIdentifiers()
247 {
248     return false;
249 }
250 
251 // ---- Unquoted Identifiers -------------------------------------------------
252 // All unquoted identifiers are stored upper case.
253 sal_Bool SAL_CALL ODatabaseMetaData::supportsMixedCaseIdentifiers()
254 {
255     return false;
256 }
257 
258 sal_Bool SAL_CALL ODatabaseMetaData::storesLowerCaseIdentifiers()
259 {
260     return false;
261 }
262 
263 sal_Bool SAL_CALL ODatabaseMetaData::storesMixedCaseIdentifiers()
264 {
265     return false;
266 }
267 
268 sal_Bool SAL_CALL ODatabaseMetaData::storesUpperCaseIdentifiers()
269 {
270     return true;
271 }
272 
273 // ---- SQL Feature Support ---------------------------------------------------
274 sal_Bool SAL_CALL ODatabaseMetaData::supportsCoreSQLGrammar()
275 {
276     return true;
277 }
278 
279 sal_Bool SAL_CALL ODatabaseMetaData::supportsMinimumSQLGrammar()
280 {
281     return true;
282 }
283 
284 sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithAddColumn()
285 {
286     return true;
287 }
288 
289 sal_Bool SAL_CALL ODatabaseMetaData::supportsAlterTableWithDropColumn()
290 {
291     return true;
292 }
293 
294 sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedDelete()
295 {
296     return true;
297 }
298 
299 sal_Bool SAL_CALL ODatabaseMetaData::supportsPositionedUpdate()
300 {
301     return true;
302 }
303 
304 sal_Bool SAL_CALL ODatabaseMetaData::supportsOuterJoins()
305 {
306     return true;
307 }
308 
309 sal_Bool SAL_CALL ODatabaseMetaData::supportsSelectForUpdate()
310 {
311     return true;
312 }
313 
314 sal_Bool SAL_CALL ODatabaseMetaData::allTablesAreSelectable()
315 {
316     // TODO: true if embedded, but unsure about remote server
317     return true;
318 }
319 
320 sal_Bool SAL_CALL ODatabaseMetaData::supportsConvert(sal_Int32,
321                                                      sal_Int32)
322 {
323     return false;
324 }
325 
326 sal_Bool SAL_CALL ODatabaseMetaData::supportsTypeConversion()
327 {
328     return false;
329 }
330 
331 sal_Bool SAL_CALL ODatabaseMetaData::supportsColumnAliasing()
332 {
333     return true;
334 }
335 
336 sal_Bool SAL_CALL ODatabaseMetaData::supportsTableCorrelationNames()
337 {
338     return true;
339 }
340 
341 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxIndexLength(  )
342 {
343     return 0; // 0 means no limit
344 }
345 
346 sal_Bool SAL_CALL ODatabaseMetaData::supportsNonNullableColumns(  )
347 {
348     return true;
349 }
350 
351 OUString SAL_CALL ODatabaseMetaData::getExtraNameCharacters(  )
352 {
353     return OUString();
354 }
355 
356 sal_Bool SAL_CALL ODatabaseMetaData::supportsDifferentTableCorrelationNames(  )
357 {
358     return false;
359 }
360 // ---- Data definition stuff -------------------------------------------------
361 sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionIgnoredInTransactions()
362 {
363     return false;
364 }
365 
366 sal_Bool SAL_CALL ODatabaseMetaData::dataDefinitionCausesTransactionCommit()
367 {
368     return true;
369 }
370 
371 sal_Bool SAL_CALL ODatabaseMetaData::supportsDataManipulationTransactionsOnly()
372 {
373     return true;
374 }
375 
376 sal_Bool SAL_CALL ODatabaseMetaData::
377         supportsDataDefinitionAndDataManipulationTransactions()
378 {
379     return false;
380 }
381 //----- Transaction Support --------------------------------------------------
382 sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactions()
383 {
384     return true;
385 }
386 
387 sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossRollback()
388 {
389     return false;
390 }
391 
392 sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenStatementsAcrossCommit()
393 {
394     return false;
395 }
396 
397 sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossCommit()
398 {
399     return false;
400 }
401 
402 sal_Bool SAL_CALL ODatabaseMetaData::supportsOpenCursorsAcrossRollback()
403 {
404     return false;
405 }
406 
407 sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleTransactions()
408 {
409     return true;
410 }
411 
412 sal_Bool SAL_CALL ODatabaseMetaData::supportsTransactionIsolationLevel(
413         sal_Int32 aLevel)
414 {
415     return  aLevel == TransactionIsolation::READ_UNCOMMITTED
416            || aLevel == TransactionIsolation::READ_COMMITTED
417            || aLevel == TransactionIsolation::REPEATABLE_READ
418            || aLevel == TransactionIsolation::SERIALIZABLE;
419 }
420 
421 sal_Int32 SAL_CALL ODatabaseMetaData::getDefaultTransactionIsolation()
422 {
423     return TransactionIsolation::REPEATABLE_READ;
424 }
425 
426 
427 sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92FullSQL(  )
428 {
429     return false;
430 }
431 
432 sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92EntryLevelSQL(  )
433 {
434     return true; // should be supported at least
435 }
436 
437 sal_Bool SAL_CALL ODatabaseMetaData::supportsIntegrityEnhancementFacility(  )
438 {
439     return true;
440 }
441 
442 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxStatements(  )
443 {
444     return 0; // 0 means no limit
445 }
446 
447 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxProcedureNameLength(  )
448 {
449     return 31; // TODO: confirm
450 }
451 
452 sal_Bool SAL_CALL ODatabaseMetaData::allProceduresAreCallable(  )
453 {
454     return false;
455 }
456 
457 sal_Bool SAL_CALL ODatabaseMetaData::supportsStoredProcedures(  )
458 {
459     return true;
460 }
461 
462 sal_Bool SAL_CALL ODatabaseMetaData::isReadOnly(  )
463 {
464     return m_pConnection->isReadOnly();
465 }
466 
467 sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFiles(  )
468 {
469     return m_pConnection->isEmbedded();
470 }
471 
472 sal_Bool SAL_CALL ODatabaseMetaData::usesLocalFilePerTable(  )
473 {
474     return false;
475 }
476 
477 sal_Bool SAL_CALL ODatabaseMetaData::nullPlusNonNullIsNull(  )
478 {
479     return false;
480 }
481 
482 sal_Bool SAL_CALL ODatabaseMetaData::supportsExpressionsInOrderBy(  )
483 {
484     return false;
485 }
486 
487 sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupBy(  )
488 {
489     return true;
490 }
491 
492 sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByBeyondSelect(  )
493 {
494     // Unsure
495     return true;
496 }
497 
498 sal_Bool SAL_CALL ODatabaseMetaData::supportsGroupByUnrelated(  )
499 {
500     // Unsure
501     return false;
502 }
503 
504 
505 sal_Bool SAL_CALL ODatabaseMetaData::supportsMultipleResultSets(  )
506 {
507     return false;
508 }
509 
510 sal_Bool SAL_CALL ODatabaseMetaData::supportsLikeEscapeClause(  )
511 {
512     return false;
513 }
514 
515 sal_Bool SAL_CALL ODatabaseMetaData::supportsOrderByUnrelated(  )
516 {
517     return false;
518 }
519 
520 sal_Bool SAL_CALL ODatabaseMetaData::supportsUnion(  )
521 {
522     return true;
523 }
524 
525 sal_Bool SAL_CALL ODatabaseMetaData::supportsUnionAll(  )
526 {
527     return true;
528 }
529 
530 sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtEnd(  )
531 {
532     return false;
533 }
534 
535 sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedAtStart(  )
536 {
537     return false;
538 }
539 
540 sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedHigh(  )
541 {
542     return false;
543 }
544 
545 sal_Bool SAL_CALL ODatabaseMetaData::nullsAreSortedLow(  )
546 {
547     return false;
548 }
549 
550 sal_Bool SAL_CALL ODatabaseMetaData::supportsCorrelatedSubqueries(  )
551 {
552     return false;
553 }
554 
555 sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInComparisons(  )
556 {
557     return false;
558 }
559 
560 sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInExists(  )
561 {
562     return false;
563 }
564 
565 sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInIns(  )
566 {
567     return false;
568 }
569 
570 sal_Bool SAL_CALL ODatabaseMetaData::supportsSubqueriesInQuantifieds(  )
571 {
572     return false;
573 }
574 
575 sal_Bool SAL_CALL ODatabaseMetaData::supportsANSI92IntermediateSQL(  )
576 {
577     return false;
578 }
579 
580 OUString SAL_CALL ODatabaseMetaData::getURL()
581 {
582     return m_pConnection->getConnectionURL();
583 }
584 
585 OUString SAL_CALL ODatabaseMetaData::getUserName(  )
586 {
587     return OUString();
588 }
589 
590 OUString SAL_CALL ODatabaseMetaData::getDriverName(  )
591 {
592     return OUString();
593 }
594 
595 OUString SAL_CALL ODatabaseMetaData::getDriverVersion()
596 {
597     return OUString();
598 }
599 
600 OUString SAL_CALL ODatabaseMetaData::getDatabaseProductVersion(  )
601 {
602     uno::Reference< XStatement > xSelect = m_pConnection->createStatement();
603 
604     uno::Reference< XResultSet > xRs = xSelect->executeQuery("SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version from rdb$database");
605     (void)xRs->next(); // first and only row
606     uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
607     return xRow->getString(1);
608 }
609 
610 OUString SAL_CALL ODatabaseMetaData::getDatabaseProductName(  )
611 {
612     return "Firebird (engine12)";
613 }
614 
615 OUString SAL_CALL ODatabaseMetaData::getProcedureTerm(  )
616 {
617     return OUString();
618 }
619 
620 sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMajorVersion(  )
621 {
622     return 1;
623 }
624 
625 sal_Int32 SAL_CALL ODatabaseMetaData::getDriverMinorVersion(  )
626 {
627     return 0;
628 }
629 
630 OUString SAL_CALL ODatabaseMetaData::getSQLKeywords(  )
631 {
632     return OUString();
633 }
634 
635 OUString SAL_CALL ODatabaseMetaData::getSearchStringEscape(  )
636 {
637     return OUString();
638 }
639 
640 OUString SAL_CALL ODatabaseMetaData::getStringFunctions(  )
641 {
642     return "ASCII_CHAR,ASCII_VAL,BIT_LENGTH,CHAR_LENGTH,CHAR_TO_UUID,CHARACTER_LENGTH,"
643            "GEN_UUID,HASH,LEFT,LOWER,LPAD,OCTET_LENGTH,OVERLAY,POSITION,REPLACE,REVERSE,"
644            "RIGHT,RPAD,SUBSTRING,TRIM,UPPER,UUID_TO_CHAR";
645 }
646 
647 OUString SAL_CALL ODatabaseMetaData::getTimeDateFunctions(  )
648 {
649     return "CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,DATEADD, DATEDIFF,"
650            "EXTRACT,'NOW','TODAY','TOMORROW','YESTERDAY'";
651 }
652 
653 OUString SAL_CALL ODatabaseMetaData::getSystemFunctions(  )
654 {
655     return OUString();
656 }
657 
658 OUString SAL_CALL ODatabaseMetaData::getNumericFunctions(  )
659 {
660     return "ABS,ACOS,ASIN,ATAN,ATAN2,BIN_AND,BIN_NOT,BIN_OR,BIN_SHL,"
661            "BIN_SHR,BIN_XOR,CEIL,CEILING,COS,COSH,COT,EXP,FLOOR,LN,"
662            "LOG,LOG10,MOD,PI,POWER,RAND,ROUND,SIGN,SIN,SINH,SQRT,TAN,TANH,TRUNC";
663 }
664 
665 sal_Bool SAL_CALL ODatabaseMetaData::supportsExtendedSQLGrammar(  )
666 {
667     return false;
668 }
669 
670 sal_Bool SAL_CALL ODatabaseMetaData::supportsFullOuterJoins(  )
671 {
672     return false;
673 }
674 
675 sal_Bool SAL_CALL ODatabaseMetaData::supportsLimitedOuterJoins(  )
676 {
677     return false;
678 }
679 
680 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInGroupBy(  )
681 {
682     return 0; // 0 means no limit
683 }
684 
685 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInOrderBy(  )
686 {
687     return 0; // 0 means no limit
688 }
689 
690 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxColumnsInSelect(  )
691 {
692     return 0; // 0 means no limit
693 }
694 
695 sal_Int32 SAL_CALL ODatabaseMetaData::getMaxUserNameLength(  )
696 {
697     return 31;
698 }
699 
700 sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetType(sal_Int32 setType)
701 {
702     switch (setType)
703     {
704         case ResultSetType::FORWARD_ONLY:
705             return true;
706         default:
707             return false;
708     }
709 }
710 
711 sal_Bool SAL_CALL ODatabaseMetaData::supportsResultSetConcurrency(
712         sal_Int32 aResultSetType,
713         sal_Int32 aConcurrency)
714 {
715     if (aResultSetType == ResultSetType::FORWARD_ONLY
716         && aConcurrency == ResultSetConcurrency::READ_ONLY)
717         return true;
718     else
719         return false;
720 }
721 
722 sal_Bool SAL_CALL ODatabaseMetaData::ownUpdatesAreVisible( sal_Int32 )
723 {
724     return false;
725 }
726 
727 sal_Bool SAL_CALL ODatabaseMetaData::ownDeletesAreVisible( sal_Int32 )
728 {
729     return false;
730 }
731 
732 sal_Bool SAL_CALL ODatabaseMetaData::ownInsertsAreVisible( sal_Int32 )
733 {
734     return false;
735 }
736 
737 sal_Bool SAL_CALL ODatabaseMetaData::othersUpdatesAreVisible( sal_Int32 )
738 {
739     return false;
740 }
741 
742 sal_Bool SAL_CALL ODatabaseMetaData::othersDeletesAreVisible( sal_Int32 )
743 {
744     return false;
745 }
746 
747 sal_Bool SAL_CALL ODatabaseMetaData::othersInsertsAreVisible( sal_Int32 )
748 {
749     return false;
750 }
751 
752 sal_Bool SAL_CALL ODatabaseMetaData::updatesAreDetected( sal_Int32 )
753 {
754     return false;
755 }
756 
757 sal_Bool SAL_CALL ODatabaseMetaData::deletesAreDetected( sal_Int32 )
758 {
759     return false;
760 }
761 
762 sal_Bool SAL_CALL ODatabaseMetaData::insertsAreDetected( sal_Int32 )
763 {
764     return false;
765 }
766 
767 sal_Bool SAL_CALL ODatabaseMetaData::supportsBatchUpdates()
768 {
769     // No batch support in firebird
770     return false;
771 }
772 
773 uno::Reference< XConnection > SAL_CALL ODatabaseMetaData::getConnection()
774 {
775     return uno::Reference<XConnection>(m_pConnection.get());
776 }
777 
778 // here follow all methods which return a resultset
779 // the first methods is an example implementation how to use this resultset
780 // of course you could implement it on your and you should do this because
781 // the general way is more memory expensive
782 
783 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTableTypes(  )
784 {
785     ODatabaseMetaDataResultSet* pResultSet = new
786         ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTableTypes);
787     uno::Reference< XResultSet > xResultSet = pResultSet;
788 
789     ODatabaseMetaDataResultSet::ORows aResults;
790     ODatabaseMetaDataResultSet::ORow aRow(2);
791 
792     aRow[0] = new ORowSetValueDecorator(); // unused
793 
794     // TODO Put these statics to one place
795     // like postgreSQL's Statics class.
796 
797     aRow[1] = new ORowSetValueDecorator(OUString("TABLE"));
798     aResults.push_back(aRow);
799 
800     aRow[1] = new ORowSetValueDecorator(OUString("VIEW"));
801     aResults.push_back(aRow);
802 
803     aRow[1] = new ORowSetValueDecorator(OUString("SYSTEM TABLE"));
804     aResults.push_back(aRow);
805 
806     pResultSet->setRows(aResults);
807     return xResultSet;
808 }
809 
810 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTypeInfo()
811 {
812     SAL_INFO("connectivity.firebird", "getTypeInfo()");
813 
814     // this returns an empty resultset where the column-names are already set
815     // in special the metadata of the resultset already returns the right columns
816     ODatabaseMetaDataResultSet* pResultSet =
817             new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTypeInfo);
818     uno::Reference< XResultSet > xResultSet = pResultSet;
819     static ODatabaseMetaDataResultSet::ORows aResults = []()
820     {
821         ODatabaseMetaDataResultSet::ORows tmp;
822         ODatabaseMetaDataResultSet::ORow aRow(19);
823 
824         // Common data
825         aRow[4] = ODatabaseMetaDataResultSet::getQuoteValue(); // Literal quote marks
826         aRow[5] = ODatabaseMetaDataResultSet::getQuoteValue(); // Literal quote marks
827         aRow[7] = new ORowSetValueDecorator(true); // Nullable
828         aRow[8] = new ORowSetValueDecorator(true); // Case sensitive
829         aRow[10] = new ORowSetValueDecorator(false); // Is unsigned
830         // FIXED_PREC_SCALE: docs state "can it be a money value? " however
831         // in reality this causes Base to treat all numbers as money formatted
832         // by default which is wrong (and formatting as money value is still
833         // possible for all values).
834         aRow[11] = new ORowSetValueDecorator(false);
835         // Localised Type Name -- TODO: implement (but can be null):
836         aRow[13] = new ORowSetValueDecorator();
837         aRow[16] = new ORowSetValueDecorator();             // Unused
838         aRow[17] = new ORowSetValueDecorator();             // Unused
839         aRow[18] = new ORowSetValueDecorator(sal_Int16(10));// Radix
840 
841         // Char
842         aRow[1] = new ORowSetValueDecorator(OUString("CHAR"));
843         aRow[2] = new ORowSetValueDecorator(DataType::CHAR);
844         aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
845         aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
846         aRow[9] = new ORowSetValueDecorator(
847                 sal_Int16(ColumnSearch::FULL)); // Searchable
848         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
849         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
850         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
851         tmp.push_back(aRow);
852 
853         // Varchar
854         aRow[1] = new ORowSetValueDecorator(OUString("VARCHAR"));
855         aRow[2] = new ORowSetValueDecorator(DataType::VARCHAR);
856         aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
857         aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
858         aRow[9] = new ORowSetValueDecorator(
859                 sal_Int16(ColumnSearch::FULL)); // Searchable
860         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
861         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
862         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
863         tmp.push_back(aRow);
864 
865         // Binary (CHAR)
866         // It is distinguished from Text type by its character set
867         aRow[1] = new ORowSetValueDecorator(OUString("CHAR"));
868         aRow[2] = new ORowSetValueDecorator(DataType::BINARY);
869         aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
870         aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
871         aRow[9] = new ORowSetValueDecorator(
872                 sal_Int16(ColumnSearch::NONE)); // Searchable
873         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
874         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
875         tmp.push_back(aRow);
876 
877         // Varbinary (VARCHAR)
878         aRow[1] = new ORowSetValueDecorator(OUString("VARCHAR"));
879         aRow[2] = new ORowSetValueDecorator(DataType::VARBINARY);
880         aRow[3] = new ORowSetValueDecorator(sal_Int16(32765)); // Prevision = max length
881         aRow[6] = new ORowSetValueDecorator(OUString("length")); // Create Params
882         aRow[9] = new ORowSetValueDecorator(
883                 sal_Int16(ColumnSearch::NONE)); // Searchable
884 
885         // Clob (SQL_BLOB)
886         aRow[1] = new ORowSetValueDecorator(OUString("BLOB")); // BLOB, with subtype 1
887         aRow[2] = new ORowSetValueDecorator(DataType::CLOB);
888         aRow[3] = new ORowSetValueDecorator(sal_Int32(2147483647)); // Precision = max length
889         aRow[6] = new ORowSetValueDecorator(); // Create Params
890         aRow[9] = new ORowSetValueDecorator(
891                 sal_Int16(ColumnSearch::FULL)); // Searchable
892         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
893         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
894         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
895         tmp.push_back(aRow);
896 
897         // Longvarbinary (SQL_BLOB)
898         // Distinguished from simple blob with a user-defined subtype.
899         aRow[2] = new ORowSetValueDecorator(DataType::LONGVARBINARY);
900         tmp.push_back(aRow);
901 
902         // Integer Types common
903         {
904             aRow[6] = new ORowSetValueDecorator(); // Create Params
905             aRow[9] = new ORowSetValueDecorator(
906                 sal_Int16(ColumnSearch::FULL)); // Searchable
907             aRow[12] = new ORowSetValueDecorator(true); // Autoincrement
908             aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
909             aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
910         }
911         // Smallint (SQL_SHORT)
912         aRow[1] = new ORowSetValueDecorator(OUString("SMALLINT"));
913         aRow[2] = new ORowSetValueDecorator(DataType::SMALLINT);
914         aRow[3] = new ORowSetValueDecorator(sal_Int16(5)); // Prevision
915         tmp.push_back(aRow);
916         // Integer (SQL_LONG)
917         aRow[1] = new ORowSetValueDecorator(OUString("INTEGER"));
918         aRow[2] = new ORowSetValueDecorator(DataType::INTEGER);
919         aRow[3] = new ORowSetValueDecorator(sal_Int16(10)); // Precision
920         tmp.push_back(aRow);
921         // Bigint (SQL_INT64)
922         aRow[1] = new ORowSetValueDecorator(OUString("BIGINT"));
923         aRow[2] = new ORowSetValueDecorator(DataType::BIGINT);
924         aRow[3] = new ORowSetValueDecorator(sal_Int16(20)); // Precision
925         tmp.push_back(aRow);
926 
927         // Decimal Types common
928         {
929             aRow[9] = new ORowSetValueDecorator(
930                 sal_Int16(ColumnSearch::FULL)); // Searchable
931             aRow[12] = new ORowSetValueDecorator(true); // Autoincrement
932         }
933 
934         aRow[6] = new ORowSetValueDecorator(OUString("PRECISION,SCALE")); // Create params
935         // Numeric
936         aRow[1] = new ORowSetValueDecorator(OUString("NUMERIC"));
937         aRow[2] = new ORowSetValueDecorator(DataType::NUMERIC);
938         aRow[3] = new ORowSetValueDecorator(sal_Int16(15)); // Precision
939         aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale
940         aRow[15] = new ORowSetValueDecorator(sal_Int16(15)); // Max scale
941         tmp.push_back(aRow);
942         // Decimal
943         aRow[1] = new ORowSetValueDecorator(OUString("DECIMAL"));
944         aRow[2] = new ORowSetValueDecorator(DataType::DECIMAL);
945         aRow[3] = new ORowSetValueDecorator(sal_Int16(15)); // Precision
946         aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale
947         aRow[15] = new ORowSetValueDecorator(sal_Int16(15)); // Max scale
948         tmp.push_back(aRow);
949 
950         aRow[6] = new ORowSetValueDecorator(); // Create Params
951         // Float (SQL_FLOAT)
952         aRow[1] = new ORowSetValueDecorator(OUString("FLOAT"));
953         aRow[2] = new ORowSetValueDecorator(DataType::FLOAT);
954         aRow[3] = new ORowSetValueDecorator(sal_Int16(7)); // Precision
955         aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale
956         aRow[15] = new ORowSetValueDecorator(sal_Int16(7)); // Max scale
957         tmp.push_back(aRow);
958         // Double (SQL_DOUBLE)
959         aRow[1] = new ORowSetValueDecorator(OUString("DOUBLE PRECISION"));
960         aRow[2] = new ORowSetValueDecorator(DataType::DOUBLE);
961         aRow[3] = new ORowSetValueDecorator(sal_Int16(15)); // Precision
962         aRow[14] = new ORowSetValueDecorator(sal_Int16(1)); // Minimum scale
963         aRow[15] = new ORowSetValueDecorator(sal_Int16(15)); // Max scale
964         tmp.push_back(aRow);
965 
966         // TODO: no idea whether D_FLOAT corresponds to an sql type
967 
968         // SQL_TIMESTAMP
969         aRow[1] = new ORowSetValueDecorator(OUString("TIMESTAMP"));
970         aRow[2] = new ORowSetValueDecorator(DataType::TIMESTAMP);
971         aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length
972         aRow[6] = new ORowSetValueDecorator(); // Create Params
973         aRow[9] = new ORowSetValueDecorator(
974                 sal_Int16(ColumnSearch::FULL)); // Searchable
975         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
976         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
977         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
978         tmp.push_back(aRow);
979 
980         // SQL_TYPE_TIME
981         aRow[1] = new ORowSetValueDecorator(OUString("TIME"));
982         aRow[2] = new ORowSetValueDecorator(DataType::TIME);
983         aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length
984         aRow[6] = new ORowSetValueDecorator(); // Create Params
985         aRow[9] = new ORowSetValueDecorator(
986                 sal_Int16(ColumnSearch::FULL)); // Searchable
987         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
988         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
989         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
990         tmp.push_back(aRow);
991 
992         // SQL_TYPE_DATE
993         aRow[1] = new ORowSetValueDecorator(OUString("DATE"));
994         aRow[2] = new ORowSetValueDecorator(DataType::DATE);
995         aRow[3] = new ORowSetValueDecorator(sal_Int32(8)); // Prevision = max length
996         aRow[6] = new ORowSetValueDecorator(); // Create Params
997         aRow[9] = new ORowSetValueDecorator(
998                 sal_Int16(ColumnSearch::FULL)); // Searchable
999         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
1000         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
1001         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
1002         tmp.push_back(aRow);
1003 
1004         // SQL_BLOB
1005         aRow[1] = new ORowSetValueDecorator(OUString("BLOB"));
1006         aRow[2] = new ORowSetValueDecorator(DataType::BLOB);
1007         aRow[3] = new ORowSetValueDecorator(sal_Int32(0)); // Prevision = max length
1008         aRow[6] = new ORowSetValueDecorator(); // Create Params
1009         aRow[9] = new ORowSetValueDecorator(
1010                 sal_Int16(ColumnSearch::NONE)); // Searchable
1011         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
1012         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
1013         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
1014         tmp.push_back(aRow);
1015 
1016         // SQL_BOOLEAN
1017         aRow[1] = new ORowSetValueDecorator(OUString("BOOLEAN"));
1018         aRow[2] = new ORowSetValueDecorator(DataType::BOOLEAN);
1019         aRow[3] = new ORowSetValueDecorator(sal_Int32(1)); // Prevision = max length
1020         aRow[6] = new ORowSetValueDecorator(); // Create Params
1021         aRow[9] = new ORowSetValueDecorator(
1022                 sal_Int16(ColumnSearch::BASIC)); // Searchable
1023         aRow[12] = new ORowSetValueDecorator(false); // Autoincrement
1024         aRow[14] = ODatabaseMetaDataResultSet::get0Value(); // Minimum scale
1025         aRow[15] = ODatabaseMetaDataResultSet::get0Value(); // Max scale
1026         tmp.push_back(aRow);
1027         return tmp;
1028     }();
1029     pResultSet->setRows(aResults);
1030     return xResultSet;
1031 }
1032 
1033 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumnPrivileges(
1034         const Any& /*aCatalog*/,
1035         const OUString& /*sSchema*/,
1036         const OUString& sTable,
1037         const OUString& sColumnNamePattern)
1038 {
1039     SAL_INFO("connectivity.firebird", "getColumnPrivileges() with "
1040              "Table: " << sTable
1041              << " & ColumnNamePattern: " << sColumnNamePattern);
1042 
1043     ODatabaseMetaDataResultSet* pResultSet = new
1044         ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eColumnPrivileges);
1045     uno::Reference< XResultSet > xResultSet = pResultSet;
1046     uno::Reference< XStatement > statement = m_pConnection->createStatement();
1047 
1048     static const char wld[] = "%";
1049     OUStringBuffer queryBuf(
1050             "SELECT "
1051             "priv.RDB$RELATION_NAME, "  // 1 Table name
1052             "priv.RDB$GRANTOR,"         // 2
1053             "priv.RDB$USER, "           // 3 Grantee
1054             "priv.RDB$PRIVILEGE, "      // 4
1055             "priv.RDB$GRANT_OPTION, "   // 5 is Grantable
1056             "priv.RDB$FIELD_NAME "      // 6 Column name
1057             "FROM RDB$USER_PRIVILEGES priv ");
1058 
1059     {
1060         OUString sAppend = "WHERE priv.RDB$RELATION_NAME = '%' ";
1061         queryBuf.append(sAppend.replaceAll("%", sTable));
1062     }
1063     if (!sColumnNamePattern.isEmpty())
1064     {
1065         OUString sAppend;
1066         if (sColumnNamePattern.match(wld))
1067             sAppend = "AND priv.RDB$FIELD_NAME LIKE '%' ";
1068         else
1069             sAppend = "AND priv.RDB$FIELD_NAME = '%' ";
1070 
1071         queryBuf.append(sAppend.replaceAll(wld, sColumnNamePattern));
1072     }
1073 
1074     queryBuf.append(" ORDER BY priv.RDB$FIELD, "
1075                               "priv.RDB$PRIVILEGE");
1076 
1077     OUString query = queryBuf.makeStringAndClear();
1078 
1079     uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr());
1080     uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1081     ODatabaseMetaDataResultSet::ORows aResults;
1082 
1083     ODatabaseMetaDataResultSet::ORow aCurrentRow(9);
1084     aCurrentRow[0] = new ORowSetValueDecorator(); // Unused
1085     aCurrentRow[1] = new ORowSetValueDecorator(); // 1. TABLE_CAT Unsupported
1086     aCurrentRow[2] = new ORowSetValueDecorator(); // 1. TABLE_SCHEM Unsupported
1087 
1088     while( rs->next() )
1089     {
1090         // 3. TABLE_NAME
1091         aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
1092         // 4. COLUMN_NAME
1093         aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(6)));
1094         aCurrentRow[5] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); // 5. GRANTOR
1095         aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // 6. GRANTEE
1096         aCurrentRow[7] = new ORowSetValueDecorator(xRow->getString(4)); // 7. Privilege
1097         aCurrentRow[8] = new ORowSetValueDecorator( ( xRow->getShort(5) == 1 ) ?
1098                     OUString("YES") : OUString("NO")); // 8. Grantable
1099 
1100         aResults.push_back(aCurrentRow);
1101     }
1102 
1103     pResultSet->setRows( aResults );
1104 
1105     return xResultSet;
1106 }
1107 
1108 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getColumns(
1109         const Any& /*catalog*/,
1110         const OUString& /*schemaPattern*/,
1111         const OUString& tableNamePattern,
1112         const OUString& columnNamePattern)
1113 {
1114     SAL_INFO("connectivity.firebird", "getColumns() with "
1115              "TableNamePattern: " << tableNamePattern <<
1116              " & ColumnNamePattern: " << columnNamePattern);
1117 
1118     OUStringBuffer queryBuf("SELECT "
1119         "relfields.RDB$RELATION_NAME, " // 1
1120         "relfields.RDB$FIELD_NAME, "    // 2
1121         "relfields.RDB$DESCRIPTION,"    // 3
1122         "relfields.RDB$DEFAULT_VALUE, " // 4
1123         "relfields.RDB$FIELD_POSITION, "// 5
1124         "fields.RDB$FIELD_TYPE, "       // 6
1125         "fields.RDB$FIELD_SUB_TYPE, "   // 7
1126         "fields.RDB$FIELD_LENGTH, "     // 8
1127         "fields.RDB$FIELD_PRECISION, "  // 9
1128         "fields.RDB$FIELD_SCALE, "      // 10
1129         // Specifically use relfields null flag -- the one in fields is used
1130         // for domains, whether a specific field is nullable is set in relfields,
1131         // this is also the one we manually fiddle when changing NULL/NOT NULL
1132         // (see Table.cxx)
1133         "relfields.RDB$NULL_FLAG, "      // 11
1134         "fields.RDB$CHARACTER_LENGTH, "   // 12
1135         "charset.RDB$CHARACTER_SET_NAME " // 13
1136         "FROM RDB$RELATION_FIELDS relfields "
1137         "JOIN RDB$FIELDS fields "
1138         "on (fields.RDB$FIELD_NAME = relfields.RDB$FIELD_SOURCE) "
1139         "LEFT JOIN RDB$CHARACTER_SETS charset "
1140         "on (fields.RDB$CHARACTER_SET_ID = charset.RDB$CHARACTER_SET_ID) "
1141         "WHERE (1 = 1) ");
1142 
1143     if (!tableNamePattern.isEmpty())
1144     {
1145         OUString sAppend;
1146         if (tableNamePattern.match("%"))
1147             sAppend = "AND relfields.RDB$RELATION_NAME LIKE '%' ";
1148         else
1149             sAppend = "AND relfields.RDB$RELATION_NAME = '%' ";
1150 
1151         queryBuf.append(sAppend.replaceAll("%", tableNamePattern));
1152     }
1153 
1154     if (!columnNamePattern.isEmpty())
1155     {
1156         OUString sAppend;
1157         if (columnNamePattern.match("%"))
1158             sAppend = "AND relfields.RDB$FIELD_NAME LIKE '%' ";
1159         else
1160             sAppend = "AND relfields.RDB$FIELD_NAME = '%' ";
1161 
1162         queryBuf.append(sAppend.replaceAll("%", columnNamePattern));
1163     }
1164 
1165     OUString query = queryBuf.makeStringAndClear();
1166 
1167     uno::Reference< XStatement > statement = m_pConnection->createStatement();
1168     uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr());
1169     uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1170 
1171     ODatabaseMetaDataResultSet::ORows aResults;
1172     ODatabaseMetaDataResultSet::ORow aCurrentRow(19);
1173 
1174     aCurrentRow[0] =  new ORowSetValueDecorator(); // Unused -- numbering starts from 0
1175     aCurrentRow[1] =  new ORowSetValueDecorator(); // Catalog - can be null
1176     aCurrentRow[2] =  new ORowSetValueDecorator(); // Schema - can be null
1177     aCurrentRow[8] =  new ORowSetValueDecorator(); // Unused
1178     aCurrentRow[10] = new ORowSetValueDecorator(sal_Int32(10)); // Radix: fixed in FB
1179     aCurrentRow[14] = new ORowSetValueDecorator(); // Unused
1180     aCurrentRow[15] = new ORowSetValueDecorator(); // Unused
1181 
1182     while( rs->next() )
1183     {
1184         // 3. TABLE_NAME
1185         aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
1186         // 4. Column Name
1187         aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2)));
1188         // 5. Datatype
1189         short aType = getFBTypeFromBlrType(xRow->getShort(6));
1190         short aScale = xRow->getShort(10);
1191         OUString sCharsetName = xRow->getString(13);
1192         // result field may be filled with spaces
1193         sCharsetName = sCharsetName.trim();
1194         ColumnTypeInfo aInfo(aType, xRow->getShort(7), aScale,
1195                 xRow->getString(13));
1196 
1197         aCurrentRow[5] = new ORowSetValueDecorator(aInfo.getSdbcType());
1198         // 6. Typename (SQL_*)
1199         aCurrentRow[6] = new ORowSetValueDecorator(aInfo.getColumnTypeName());
1200 
1201         // 7. Column Sizes
1202         {
1203             sal_Int32 aColumnSize = 0;
1204             switch (aType)
1205             {
1206                 case SQL_TEXT:
1207                 case SQL_VARYING:
1208                     aColumnSize = xRow->getShort(12);
1209                     break;
1210                 case SQL_SHORT:
1211                 case SQL_LONG:
1212                 case SQL_FLOAT:
1213                 case SQL_DOUBLE:
1214                 case SQL_D_FLOAT:
1215                 case SQL_INT64:
1216                 case SQL_QUAD:
1217                     aColumnSize = xRow->getShort(9);
1218                     break;
1219                 case SQL_TIMESTAMP:
1220                 case SQL_BLOB:
1221                 case SQL_ARRAY:
1222                 case SQL_TYPE_TIME:
1223                 case SQL_TYPE_DATE:
1224                 case SQL_NULL:
1225                     // TODO: implement.
1226                     break;
1227             }
1228             aCurrentRow[7] = new ORowSetValueDecorator(aColumnSize);
1229         }
1230 
1231         // 9. Decimal digits (scale)
1232         // fb stores a negative number
1233         aCurrentRow[9] = new ORowSetValueDecorator( static_cast<sal_Int16>(-aScale) );
1234 
1235         // 11. Nullable
1236         if (xRow->getShort(11))
1237         {
1238             aCurrentRow[11] = new ORowSetValueDecorator(ColumnValue::NO_NULLS);
1239         }
1240         else
1241         {
1242             aCurrentRow[11] = new ORowSetValueDecorator(ColumnValue::NULLABLE);
1243         }
1244         // 12. Comments -- may be omitted
1245         {
1246             OUString aDescription;
1247             uno::Reference< XBlob > xDescriptionBlob = xRow->getBlob(3);
1248             if (xDescriptionBlob.is())
1249             {
1250                 sal_Int32 aBlobLength = static_cast<sal_Int32>(xDescriptionBlob->length());
1251                 aDescription = OUString(reinterpret_cast<char*>(xDescriptionBlob->getBytes(0, aBlobLength).getArray()),
1252                                         aBlobLength,
1253                                         RTL_TEXTENCODING_UTF8);
1254             }
1255             aCurrentRow[12] = new ORowSetValueDecorator(aDescription);
1256         }
1257         // 13. Default --  may be omitted.
1258         {
1259             uno::Reference< XBlob > xDefaultValueBlob = xRow->getBlob(4);
1260             if (xDefaultValueBlob.is())
1261             {
1262                 // TODO: Implement
1263             }
1264             aCurrentRow[13] = new ORowSetValueDecorator();
1265         }
1266 
1267         // 16. Bytes in Column for char
1268         if (aType == SQL_TEXT)
1269         {
1270             aCurrentRow[16] = new ORowSetValueDecorator(xRow->getShort(8));
1271         }
1272         else if (aType == SQL_VARYING)
1273         {
1274             aCurrentRow[16] = new ORowSetValueDecorator(sal_Int32(32767));
1275         }
1276         else
1277         {
1278             aCurrentRow[16] = new ORowSetValueDecorator(sal_Int32(0));
1279         }
1280         // 17. Index of column
1281         {
1282             short nColumnNumber = xRow->getShort(5);
1283             // Firebird stores column numbers beginning with 0 internally
1284             // SDBC expects column numbering to begin with 1.
1285             aCurrentRow[17] = new ORowSetValueDecorator(sal_Int32(nColumnNumber + 1));
1286         }
1287         // 18. Is nullable
1288         if (xRow->getShort(9))
1289         {
1290             aCurrentRow[18] = new ORowSetValueDecorator(OUString("NO"));
1291         }
1292         else
1293         {
1294             aCurrentRow[18] = new ORowSetValueDecorator(OUString("YES"));
1295         }
1296 
1297         aResults.push_back(aCurrentRow);
1298     }
1299     ODatabaseMetaDataResultSet* pResultSet = new
1300             ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eColumns);
1301     uno::Reference< XResultSet > xResultSet = pResultSet;
1302     pResultSet->setRows( aResults );
1303 
1304     return xResultSet;
1305 }
1306 
1307 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTables(
1308         const Any& /*catalog*/,
1309         const OUString& /*schemaPattern*/,
1310         const OUString& tableNamePattern,
1311         const Sequence< OUString >& types)
1312 {
1313     SAL_INFO("connectivity.firebird", "getTables() with "
1314              "TableNamePattern: " << tableNamePattern);
1315 
1316     ODatabaseMetaDataResultSet* pResultSet = new
1317         ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTables);
1318     uno::Reference< XResultSet > xResultSet = pResultSet;
1319     uno::Reference< XStatement > statement = m_pConnection->createStatement();
1320 
1321     static const char wld[] = "%";
1322     OUStringBuffer queryBuf(
1323             "SELECT "
1324             "RDB$RELATION_NAME, "
1325             "RDB$SYSTEM_FLAG, "
1326             "RDB$RELATION_TYPE, "
1327             "RDB$DESCRIPTION, "
1328             "RDB$VIEW_BLR "
1329             "FROM RDB$RELATIONS "
1330             "WHERE ");
1331 
1332     // TODO: GLOBAL TEMPORARY, LOCAL TEMPORARY, ALIAS, SYNONYM
1333     if (!types.hasElements() || (types.getLength() == 1 && types[0].match(wld)))
1334     {
1335         // All table types? I.e. includes system tables.
1336         queryBuf.append("(RDB$RELATION_TYPE = 0 OR RDB$RELATION_TYPE = 1) ");
1337     }
1338     else
1339     {
1340         queryBuf.append("( (0 = 1) ");
1341         for (int i = 0; i < types.getLength(); i++)
1342         {
1343             if (types[i] == "SYSTEM TABLE")
1344                 queryBuf.append("OR (RDB$SYSTEM_FLAG = 1 AND RDB$VIEW_BLR IS NULL) ");
1345             else if (types[i] == "TABLE")
1346                 queryBuf.append("OR (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0 AND RDB$VIEW_BLR IS NULL) ");
1347             else if (types[i] == "VIEW")
1348                 queryBuf.append("OR (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0 AND RDB$VIEW_BLR IS NOT NULL) ");
1349             else
1350                 throw SQLException(); // TODO: implement other types, see above.
1351         }
1352         queryBuf.append(") ");
1353     }
1354 
1355     if (!tableNamePattern.isEmpty())
1356     {
1357         OUString sAppend;
1358         if (tableNamePattern.match(wld))
1359             sAppend = "AND RDB$RELATION_NAME LIKE '%' ";
1360         else
1361             sAppend = "AND RDB$RELATION_NAME = '%' ";
1362 
1363         queryBuf.append(sAppend.replaceAll(wld, tableNamePattern));
1364     }
1365 
1366     queryBuf.append(" ORDER BY RDB$RELATION_TYPE, RDB$RELATION_NAME");
1367 
1368     OUString query = queryBuf.makeStringAndClear();
1369 
1370     uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr());
1371     uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1372     ODatabaseMetaDataResultSet::ORows aResults;
1373 
1374     ODatabaseMetaDataResultSet::ORow aCurrentRow(6);
1375     aCurrentRow[0] = new ORowSetValueDecorator(); // 0. Unused
1376     aCurrentRow[1] = new ORowSetValueDecorator(); // 1. Table_Cat Unsupported
1377     aCurrentRow[2] = new ORowSetValueDecorator(); // 2. Table_Schem Unsupported
1378 
1379     while( rs->next() )
1380     {
1381         // 3. TABLE_NAME
1382         aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
1383         // 4. TABLE_TYPE
1384         {
1385             // TODO: check this as the docs are a bit unclear.
1386             sal_Int16 nSystemFlag = xRow->getShort(2);
1387             sal_Int16 nTableType  = xRow->getShort(3);
1388             xRow->getBlob(5); // We have to retrieve a column to verify it is null.
1389             bool aIsView      = !xRow->wasNull();
1390             OUString sTableType;
1391 
1392             if (nSystemFlag == 1)
1393             {
1394                 sTableType = "SYSTEM TABLE";
1395             }
1396             else if (aIsView)
1397             {
1398                 sTableType = "VIEW";
1399             }
1400             else
1401             {
1402                 if (nTableType == 0)
1403                     sTableType = "TABLE";
1404             }
1405 
1406             aCurrentRow[4] = new ORowSetValueDecorator(sTableType);
1407         }
1408         // 5. REMARKS
1409         {
1410             uno::Reference< XBlob > xBlob   = xRow->getBlob(4);
1411             OUString sDescription;
1412 
1413             if (xBlob.is())
1414             {
1415                 // TODO: we should actually be using CLOB here instead.
1416                 // However we haven't implemented CLOB yet, so use BLOB.
1417                 sal_Int32 aBlobLength = static_cast<sal_Int32>(xBlob->length());
1418                 sDescription = OUString(reinterpret_cast<char*>(xBlob->getBytes(0, aBlobLength).getArray()),
1419                                         aBlobLength,
1420                                         RTL_TEXTENCODING_UTF8);
1421             }
1422 
1423             aCurrentRow[5] = new ORowSetValueDecorator(sDescription);
1424         }
1425 
1426         aResults.push_back(aCurrentRow);
1427     }
1428 
1429     pResultSet->setRows( aResults );
1430 
1431     return xResultSet;
1432 }
1433 
1434 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedureColumns(
1435     const Any&, const OUString&,
1436     const OUString&, const OUString& )
1437 {
1438     SAL_WARN("connectivity.firebird", "Not yet implemented");
1439     OSL_FAIL("Not implemented yet!");
1440     // TODO implement
1441     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eProcedureColumns);
1442 }
1443 
1444 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getProcedures(
1445     const Any&, const OUString&,
1446     const OUString& )
1447 {
1448     SAL_WARN("connectivity.firebird", "Not yet implemented");
1449     OSL_FAIL("Not implemented yet!");
1450     // TODO implement
1451     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eProcedures);
1452 }
1453 
1454 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getVersionColumns(
1455     const Any&, const OUString&, const OUString& )
1456 {
1457     SAL_WARN("connectivity.firebird", "Not yet implemented");
1458     OSL_FAIL("Not implemented yet!");
1459     // TODO implement
1460     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eVersionColumns);
1461 }
1462 
1463 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getExportedKeys(
1464     const Any&, const OUString&, const OUString& table )
1465 {
1466     return ODatabaseMetaData::lcl_getKeys(false, table);
1467 }
1468 
1469 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getImportedKeys(
1470     const Any&, const OUString&, const OUString& table )
1471 {
1472     return ODatabaseMetaData::lcl_getKeys(true, table);
1473 }
1474 
1475 uno::Reference< XResultSet > ODatabaseMetaData::lcl_getKeys(const bool& bIsImport, const OUString& table )
1476 {
1477     ODatabaseMetaDataResultSet* pResultSet = new
1478         ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eImportedKeys);
1479     uno::Reference< XResultSet > xResultSet = pResultSet;
1480 
1481     uno::Reference< XStatement > statement = m_pConnection->createStatement();
1482 
1483     OUString sSQL = "SELECT "
1484            "RDB$REF_CONSTRAINTS.RDB$UPDATE_RULE, " // 1 update rule
1485            "RDB$REF_CONSTRAINTS.RDB$DELETE_RULE, " // 2 delete rule
1486            "RDB$REF_CONSTRAINTS.RDB$CONST_NAME_UQ, " // 3 primary or unique key name
1487            "RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME, " // 4 foreign key name
1488            "PRIM.RDB$DEFERRABLE, " // 5 deferrability
1489            "PRIM.RDB$INITIALLY_DEFERRED, " // 6 deferrability
1490            "PRIM.RDB$RELATION_NAME, " // 7 PK table name
1491            "PRIMARY_INDEX.RDB$FIELD_NAME, " // 8 PK column name
1492            "PRIMARY_INDEX.RDB$FIELD_POSITION, " // 9 PK sequence number
1493            "FOREI.RDB$RELATION_NAME, " // 10 FK table name
1494            "FOREIGN_INDEX.RDB$FIELD_NAME " // 11 FK column name
1495            "FROM RDB$REF_CONSTRAINTS "
1496            "INNER JOIN RDB$RELATION_CONSTRAINTS AS PRIM "
1497            "ON RDB$REF_CONSTRAINTS.RDB$CONST_NAME_UQ = PRIM.RDB$CONSTRAINT_NAME "
1498            "INNER JOIN RDB$RELATION_CONSTRAINTS AS FOREI "
1499            "ON RDB$REF_CONSTRAINTS.RDB$CONSTRAINT_NAME = FOREI.RDB$CONSTRAINT_NAME "
1500            "INNER JOIN RDB$INDEX_SEGMENTS AS PRIMARY_INDEX "
1501            "ON PRIM.RDB$INDEX_NAME = PRIMARY_INDEX.RDB$INDEX_NAME "
1502            "INNER JOIN RDB$INDEX_SEGMENTS AS FOREIGN_INDEX "
1503            "ON FOREI.RDB$INDEX_NAME = FOREIGN_INDEX.RDB$INDEX_NAME "
1504            "WHERE FOREI.RDB$CONSTRAINT_TYPE = 'FOREIGN KEY' ";
1505     if (bIsImport)
1506         sSQL += "AND FOREI.RDB$RELATION_NAME = '"+ table +"'";
1507     else
1508         sSQL += "AND PRIM.RDB$RELATION_NAME = '"+ table +"'";
1509 
1510     uno::Reference< XResultSet > rs = statement->executeQuery(sSQL);
1511     uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1512 
1513     ODatabaseMetaDataResultSet::ORows aResults;
1514     ODatabaseMetaDataResultSet::ORow aCurrentRow(15);
1515 
1516     // TODO is it necessary to initialize these?
1517     aCurrentRow[0] = new ORowSetValueDecorator(); // Unused
1518     aCurrentRow[1] = new ORowSetValueDecorator(); // PKTABLE_CAT unsupported
1519     aCurrentRow[2] = new ORowSetValueDecorator(); // PKTABLE_SCHEM unsupported
1520     aCurrentRow[5] = new ORowSetValueDecorator(); // FKTABLE_CAT unsupported
1521     aCurrentRow[6] = new ORowSetValueDecorator(); // FKTABLE_SCHEM unsupported
1522 
1523     std::map< OUString,sal_Int32> aRuleMap;
1524     aRuleMap[ OUString("CASCADE")] = KeyRule::CASCADE;
1525     aRuleMap[ OUString("RESTRICT")] = KeyRule::RESTRICT;
1526     aRuleMap[ OUString("SET NULL")] = KeyRule::SET_NULL;
1527     aRuleMap[ OUString("SET DEFAULT")] = KeyRule::SET_DEFAULT;
1528     aRuleMap[ OUString("NO ACTION")] = KeyRule::NO_ACTION;
1529 
1530     while(rs->next())
1531     {
1532         aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(7))); // PK table
1533         aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(8))); // PK column
1534         aCurrentRow[7] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(10))); // FK table
1535         aCurrentRow[8] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(11))); // FK column
1536 
1537         aCurrentRow[9] = new ORowSetValueDecorator(xRow->getShort(9)); // PK sequence number
1538         aCurrentRow[10] = new ORowSetValueDecorator(aRuleMap[sanitizeIdentifier(xRow->getString(1))]); // update role
1539         aCurrentRow[11] = new ORowSetValueDecorator(aRuleMap[sanitizeIdentifier(xRow->getString(2))]); // delete role
1540 
1541         aCurrentRow[12] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4))); // FK name
1542         aCurrentRow[13] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // PK name
1543 
1544         aCurrentRow[14] = new ORowSetValueDecorator(Deferrability::NONE); // deferrability
1545 
1546         // deferrability is currently not supported, but may be supported in the future.
1547         /*
1548         aCurrentRow[14] = (xRow->getString(5) == "NO" ?
1549                           new ORowSetValueDecorator(Deferrability::NONE)
1550                         : (xRow->getString(6) == "NO" ?
1551                             new ORowSetValueDecorator(Deferrability::INITIALLY_IMMEDIATE)
1552                           : new ORowSetValueDecorator(Deferrability::INITIALLY_DEFERRED));
1553         */
1554 
1555         aResults.push_back(aCurrentRow);
1556     }
1557 
1558     pResultSet->setRows( aResults );
1559     return xResultSet;
1560 }
1561 
1562 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getPrimaryKeys(
1563         const Any& /*aCatalog*/,
1564         const OUString& /*sSchema*/,
1565         const OUString& sTable)
1566 {
1567     SAL_INFO("connectivity.firebird", "getPrimaryKeys() with "
1568              "Table: " << sTable);
1569 
1570     OUString sAppend = "WHERE constr.RDB$RELATION_NAME = '%' ";
1571     OUString sQuery = "SELECT "
1572         "constr.RDB$RELATION_NAME, "    // 1. Table Name
1573         "inds.RDB$FIELD_NAME, "         // 2. Column Name
1574         "inds.RDB$FIELD_POSITION, "     // 3. Sequence Number
1575         "constr.RDB$CONSTRAINT_NAME "   // 4 Constraint name
1576         "FROM RDB$RELATION_CONSTRAINTS constr "
1577         "JOIN RDB$INDEX_SEGMENTS inds "
1578         "on (constr.RDB$INDEX_NAME = inds.RDB$INDEX_NAME) " +
1579         sAppend.replaceAll("%", sTable) +
1580         "AND constr.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY' "
1581                     "ORDER BY inds.RDB$FIELD_NAME";
1582 
1583     uno::Reference< XStatement > xStatement = m_pConnection->createStatement();
1584     uno::Reference< XResultSet > xRs = xStatement->executeQuery(sQuery);
1585     uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
1586 
1587     ODatabaseMetaDataResultSet::ORows aResults;
1588     ODatabaseMetaDataResultSet::ORow aCurrentRow(7);
1589 
1590     aCurrentRow[0] =  new ORowSetValueDecorator(); // Unused -- numbering starts from 0
1591     aCurrentRow[1] =  new ORowSetValueDecorator(); // Catalog - can be null
1592     aCurrentRow[2] =  new ORowSetValueDecorator(); // Schema - can be null
1593 
1594     while(xRs->next())
1595     {
1596         // 3. Table Name
1597         if (xRs->getRow() == 1) // Table name doesn't change, so only retrieve once
1598         {
1599             aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
1600         }
1601         // 4. Column Name
1602         aCurrentRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2)));
1603         // 5. KEY_SEQ (which key in the sequence)
1604         aCurrentRow[5] = new ORowSetValueDecorator(xRow->getShort(3));
1605         // 6. Primary Key Name
1606         aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4)));
1607 
1608         aResults.push_back(aCurrentRow);
1609     }
1610     ODatabaseMetaDataResultSet* pResultSet = new
1611             ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::ePrimaryKeys);
1612     uno::Reference< XResultSet > xResultSet = pResultSet;
1613     pResultSet->setRows( aResults );
1614 
1615     return xResultSet;
1616 }
1617 
1618 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getIndexInfo(
1619         const Any& /*aCatalog*/,
1620         const OUString& /*sSchema*/,
1621         const OUString& sTable,
1622         sal_Bool bIsUnique,
1623         sal_Bool) // TODO: what is bIsApproximate?
1624 
1625 {
1626     // Apparently this method can also return a "tableIndexStatistic"
1627     // However this is only mentioned in XDatabaseMetaData.idl (whose comments
1628     // are duplicated in the postgresql driver), and is otherwise undocumented.
1629     SAL_INFO("connectivity.firebird", "getPrimaryKeys() with "
1630              "Table: " << sTable);
1631 
1632     OUStringBuffer aQueryBuf("SELECT "
1633         "indices.RDB$RELATION_NAME, "               // 1. Table Name
1634         "index_segments.RDB$FIELD_NAME, "           // 2. Column Name
1635         "index_segments.RDB$FIELD_POSITION, "       // 3. Sequence Number
1636         "indices.RDB$INDEX_NAME, "                  // 4. Index name
1637         "indices.RDB$UNIQUE_FLAG, "                 // 5. Unique Flag
1638         "indices.RDB$INDEX_TYPE "                   // 6. Index Type
1639         "FROM RDB$INDICES indices "
1640         "JOIN RDB$INDEX_SEGMENTS index_segments "
1641         "on (indices.RDB$INDEX_NAME = index_segments.RDB$INDEX_NAME) "
1642         "WHERE indices.RDB$RELATION_NAME = '" + sTable + "' "
1643         "AND (indices.RDB$SYSTEM_FLAG = 0) ");
1644     // Not sure whether we should exclude system indices, but otoh. we never
1645     // actually deal with system tables (system indices only apply to system
1646     // tables) within the GUI.
1647 
1648     // Only filter if true (according to the docs), i.e.:
1649     // If false we return all indices, if true we return only unique indices
1650     if (bIsUnique)
1651         aQueryBuf.append("AND (indices.RDB$UNIQUE_FLAG = 1) ");
1652 
1653     OUString sQuery = aQueryBuf.makeStringAndClear();
1654 
1655     uno::Reference< XStatement > xStatement = m_pConnection->createStatement();
1656     uno::Reference< XResultSet > xRs = xStatement->executeQuery(sQuery);
1657     uno::Reference< XRow > xRow( xRs, UNO_QUERY_THROW );
1658 
1659     ODatabaseMetaDataResultSet::ORows aResults;
1660     ODatabaseMetaDataResultSet::ORow aCurrentRow(14);
1661 
1662     aCurrentRow[0] = new ORowSetValueDecorator(); // Unused -- numbering starts from 0
1663     aCurrentRow[1] = new ORowSetValueDecorator(); // Catalog - can be null
1664     aCurrentRow[2] = new ORowSetValueDecorator(); // Schema - can be null
1665     aCurrentRow[5] = new ORowSetValueDecorator(); // Index Catalog -- can be null
1666     // According to wikipedia firebird uses clustered indices.
1667     // The documentation does not specifically seem to specify this.
1668     aCurrentRow[7] = new ORowSetValueDecorator(IndexType::CLUSTERED); // 7. INDEX TYPE
1669     aCurrentRow[13] = new ORowSetValueDecorator(); // Filter Condition -- can be null
1670 
1671     while(xRs->next())
1672     {
1673         // 3. Table Name
1674         if (xRs->getRow() == 1) // Table name doesn't change, so only retrieve once
1675         {
1676             aCurrentRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
1677         }
1678 
1679         // 4. NON_UNIQUE -- i.e. specifically negate here.
1680         aCurrentRow[4] = new ORowSetValueDecorator(xRow->getShort(5) == 0);
1681         // 6. INDEX NAME
1682         aCurrentRow[6] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(4)));
1683 
1684         // 8. ORDINAL POSITION
1685         aCurrentRow[8] = new ORowSetValueDecorator(xRow->getShort(3));
1686         // 9. COLUMN NAME
1687         aCurrentRow[9] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2)));
1688         // 10. ASC(ending)/DESC(ending)
1689         if (xRow->getShort(6) == 1)
1690             aCurrentRow[10] = new ORowSetValueDecorator(OUString("D"));
1691         else
1692             aCurrentRow[10] = new ORowSetValueDecorator(OUString("A"));
1693         // TODO: double check this^^^, doesn't seem to be officially documented anywhere.
1694         // 11. CARDINALITY
1695         aCurrentRow[11] = new ORowSetValueDecorator(sal_Int32(0)); // TODO: determine how to do this
1696         // 12. PAGES
1697         aCurrentRow[12] = new ORowSetValueDecorator(sal_Int32(0)); // TODO: determine how to do this
1698 
1699         aResults.push_back(aCurrentRow);
1700     }
1701     ODatabaseMetaDataResultSet* pResultSet = new
1702             ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::ePrimaryKeys);
1703     uno::Reference< XResultSet > xResultSet = pResultSet;
1704     pResultSet->setRows( aResults );
1705 
1706     return xResultSet;
1707 }
1708 
1709 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getBestRowIdentifier(
1710     const Any&, const OUString&, const OUString&, sal_Int32,
1711     sal_Bool )
1712 {
1713     OSL_FAIL("Not implemented yet!");
1714     // TODO implement
1715     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eBestRowIdentifier);
1716 }
1717 
1718 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTablePrivileges(
1719         const Any& /*aCatalog*/,
1720         const OUString& /*sSchemaPattern*/,
1721         const OUString& sTableNamePattern)
1722 {
1723     SAL_INFO("connectivity.firebird", "getTablePrivileges() with "
1724              "TableNamePattern: " << sTableNamePattern);
1725 
1726     ODatabaseMetaDataResultSet* pResultSet = new
1727         ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eTablePrivileges);
1728     uno::Reference< XResultSet > xResultSet = pResultSet;
1729     uno::Reference< XStatement > statement = m_pConnection->createStatement();
1730 
1731     // TODO: column specific privileges are included, we may need
1732     // to have WHERE RDB$FIELD_NAME = NULL or similar.
1733     static const char wld[] = "%";
1734     OUStringBuffer queryBuf(
1735             "SELECT "
1736             "priv.RDB$RELATION_NAME, "  // 1
1737             "priv.RDB$GRANTOR,"         // 2
1738             "priv.RDB$USER, "           // 3 Grantee
1739             "priv.RDB$PRIVILEGE, "      // 4
1740             "priv.RDB$GRANT_OPTION "    // 5 is Grantable
1741             "FROM RDB$USER_PRIVILEGES priv ");
1742 
1743     if (!sTableNamePattern.isEmpty())
1744     {
1745         OUString sAppend;
1746         if (sTableNamePattern.match(wld))
1747             sAppend = "WHERE priv.RDB$RELATION_NAME LIKE '%' ";
1748         else
1749             sAppend = "WHERE priv.RDB$RELATION_NAME = '%' ";
1750 
1751         queryBuf.append(sAppend.replaceAll(wld, sTableNamePattern));
1752     }
1753     queryBuf.append(" ORDER BY priv.RDB$RELATION_TYPE, "
1754                               "priv.RDB$RELATION_NAME, "
1755                               "priv.RDB$PRIVILEGE");
1756 
1757     OUString query = queryBuf.makeStringAndClear();
1758 
1759     uno::Reference< XResultSet > rs = statement->executeQuery(query.getStr());
1760     uno::Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1761     ODatabaseMetaDataResultSet::ORows aResults;
1762 
1763     ODatabaseMetaDataResultSet::ORow aRow(8);
1764     aRow[0] = new ORowSetValueDecorator(); // Unused
1765     aRow[1] = new ORowSetValueDecorator(); // TABLE_CAT unsupported
1766     aRow[2] = new ORowSetValueDecorator(); // TABLE_SCHEM unsupported.
1767 
1768     while( rs->next() )
1769     {
1770         // 3. TABLE_NAME
1771         aRow[3] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(1)));
1772         aRow[4] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(2))); // 4. GRANTOR
1773         aRow[5] = new ORowSetValueDecorator(sanitizeIdentifier(xRow->getString(3))); // 5. GRANTEE
1774         aRow[6] = new ORowSetValueDecorator(xRow->getString(4)); // 6. Privilege
1775         aRow[7] = new ORowSetValueDecorator(bool(xRow->getBoolean(5))); // 7. Is Grantable
1776 
1777         aResults.push_back(aRow);
1778     }
1779 
1780     pResultSet->setRows( aResults );
1781 
1782     return xResultSet;
1783 }
1784 
1785 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getCrossReference(
1786     const Any&, const OUString&,
1787     const OUString&, const Any&,
1788     const OUString&, const OUString& )
1789 {
1790     OSL_FAIL("Not implemented yet!");
1791     // TODO implement
1792     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eCrossReference);
1793 }
1794 
1795 uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getUDTs( const Any&, const OUString&, const OUString&, const Sequence< sal_Int32 >& )
1796 {
1797     OSL_FAIL("Not implemented yet!");
1798     // TODO implement
1799     return new ODatabaseMetaDataResultSet(ODatabaseMetaDataResultSet::eUDTs);
1800 }
1801 
1802 
1803 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1804