QGIS API Documentation  3.25.0-Master (dec16ba68b)
qgspostgresstringutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspostgresstringutils.cpp
3  ---------------------
4  begin : July 2019
5  copyright : (C) 2019 by David Signer
6  email : david at opengis dot ch
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 
16 #include "qgspostgresstringutils.h"
17 #include "qgsmessagelog.h"
18 
19 #include <QRegularExpression>
20 
21 #include <nlohmann/json.hpp>
22 
23 using namespace nlohmann;
24 
25 static void jumpSpace( const QString &txt, int &i )
26 {
27  while ( i < txt.length() && txt.at( i ).isSpace() )
28  ++i;
29 }
30 
31 QString QgsPostgresStringUtils::getNextString( const QString &txt, int &i, const QString &sep )
32 {
33  jumpSpace( txt, i );
34  QString cur = txt.mid( i );
35  if ( cur.startsWith( '"' ) )
36  {
37  const QRegularExpression stringRe( QRegularExpression::anchoredPattern( "^\"((?:\\\\.|[^\"\\\\])*)\".*" ) );
38  const QRegularExpressionMatch match = stringRe.match( cur );
39  if ( !match.hasMatch() )
40  {
41  QgsMessageLog::logMessage( QObject::tr( "Cannot find end of double quoted string: %1" ).arg( txt ), QObject::tr( "PostgresStringUtils" ) );
42  return QString();
43  }
44  i += match.captured( 1 ).length() + 2;
45  jumpSpace( txt, i );
46  if ( !QStringView{txt}.mid( i ).startsWith( sep ) && i < txt.length() )
47  {
48  QgsMessageLog::logMessage( QObject::tr( "Cannot find separator: %1" ).arg( txt.mid( i ) ), QObject::tr( "PostgresStringUtils" ) );
49  return QString();
50  }
51  i += sep.length();
52  return match.captured( 1 ).replace( QLatin1String( "\\\"" ), QLatin1String( "\"" ) ).replace( QLatin1String( "\\\\" ), QLatin1String( "\\" ) );
53  }
54  else
55  {
56  int sepPos = cur.indexOf( sep );
57  if ( sepPos < 0 )
58  {
59  i += cur.length();
60  return cur.trimmed();
61  }
62  i += sepPos + sep.length();
63  return cur.left( sepPos ).trimmed();
64  }
65 }
66 
67 QVariantList QgsPostgresStringUtils::parseArray( const QString &string )
68 {
69  QVariantList variantList;
70 
71  //it's a postgres array
72  QString newVal = string.mid( 1, string.length() - 2 );
73 
74  if ( newVal.trimmed().startsWith( '{' ) )
75  {
76  //it's a multidimensional array
77  QStringList values;
78  QString subarray = newVal;
79  while ( !subarray.isEmpty() )
80  {
81  bool escaped = false;
82  int openedBrackets = 1;
83  int i = 0;
84  while ( i < subarray.length() && openedBrackets > 0 )
85  {
86  ++i;
87 
88  if ( subarray.at( i ) == '}' && !escaped ) openedBrackets--;
89  else if ( subarray.at( i ) == '{' && !escaped ) openedBrackets++;
90 
91  escaped = !escaped ? subarray.at( i ) == '\\' : false;
92  }
93 
94  variantList.append( subarray.left( ++i ) );
95  i = subarray.indexOf( ',', i );
96  i = i > 0 ? subarray.indexOf( '{', i ) : -1;
97  if ( i == -1 )
98  break;
99 
100  subarray = subarray.mid( i );
101  }
102  }
103  else
104  {
105  int i = 0;
106  while ( i < newVal.length() )
107  {
108  const QString value = getNextString( newVal, i, QStringLiteral( "," ) );
109  if ( value.isNull() )
110  {
111  QgsMessageLog::logMessage( QObject::tr( "Error parsing PG like array: %1" ).arg( newVal ), QObject::tr( "PostgresStringUtils" ) );
112  break;
113  }
114  variantList.append( value );
115  }
116  }
117 
118  return variantList;
119 
120 }
121 
122 QString QgsPostgresStringUtils::buildArray( const QVariantList &list )
123 {
124  QStringList sl;
125  for ( const QVariant &v : std::as_const( list ) )
126  {
127  // Convert to proper type
128  switch ( v.type() )
129  {
130  case QVariant::Type::Int:
131  case QVariant::Type::LongLong:
132  sl.push_back( v.toString() );
133  break;
134  default:
135  QString newS = v.toString();
136  if ( newS.startsWith( '{' ) )
137  {
138  sl.push_back( newS );
139  }
140  else
141  {
142  newS.replace( '\\', QLatin1String( R"(\\)" ) );
143  newS.replace( '\"', QLatin1String( R"(\")" ) );
144  sl.push_back( "\"" + newS + "\"" );
145  }
146  break;
147  }
148  }
149  //store as a formatted string because the fields supports only string
150  QString s = sl.join( ',' ).prepend( '{' ).append( '}' );
151 
152  return s;
153 }
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static QString buildArray(const QVariantList &list)
Build a postgres array like formatted list in a string from a QVariantList.
static QVariantList parseArray(const QString &string)
Returns a QVariantList created out of a string containing an array in postgres array format {1,...