1 /***************************************************************************
2     testqgspostgresconn.cpp
3     ---------------------
4     begin                : August 2016
5     copyright            : (C) 2016 by Patrick Valsecchi
6     email                : patrick dot valsecchi at camptocamp dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 #include "qgstest.h"
16 #include <QObject>
17 
18 #include <qgspostgresconn.h>
19 #include <qgsfields.h>
20 #include <qgspostgresprovider.h>
21 
22 // Helper function for QCOMPARE
toString(const QgsPostgresGeometryColumnType & t)23 char *toString( const QgsPostgresGeometryColumnType &t )
24 {
25   const char *ptr;
26   switch ( t )
27   {
28     case SctNone:
29       ptr = "None";
30       break;
31     case SctGeometry:
32       ptr = "Geometry";
33       break;
34     case SctGeography:
35       ptr = "Geography";
36       break;
37     case SctTopoGeometry:
38       ptr = "TopoGeometry";
39       break;
40     case SctPcPatch:
41       ptr = "PcPatch";
42       break;
43     case SctRaster:
44       ptr = "Raster";
45       break;
46     default:
47       ptr = "Unknown";
48       break;
49   }
50   char *dst = new char[16];
51   return qstrcpy( dst, ptr );
52 }
53 
54 class TestQgsPostgresConn: public QObject
55 {
56     Q_OBJECT
57 
58   private:
59     QgsPostgresConn *_connection;
60 
getConnection()61     QgsPostgresConn *getConnection()
62     {
63       if ( ! _connection )
64       {
65         const char *connstring = getenv( "QGIS_PGTEST_DB" );
66         if ( NULL == connstring ) connstring = "service=qgis_test";
67         _connection = QgsPostgresConn::connectDb( connstring, true );
68         assert( _connection );
69       }
70       return _connection;
71     }
72 
73   private slots:
initTestCase()74     void initTestCase() // will be called before the first testfunction is executed.
75     {
76       this->_connection = 0;
77     }
cleanupTestCase()78     void cleanupTestCase() // will be called after the last testfunction was executed.
79     {
80       if ( this->_connection ) this->_connection->unref();
81     }
82 
quotedValueHstore()83     void quotedValueHstore()
84     {
85       QVariantMap map;
86       map[QStringLiteral( "1" )] = "2";
87       map[QStringLiteral( "a" )] = "b \"c' \\x";
88 
89       const QString actual = QgsPostgresConn::quotedValue( map );
90       QCOMPARE( actual, QString( "E'\"1\"=>\"2\",\"a\"=>\"b \\\\\"c\\' \\\\\\\\x\"'::hstore" ) );
91     }
92 
quotedValueString()93     void quotedValueString()
94     {
95       QCOMPARE( QgsPostgresConn::quotedValue( "b" ), QString( "'b'" ) );
96       QCOMPARE( QgsPostgresConn::quotedValue( "b's" ), QString( "'b''s'" ) );
97       QCOMPARE( QgsPostgresConn::quotedValue( "b \"c' \\x" ), QString( "E'b \"c'' \\\\x'" ) );
98     }
99 
quotedValueDatetime()100     void quotedValueDatetime()
101     {
102       QCOMPARE( QgsPostgresConn::quotedValue( QDateTime::fromString( "2020-05-07 17:56:00", "yyyy-MM-dd HH:mm:ss" ) ), QString( "'2020-05-07T17:56:00.000'" ) );
103 
104       QgsFields fields;
105       QgsField f;
106       f.setName( "ts" );
107       f.setType( QVariant::DateTime );
108       f.setTypeName( "timestamp" );
109       fields.append( f );
110       QgsField f2;
111       f2.setName( "pk" );
112       f2.setType( QVariant::LongLong );
113       f2.setTypeName( "serial8" );
114       fields.append( f2 );
115 
116       QList<int> pkAttrs;
117       pkAttrs.append( 0 );
118       pkAttrs.append( 1 );
119 
120       QgsPostgresSharedData *shared = new QgsPostgresSharedData;
121       QVariantList qvlist;
122       qvlist.append( QDateTime::fromString( "2020-05-07 17:56:00", "yyyy-MM-dd HH:mm:ss" ) );
123       qvlist.append( 123LL );
124       shared->insertFid( 1LL, qvlist );
125 
126       QCOMPARE( QgsPostgresUtils::whereClause( 1LL, fields, NULL, QgsPostgresPrimaryKeyType::PktFidMap, pkAttrs, std::shared_ptr<QgsPostgresSharedData>( shared ) ), QString( "\"ts\"='2020-05-07T17:56:00.000' AND \"pk\"=123" ) );
127     }
128 
quotedValueStringArray()129     void quotedValueStringArray()
130     {
131       QStringList list;
132       list << QStringLiteral( "a" ) << QStringLiteral( "b \"c' \\x" );
133       const QString actual = QgsPostgresConn::quotedValue( list );
134       QCOMPARE( actual, QString( "E'{\"a\",\"b \\\\\"c\\' \\\\\\\\x\"}'" ) );
135     }
136 
quotedValueIntArray()137     void quotedValueIntArray()
138     {
139       QVariantList list;
140       list << 1 << -5;
141       const QString actual = QgsPostgresConn::quotedValue( list );
142       QCOMPARE( actual, QString( "E'{1,-5}'" ) );
143     }
144 
quotedValue2DimArray()145     void quotedValue2DimArray()
146     {
147       QStringList list;
148       list << QStringLiteral( "{\"hello foo\",b}" ) << QStringLiteral( "{c,\"hello bar\"}" );
149       const QString actual = QgsPostgresConn::quotedValue( list );
150       QCOMPARE( actual, QString( "E'{{\"hello foo\",b},{c,\"hello bar\"}}'" ) );
151     }
152 
supportedLayers()153     void supportedLayers()
154     {
155       QgsPostgresConn *conn = getConnection();
156       QVERIFY( conn != 0 );
157       QVector<QgsPostgresLayerProperty> layers;
158       QMap<QString, QgsPostgresLayerProperty> layersMap;
159 
160       const bool success = conn->supportedLayers(
161                              layers,
162                              false, // searchGeometryColumnsOnly
163                              false, // searchPublicOnly
164                              false, // allowGeometrylessTables
165                              "qgis_test" // schema
166                            );
167       QVERIFY( success );
168 
169       // Test no duplicates are reported by supportedLayers
170       for ( const auto &l : layers )
171       {
172         const QString key = QString( "%1.%2.%3" ).arg( l.schemaName, l.tableName, l.geometryColName );
173         const auto i = layersMap.find( key );
174         if ( i != layersMap.end() )
175         {
176           QFAIL(
177             QString(
178               "Layer %1 returned multiple times by supportedLayers"
179             ).arg( key ).toUtf8().data()
180           );
181         }
182         layersMap.insert( key, l );
183       }
184 
185       // Test qgis_test.TopoLayer1.topogeom
186       const QString key = QString( "qgis_test.TopoLayer1.topogeom" );
187       const auto lit = layersMap.find( key );
188       QVERIFY2(
189         lit != layersMap.end(),
190         "Layer qgis_test.TopoLayer1.topogeom not returned by supportedLayers"
191       );
192       QCOMPARE( lit->geometryColName, "topogeom" );
193       QCOMPARE( lit->geometryColType, SctTopoGeometry );
194       // TODO: add more tests
195 
196     }
197 
198 };
199 
200 QGSTEST_MAIN( TestQgsPostgresConn )
201 #include "testqgspostgresconn.moc"
202