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