1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
3  *
4  *  Effective License of whole file:
5  *
6  *    This library is free software; you can redistribute it and/or
7  *    modify it under the terms of the GNU Lesser General Public
8  *    License version 2.1, as published by the Free Software Foundation.
9  *
10  *    This library is distributed in the hope that it will be useful,
11  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *    Lesser General Public License for more details.
14  *
15  *    You should have received a copy of the GNU Lesser General Public
16  *    License along with this library; if not, write to the Free Software
17  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
18  *    MA  02111-1307  USA
19  *
20  *  Parts "Copyright by Sun Microsystems, Inc" prior to August 2011:
21  *
22  *    The Contents of this file are made available subject to the terms of
23  *    the GNU Lesser General Public License Version 2.1
24  *
25  *    Copyright: 2000 by Sun Microsystems, Inc.
26  *
27  *    Contributor(s): Joerg Budischewski
28  *
29  *  All parts contributed on or after August 2011:
30  *
31  *    This Source Code Form is subject to the terms of the Mozilla Public
32  *    License, v. 2.0. If a copy of the MPL was not distributed with this
33  *    file, You can obtain one at http://mozilla.org/MPL/2.0/.
34  *
35  *   Some portions were adapted from JDBC PostgreSQL driver:
36  *
37  *    Copyright (c) 2004-2008, PostgreSQL Global Development Group
38  *
39  *   Licence of original JDBC driver code:
40  *
41  *    Redistribution and use in source and binary forms, with or without
42  *    modification, are permitted provided that the following conditions are met:
43  *
44  *    1. Redistributions of source code must retain the above copyright notice,
45  *       this list of conditions and the following disclaimer.
46  *    2. Redistributions in binary form must reproduce the above copyright notice,
47  *       this list of conditions and the following disclaimer in the documentation
48  *       and/or other materials provided with the distribution.
49  *    3. Neither the name of the PostgreSQL Global Development Group nor the names
50  *       of its contributors may be used to endorse or promote products derived
51  *       from this software without specific prior written permission.
52  *
53  *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54  *    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  *    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  *    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
57  *    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
58  *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
59  *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
60  *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
61  *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
62  *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
63  *    POSSIBILITY OF SUCH DAMAGE.
64  *
65  ************************************************************************/
66 
67 #include <algorithm>
68 #include "pq_databasemetadata.hxx"
69 #include "pq_driver.hxx"
70 #include "pq_sequenceresultset.hxx"
71 #include "pq_statics.hxx"
72 #include "pq_tools.hxx"
73 
74 #include <rtl/ustrbuf.hxx>
75 
76 #include <com/sun/star/sdbc/TransactionIsolation.hpp>
77 #include <com/sun/star/sdbc/ResultSetType.hpp>
78 #include <com/sun/star/sdbc/XPreparedStatement.hpp>
79 #include <com/sun/star/sdbc/XParameters.hpp>
80 #include <com/sun/star/sdbc/DataType.hpp>
81 #include <com/sun/star/sdbc/IndexType.hpp>
82 #include <com/sun/star/sdbc/ColumnValue.hpp>
83 #include <com/sun/star/sdbc/ColumnSearch.hpp>
84 #include <com/sun/star/sdbc/KeyRule.hpp>
85 #include <com/sun/star/sdbc/Deferrability.hpp>
86 
87 using ::osl::MutexGuard;
88 
89 
90 using namespace com::sun::star::sdbc;
91 
92 using com::sun::star::uno::Reference;
93 using com::sun::star::uno::Sequence;
94 using com::sun::star::uno::Any;
95 using com::sun::star::uno::UNO_QUERY;
96 using com::sun::star::uno::UNO_QUERY_THROW;
97 
98 namespace pq_sdbc_driver
99 {
100 #define QUOTEME(X)  #X
101 #define STRINGIFY(X) QUOTEME(X)
102 
103 // These are pre-processor versions of KeyRule.idl declarations
104 // These are inherited from JDBC, and thus won't change anytime soon.
105 // Having them as pre-processor definitions allows to include them
106 // into compile-time strings (through STRINGIFY), which can be passed to ASCII_STR.
107 // That is without resorting to horrendous hacks in template meta-programming.
108 #define KEYRULE_CASCADE      0
109 #define KEYRULE_RESTRICT     1
110 #define KEYRULE_SET_NULL     2
111 #define KEYRULE_NO_ACTION    4
112 #define KEYRULE_SET_DEFAULT  4
113 // Ditto for Deferrability.idl
114 #define DEFERRABILITY_INITIALLY_DEFERRED  5
115 #define DEFERRABILITY_INITIALLY_IMMEDIATE 6
116 #define DEFERRABILITY_NONE                7
117 
DatabaseMetaData(const::rtl::Reference<comphelper::RefCountedMutex> & refMutex,const css::uno::Reference<css::sdbc::XConnection> & origin,ConnectionSettings * pSettings)118 DatabaseMetaData::DatabaseMetaData(
119     const ::rtl::Reference< comphelper::RefCountedMutex > & refMutex,
120     const css::uno::Reference< css::sdbc::XConnection >  & origin,
121     ConnectionSettings *pSettings )
122   : m_xMutex( refMutex ),
123     m_pSettings( pSettings ),
124     m_origin( origin ),
125     m_getIntSetting_stmt ( m_origin->prepareStatement("SELECT setting FROM pg_catalog.pg_settings WHERE name=?") )
126 {
127     init_getReferences_stmt();
128     init_getPrivs_stmt();
129 }
130 
allProceduresAreCallable()131 sal_Bool DatabaseMetaData::allProceduresAreCallable(  )
132 {
133     // TODO
134     return false;
135 }
136 
allTablesAreSelectable()137 sal_Bool DatabaseMetaData::allTablesAreSelectable(  )
138 {
139     return true;
140 }
141 
getURL()142 OUString DatabaseMetaData::getURL(  )
143 {
144     // TODO
145     // LEM TODO: implement
146     return OUString();
147 }
148 
getUserName()149 OUString DatabaseMetaData::getUserName(  )
150 {
151     return m_pSettings->user;
152 }
153 
isReadOnly()154 sal_Bool DatabaseMetaData::isReadOnly(  )
155 {
156     return false;
157 }
158 
159 
nullsAreSortedHigh()160 sal_Bool DatabaseMetaData::nullsAreSortedHigh(  )
161 {
162     // Whether NULL values are considered, for sorting purposes, LARGER than any other value.
163     // Specification: http://download.oracle.com/javase/6/docs/api/java/sql/DatabaseMetaData.html#nullsAreSortedHigh()
164     // PostgreSQL behaviour: http://www.postgresql.org/docs/9.1/static/queries-order.html
165     return true;
166 }
167 
nullsAreSortedLow()168 sal_Bool DatabaseMetaData::nullsAreSortedLow(  )
169 {
170     return ! nullsAreSortedHigh();
171 }
172 
nullsAreSortedAtStart()173 sal_Bool DatabaseMetaData::nullsAreSortedAtStart(  )
174 {
175     return false;
176 }
177 
nullsAreSortedAtEnd()178 sal_Bool DatabaseMetaData::nullsAreSortedAtEnd(  )
179 {
180     return false;
181 }
182 
getDatabaseProductName()183 OUString DatabaseMetaData::getDatabaseProductName(  )
184 {
185     return "PostgreSQL";
186 }
187 
getDatabaseProductVersion()188 OUString DatabaseMetaData::getDatabaseProductVersion(  )
189 {
190     return OUString::createFromAscii( PQparameterStatus( m_pSettings->pConnection, "server_version" ) );
191 }
getDriverName()192 OUString DatabaseMetaData::getDriverName(  )
193 {
194     return "postgresql-sdbc";
195 }
196 
getDriverVersion()197 OUString DatabaseMetaData::getDriverVersion(  )
198 {
199     return PQ_SDBC_DRIVER_VERSION;
200 }
201 
getDriverMajorVersion()202 sal_Int32 DatabaseMetaData::getDriverMajorVersion(  )
203 {
204     return PQ_SDBC_MAJOR;
205 }
206 
getDriverMinorVersion()207 sal_Int32 DatabaseMetaData::getDriverMinorVersion(  )
208 {
209     return PQ_SDBC_MINOR;
210 }
211 
usesLocalFiles()212 sal_Bool DatabaseMetaData::usesLocalFiles(  )
213 {
214     // LEM TODO:
215     //           http://wiki.openoffice.org/wiki/Documentation/DevGuide/Database/XDatabaseMetaData_Interface
216     //           says "Returns true when the catalog name of the
217     //           database should not appear in the DatasourceBrowser
218     //           of OpenOffice.org API, otherwise false is returned."
219     //           So, hmmm, think about it.
220     return false;
221 }
222 
usesLocalFilePerTable()223 sal_Bool DatabaseMetaData::usesLocalFilePerTable(  )
224 {
225     return false;
226 }
227 
supportsMixedCaseIdentifiers()228 sal_Bool DatabaseMetaData::supportsMixedCaseIdentifiers(  )
229 {
230     return false;
231 }
232 
storesUpperCaseIdentifiers()233 sal_Bool DatabaseMetaData::storesUpperCaseIdentifiers(  )
234 {
235     return false;
236 }
237 
storesLowerCaseIdentifiers()238 sal_Bool DatabaseMetaData::storesLowerCaseIdentifiers(  )
239 {
240     return true;
241 }
242 
243 
storesMixedCaseIdentifiers()244 sal_Bool DatabaseMetaData::storesMixedCaseIdentifiers(  )
245 {
246     return false;
247 }
248 
249 
supportsMixedCaseQuotedIdentifiers()250 sal_Bool DatabaseMetaData::supportsMixedCaseQuotedIdentifiers(  )
251 {
252     return true;
253 }
254 
storesUpperCaseQuotedIdentifiers()255 sal_Bool DatabaseMetaData::storesUpperCaseQuotedIdentifiers(  )
256 {
257     return false;
258 }
259 
260 
storesLowerCaseQuotedIdentifiers()261 sal_Bool DatabaseMetaData::storesLowerCaseQuotedIdentifiers(  )
262 {
263     return false;
264 }
265 
266 
storesMixedCaseQuotedIdentifiers()267 sal_Bool DatabaseMetaData::storesMixedCaseQuotedIdentifiers(  )
268 {
269     return false;
270 }
271 
272 
getIdentifierQuoteString()273 OUString DatabaseMetaData::getIdentifierQuoteString(  )
274 {
275     return "\"";
276 }
277 
getSQLKeywords()278 OUString DatabaseMetaData::getSQLKeywords(  )
279 {
280     // In Java 6, this is all keywords that are not SQL:2003
281     // In Java 2 v1.4 and as per LibreOffice SDK doc, this is all keywords that are not SQL92
282     // I understand this to mean "reserved keywords" only.
283     // See http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html
284     // LEM TODO: consider using pg_get_keywords(), filter on catcode
285     return
286         "ANALYSE,"
287         "ANALYZE,"
288         "ARRAY," //SQL:1999
289         "ASYMMETRIC," //SQL:2003
290         "BINARY," //SQL:1999
291         "CONCURRENTLY,"
292         "CURRENT_CATALOG," //SQL:2008
293         "CURRENT_ROLE," //SQL:1999
294         "CURRENT_SCHEMA," //SQL:2008
295         "DO,"
296         "FREEZE,"
297         "ILIKE,"
298         "ISNULL,"
299         "LIMIT," //SQL:1999; non-reserved in SQL:2003
300         "LOCALTIME," //SQL:1999
301         "LOCALTIMESTAMP," //SQL:1999
302         "NOTNULL,"
303         "OFFSET," //SQL:2008
304         "OVER," //SQL:2003
305         "PLACING," //non-reserved in SQL:2003
306         "RETURNING," //non-reserved in SQL:2008
307         "SIMILAR," //SQL:2003
308         "VARIADIC,"
309         "VERBOSE,"
310         "WINDOW" //SQL:2003
311  ;
312 }
getNumericFunctions()313 OUString DatabaseMetaData::getNumericFunctions(  )
314 {
315     // See http://www.postgresql.org/docs/9.1/static/functions-math.html
316     // LEM TODO: Err... http://wiki.openoffice.org/wiki/Documentation/DevGuide/Database/Support_Scalar_Functions
317     //           says this should be "Open Group CLI" names, not PostgreSQL names.
318     //           Currently this is just a list of supported functions in PostgreSQL, with PostgreSQL names.
319     //           And it is my job to map from Open Group CLI names/syntax to PostgreSQL names/syntax. Where? By parsing the SQL???
320     //           Should look at what the JDBC driver is doing.
321     return
322         "abs,"
323         "cbrt,"
324         "ceil,"
325         "ceiling,"
326         "degrees,"
327         "div,"
328         "exp,"
329         "floor,"
330         "ln,"
331         "log,"
332         "mod,"
333         "pi,"
334         "power,"
335         "radians,"
336         "random,"
337         "round,"
338         "setseed,"
339         "sign,"
340         "sqrt,"
341         "trunc,"
342         "width_bucket,"
343         "acos,"
344         "asin,"
345         "atan,"
346         "atan2,"
347         "cos,"
348         "cot,"
349         "sin,"
350         "tan"
351  ;
352 }
353 
getStringFunctions()354 OUString DatabaseMetaData::getStringFunctions(  )
355 {
356     // See http://www.postgresql.org/docs/9.1/static/functions-string.html
357     return
358         "bit_length,"
359         "char_length,"
360         "character_length,"
361         "lower,"
362         "octet_length,"
363         "overlay,"
364         "position,"
365         "substring,"
366         "trim,"
367         "upper,"
368         "ascii,"
369         "btrim,"
370         "chr,"
371         "concat,"
372         "concat_ws,"
373         "convert,"
374         "convert_from,"
375         "convert_to,"
376         "decode,"
377         "encode,"
378         "format,"
379         "initcap,"
380         "left,"
381         "length,"
382         "lpad,"
383         "ltrim,"
384         "md5,"
385         "pg_client_encoding,"
386         "quote_ident,"
387         "quote_literal,"
388         "quote_nullable,"
389         "regexp_matches,"
390         "regexp_replace,"
391         "regexp_split_to_array,"
392         "regexp_split_to_table,"
393         "repeat,"
394         "replace,"
395         "reverse,"
396         "right,"
397         "rpad,"
398         "rtrim,"
399         "split_part,"
400         "strpos,"
401         "substr,"
402         "to_ascii,"
403         "to_hex,"
404         "translate"
405  ;
406 }
407 
getSystemFunctions()408 OUString DatabaseMetaData::getSystemFunctions(  )
409 {
410     // See http://www.postgresql.org/docs/9.1/static/functions-info.html
411     // and http://www.postgresql.org/docs/9.1/static/functions-admin.html
412     return
413         "current_catalog,"
414         "current_database,"
415         "current_query,"
416         "current_schema,"
417         "current_schemas,"
418         "current_user,"
419         "inet_client_addr,"
420         "inet_client_port,"
421         "inet_server_addr,"
422         "inet_server_port,"
423         "pg_backend_pid,"
424         "pg_conf_load_time,"
425         "pg_is_other_temp_schema,"
426         "pg_listening_channels,"
427         "pg_my_temp_schema,"
428         "pg_postmaster_start_time,"
429         "session_user,"
430         "user,"
431         "version,"
432         "has_any_column_privilege,"
433         "has_any_column_privilege,"
434         "has_any_column_privilege,"
435         "has_column_privilege,"
436         "has_database_privilege,"
437         "has_foreign_data_wrapper_privilege,"
438         "has_function_privilege,"
439         "has_language_privilege,"
440         "has_schema_privilege,"
441         "has_sequence_privilege,"
442         "has_server_privilege,"
443         "has_table_privilege,"
444         "has_tablespace_privilege,"
445         "pg_has_role,"
446         "pg_collation_is_visible,"
447         "pg_conversion_is_visible,"
448         "pg_function_is_visible,"
449         "pg_opclass_is_visible,"
450         "pg_operator_is_visible,"
451         "pg_table_is_visible,"
452         "pg_ts_config_is_visible,"
453         "pg_ts_dict_is_visible,"
454         "pg_ts_parser_is_visible,"
455         "pg_ts_template_is_visible,"
456         "pg_type_is_visible,"
457         "format_type,"
458         "pg_describe_object,"
459         "pg_get_constraintdef,"
460         "pg_get_expr,"
461         "pg_get_functiondef,"
462         "pg_get_function_arguments,"
463         "pg_get_function_identity_arguments,"
464         "pg_get_function_result,"
465         "pg_get_indexdef,"
466         "pg_get_keywords,"
467         "pg_get_ruledef,"
468         "pg_get_serial_sequence,"
469         "pg_get_triggerdef,"
470         "pg_get_userbyid,"
471         "pg_get_viewdef,"
472         "pg_options_to_table,"
473         "pg_tablespace_databases,"
474         "pg_typeof,"
475         "col_description,"
476         "obj_description,"
477         "shobj_description,"
478         "txid_current,"
479         "txid_current_snapshot,"
480         "txid_snapshot_xip,"
481         "txid_snapshot_xmax,"
482         "txid_snapshot_xmin,"
483         "txid_visible_in_snapshot,"
484         "xmin,"
485         "xmax,"
486         "xip_list,"
487         "current_setting,"
488         "set_config,"
489         "pg_cancel_backend,"
490         "pg_reload_conf,"
491         "pg_rotate_logfile,"
492         "pg_terminate_backend,"
493         "pg_create_restore_point,"
494         "pg_current_xlog_insert_location,"
495         "pg_current_xlog_location,"
496         "pg_start_backup,"
497         "pg_stop_backup,"
498         "pg_switch_xlog,"
499         "pg_xlogfile_name,"
500         "pg_xlogfile_name_offset,"
501         "pg_is_in_recovery,"
502         "pg_last_xlog_receive_location,"
503         "pg_last_xlog_replay_location,"
504         "pg_last_xact_replay_timestamp,"
505         "pg_is_xlog_replay_paused,"
506         "pg_xlog_replay_pause,"
507         "pg_xlog_replay_resume,"
508         "pg_column_size,"
509         "pg_database_size,"
510         "pg_indexes_size,"
511         "pg_relation_size,"
512         "pg_size_pretty,"
513         "pg_table_size,"
514         "pg_tablespace_size,"
515         "pg_tablespace_size,"
516         "pg_total_relation_size,"
517         "pg_relation_filenode,"
518         "pg_relation_filepath,"
519         "pg_ls_dir,"
520         "pg_read_file,"
521         "pg_read_binary_file,"
522         "pg_stat_file,"
523         "pg_advisory_lock,"
524         "pg_advisory_lock_shared,"
525         "pg_advisory_unlock,"
526         "pg_advisory_unlock_all,"
527         "pg_advisory_unlock_shared,"
528         "pg_advisory_xact_lock,"
529         "pg_advisory_xact_lock_shared,"
530         "pg_try_advisory_lock,"
531         "pg_try_advisory_lock_shared,"
532         "pg_try_advisory_xact_lock,"
533         "pg_try_advisory_xact_lock_shared,"
534         "pg_sleep"
535  ;
536 }
getTimeDateFunctions()537 OUString DatabaseMetaData::getTimeDateFunctions(  )
538 {
539     // TODO
540     return
541         "age,"
542         "age,"
543         "clock_timestamp,"
544         "current_date,"
545         "current_time,"
546         "current_timestamp,"
547         "date_part,"
548         "date_part,"
549         "date_trunc,"
550         "extract,"
551         "extract,"
552         "isfinite,"
553         "isfinite,"
554         "isfinite,"
555         "justify_days,"
556         "justify_hours,"
557         "justify_interval,"
558         "localtime,"
559         "localtimestamp,"
560         "now,"
561         "statement_timestamp,"
562         "timeofday,"
563         "transaction_timestamp,"
564  ;
565 }
getSearchStringEscape()566 OUString DatabaseMetaData::getSearchStringEscape(  )
567 {
568     return "\\";
569 }
getExtraNameCharacters()570 OUString DatabaseMetaData::getExtraNameCharacters(  )
571 {
572     return "$";
573 }
574 
supportsAlterTableWithAddColumn()575 sal_Bool DatabaseMetaData::supportsAlterTableWithAddColumn(  )
576 {
577     return true;
578 }
579 
supportsAlterTableWithDropColumn()580 sal_Bool DatabaseMetaData::supportsAlterTableWithDropColumn(  )
581 {
582     return true;
583 }
584 
supportsColumnAliasing()585 sal_Bool DatabaseMetaData::supportsColumnAliasing(  )
586 {
587     return true;
588 }
589 
nullPlusNonNullIsNull()590 sal_Bool DatabaseMetaData::nullPlusNonNullIsNull(  )
591 {
592     return true;
593 }
594 
supportsTypeConversion()595 sal_Bool DatabaseMetaData::supportsTypeConversion(  )
596 {
597     // LEM: this is specifically whether the "CONVERT" function is supported
598     //      It seems that in PostgreSQL, that function is only for string encoding, so no.
599     return false;
600 }
601 
supportsConvert(sal_Int32,sal_Int32)602 sal_Bool DatabaseMetaData::supportsConvert( sal_Int32, sal_Int32 )
603 {
604     return false;
605 }
606 
supportsTableCorrelationNames()607 sal_Bool DatabaseMetaData::supportsTableCorrelationNames(  )
608 {
609     // LEM: A correlation name is "bar" in "SELECT foo FROM qux [AS] bar WHERE ..."
610     return true;
611 }
612 
613 
supportsDifferentTableCorrelationNames()614 sal_Bool DatabaseMetaData::supportsDifferentTableCorrelationNames(  )
615 {
616     return false;
617 }
supportsExpressionsInOrderBy()618 sal_Bool DatabaseMetaData::supportsExpressionsInOrderBy(  )
619 {
620     return true;
621 }
622 
supportsOrderByUnrelated()623 sal_Bool DatabaseMetaData::supportsOrderByUnrelated(  )
624 {
625     return true;
626 }
627 
supportsGroupBy()628 sal_Bool DatabaseMetaData::supportsGroupBy(  )
629 {
630     return true;
631 }
632 
supportsGroupByUnrelated()633 sal_Bool DatabaseMetaData::supportsGroupByUnrelated(  )
634 {
635     return true;
636 }
637 
supportsGroupByBeyondSelect()638 sal_Bool DatabaseMetaData::supportsGroupByBeyondSelect(  )
639 {
640     return true;
641 }
642 
supportsLikeEscapeClause()643 sal_Bool DatabaseMetaData::supportsLikeEscapeClause(  )
644 {
645     return true;
646 }
647 
supportsMultipleResultSets()648 sal_Bool DatabaseMetaData::supportsMultipleResultSets(  )
649 {
650     return true;
651 }
652 
supportsMultipleTransactions()653 sal_Bool DatabaseMetaData::supportsMultipleTransactions(  )
654 {
655     // Allows multiple transactions open at once (on different connections!)
656     return true;
657 }
658 
supportsNonNullableColumns()659 sal_Bool DatabaseMetaData::supportsNonNullableColumns(  )
660 {
661     return true;
662 }
663 
664 
supportsMinimumSQLGrammar()665 sal_Bool DatabaseMetaData::supportsMinimumSQLGrammar(  )
666 {
667     return true;
668 }
669 
supportsCoreSQLGrammar()670 sal_Bool DatabaseMetaData::supportsCoreSQLGrammar(  )
671 {
672     // LEM: jdbc driver says not, although the comments in it seem old
673     //      fdo#45249 Base query design won't use any aggregate function
674     //      (except COUNT(*) unless we say yes, so say yes.
675     //      Actually, Base assumes *also* support for aggregate functions "collect, fusion, intersection"
676     //      as soon as supportsCoreSQLGrammar() returns true.
677     //      Those are *not* Core SQL, though. They are in optional feature S271 "Basic multiset support"
678     return true;
679 }
680 
supportsExtendedSQLGrammar()681 sal_Bool DatabaseMetaData::supportsExtendedSQLGrammar(  )
682 {
683     return false;
684 }
685 
supportsANSI92EntryLevelSQL()686 sal_Bool DatabaseMetaData::supportsANSI92EntryLevelSQL(  )
687 {
688     return true;
689 }
690 
supportsANSI92IntermediateSQL()691 sal_Bool DatabaseMetaData::supportsANSI92IntermediateSQL(  )
692 {
693     // LEM: jdbc driver says not, although the comments in it seem old
694     return false;
695 }
696 
supportsANSI92FullSQL()697 sal_Bool DatabaseMetaData::supportsANSI92FullSQL(  )
698 {
699     // LEM: jdbc driver says not, although the comments in it seem old
700     return false;
701 }
702 
supportsIntegrityEnhancementFacility()703 sal_Bool DatabaseMetaData::supportsIntegrityEnhancementFacility(  )
704 {
705     // LEM: jdbc driver says yes, although comment says they are not sure what this means...
706     return true;
707 }
708 
supportsOuterJoins()709 sal_Bool DatabaseMetaData::supportsOuterJoins(  )
710 {
711     return true;
712 }
713 
supportsFullOuterJoins()714 sal_Bool DatabaseMetaData::supportsFullOuterJoins(  )
715 {
716     return true;
717 }
718 
supportsLimitedOuterJoins()719 sal_Bool DatabaseMetaData::supportsLimitedOuterJoins(  )
720 {
721     return true;
722 }
723 
724 
getSchemaTerm()725 OUString DatabaseMetaData::getSchemaTerm(  )
726 {
727     return "SCHEMA";
728 }
729 
getProcedureTerm()730 OUString DatabaseMetaData::getProcedureTerm(  )
731 {
732     return "function";
733 }
734 
getCatalogTerm()735 OUString DatabaseMetaData::getCatalogTerm(  )
736 {
737     return "DATABASE";
738 }
739 
isCatalogAtStart()740 sal_Bool DatabaseMetaData::isCatalogAtStart(  )
741 {
742     return true;
743 }
744 
getCatalogSeparator()745 OUString DatabaseMetaData::getCatalogSeparator(  )
746 {
747     return ".";
748 }
749 
supportsSchemasInDataManipulation()750 sal_Bool DatabaseMetaData::supportsSchemasInDataManipulation(  )
751 {
752     return true;
753 }
754 
supportsSchemasInProcedureCalls()755 sal_Bool DatabaseMetaData::supportsSchemasInProcedureCalls(  )
756 {
757     return true;
758 }
759 
supportsSchemasInTableDefinitions()760 sal_Bool DatabaseMetaData::supportsSchemasInTableDefinitions(  )
761 {
762     return true;
763 }
764 
supportsSchemasInIndexDefinitions()765 sal_Bool DatabaseMetaData::supportsSchemasInIndexDefinitions(  )
766 {
767     return true;
768 }
769 
supportsSchemasInPrivilegeDefinitions()770 sal_Bool DatabaseMetaData::supportsSchemasInPrivilegeDefinitions(  )
771 {
772     return true;
773 }
774 
supportsCatalogsInDataManipulation()775 sal_Bool DatabaseMetaData::supportsCatalogsInDataManipulation(  )
776 {
777     return false;
778 }
779 
supportsCatalogsInProcedureCalls()780 sal_Bool DatabaseMetaData::supportsCatalogsInProcedureCalls(  )
781 {
782     return false;
783 }
784 
supportsCatalogsInTableDefinitions()785 sal_Bool DatabaseMetaData::supportsCatalogsInTableDefinitions(  )
786 {
787     return false;
788 }
789 
790 
supportsCatalogsInIndexDefinitions()791 sal_Bool DatabaseMetaData::supportsCatalogsInIndexDefinitions(  )
792 {
793     return false;
794 }
795 
796 
supportsCatalogsInPrivilegeDefinitions()797 sal_Bool DatabaseMetaData::supportsCatalogsInPrivilegeDefinitions(  )
798 {
799     return false;
800 }
801 
802 
803 // LEM TODO: positioned (through cursor) updates and deletes seem
804 // to be supported; see {UPDATE,DELETE} /table/ (...) WHERE CURRENT OF /cursor_name/" syntax
805 // and http://www.postgresql.org/docs/9.1/static/view-pg-cursors.html
806 // http://www.postgresql.org/docs/9.1/static/libpq-example.html actually uses a cursor :)
supportsPositionedDelete()807 sal_Bool DatabaseMetaData::supportsPositionedDelete(  )
808 {
809     // LEM: jdbc driver says not, although the comments in it seem old
810     return false;
811 }
812 
supportsPositionedUpdate()813 sal_Bool DatabaseMetaData::supportsPositionedUpdate(  )
814 {
815     // LEM: jdbc driver says not, although the comments in it seem old
816     return false;
817 }
818 
819 
supportsSelectForUpdate()820 sal_Bool DatabaseMetaData::supportsSelectForUpdate(  )
821 {
822     return true;
823 }
824 
825 
supportsStoredProcedures()826 sal_Bool DatabaseMetaData::supportsStoredProcedures(  )
827 {
828     return true;
829 }
830 
831 
supportsSubqueriesInComparisons()832 sal_Bool DatabaseMetaData::supportsSubqueriesInComparisons(  )
833 {
834     return true;
835 }
836 
supportsSubqueriesInExists()837 sal_Bool DatabaseMetaData::supportsSubqueriesInExists(  )
838 {
839     return true;
840 }
841 
supportsSubqueriesInIns()842 sal_Bool DatabaseMetaData::supportsSubqueriesInIns(  )
843 {
844     return true;
845 }
846 
supportsSubqueriesInQuantifieds()847 sal_Bool DatabaseMetaData::supportsSubqueriesInQuantifieds(  )
848 {
849     // LEM: jdbc driver says yes, although comment says they don't know what this means...
850     return true;
851 }
852 
supportsCorrelatedSubqueries()853 sal_Bool DatabaseMetaData::supportsCorrelatedSubqueries(  )
854 {
855     return true;
856 }
supportsUnion()857 sal_Bool DatabaseMetaData::supportsUnion(  )
858 {
859     return true;
860 }
861 
supportsUnionAll()862 sal_Bool DatabaseMetaData::supportsUnionAll(  )
863 {
864     return true;
865 }
866 
supportsOpenCursorsAcrossCommit()867 sal_Bool DatabaseMetaData::supportsOpenCursorsAcrossCommit(  )
868 {
869     return false;
870 }
871 
supportsOpenCursorsAcrossRollback()872 sal_Bool DatabaseMetaData::supportsOpenCursorsAcrossRollback(  )
873 {
874     return false;
875 }
876 
supportsOpenStatementsAcrossCommit()877 sal_Bool DatabaseMetaData::supportsOpenStatementsAcrossCommit(  )
878 {
879     return true;
880 }
supportsOpenStatementsAcrossRollback()881 sal_Bool DatabaseMetaData::supportsOpenStatementsAcrossRollback(  )
882 {
883     return true;
884 }
885 
getMaxBinaryLiteralLength()886 sal_Int32 DatabaseMetaData::getMaxBinaryLiteralLength(  )
887 {
888     return 0;
889 }
890 
getMaxCharLiteralLength()891 sal_Int32 DatabaseMetaData::getMaxCharLiteralLength(  )
892 {
893     return 0;
894 }
895 
896 // Copied / adapted / simplified from JDBC driver
getIntSetting(const OUString & settingName)897 sal_Int32 DatabaseMetaData::getIntSetting(const OUString& settingName)
898 {
899     MutexGuard guard( m_xMutex->GetMutex() );
900 
901     Reference< XParameters > params(m_getIntSetting_stmt, UNO_QUERY_THROW );
902     params->setString(1, settingName );
903     Reference< XResultSet > rs = m_getIntSetting_stmt->executeQuery();
904     Reference< XRow > xRow( rs , UNO_QUERY_THROW );
905     OSL_VERIFY(rs->next());
906     OSL_ENSURE(rs->isFirst(), "postgresql-sdbc DatabaseMetaData getIntSetting not on first row");
907     OSL_ENSURE(rs->isLast(),  "postgresql-sdbc DatabaseMetaData getIntSetting not on last row");
908     return xRow->getInt(1);
909 }
910 
getMaxNameLength()911 sal_Int32 DatabaseMetaData::getMaxNameLength()
912 {
913     if ( m_pSettings->maxNameLen == 0)
914         m_pSettings->maxNameLen = getIntSetting( "max_identifier_length" );
915     OSL_ENSURE(m_pSettings->maxNameLen, "postgresql-sdbc: maxNameLen is zero");
916     return m_pSettings->maxNameLen;
917 }
918 
getMaxIndexKeys()919 sal_Int32 DatabaseMetaData::getMaxIndexKeys()
920 {
921     if ( m_pSettings->maxIndexKeys == 0)
922         m_pSettings->maxIndexKeys = getIntSetting("max_index_keys");
923     OSL_ENSURE(m_pSettings->maxIndexKeys, "postgresql-sdbc: maxIndexKeys is zero");
924     return m_pSettings->maxIndexKeys;
925 }
926 
getMaxColumnNameLength()927 sal_Int32 DatabaseMetaData::getMaxColumnNameLength(  )
928 {
929     return getMaxNameLength();
930 }
931 
getMaxColumnsInGroupBy()932 sal_Int32 DatabaseMetaData::getMaxColumnsInGroupBy(  )
933 {
934     return 0;
935 }
936 
getMaxColumnsInIndex()937 sal_Int32 DatabaseMetaData::getMaxColumnsInIndex(  )
938 {
939     return getMaxIndexKeys();
940 }
941 
getMaxColumnsInOrderBy()942 sal_Int32 DatabaseMetaData::getMaxColumnsInOrderBy(  )
943 {
944     return 0;
945 }
946 
getMaxColumnsInSelect()947 sal_Int32 DatabaseMetaData::getMaxColumnsInSelect(  )
948 {
949     return 0;
950 }
951 
getMaxColumnsInTable()952 sal_Int32 DatabaseMetaData::getMaxColumnsInTable(  )
953 {
954     return 1600;
955 }
956 
getMaxConnections()957 sal_Int32 DatabaseMetaData::getMaxConnections(  )
958 {
959     // LEM: The JDBC driver returns an arbitrary 8192; truth is as much as OS / hardware supports
960     return 0;
961 }
962 
getMaxCursorNameLength()963 sal_Int32 DatabaseMetaData::getMaxCursorNameLength(  ) //TODO, don't know
964 {
965     return getMaxNameLength();
966 }
967 
getMaxIndexLength()968 sal_Int32 DatabaseMetaData::getMaxIndexLength(  ) //TODO, don't know
969 {
970     // LEM: that's the index itself, not its name
971     return 0;
972 }
973 
getMaxSchemaNameLength()974 sal_Int32 DatabaseMetaData::getMaxSchemaNameLength(  )
975 {
976     return getMaxNameLength();
977 }
978 
getMaxProcedureNameLength()979 sal_Int32 DatabaseMetaData::getMaxProcedureNameLength(  )
980 {
981     return getMaxNameLength();
982 }
983 
getMaxCatalogNameLength()984 sal_Int32 DatabaseMetaData::getMaxCatalogNameLength(  )
985 {
986     return getMaxNameLength();
987 }
988 
getMaxRowSize()989 sal_Int32 DatabaseMetaData::getMaxRowSize(  )
990 {
991     // jdbc driver says 1GB, but http://www.postgresql.org/about/ says 1.6TB
992     // and that 1GB is the maximum _field_ size
993     // The row limit does not fit into a sal_Int32
994     return 0;
995 }
996 
doesMaxRowSizeIncludeBlobs()997 sal_Bool DatabaseMetaData::doesMaxRowSizeIncludeBlobs(  )
998 {
999     // LEM: Err... PostgreSQL basically does not do BLOBs well
1000     //      In any case, BLOBs do not change the maximal row length AFAIK
1001     return true;
1002 }
1003 
getMaxStatementLength()1004 sal_Int32 DatabaseMetaData::getMaxStatementLength(  )
1005 {
1006     // LEM: actually, that would be 2^sizeof(size_t)-1
1007     //      on the server? on the client (because of libpq)? minimum of the two? not sure
1008     //      Anyway, big, so say unlimited.
1009     return 0;
1010 }
1011 
getMaxStatements()1012 sal_Int32 DatabaseMetaData::getMaxStatements(  ) //TODO, don't know
1013 {
1014     return 0;
1015 }
1016 
getMaxTableNameLength()1017 sal_Int32 DatabaseMetaData::getMaxTableNameLength(  )
1018 {
1019     return getMaxNameLength();
1020 }
1021 
getMaxTablesInSelect()1022 sal_Int32 DatabaseMetaData::getMaxTablesInSelect(  )
1023 {
1024     return 0;
1025 }
1026 
getMaxUserNameLength()1027 sal_Int32 DatabaseMetaData::getMaxUserNameLength(  )
1028 {
1029     return getMaxNameLength();
1030 }
1031 
getDefaultTransactionIsolation()1032 sal_Int32 DatabaseMetaData::getDefaultTransactionIsolation(  )
1033 {
1034     return css::sdbc::TransactionIsolation::READ_COMMITTED;
1035 }
1036 
supportsTransactions()1037 sal_Bool DatabaseMetaData::supportsTransactions(  )
1038 {
1039     return true;
1040 }
1041 
supportsTransactionIsolationLevel(sal_Int32 level)1042 sal_Bool DatabaseMetaData::supportsTransactionIsolationLevel( sal_Int32 level )
1043 {
1044     if ( level == css::sdbc::TransactionIsolation::READ_COMMITTED
1045          || level == css::sdbc::TransactionIsolation::SERIALIZABLE
1046          || level == css::sdbc::TransactionIsolation::READ_UNCOMMITTED
1047          || level == css::sdbc::TransactionIsolation::REPEATABLE_READ)
1048         return true;
1049     else
1050         return false;
1051 }
1052 
supportsDataDefinitionAndDataManipulationTransactions()1053 sal_Bool DatabaseMetaData::supportsDataDefinitionAndDataManipulationTransactions(  )
1054 {
1055     return true;
1056 }
1057 
supportsDataManipulationTransactionsOnly()1058 sal_Bool DatabaseMetaData::supportsDataManipulationTransactionsOnly(  )
1059 {
1060     return false;
1061 }
1062 
dataDefinitionCausesTransactionCommit()1063 sal_Bool DatabaseMetaData::dataDefinitionCausesTransactionCommit(  )
1064 {
1065     return false;
1066 }
1067 
dataDefinitionIgnoredInTransactions()1068 sal_Bool DatabaseMetaData::dataDefinitionIgnoredInTransactions(  )
1069 {
1070     return false;
1071 }
1072 
getProcedures(const css::uno::Any &,const OUString &,const OUString &)1073 css::uno::Reference< XResultSet > DatabaseMetaData::getProcedures(
1074     const css::uno::Any&,
1075     const OUString&,
1076     const OUString& )
1077 {
1078 //        1.  PROCEDURE_CAT string =&gt; procedure catalog (may be NULL )
1079 //        2. PROCEDURE_SCHEM string =&gt; procedure schema (may be NULL )
1080 //        3. PROCEDURE_NAME string =&gt; procedure name
1081 //        4. reserved for future use
1082 //        5. reserved for future use
1083 //        6. reserved for future use
1084 //        7. REMARKS string =&gt; explanatory comment on the procedure
1085 //        8. PROCEDURE_TYPE short =&gt; kind of procedure:
1086 //               * UNKNOWN - May return a result
1087 //               * NO - Does not return a result
1088 //               * RETURN - Returns a result
1089 
1090 // LEM TODO: implement
1091 // LEM TODO: at least fake the columns, even if no row.
1092     MutexGuard guard( m_xMutex->GetMutex() );
1093     return new SequenceResultSet(
1094         m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > > (), m_pSettings->tc );
1095 }
1096 
getProcedureColumns(const css::uno::Any &,const OUString &,const OUString &,const OUString &)1097 css::uno::Reference< XResultSet > DatabaseMetaData::getProcedureColumns(
1098     const css::uno::Any&,
1099     const OUString&,
1100     const OUString&,
1101     const OUString& )
1102 {
1103     MutexGuard guard( m_xMutex->GetMutex() );
1104 // LEM TODO: implement
1105 // LEM TODO: at least fake the columns, even if no row.
1106     return new SequenceResultSet(
1107         m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc );
1108 }
1109 
getTables(const css::uno::Any &,const OUString & schemaPattern,const OUString & tableNamePattern,const css::uno::Sequence<OUString> &)1110 css::uno::Reference< XResultSet > DatabaseMetaData::getTables(
1111     const css::uno::Any&,
1112     const OUString& schemaPattern,
1113     const OUString& tableNamePattern,
1114     const css::uno::Sequence< OUString >& )
1115 {
1116     Statics &statics = getStatics();
1117 
1118     MutexGuard guard( m_xMutex->GetMutex() );
1119 
1120     if (isLog(m_pSettings, LogLevel::Info))
1121     {
1122         log(m_pSettings, LogLevel::Info,
1123             ("DatabaseMetaData::getTables got called with " + schemaPattern + "."
1124              + tableNamePattern));
1125     }
1126     // ignore catalog, as a single pq connection does not support multiple catalogs
1127 
1128     // LEM TODO: this does not give the right column names, not the right number of columns, etc.
1129     // Take "inspiration" from JDBC driver
1130     // Ah, this is used to create a XResultSet manually... Try to do it directly in SQL
1131     Reference< XPreparedStatement > statement = m_origin->prepareStatement(
1132             "SELECT "
1133             "DISTINCT ON (pg_namespace.nspname, relname ) " // avoid duplicates (pg_settings !)
1134             "pg_namespace.nspname, relname, relkind, pg_description.description "
1135             "FROM pg_namespace, pg_class LEFT JOIN pg_description ON pg_class.oid = pg_description.objoid "
1136             "WHERE relnamespace = pg_namespace.oid "
1137             "AND ( relkind = 'r' OR relkind = 'v') "
1138             "AND pg_namespace.nspname LIKE ? "
1139             "AND relname LIKE ? "
1140 //            "ORDER BY pg_namespace.nspname || relname"
1141             );
1142 
1143     Reference< XParameters > parameters( statement, UNO_QUERY_THROW );
1144     parameters->setString( 1 , schemaPattern );
1145     parameters->setString( 2 , tableNamePattern );
1146 
1147     Reference< XResultSet > rs = statement->executeQuery();
1148     Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1149     std::vector< std::vector<Any> > vec;
1150 
1151     while( rs->next() )
1152     {
1153         std::vector< Any > row( 5 );
1154 
1155         row[0] <<= m_pSettings->catalog;
1156         row[1] <<= xRow->getString( 1 );
1157         row[2] <<= xRow->getString( 2 );
1158         OUString type = xRow->getString(3);
1159         if( type == "r" )
1160         {
1161             if( xRow->getString(1) == "pg_catalog" )
1162             {
1163                 row[3] <<= statics.SYSTEM_TABLE;
1164             }
1165             else
1166             {
1167                 row[3] <<= statics.TABLE;
1168             }
1169         }
1170         else if( type == "v" )
1171         {
1172             row[3] <<= statics.VIEW;
1173         }
1174         else
1175         {
1176             row[3] <<= statics.UNKNOWN;
1177         }
1178         row[4] <<= xRow->getString(4);
1179 
1180         // no description in postgresql AFAIK
1181         vec.push_back( row );
1182     }
1183     Reference< XCloseable > closeable( statement, UNO_QUERY );
1184     if( closeable.is() )
1185         closeable->close();
1186 
1187     return new SequenceResultSet(
1188         m_xMutex, *this, statics.tablesRowNames, vec, m_pSettings->tc );
1189 }
1190 
1191 namespace
1192 {
1193     // sort no schema first, then "public", then normal schemas, then internal schemas
compare_schema(const OUString & nsA,const OUString & nsB)1194     int compare_schema(const OUString &nsA, const OUString &nsB)
1195     {
1196         if (nsA.isEmpty())
1197         {
1198             return nsB.isEmpty() ? 0 : -1;
1199         }
1200         else if (nsB.isEmpty())
1201         {
1202             assert(!nsA.isEmpty());
1203             return 1;
1204         }
1205         else if(nsA == "public")
1206         {
1207             return (nsB == "public") ? 0 : -1;
1208         }
1209         else if(nsB == "public")
1210         {
1211             assert(nsA != "public");
1212             return 1;
1213         }
1214         else if(nsA.startsWith("pg_"))
1215         {
1216             if(nsB.startsWith("pg_"))
1217                 return nsA.compareTo(nsB);
1218             else
1219                 return 1;
1220         }
1221         else if(nsB.startsWith("pg_"))
1222         {
1223             return -1;
1224         }
1225         else
1226         {
1227             return nsA.compareTo(nsB);
1228         }
1229     }
1230 
1231     struct SortInternalSchemasLastAndPublicFirst
1232     {
operator ()pq_sdbc_driver::__anon4e7f8dfb0111::SortInternalSchemasLastAndPublicFirst1233         bool operator () ( const std::vector< Any >  & a, const std::vector< Any >  & b )
1234         {
1235             OUString valueA;
1236             OUString valueB;
1237             a[0] >>= valueA;
1238             b[0] >>= valueB;
1239             return compare_schema(valueA, valueB);
1240         }
1241     };
1242 }
1243 
getSchemas()1244 css::uno::Reference< XResultSet > DatabaseMetaData::getSchemas(  )
1245 {
1246     MutexGuard guard( m_xMutex->GetMutex() );
1247 
1248     if (isLog(m_pSettings, LogLevel::Info))
1249     {
1250         log(m_pSettings, LogLevel::Info, "DatabaseMetaData::getSchemas() got called");
1251     }
1252     // <b>TABLE_SCHEM</b> string =&amp;gt; schema name
1253     Reference< XStatement > statement = m_origin->createStatement();
1254     Reference< XResultSet > rs = statement->executeQuery(
1255         "SELECT nspname from pg_namespace" );
1256     // LEM TODO: look at JDBC driver and consider doing the same
1257     //           in particular, excluding temporary schemas, but maybe better through pg_is_other_temp_schema(oid) OR  == pg_my_temp_schema()
1258 
1259     Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1260     std::vector< std::vector<Any> > vec;
1261     while( rs->next() )
1262     {
1263         std::vector<Any> row(1);
1264         row[0] <<= xRow->getString(1);
1265         vec.push_back( row );
1266     }
1267 
1268     // sort public first, sort internal schemas last, sort rest in alphabetic order
1269     std::sort( vec.begin(), vec.end(), SortInternalSchemasLastAndPublicFirst() );
1270 
1271     Reference< XCloseable > closeable( statement, UNO_QUERY );
1272     if( closeable.is() )
1273         closeable->close();
1274     return new SequenceResultSet(
1275         m_xMutex, *this, getStatics().schemaNames, vec, m_pSettings->tc );
1276 }
1277 
getCatalogs()1278 css::uno::Reference< XResultSet > DatabaseMetaData::getCatalogs(  )
1279 {
1280     // LEM TODO: return the current catalog like JDBC driver?
1281     //           at least fake the columns, even if no content
1282     MutexGuard guard( m_xMutex->GetMutex() );
1283     return new SequenceResultSet(
1284         m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc );
1285 }
1286 
getTableTypes()1287 css::uno::Reference< XResultSet > DatabaseMetaData::getTableTypes(  )
1288 {
1289     // LEM TODO: this can be made dynamic, see JDBC driver
1290     MutexGuard guard( m_xMutex->GetMutex() );
1291     return new SequenceResultSet(
1292         m_xMutex, *this, getStatics().tableTypeNames, getStatics().tableTypeData,
1293         m_pSettings->tc );
1294 }
1295 
1296 
1297 /** returns the constant from sdbc.DataType
1298  */
typeNameToDataType(const OUString & typeName,const OUString & typtype)1299 sal_Int32 typeNameToDataType( const OUString &typeName, const OUString &typtype )
1300 {
1301 //     sal_Int32 ret = css::sdbc::DataType::DISTINCT;
1302     // map all unknown types to memo (longvarchar). This allows to show them in
1303     // string representation. Additionally, the edit-table-type-selection-box
1304     // is not so unusable anymore.
1305     sal_Int32 ret = css::sdbc::DataType::LONGVARCHAR;
1306     if( typtype == "b" )
1307     {
1308         // as long as the OOo framework does not support arrays,
1309         // the user is better of with interpreting arrays as strings !
1310 //         if( typeName.getLength() && '_' == typeName[0] )
1311 //         {
1312 //             it's just a naming convention, but as long as we don't have anything better,
1313 //             we take it as granted
1314 //             ret = css::sdbc::DataType::ARRAY;
1315 //         }
1316         // base type
1317         Statics &statics = getStatics();
1318         BaseTypeMap::const_iterator ii = statics.baseTypeMap.find( typeName );
1319         if( ii != statics.baseTypeMap.end() )
1320         {
1321             ret = ii->second;
1322         }
1323     }
1324     else if( typtype == "c" )
1325     {
1326         ret = css::sdbc::DataType::STRUCT;
1327     }
1328     else if( typtype == "d" )
1329     {
1330         ret = css::sdbc::DataType::LONGVARCHAR;
1331     }
1332     return ret;
1333 }
1334 
1335 namespace {
isSystemColumn(sal_Int16 attnum)1336     bool isSystemColumn( sal_Int16 attnum )
1337     {
1338         return attnum <= 0;
1339     }
1340 
1341     // is not exported by the postgres header
1342     const int PQ_VARHDRSZ = sizeof( sal_Int32 );
1343 
1344     // Oh, quelle horreur
1345     // LEM TODO: Need to severely rewrite that!
1346     // should probably just "do the same" as ODBC or JDBC drivers...
extractPrecisionAndScale(sal_Int32 dataType,sal_Int32 atttypmod,sal_Int32 * precision,sal_Int32 * scale)1347     void extractPrecisionAndScale(
1348         sal_Int32 dataType, sal_Int32 atttypmod, sal_Int32 *precision, sal_Int32 *scale )
1349     {
1350         if( atttypmod < PQ_VARHDRSZ )
1351         {
1352             *precision = 0;
1353             *scale = 0;
1354         }
1355         else
1356         {
1357             switch( dataType )
1358             {
1359             case css::sdbc::DataType::NUMERIC:
1360             case css::sdbc::DataType::DECIMAL:
1361             {
1362                 *precision = ( ( atttypmod - PQ_VARHDRSZ ) >> 16 ) & 0xffff;
1363                 *scale = (atttypmod - PQ_VARHDRSZ ) & 0xffff;
1364                 break;
1365             }
1366             default:
1367                 *precision = atttypmod - PQ_VARHDRSZ;
1368                 *scale = 0;
1369             }
1370         }
1371     }
1372 
1373     struct DatabaseTypeDescription
1374     {
DatabaseTypeDescriptionpq_sdbc_driver::__anon4e7f8dfb0211::DatabaseTypeDescription1375         DatabaseTypeDescription()
1376         {}
DatabaseTypeDescriptionpq_sdbc_driver::__anon4e7f8dfb0211::DatabaseTypeDescription1377         DatabaseTypeDescription( const OUString &name, const OUString & type ) :
1378             typeName( name ),
1379             typeType( type )
1380         {}
DatabaseTypeDescriptionpq_sdbc_driver::__anon4e7f8dfb0211::DatabaseTypeDescription1381         DatabaseTypeDescription( const DatabaseTypeDescription &source ) :
1382             typeName( source.typeName ),
1383             typeType( source.typeType )
1384         {}
operator =pq_sdbc_driver::__anon4e7f8dfb0211::DatabaseTypeDescription1385         DatabaseTypeDescription & operator = ( const DatabaseTypeDescription & source )
1386         {
1387             typeName = source.typeName;
1388             typeType = source.typeType;
1389             return *this;
1390         }
1391         OUString typeName;
1392         OUString typeType;
1393     };
1394 }
1395 
1396 typedef std::unordered_map
1397 <
1398     sal_Int32,
1399     DatabaseTypeDescription
1400 > Oid2DatabaseTypeDescriptionMap;
1401 
columnMetaData2DatabaseTypeDescription(Oid2DatabaseTypeDescriptionMap & oidMap,const Reference<XResultSet> & rs,const Reference<XStatement> & stmt)1402 static void columnMetaData2DatabaseTypeDescription(
1403     Oid2DatabaseTypeDescriptionMap &oidMap,
1404     const Reference< XResultSet > &rs,
1405     const Reference< XStatement > &stmt )
1406 {
1407     Reference< XRow > row( rs, UNO_QUERY_THROW );
1408     int domains = 0;
1409     OUStringBuffer queryBuf(128);
1410     queryBuf.append( "SELECT oid,typtype,typname FROM pg_TYPE WHERE " );
1411     while( rs->next() )
1412     {
1413         if( row->getString( 9 ) == "d" && oidMap.find( row->getInt( 12 ) ) == oidMap.end() )
1414         {
1415             oidMap[row->getInt(12)] = DatabaseTypeDescription();
1416             if( domains )
1417                 queryBuf.append( " OR " );
1418             queryBuf.append( "oid = " );
1419             queryBuf.append( row->getInt(12 ) );
1420             domains ++;
1421         }
1422     }
1423     rs->beforeFirst();
1424 
1425     if( domains )
1426     {
1427         Reference< XResultSet > rsDomain = stmt->executeQuery( queryBuf.makeStringAndClear() );
1428         row.set( rsDomain, UNO_QUERY_THROW );
1429         while( rsDomain->next() )
1430         {
1431             oidMap[row->getInt(1)] = DatabaseTypeDescription(row->getString(3), row->getString(2) );
1432         }
1433         disposeNoThrow( stmt );
1434     }
1435 
1436 }
1437 
1438 
getColumns(const css::uno::Any &,const OUString & schemaPattern,const OUString & tableNamePattern,const OUString & columnNamePattern)1439 css::uno::Reference< XResultSet > DatabaseMetaData::getColumns(
1440     const css::uno::Any&,
1441     const OUString& schemaPattern,
1442     const OUString& tableNamePattern,
1443     const OUString& columnNamePattern )
1444 {
1445     // LEM TODO: review in comparison with JDBC driver
1446     Statics &statics = getStatics();
1447 
1448     // continue !
1449     MutexGuard guard( m_xMutex->GetMutex() );
1450 
1451     if (isLog(m_pSettings, LogLevel::Info))
1452     {
1453         log(m_pSettings, LogLevel::Info,
1454             ("DatabaseMetaData::getColumns got called with " + schemaPattern + "."
1455              + tableNamePattern + "." + columnNamePattern));
1456     }
1457 
1458     // ignore catalog, as a single pq connection
1459     // does not support multiple catalogs anyway
1460     // We don't use information_schema.columns because it contains
1461     // only the columns the current user has any privilege over.
1462 
1463     //  1. TABLE_CAT string => table catalog (may be NULL)
1464     //               => not supported
1465     //  2. TABLE_SCHEM string => table schema (may be NULL)
1466     //               => pg_namespace.nspname
1467     //  3. TABLE_NAME string => table name
1468     //               => pg_class.relname
1469     //  4. COLUMN_NAME string => column name
1470     //               => pg_attribute.attname
1471     //  5. DATA_TYPE short => SQL type from java.sql.Types
1472     //               => pg_type.typname => sdbc.DataType
1473     //  6. TYPE_NAME string => Data source dependent type name, for a UDT the
1474     //                         type name is fully qualified
1475     //               => pg_type.typname
1476     //  7. COLUMN_SIZE long => column size. For char or date types this is
1477     //                         the maximum number of characters, for numeric
1478     //                         or decimal types this is precision.
1479     //               => pg_attribute.atttypmod
1480     //  8. BUFFER_LENGTH is not used.
1481     //               => not used
1482     //  9. DECIMAL_DIGITS long => the number of fractional digits
1483     //               => don't know ! TODO !
1484     //  10. NUM_PREC_RADIX long => Radix (typically either 10 or 2)
1485     //               => TODO ??
1486     //  11. NULLABLE long => is NULL allowed?
1487     //                      NO_NULLS - might not allow NULL values
1488     //                      NULABLE - definitely allows NULL values
1489     //                      NULLABLE_UNKNOWN - nullability unknown
1490     //               => pg_attribute.attnotnull
1491     //  12. REMARKS string => comment describing column (may be NULL )
1492     //               => pg_description.description
1493     //  13. COLUMN_DEF string => default value (may be NULL)
1494     //               => pg_type.typdefault
1495     //  14. SQL_DATA_TYPE long => unused
1496     //               => empty
1497     //  15. SQL_DATETIME_SUB long => unused
1498     //               => empty
1499     //  16. CHAR_OCTET_LENGTH long => for char types the maximum number of
1500     //                                bytes in the column
1501     //               => pg_type.typlen
1502     //  17. ORDINAL_POSITION int => index of column in table (starting at 1)
1503     //                              pg_attribute.attnum
1504     //  18. IS_NULLABLE string => "NO" means column definitely does not allow
1505     //                            NULL values; "YES" means the column might
1506     //                            allow NULL values. An empty string means
1507     //                            nobody knows.
1508     //               => pg_attribute.attnotnull
1509     OUString strDefaultValue = getColExprForDefaultSettingVal(m_pSettings);
1510     Reference< XPreparedStatement > statement = m_origin->prepareStatement(
1511             "SELECT pg_namespace.nspname, "  // 1
1512             "pg_class.relname, "             // 2
1513             "pg_attribute.attname, "         // 3
1514             "pg_type.typname, "              // 4
1515             "pg_attribute.atttypmod, "       // 5
1516             "pg_attribute.attnotnull, "      // 6
1517             "pg_type.typdefault, "           // 7
1518             "pg_type.typtype, "              // 8
1519             + strDefaultValue +              // 9
1520             ",pg_description.description, "   // 10
1521             "pg_type.typbasetype, "          // 11
1522             "pg_attribute.attnum "           // 12
1523             "FROM pg_class, "
1524                  "pg_attribute LEFT JOIN pg_attrdef ON pg_attribute.attrelid = pg_attrdef.adrelid AND pg_attribute.attnum = pg_attrdef.adnum "
1525                               "LEFT JOIN pg_description ON pg_attribute.attrelid = pg_description.objoid AND pg_attribute.attnum=pg_description.objsubid,"
1526                  " pg_type, pg_namespace "
1527             "WHERE pg_attribute.attrelid = pg_class.oid "
1528                    "AND pg_attribute.atttypid = pg_type.oid "
1529                    "AND pg_class.relnamespace = pg_namespace.oid "
1530                    "AND NOT pg_attribute.attisdropped "
1531                    "AND pg_namespace.nspname LIKE ? "
1532                    "AND pg_class.relname LIKE ? "
1533                    "AND pg_attribute.attname LIKE ? "
1534             "ORDER BY pg_namespace.nspname, pg_class.relname, pg_attribute.attnum"
1535             );
1536 
1537     Reference< XParameters > parameters( statement, UNO_QUERY_THROW );
1538     parameters->setString( 1 , schemaPattern );
1539     parameters->setString( 2 , tableNamePattern );
1540     parameters->setString( 3 , columnNamePattern );
1541 
1542     Reference< XResultSet > rs = statement->executeQuery();
1543     Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1544     std::vector< std::vector<Any> > vec;
1545 
1546     Oid2DatabaseTypeDescriptionMap domainMap;
1547     Reference< XStatement > domainTypeStmt = m_origin->createStatement();
1548     columnMetaData2DatabaseTypeDescription( domainMap, rs, domainTypeStmt );
1549 
1550     sal_uInt32 colNum(0);
1551     OUString sSchema( "#invalid#" );
1552     OUString sTable(  "#invalid#" );
1553 
1554     while( rs->next() )
1555     {
1556         if( ! isSystemColumn( xRow->getShort( 12 ) ) )
1557         {
1558             OUString sNewSchema( xRow->getString(1) );
1559             OUString sNewTable(  xRow->getString(2) );
1560             if ( sNewSchema != sSchema || sNewTable != sTable )
1561             {
1562                 colNum = 1;
1563                 sSchema = sNewSchema;
1564                 sTable = sNewTable;
1565             }
1566             else
1567                 ++colNum;
1568             sal_Int32 precision, scale, type;
1569             std::vector< Any > row( 18 );
1570             row[0] <<= m_pSettings->catalog;
1571             row[1] <<= sNewSchema;
1572             row[2] <<= sNewTable;
1573             row[3] <<= xRow->getString(3);
1574             if( xRow->getString(8) == "d" )
1575             {
1576                 DatabaseTypeDescription desc( domainMap[xRow->getInt(11)] );
1577                 type = typeNameToDataType( desc.typeName, desc.typeType );
1578             }
1579             else
1580             {
1581                 type = typeNameToDataType( xRow->getString(4), xRow->getString(8) );
1582             }
1583             extractPrecisionAndScale( type, xRow->getInt(5) , &precision, &scale );
1584             row[4] <<= type;
1585             row[5] <<= xRow->getString(4);
1586             row[6] <<= precision;
1587             // row[7] BUFFER_LENGTH not used
1588             row[8] <<= scale;
1589             // row[9] RADIX TODO
1590             if( xRow->getBoolean( 6 ) && ! isSystemColumn(xRow->getInt( 12 )) )
1591             {
1592                 row[10] <<= OUString::number(css::sdbc::ColumnValue::NO_NULLS);
1593                 row[17] <<= statics.NO;
1594             }
1595             else
1596             {
1597                 row[10] <<= OUString::number(css::sdbc::ColumnValue::NULLABLE);
1598                 row[17] <<= statics.YES;
1599             }
1600 
1601             row[11] <<= xRow->getString( 10 ); // comment
1602             row[12] <<= xRow->getString( 9 ); // COLUMN_DEF = pg_type.typdefault
1603             // row[13] SQL_DATA_TYPE    not used
1604             // row[14] SQL_DATETIME_SUB not used
1605             row[15] <<= precision;
1606             row[16] <<= colNum ;
1607 
1608             vec.push_back( row );
1609         }
1610     }
1611     Reference< XCloseable > closeable( statement, UNO_QUERY );
1612     if( closeable.is() )
1613         closeable->close();
1614 
1615     return new SequenceResultSet(
1616         m_xMutex, *this, statics.columnRowNames, vec, m_pSettings->tc );
1617 }
1618 
getColumnPrivileges(const css::uno::Any &,const OUString & schema,const OUString & table,const OUString & columnNamePattern)1619 css::uno::Reference< XResultSet > DatabaseMetaData::getColumnPrivileges(
1620     const css::uno::Any&,
1621     const OUString& schema,
1622     const OUString& table,
1623     const OUString& columnNamePattern )
1624 {
1625     MutexGuard guard( m_xMutex->GetMutex() );
1626 
1627     if (isLog(m_pSettings, LogLevel::Info))
1628     {
1629         log(m_pSettings, LogLevel::Info,
1630             ("DatabaseMetaData::getColumnPrivileges got called with " + schema + "." + table + "."
1631              + columnNamePattern));
1632     }
1633 
1634     Reference< XParameters > parameters( m_getColumnPrivs_stmt, UNO_QUERY_THROW );
1635     parameters->setString( 1 , schema );
1636     parameters->setString( 2 , table );
1637     parameters->setString( 3 , columnNamePattern );
1638 
1639     Reference< XResultSet > rs = m_getColumnPrivs_stmt->executeQuery();
1640 
1641     return rs;
1642 }
1643 
getTablePrivileges(const css::uno::Any &,const OUString & schemaPattern,const OUString & tableNamePattern)1644 css::uno::Reference< XResultSet > DatabaseMetaData::getTablePrivileges(
1645     const css::uno::Any&,
1646     const OUString& schemaPattern,
1647     const OUString& tableNamePattern )
1648 {
1649     MutexGuard guard( m_xMutex->GetMutex() );
1650 
1651     if (isLog(m_pSettings, LogLevel::Info))
1652     {
1653         log(m_pSettings, LogLevel::Info,
1654             ("DatabaseMetaData::getTablePrivileges got called with " + schemaPattern + "."
1655              + tableNamePattern));
1656     }
1657 
1658     Reference< XParameters > parameters( m_getTablePrivs_stmt, UNO_QUERY_THROW );
1659     parameters->setString( 1 , schemaPattern );
1660     parameters->setString( 2 , tableNamePattern );
1661 
1662     Reference< XResultSet > rs = m_getTablePrivs_stmt->executeQuery();
1663 
1664     return rs;
1665 }
1666 
getBestRowIdentifier(const css::uno::Any &,const OUString &,const OUString &,sal_Int32,sal_Bool)1667 css::uno::Reference< XResultSet > DatabaseMetaData::getBestRowIdentifier(
1668     const css::uno::Any&,
1669     const OUString&,
1670     const OUString&,
1671     sal_Int32,
1672     sal_Bool )
1673 {
1674     //LEM TODO: implement! See JDBC driver
1675     MutexGuard guard( m_xMutex->GetMutex() );
1676     return new SequenceResultSet(
1677         m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc );
1678 }
1679 
getVersionColumns(const css::uno::Any &,const OUString &,const OUString &)1680 css::uno::Reference< XResultSet > DatabaseMetaData::getVersionColumns(
1681     const css::uno::Any&,
1682     const OUString&,
1683     const OUString& )
1684 {
1685     //LEM TODO: implement! See JDBC driver
1686     MutexGuard guard( m_xMutex->GetMutex() );
1687     return new SequenceResultSet(
1688         m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc );
1689 }
1690 
getPrimaryKeys(const css::uno::Any &,const OUString & schema,const OUString & table)1691 css::uno::Reference< XResultSet > DatabaseMetaData::getPrimaryKeys(
1692     const css::uno::Any&,
1693     const OUString& schema,
1694     const OUString& table )
1695 {
1696     //LEM TODO: review
1697     MutexGuard guard( m_xMutex->GetMutex() );
1698 
1699 //        1.  TABLE_CAT string =&gt; table catalog (may be NULL )
1700 //        2. TABLE_SCHEM string =&gt; table schema (may be NULL )
1701 //        3. TABLE_NAME string =&gt; table name
1702 //        4. COLUMN_NAME string =&gt; column name
1703 //        5. KEY_SEQ short =&gt; sequence number within primary key
1704 //        6. PK_NAME string =&gt; primary key name (may be NULL )
1705 
1706     if (isLog(m_pSettings, LogLevel::Info))
1707     {
1708         log(m_pSettings, LogLevel::Info,
1709             "DatabaseMetaData::getPrimaryKeys got called with " + schema + "." + table);
1710     }
1711 
1712     Reference< XPreparedStatement > statement = m_origin->prepareStatement(
1713             "SELECT nmsp.nspname, "
1714                     "cl.relname, "
1715                     "con.conkey, "
1716                     "con.conname, "
1717                     "con.conrelid "
1718             "FROM pg_constraint as con,pg_class as cl, pg_namespace as nmsp "
1719             "WHERE con.connamespace = nmsp.oid AND con.conrelid = cl.oid AND con.contype = 'p' "
1720                 "AND nmsp.nspname LIKE ? AND cl.relname LIKE ?" );
1721 
1722     Reference< XParameters > parameters( statement, UNO_QUERY_THROW );
1723     parameters->setString( 1 , schema );
1724     parameters->setString( 2 , table );
1725 
1726     Reference< XResultSet > rs = statement->executeQuery();
1727     Reference< XRow > xRow( rs, UNO_QUERY_THROW );
1728     std::vector< std::vector<Any> > vec;
1729 
1730     while( rs->next() )
1731     {
1732         std::vector< Any > row( 6 );
1733         row[0] <<= m_pSettings->catalog;
1734         row[1] <<= xRow->getString(1);
1735         row[2] <<= xRow->getString(2);
1736         OUString array = xRow->getString(3);
1737         row[4] <<= xRow->getString(5); // the relid
1738         row[5] <<= xRow->getString(4);
1739 
1740         int i = 0;
1741         // now retrieve the columns information
1742         // unfortunately, postgresql does not allow array of variable size in
1743         // WHERE clauses (in the default installation), so we have to choose
1744         // this expensive and somewhat ugly way
1745         // annotation: postgresql shouldn't have chosen an array here, instead they
1746         //             should have multiple rows per table
1747         // LEM: to transform an array into several rows, see unnest;
1748         //      it is as simple as "SELECT foo, bar, unnest(qux) FROM ..."
1749         //      where qux is the column that contains an array.
1750         while( array[i] && '}' != array[i] )
1751         {
1752             i++;
1753             int start = i;
1754             while( array[i] && array[i] != '}' && array[i] != ',' ) i++;
1755             row[3] <<= array.copy(start, i - start );
1756             vec.push_back( row );
1757         }
1758     }
1759 
1760     {
1761         Reference< XCloseable > closeable( statement, UNO_QUERY );
1762         if( closeable.is() )
1763             closeable->close();
1764     }
1765 
1766 
1767     OUString lastTableOid;
1768     sal_Int32 index = 0;
1769     std::vector< std::vector< Any > > ret( vec.size() );
1770     int elements = 0;
1771     for (auto const& elem : vec)
1772     {
1773 
1774         std::vector< Any > row = elem;
1775         OUString tableOid;
1776         OUString attnum;
1777 
1778         row[4] >>= tableOid;
1779         row[3] >>= attnum;
1780         statement = m_origin->prepareStatement(
1781                 "SELECT att.attname FROM "
1782                 "pg_attribute AS att, pg_class AS cl WHERE "
1783                 "att.attrelid = ? AND att.attnum = ?" );
1784 
1785         parameters.set( statement, UNO_QUERY_THROW );
1786         parameters->setString( 1 , tableOid );
1787         parameters->setString( 2 , attnum );
1788 
1789         rs = statement->executeQuery();
1790         xRow.set( rs, UNO_QUERY_THROW );
1791         if( rs->next() )
1792         {
1793             // column name
1794             row[3] <<= xRow->getString( 1 );
1795             if( tableOid != lastTableOid )
1796                 index = 1;
1797             lastTableOid = tableOid;
1798             row[4] <<= OUString::number( index );
1799             index ++;
1800         }
1801         {
1802             Reference< XCloseable > closeable( statement, UNO_QUERY );
1803             if( closeable.is() )
1804                 closeable->close();
1805         }
1806         ret[elements] = row;
1807         elements ++;
1808     }
1809     return new SequenceResultSet(
1810         m_xMutex, *this, getStatics().primaryKeyNames, ret, m_pSettings->tc );
1811 }
1812 
1813 // Copied / adapted / simplified from JDBC driver
1814 #define SQL_CASE_KEYRULE "  WHEN 'c' THEN " STRINGIFY(KEYRULE_CASCADE) \
1815                          "  WHEN 'n' THEN " STRINGIFY(KEYRULE_SET_NULL) \
1816                          "  WHEN 'd' THEN " STRINGIFY(KEYRULE_SET_DEFAULT) \
1817                          "  WHEN 'r' THEN " STRINGIFY(KEYRULE_RESTRICT) \
1818                          "  WHEN 'a' THEN " STRINGIFY(KEYRULE_NO_ACTION) \
1819                          "  ELSE NULL "
1820 
1821 #define SQL_GET_REFERENCES \
1822     "WITH con AS (SELECT oid, conname, contype, condeferrable, condeferred, conrelid, confrelid,  confupdtype, confdeltype, generate_subscripts(conkey,1) AS conkeyseq, unnest(conkey) AS conkey , unnest(confkey) AS confkey FROM pg_catalog.pg_constraint) " \
1823     "SELECT NULL::text AS PKTABLE_CAT, pkn.nspname AS PKTABLE_SCHEM, pkc.relname AS PKTABLE_NAME, pka.attname AS PKCOLUMN_NAME, " \
1824     " NULL::text AS FKTABLE_CAT, fkn.nspname AS FKTABLE_SCHEM, fkc.relname AS FKTABLE_NAME, fka.attname AS FKCOLUMN_NAME, " \
1825     " con.conkeyseq AS KEY_SEQ, " \
1826     " CASE con.confupdtype " \
1827     SQL_CASE_KEYRULE \
1828     " END AS UPDATE_RULE, " \
1829     " CASE con.confdeltype " \
1830     SQL_CASE_KEYRULE \
1831     " END AS DELETE_RULE, " \
1832     " con.conname AS FK_NAME, pkic.relname AS PK_NAME, " \
1833     " CASE " \
1834     "  WHEN con.condeferrable AND con.condeferred THEN " STRINGIFY(DEFERRABILITY_INITIALLY_DEFERRED) \
1835     "  WHEN con.condeferrable THEN " STRINGIFY(DEFERRABILITY_INITIALLY_IMMEDIATE) \
1836     "  ELSE " STRINGIFY(DEFERRABILITY_NONE) \
1837     " END AS DEFERRABILITY " \
1838     "FROM " \
1839     " pg_catalog.pg_namespace pkn, pg_catalog.pg_class pkc, pg_catalog.pg_attribute pka, " \
1840     " pg_catalog.pg_namespace fkn, pg_catalog.pg_class fkc, pg_catalog.pg_attribute fka, " \
1841     " con, pg_catalog.pg_depend dep, pg_catalog.pg_class pkic " \
1842     "WHERE pkn.oid = pkc.relnamespace AND pkc.oid = pka.attrelid AND pka.attnum = con.confkey AND con.confrelid = pkc.oid " \
1843     " AND  fkn.oid = fkc.relnamespace AND fkc.oid = fka.attrelid AND fka.attnum = con.conkey  AND con.conrelid  = fkc.oid " \
1844     " AND con.contype = 'f' AND con.oid = dep.objid AND pkic.oid = dep.refobjid AND pkic.relkind = 'i' AND dep.classid = 'pg_constraint'::regclass::oid AND dep.refclassid = 'pg_class'::regclass::oid "
1845 
1846 #define SQL_GET_REFERENCES_PSCHEMA " AND pkn.nspname = ? "
1847 #define SQL_GET_REFERENCES_PTABLE  " AND pkc.relname = ? "
1848 #define SQL_GET_REFERENCES_FSCHEMA " AND fkn.nspname = ? "
1849 #define SQL_GET_REFERENCES_FTABLE  " AND fkc.relname = ? "
1850 #define SQL_GET_REFERENCES_ORDER_SOME_PTABLE "ORDER BY fkn.nspname, fkc.relname, conkeyseq"
1851 #define SQL_GET_REFERENCES_ORDER_NO_PTABLE   "ORDER BY pkn.nspname, pkc.relname, conkeyseq"
1852 
1853 #define SQL_GET_REFERENCES_NONE_NONE_NONE_NONE \
1854     SQL_GET_REFERENCES \
1855     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1856 
1857 #define SQL_GET_REFERENCES_SOME_NONE_NONE_NONE \
1858     SQL_GET_REFERENCES \
1859     SQL_GET_REFERENCES_PSCHEMA \
1860     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1861 
1862 #define SQL_GET_REFERENCES_NONE_SOME_NONE_NONE \
1863     SQL_GET_REFERENCES \
1864     SQL_GET_REFERENCES_PTABLE \
1865     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1866 
1867 #define SQL_GET_REFERENCES_SOME_SOME_NONE_NONE \
1868     SQL_GET_REFERENCES \
1869     SQL_GET_REFERENCES_PSCHEMA \
1870     SQL_GET_REFERENCES_PTABLE \
1871     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1872 
1873 #define SQL_GET_REFERENCES_NONE_NONE_SOME_NONE \
1874     SQL_GET_REFERENCES \
1875     SQL_GET_REFERENCES_FSCHEMA \
1876     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1877 
1878 #define SQL_GET_REFERENCES_NONE_NONE_NONE_SOME \
1879     SQL_GET_REFERENCES \
1880     SQL_GET_REFERENCES_FTABLE \
1881     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1882 
1883 #define SQL_GET_REFERENCES_NONE_NONE_SOME_SOME \
1884     SQL_GET_REFERENCES \
1885     SQL_GET_REFERENCES_FSCHEMA \
1886     SQL_GET_REFERENCES_FTABLE \
1887     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1888 
1889 #define SQL_GET_REFERENCES_SOME_NONE_SOME_NONE \
1890     SQL_GET_REFERENCES \
1891     SQL_GET_REFERENCES_PSCHEMA \
1892     SQL_GET_REFERENCES_FSCHEMA \
1893     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1894 
1895 #define SQL_GET_REFERENCES_SOME_NONE_NONE_SOME \
1896     SQL_GET_REFERENCES \
1897     SQL_GET_REFERENCES_PSCHEMA \
1898     SQL_GET_REFERENCES_FTABLE \
1899     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1900 
1901 #define SQL_GET_REFERENCES_SOME_NONE_SOME_SOME \
1902     SQL_GET_REFERENCES \
1903     SQL_GET_REFERENCES_PSCHEMA \
1904     SQL_GET_REFERENCES_FSCHEMA \
1905     SQL_GET_REFERENCES_FTABLE \
1906     SQL_GET_REFERENCES_ORDER_NO_PTABLE
1907 
1908 #define SQL_GET_REFERENCES_NONE_SOME_SOME_NONE \
1909     SQL_GET_REFERENCES \
1910     SQL_GET_REFERENCES_PTABLE \
1911     SQL_GET_REFERENCES_FSCHEMA \
1912     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1913 
1914 #define SQL_GET_REFERENCES_NONE_SOME_NONE_SOME \
1915     SQL_GET_REFERENCES \
1916     SQL_GET_REFERENCES_PTABLE \
1917     SQL_GET_REFERENCES_FTABLE \
1918     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1919 
1920 #define SQL_GET_REFERENCES_NONE_SOME_SOME_SOME \
1921     SQL_GET_REFERENCES \
1922     SQL_GET_REFERENCES_PTABLE \
1923     SQL_GET_REFERENCES_FSCHEMA \
1924     SQL_GET_REFERENCES_FTABLE \
1925     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1926 
1927 #define SQL_GET_REFERENCES_SOME_SOME_SOME_NONE \
1928     SQL_GET_REFERENCES \
1929     SQL_GET_REFERENCES_PSCHEMA \
1930     SQL_GET_REFERENCES_PTABLE \
1931     SQL_GET_REFERENCES_FSCHEMA \
1932     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1933 
1934 #define SQL_GET_REFERENCES_SOME_SOME_NONE_SOME \
1935     SQL_GET_REFERENCES \
1936     SQL_GET_REFERENCES_PSCHEMA \
1937     SQL_GET_REFERENCES_PTABLE \
1938     SQL_GET_REFERENCES_FTABLE \
1939     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1940 
1941 #define SQL_GET_REFERENCES_SOME_SOME_SOME_SOME \
1942     SQL_GET_REFERENCES \
1943     SQL_GET_REFERENCES_PSCHEMA \
1944     SQL_GET_REFERENCES_PTABLE \
1945     SQL_GET_REFERENCES_FSCHEMA \
1946     SQL_GET_REFERENCES_FTABLE \
1947     SQL_GET_REFERENCES_ORDER_SOME_PTABLE
1948 
init_getReferences_stmt()1949 void DatabaseMetaData::init_getReferences_stmt ()
1950 {
1951     m_getReferences_stmt[0]  = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_NONE_NONE);
1952     m_getReferences_stmt[1]  = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_NONE_NONE);
1953     m_getReferences_stmt[2]  = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_NONE_NONE);
1954     m_getReferences_stmt[3]  = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_NONE_NONE);
1955     m_getReferences_stmt[4]  = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_SOME_NONE);
1956     m_getReferences_stmt[5]  = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_SOME_NONE);
1957     m_getReferences_stmt[6]  = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_SOME_NONE);
1958     m_getReferences_stmt[7]  = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_SOME_NONE);
1959     m_getReferences_stmt[8]  = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_NONE_SOME);
1960     m_getReferences_stmt[9]  = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_NONE_SOME);
1961     m_getReferences_stmt[10] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_NONE_SOME);
1962     m_getReferences_stmt[11] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_NONE_SOME);
1963     m_getReferences_stmt[12] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_NONE_SOME_SOME);
1964     m_getReferences_stmt[13] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_NONE_SOME_SOME);
1965     m_getReferences_stmt[14] = m_origin->prepareStatement(SQL_GET_REFERENCES_NONE_SOME_SOME_SOME);
1966     m_getReferences_stmt[15] = m_origin->prepareStatement(SQL_GET_REFERENCES_SOME_SOME_SOME_SOME);
1967 }
1968 
init_getPrivs_stmt()1969 void DatabaseMetaData::init_getPrivs_stmt ()
1970 {
1971     OUStringBuffer sSQL(300);
1972     sSQL.append(
1973             " SELECT dp.TABLE_CAT, dp.TABLE_SCHEM, dp.TABLE_NAME, dp.GRANTOR, pr.rolname AS GRANTEE, dp.privilege, dp.is_grantable "
1974             " FROM ("
1975             "  SELECT table_catalog AS TABLE_CAT, table_schema AS TABLE_SCHEM, table_name,"
1976             "         grantor, grantee, privilege_type AS PRIVILEGE, is_grantable"
1977             "  FROM information_schema.table_privileges");
1978     if ( PQserverVersion( m_pSettings->pConnection ) < 90200 )
1979         // information_schema.table_privileges does not fill in default ACLs when no ACL
1980         // assume default ACL is "owner has all privileges" and add it
1981         sSQL.append(
1982             " UNION "
1983             "  SELECT current_database() AS TABLE_CAT, pn.nspname AS TABLE_SCHEM, c.relname AS TABLE_NAME,"
1984             "         ro.rolname AS GRANTOR, rg.rolname AS GRANTEE, p.privilege, 'YES' AS is_grantable"
1985             "  FROM pg_catalog.pg_class c,"
1986             "       (VALUES ('SELECT'), ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) p (privilege),"
1987             "       pg_catalog.pg_roles ro,"
1988             "       (  SELECT oid, rolname FROM pg_catalog.pg_roles"
1989             "         UNION ALL"
1990             "          VALUES (0::oid, 'PUBLIC')"
1991             "       ) AS rg (oid, rolname),"
1992             "       pg_catalog.pg_namespace pn"
1993             "  WHERE c.relkind IN ('r', 'v') AND c.relacl IS NULL AND pg_has_role(rg.oid, c.relowner, 'USAGE')"
1994             "        AND c.relowner=ro.oid AND c.relnamespace = pn.oid");
1995     sSQL.append(
1996             " ) dp,"
1997             " (SELECT oid, rolname FROM pg_catalog.pg_roles UNION ALL VALUES (0, 'PUBLIC')) pr"
1998             " WHERE table_schem LIKE ? AND table_name LIKE ? AND (dp.grantee = 'PUBLIC' OR pg_has_role(pr.oid, dp.grantee, 'USAGE'))"
1999             " ORDER BY table_schem, table_name, privilege" );
2000 
2001     m_getTablePrivs_stmt = m_origin->prepareStatement( sSQL.makeStringAndClear() );
2002 
2003     sSQL.append(
2004             " SELECT dp.TABLE_CAT, dp.TABLE_SCHEM, dp.TABLE_NAME, dp.COLUMN_NAME, dp.GRANTOR, pr.rolname AS GRANTEE, dp.PRIVILEGE, dp.IS_GRANTABLE FROM ("
2005             "  SELECT table_catalog AS TABLE_CAT, table_schema AS TABLE_SCHEM, table_name, column_name,"
2006             "         grantor, grantee, privilege_type AS PRIVILEGE, is_grantable"
2007             "  FROM information_schema.column_privileges");
2008     if ( PQserverVersion( m_pSettings->pConnection ) < 90200 )
2009         // information_schema.table_privileges does not fill in default ACLs when no ACL
2010         // assume default ACL is "owner has all privileges" and add it
2011         sSQL.append(
2012             " UNION "
2013             "  SELECT current_database() AS TABLE_CAT, pn.nspname AS TABLE_SCHEM, c.relname AS TABLE_NAME, a.attname AS column_name,"
2014             "         ro.rolname AS GRANTOR, rg.rolname AS GRANTEE, p.privilege, 'YES' AS is_grantable"
2015             "  FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a,"
2016             "       (VALUES ('SELECT'), ('INSERT'), ('UPDATE'), ('REFERENCES')) p (privilege),"
2017             "       pg_catalog.pg_roles ro,"
2018             "       (  SELECT oid, rolname FROM pg_catalog.pg_roles"
2019             "         UNION ALL"
2020             "          VALUES (0::oid, 'PUBLIC')"
2021             "       ) AS rg (oid, rolname),"
2022             "       pg_catalog.pg_namespace pn"
2023             "  WHERE c.relkind IN ('r', 'v') AND c.relacl IS NULL AND pg_has_role(rg.oid, c.relowner, 'USAGE')"
2024             "        AND c.relowner=ro.oid AND c.relnamespace = pn.oid AND a.attrelid = c.oid AND a.attnum > 0");
2025     sSQL.append(
2026             " ) dp,"
2027             " (SELECT oid, rolname FROM pg_catalog.pg_roles UNION ALL VALUES (0, 'PUBLIC')) pr"
2028             " WHERE table_schem = ? AND table_name = ? AND column_name LIKE ? AND (dp.grantee = 'PUBLIC' OR pg_has_role(pr.oid, dp.grantee, 'USAGE'))"
2029             " ORDER BY column_name, privilege" );
2030 
2031     m_getColumnPrivs_stmt = m_origin->prepareStatement( sSQL.makeStringAndClear() );
2032 }
2033 
getImportedExportedKeys(const Any &,const OUString & primarySchema,const OUString & primaryTable,const Any &,const OUString & foreignSchema,const OUString & foreignTable)2034 css::uno::Reference< XResultSet > DatabaseMetaData::getImportedExportedKeys(
2035     const Any& /* primaryCatalog */,
2036     const OUString& primarySchema,
2037     const OUString& primaryTable,
2038     const Any& /* foreignCatalog */,
2039     const OUString& foreignSchema,
2040     const OUString& foreignTable )
2041 {
2042     unsigned int i = 0;
2043     if ( ! primarySchema.isEmpty() )
2044         i |=  0x01;
2045     if ( ! primaryTable.isEmpty() )
2046         i |=  0x02;
2047     if ( ! foreignSchema.isEmpty() )
2048         i |=  0x04;
2049     if ( ! foreignTable.isEmpty() )
2050         i |=  0x08;
2051 
2052     Reference< XPreparedStatement > stmt = m_getReferences_stmt[i];
2053     Reference< XParameters > param ( stmt, UNO_QUERY_THROW );
2054 
2055     unsigned int j = 1;
2056     if ( i & 0x01 )
2057         param->setString( j++, primarySchema );
2058     if ( i & 0x02 )
2059         param->setString( j++, primaryTable  );
2060     if ( i & 0x04 )
2061         param->setString( j++, foreignSchema );
2062     if ( i & 0x08 )
2063         param->setString( j++, foreignTable  );
2064 
2065     Reference< XResultSet > rs = stmt->executeQuery();
2066 
2067     return rs;
2068 }
2069 
2070 
getImportedKeys(const css::uno::Any & catalog,const OUString & schema,const OUString & table)2071 css::uno::Reference< XResultSet > DatabaseMetaData::getImportedKeys(
2072     const css::uno::Any& catalog,
2073     const OUString& schema,
2074     const OUString& table )
2075 {
2076     return getImportedExportedKeys(Any(), OUString(), OUString(), catalog, schema, table);
2077 }
2078 
getExportedKeys(const css::uno::Any & catalog,const OUString & schema,const OUString & table)2079 css::uno::Reference< XResultSet > DatabaseMetaData::getExportedKeys(
2080     const css::uno::Any& catalog,
2081     const OUString& schema,
2082     const OUString& table )
2083 {
2084     return getImportedExportedKeys(catalog, schema, table, Any(), OUString(), OUString());
2085 }
2086 
getCrossReference(const css::uno::Any & primaryCatalog,const OUString & primarySchema,const OUString & primaryTable,const css::uno::Any & foreignCatalog,const OUString & foreignSchema,const OUString & foreignTable)2087 css::uno::Reference< XResultSet > DatabaseMetaData::getCrossReference(
2088     const css::uno::Any& primaryCatalog,
2089     const OUString& primarySchema,
2090     const OUString& primaryTable,
2091     const css::uno::Any& foreignCatalog,
2092     const OUString& foreignSchema,
2093     const OUString& foreignTable )
2094 {
2095     return getImportedExportedKeys( primaryCatalog, primarySchema, primaryTable, foreignCatalog, foreignSchema, foreignTable );
2096 }
2097 
2098 namespace
2099 {
2100     struct TypeInfoByDataTypeSorter
2101     {
operator ()pq_sdbc_driver::__anon4e7f8dfb0311::TypeInfoByDataTypeSorter2102         bool operator () ( const std::vector< Any > & a, const std::vector< Any > & b )
2103         {
2104             OUString valueA;
2105             OUString valueB;
2106             a[1 /*DATA_TYPE*/] >>= valueA;
2107             b[1 /*DATA_TYPE*/] >>= valueB;
2108             if( valueB.toInt32() == valueA.toInt32() )
2109             {
2110                 OUString nameA;
2111                 OUString nameB;
2112                 a[0 /*TYPE_NAME*/] >>= nameA;
2113                 b[0 /*TYPE_NAME*/] >>= nameB;
2114                 OUString nsA, tnA, nsB, tnB;
2115 
2116                 // parse typename into schema and typename
2117                 sal_Int32 nIndex=0;
2118                 nsA = nameA.getToken(0, '.', nIndex);
2119                 if (nIndex<0)
2120                 {
2121                     tnA = nsA;
2122                     nsA.clear();
2123                 }
2124                 else
2125                 {
2126                     tnA = nameA.getToken(0, '.', nIndex);
2127                     assert(nIndex < 0);
2128                 }
2129 
2130                 nIndex=0;
2131                 nsB = nameB.getToken(0, '.', nIndex);
2132                 if (nIndex<0)
2133                 {
2134                     tnB = nsB;
2135                     nsB.clear();
2136                 }
2137                 else
2138                 {
2139                     tnB = nameB.getToken(0, '.', nIndex);
2140                     assert(nIndex < 0);
2141                 }
2142 
2143                 const int ns_comp = compare_schema(nsA, nsB);
2144                 if(ns_comp == 0)
2145                 {
2146                     if(nsA.isEmpty())
2147                     {
2148                         assert(nsB.isEmpty());
2149                         // within each type category, sort privileged choice first
2150                         if( tnA == "int4" || tnA == "varchar" || tnA == "char" || tnA == "text")
2151                             return true;
2152                         if( tnB == "int4" || tnB == "varchar" || tnB == "char" || tnB == "text")
2153                             return false;
2154                     }
2155                     return nameA.compareTo( nameB ) < 0;
2156                 }
2157                 else
2158                 {
2159                     return ns_comp < 0;
2160                 }
2161             }
2162 
2163             return valueA.toInt32() < valueB.toInt32();
2164         }
2165     };
2166 
calcSearchable(sal_Int32 dataType)2167     sal_Int32 calcSearchable( sal_Int32 dataType )
2168     {
2169         sal_Int32 ret = css::sdbc::ColumnSearch::FULL;
2170         if( css::sdbc::DataType::BINARY == dataType ||
2171             css::sdbc::DataType::VARBINARY == dataType ||
2172             css::sdbc::DataType::LONGVARBINARY == dataType )
2173             ret = css::sdbc::ColumnSearch::NONE;
2174 
2175         return ret;
2176     }
2177 
getMaxScale(sal_Int32 dataType)2178     sal_Int32 getMaxScale( sal_Int32 dataType )
2179     {
2180         // LEM TODO: review, see where used, see JDBC, ...
2181         sal_Int32 ret = 0;
2182         if( dataType == css::sdbc::DataType::NUMERIC )
2183             ret = 1000; // see pg-docs DataType/numeric
2184 //     else if( dataType == DataType::DOUBLE )
2185 //         ret = 308;
2186 //     else if( dataType == DataType::FLOAT )
2187 //         ret =
2188         return ret;
2189     }
2190 
construct_full_typename(const OUString & ns,const OUString & tn)2191     OUString construct_full_typename(const OUString &ns, const OUString &tn)
2192     {
2193         if(ns.isEmpty() || ns == "pg_catalog")
2194             return tn;
2195         else
2196             return ns + "." + tn;
2197     }
2198 
pgTypeInfo2ResultSet(std::vector<std::vector<Any>> & vec,const Reference<XResultSet> & rs)2199     void pgTypeInfo2ResultSet(
2200          std::vector< std::vector<Any> > &vec,
2201          const Reference< XResultSet > &rs )
2202     {
2203         static const sal_Int32 TYPE_NAME = 0;  // string Type name
2204         static const sal_Int32 DATA_TYPE = 1;  // short SQL data type from java.sql.Types
2205         static const sal_Int32 PRECISION = 2;  // long maximum precision
2206         static const sal_Int32 CREATE_PARAMS = 5; // string => parameters used in creating the type (may be NULL )
2207         static const sal_Int32 NULLABLE  = 6;  // short ==> can you use NULL for this type?
2208                                                // - NO_NULLS - does not allow NULL values
2209                                                // - NULLABLE - allows NULL values
2210                                                // - NULLABLE_UNKNOWN - nullability unknown
2211 
2212         static const sal_Int32 CASE_SENSITIVE = 7; // boolean==> is it case sensitive
2213         static const sal_Int32 SEARCHABLE = 8;  // short ==>; can you use
2214                                                 // "WHERE" based on this type:
2215                                                 //   - NONE - No support
2216                                                 //   - CHAR - Only supported with WHERE .. LIKE
2217                                                 //   - BASIC - Supported except for WHERE .. LIKE
2218                                                 //   - FULL - Supported for all WHERE ..
2219         static const sal_Int32 UNSIGNED_ATTRIBUTE = 9; // boolean ==> is it unsigned?
2220         // FIXED_PREC_SCALE = 10; boolean ==> can it be a money value?
2221         static const sal_Int32 AUTO_INCREMENT = 11; // boolean ==> can it be used for
2222                                                     // an auto-increment value?
2223         static const sal_Int32 MINIMUM_SCALE = 13; // short ==> minimum scale supported
2224         static const sal_Int32 MAXIMUM_SCALE = 14; // short ==> maximum scale supported
2225         static const sal_Int32 NUM_PREC_RADIX = 17; // long ==> usually 2 or 10
2226 
2227         /*  not filled so far
2228             3. LITERAL_PREFIX string ==> prefix used to quote a literal
2229                                          (may be <NULL/>)
2230             4. LITERAL_SUFFIX string ==> suffix used to quote a literal
2231                                          (may be <NULL/>)
2232             5. CREATE_PARAMS string ==> parameters used in creating the type (may be <NULL/>)
2233             12. LOCAL_TYPE_NAME  string ==> localized version of type name (may be <NULL/>)
2234             15, SQL_DATA_TYPE long ==> unused
2235             16. SQL_DATETIME_SUB long ==> unused
2236          */
2237         Reference< XRow > xRow( rs, UNO_QUERY_THROW );
2238         while( rs->next() )
2239         {
2240             std::vector< Any > row(18);
2241 
2242             sal_Int32 dataType =typeNameToDataType(xRow->getString(5),xRow->getString(2));
2243             sal_Int32 precision = xRow->getString(3).toInt32();
2244 
2245             if( dataType == css::sdbc::DataType::CHAR  ||
2246                 ( dataType == css::sdbc::DataType::VARCHAR &&
2247                   xRow->getString(TYPE_NAME+1).equalsIgnoreAsciiCase("varchar") ) )
2248             {
2249                 // reflect varchar as varchar with upper limit !
2250                 //NOTE: the sql spec requires varchar to have an upper limit, however
2251                 //      in postgresql the upper limit is optional, no limit means unlimited
2252                 //      length (=1GB).
2253                 precision = 0x40000000; // about 1 GB, see character type docs in postgresql
2254                 row[CREATE_PARAMS] <<= OUString("length");
2255             }
2256             else if( dataType == css::sdbc::DataType::NUMERIC )
2257             {
2258                 precision = 1000;
2259                 row[CREATE_PARAMS] <<= OUString("length, scale");
2260             }
2261 
2262             row[TYPE_NAME] <<= construct_full_typename(xRow->getString(6), xRow->getString(1));
2263             row[DATA_TYPE] <<= OUString::number(dataType);
2264             row[PRECISION] <<= OUString::number( precision );
2265             sal_Int32 nullable = xRow->getBoolean(4) ?
2266                 css::sdbc::ColumnValue::NO_NULLS :
2267                 css::sdbc::ColumnValue::NULLABLE;
2268             row[NULLABLE] <<= OUString::number(nullable);
2269             row[CASE_SENSITIVE] <<= OUString::number(1);
2270             row[SEARCHABLE] <<= OUString::number( calcSearchable( dataType ) );
2271             row[UNSIGNED_ATTRIBUTE] <<= OUString("0");
2272             if( css::sdbc::DataType::INTEGER == dataType ||
2273                 css::sdbc::DataType::BIGINT == dataType )
2274                 row[AUTO_INCREMENT] <<= OUString("1");     // TODO
2275             else
2276                 row[AUTO_INCREMENT] <<= OUString("0");     // TODO
2277             row[MINIMUM_SCALE] <<= OUString("0");      // TODO: what is this ?
2278             row[MAXIMUM_SCALE] <<= OUString::number( getMaxScale( dataType ) );
2279             row[NUM_PREC_RADIX] <<= OUString("10");    // TODO: what is this ?
2280             vec.push_back( row );
2281         }
2282     }
2283 }
2284 
2285 
getTypeInfo()2286 css::uno::Reference< XResultSet > DatabaseMetaData::getTypeInfo(  )
2287 {
2288     // Note: Indexes start at 0 (in the API doc, they start at 1)
2289     MutexGuard guard( m_xMutex->GetMutex() );
2290 
2291     if (isLog(m_pSettings, LogLevel::Info))
2292     {
2293         log(m_pSettings, LogLevel::Info, "DatabaseMetaData::getTypeInfo() got called");
2294     }
2295 
2296     Reference< XStatement > statement = m_origin->createStatement();
2297     Reference< XResultSet > rs = statement->executeQuery(
2298           "SELECT pg_type.typname AS typname," //1
2299           "pg_type.typtype AS typtype,"        //2
2300           "pg_type.typlen AS typlen,"          //3
2301           "pg_type.typnotnull AS typnotnull,"  //4
2302           "pg_type.typname AS typname, "       //5
2303           "pg_namespace.nspname as typns "     //6
2304           "FROM pg_type LEFT JOIN pg_namespace ON pg_type.typnamespace=pg_namespace.oid "
2305           "WHERE pg_type.typtype = 'b' "
2306           "OR pg_type.typtype = 'p'"
2307             );
2308 
2309     std::vector< std::vector<Any> > vec;
2310     pgTypeInfo2ResultSet( vec, rs );
2311 
2312     // check for domain types
2313     rs = statement->executeQuery(
2314         "SELECT t1.typname as typname,"
2315         "t2.typtype AS typtype,"
2316         "t2.typlen AS typlen,"
2317         "t2.typnotnull AS typnotnull,"
2318         "t2.typname as realtypname, "
2319         "pg_namespace.nspname as typns "
2320         "FROM pg_type as t1 LEFT JOIN pg_type AS t2 ON t1.typbasetype=t2.oid LEFT JOIN pg_namespace ON t1.typnamespace=pg_namespace.oid "
2321         "WHERE t1.typtype = 'd'" );
2322     pgTypeInfo2ResultSet( vec, rs );
2323 
2324     std::sort( vec.begin(), vec.end(), TypeInfoByDataTypeSorter() );
2325 
2326     return new SequenceResultSet(
2327         m_xMutex,
2328         *this,
2329         getStatics().typeinfoColumnNames,
2330         vec,
2331         m_pSettings->tc,
2332         &( getStatics().typeInfoMetaData ));
2333 }
2334 
2335 
getIndexInfo(const css::uno::Any &,const OUString & schema,const OUString & table,sal_Bool unique,sal_Bool)2336 css::uno::Reference< XResultSet > DatabaseMetaData::getIndexInfo(
2337     const css::uno::Any& ,
2338     const OUString& schema,
2339     const OUString& table,
2340     sal_Bool unique,
2341     sal_Bool )
2342 {
2343     //LEM TODO: review
2344     MutexGuard guard( m_xMutex->GetMutex() );
2345 
2346     /*
2347        1. TABLE_CAT string -> table catalog (may be NULL )
2348        2. TABLE_SCHEM string -> table schema (may be NULL )
2349        3. TABLE_NAME string -> table name
2350        4. NON_UNIQUE boolean -> Can index values be non-unique?
2351                                 false when TYPE is tableIndexStatistic
2352        5. INDEX_QUALIFIER string -> index catalog (may be NULL );
2353                                 NULL when TYPE is tableIndexStatistic
2354        6. INDEX_NAME string -> index name; NULL when TYPE is tableIndexStatistic
2355        7. TYPE short -> index type:
2356               * 0 - this identifies table statistics that are returned
2357                     in conjunction with a table's index descriptions
2358               * CLUSTERED - this is a clustered index
2359               * HASHED - this is a hashed index
2360               * OTHER - this is some other style of index
2361        8. ORDINAL_POSITION short -> column sequence number within index;
2362                                     zero when TYPE is tableIndexStatistic
2363        9. COLUMN_NAME string -> column name; NULL when TYPE is tableIndexStatistic
2364       10. ASC_OR_DESC string -> column sort sequence, "A"= ascending,
2365                                 "D" = descending, may be NULL if sort sequence
2366                                 is not supported; NULL when TYPE is tableIndexStatistic
2367       11. CARDINALITY long -> When TYPE is tableIndexStatistic, then this is
2368                               the number of rows in the table; otherwise, it
2369                               is the number of unique values in the index.
2370       12. PAGES long -> When TYPE is tableIndexStatisic then this is
2371                         the number of pages used for the table, otherwise
2372                         it is the number of pages used for the current index.
2373       13. FILTER_CONDITION string -> Filter condition, if any. (may be NULL )
2374 
2375     */
2376     static const sal_Int32 C_SCHEMA = 1;
2377     static const sal_Int32 C_TABLENAME = 2;
2378     static const sal_Int32 C_INDEXNAME = 3;
2379     static const sal_Int32 C_IS_CLUSTERED = 4;
2380     static const sal_Int32 C_IS_UNIQUE = 5;
2381     // C_IS_PRIMARY = 6
2382     static const sal_Int32 C_COLUMNS = 7;
2383 
2384     static const sal_Int32 R_TABLE_SCHEM = 1;
2385     static const sal_Int32 R_TABLE_NAME = 2;
2386     static const sal_Int32 R_NON_UNIQUE = 3;
2387     static const sal_Int32 R_INDEX_NAME = 5;
2388     static const sal_Int32 R_TYPE = 6;
2389     static const sal_Int32 R_ORDINAL_POSITION = 7;
2390     static const sal_Int32 R_COLUMN_NAME = 8;
2391 
2392     Reference< XPreparedStatement > stmt = m_origin->prepareStatement(
2393             "SELECT nspname, "          // 1
2394                    "pg_class.relname, " // 2
2395                    "class2.relname, "   // 3
2396                    "indisclustered, "   // 4
2397                    "indisunique, "      // 5
2398                    "indisprimary, "     // 6
2399                    "indkey "            // 7
2400             "FROM pg_index INNER JOIN pg_class ON indrelid = pg_class.oid "
2401                           "INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid "
2402                           "INNER JOIN pg_class as class2 ON pg_index.indexrelid = class2.oid "
2403             "WHERE nspname = ? AND pg_class.relname = ?" );
2404 
2405     Reference< XParameters > param ( stmt, UNO_QUERY_THROW );
2406     param->setString( 1, schema );
2407     param->setString( 2, table );
2408     Reference< XResultSet > rs = stmt->executeQuery();
2409     Reference< XRow > xRow ( rs, UNO_QUERY_THROW );
2410 
2411     std::vector< std::vector<Any> > vec;
2412     while( rs->next() )
2413     {
2414         std::vector< sal_Int32 > columns = parseIntArray( xRow->getString(C_COLUMNS) );
2415         Reference< XPreparedStatement > columnsStmt = m_origin->prepareStatement(
2416                 "SELECT attnum, attname "
2417                 "FROM pg_attribute "
2418                 "     INNER JOIN pg_class ON attrelid = pg_class.oid "
2419                 "     INNER JOIN pg_namespace ON pg_class.relnamespace=pg_namespace.oid "
2420                 "     WHERE pg_namespace.nspname=?  AND pg_class.relname=?" );
2421         Reference< XParameters > paramColumn ( columnsStmt, UNO_QUERY_THROW );
2422         OUString currentSchema = xRow->getString( C_SCHEMA );
2423         OUString currentTable = xRow->getString( C_TABLENAME );
2424         OUString currentIndexName = xRow->getString( C_INDEXNAME );
2425         bool isNonUnique = ! xRow->getBoolean( C_IS_UNIQUE );
2426         sal_Int32 indexType =  xRow->getBoolean( C_IS_CLUSTERED ) ?
2427             css::sdbc::IndexType::CLUSTERED :
2428             css::sdbc::IndexType::HASHED;
2429 
2430         paramColumn->setString( C_SCHEMA, currentSchema );
2431         paramColumn->setString( C_TABLENAME, currentTable );
2432 
2433         Reference< XResultSet > rsColumn = columnsStmt->executeQuery();
2434         Reference< XRow > rowColumn( rsColumn, UNO_QUERY_THROW );
2435         while( rsColumn->next() )
2436         {
2437             auto findIt = std::find( columns.begin(), columns.end(), rowColumn->getInt( 1 ) );
2438             if( findIt != columns.end() && ( ! isNonUnique || !  unique ) )
2439             {
2440                 std::vector< Any > result( 13 );
2441                 result[R_TABLE_SCHEM] <<= currentSchema;
2442                 result[R_TABLE_NAME] <<= currentTable;
2443                 result[R_INDEX_NAME] <<= currentIndexName;
2444                 result[R_NON_UNIQUE] <<= isNonUnique;
2445                 result[R_TYPE] <<= indexType;
2446                 result[R_COLUMN_NAME] <<= rowColumn->getString(2);
2447                 sal_Int32 nPos = static_cast<sal_Int32>(findIt - columns.begin() +1); // MSVC++ nonsense
2448                 result[R_ORDINAL_POSITION] <<= nPos;
2449                 vec.push_back( result );
2450             }
2451         }
2452     }
2453     return new SequenceResultSet(
2454         m_xMutex, *this, getStatics().indexinfoColumnNames,
2455         vec,
2456         m_pSettings->tc );
2457 }
2458 
supportsResultSetType(sal_Int32 setType)2459 sal_Bool DatabaseMetaData::supportsResultSetType( sal_Int32 setType )
2460 {
2461     if ( setType == css::sdbc::ResultSetType::SCROLL_SENSITIVE )
2462         return false;
2463     else
2464         return true;
2465 }
2466 
supportsResultSetConcurrency(sal_Int32 setType,sal_Int32)2467 sal_Bool DatabaseMetaData::supportsResultSetConcurrency(
2468     sal_Int32 setType, sal_Int32 )
2469 {
2470     if ( ! supportsResultSetType( setType ) )
2471         return false;
2472     else
2473         return true;
2474 }
2475 
ownUpdatesAreVisible(sal_Int32)2476 sal_Bool DatabaseMetaData::ownUpdatesAreVisible( sal_Int32 /* setType */ )
2477 {
2478     return true;
2479 }
2480 
ownDeletesAreVisible(sal_Int32)2481 sal_Bool DatabaseMetaData::ownDeletesAreVisible( sal_Int32 /* setType */ )
2482 {
2483     return true;
2484 }
2485 
ownInsertsAreVisible(sal_Int32)2486 sal_Bool DatabaseMetaData::ownInsertsAreVisible( sal_Int32 /* setType */ )
2487 {
2488     return true;
2489 }
2490 
othersUpdatesAreVisible(sal_Int32)2491 sal_Bool DatabaseMetaData::othersUpdatesAreVisible( sal_Int32 /* setType */ )
2492 {
2493     return false;
2494 }
2495 
othersDeletesAreVisible(sal_Int32)2496 sal_Bool DatabaseMetaData::othersDeletesAreVisible( sal_Int32 /* setType */ )
2497 {
2498     return false;
2499 }
2500 
othersInsertsAreVisible(sal_Int32)2501 sal_Bool DatabaseMetaData::othersInsertsAreVisible( sal_Int32 /* setType */ )
2502 {
2503     return false;
2504 }
2505 
updatesAreDetected(sal_Int32)2506 sal_Bool DatabaseMetaData::updatesAreDetected( sal_Int32 /* setType */ )
2507 {
2508     return false;
2509 }
2510 
deletesAreDetected(sal_Int32)2511 sal_Bool DatabaseMetaData::deletesAreDetected( sal_Int32 /* setType */ )
2512 {
2513     return false;
2514 }
insertsAreDetected(sal_Int32)2515 sal_Bool DatabaseMetaData::insertsAreDetected( sal_Int32 /* setType */ )
2516 {
2517     return false;
2518 }
2519 
supportsBatchUpdates()2520 sal_Bool DatabaseMetaData::supportsBatchUpdates(  )
2521 {
2522     return true;
2523 }
2524 
getUDTs(const css::uno::Any &,const OUString &,const OUString &,const css::uno::Sequence<sal_Int32> &)2525 css::uno::Reference< XResultSet > DatabaseMetaData::getUDTs( const css::uno::Any&, const OUString&, const OUString&, const css::uno::Sequence< sal_Int32 >& )
2526 {
2527     //LEM TODO: implement! See JDBC driver
2528     MutexGuard guard( m_xMutex->GetMutex() );
2529     return new SequenceResultSet(
2530         m_xMutex, *this, std::vector< OUString >(), std::vector< std::vector< Any > >(), m_pSettings->tc );
2531 }
2532 
getConnection()2533 css::uno::Reference< css::sdbc::XConnection > DatabaseMetaData::getConnection()
2534 {
2535     return m_origin;
2536 }
2537 }
2538 
2539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2540