@@ -475,6 +475,32 @@ void QgsMssqlProvider::loadFields()
475475 }
476476}
477477
478+ QString QgsMssqlProvider::quotedValue ( const QVariant& value )
479+ {
480+ if ( value.isNull () )
481+ return " NULL" ;
482+
483+ switch ( value.type () )
484+ {
485+ case QVariant::Int:
486+ case QVariant::LongLong:
487+ case QVariant::Double:
488+ return value.toString ();
489+
490+ case QVariant::Bool:
491+ return value.toBool () ? " 1" : " 0" ;
492+
493+ default :
494+ case QVariant::String:
495+ QString v = value.toString ();
496+ v.replace ( ' \' ' , " ''" );
497+ if ( v.contains ( ' \\ ' ) )
498+ return v.replace ( ' \\ ' , " \\\\ " ).prepend ( " N'" ).append ( ' \' ' );
499+ else
500+ return v.prepend ( ' \' ' ).append ( ' \' ' );
501+ }
502+ }
503+
478504QVariant QgsMssqlProvider::defaultValue ( int fieldId )
479505{
480506 if ( mDefaultValues .contains ( fieldId ) )
@@ -1934,3 +1960,321 @@ QGISEXTERN QgsVectorLayerImport::ImportError createEmptyLayer(
19341960 oldToNewAttrIdxMap, errorMessage, options
19351961 );
19361962}
1963+ QGISEXTERN bool saveStyle ( const QString& uri, const QString& qmlStyle, const QString& sldStyle,
1964+ const QString& styleName, const QString& styleDescription,
1965+ const QString& uiFileContent, bool useAsDefault, QString& errCause )
1966+ {
1967+ QgsDataSourceURI dsUri ( uri );
1968+ // connect to database
1969+ QSqlDatabase mDatabase = QgsMssqlProvider::GetDatabase ( dsUri.service (), dsUri.host (), dsUri.database (), dsUri.username (), dsUri.password () );
1970+
1971+ if ( !QgsMssqlProvider::OpenDatabase ( mDatabase ) )
1972+ {
1973+ QgsDebugMsg ( " Error connecting to database" );
1974+ QgsDebugMsg ( mDatabase .lastError ().text () );
1975+ return false ;
1976+ }
1977+
1978+ QSqlQuery query = QSqlQuery ( mDatabase );
1979+ query.setForwardOnly ( true );
1980+ if ( !query.exec ( QString ( " SELECT COUNT(*) FROM information_schema.tables WHERE table_name= N'layer_styles'" ) ) )
1981+ {
1982+ QString msg = query.lastError ().text ();
1983+ QgsDebugMsg ( msg );
1984+ return false ;
1985+ }
1986+ if ( query.isActive () && query.next () && query.value ( 0 ).toInt () == 0 )
1987+ {
1988+ QgsDebugMsg ( " Need to create styles table" );
1989+ bool execOk = query.exec ( QString ( " CREATE TABLE [dbo].[layer_styles]("
1990+ " [id] int IDENTITY(1,1) PRIMARY KEY,"
1991+ " [f_table_catalog] [varchar](1024) NULL,"
1992+ " [f_table_schema] [varchar](1024) NULL,"
1993+ " [f_table_name] [varchar](1024) NULL,"
1994+ " [f_geometry_column] [varchar](1024) NULL,"
1995+ " [styleName] [varchar](1024) NULL,"
1996+ " [styleQML] [text] NULL,"
1997+ " [styleSLD] [text] NULL,"
1998+ " [useAsDefault] [int] NULL,"
1999+ " [description] [text] NULL,"
2000+ " [owner] [varchar](1024) NULL,"
2001+ " [ui] [text] NULL,"
2002+ " [update_time] [datetime] NULL"
2003+ " ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]" ));
2004+ if ( !execOk )
2005+ {
2006+ errCause = QObject::tr ( " Unable to save layer style. It's not possible to create the destination table on the database. Maybe this is due to table permissions. Please contact your database admin" );
2007+ return false ;
2008+ }
2009+ query.finish ();
2010+ query.clear ();
2011+ }
2012+
2013+ QString uiFileColumn;
2014+ QString uiFileValue;
2015+ if ( !uiFileContent.isEmpty () )
2016+ {
2017+ uiFileColumn = " ,ui" ;
2018+ uiFileValue = QString ( " ,XMLPARSE(DOCUMENT %1)" ).arg ( uiFileContent );
2019+ }
2020+ QgsDebugMsg ( " Ready to insert new style" );
2021+ // Note: in the construction of the INSERT and UPDATE strings the qmlStyle and sldStyle values
2022+ // can contain user entered strings, which may themselves include %## values that would be
2023+ // replaced by the QString.arg function. To ensure that the final SQL string is not corrupt these
2024+ // two values are both replaced in the final .arg call of the string construction.
2025+
2026+ QString sql = QString ( " INSERT INTO layer_styles"
2027+ " (f_table_catalog,f_table_schema,f_table_name,f_geometry_column,styleName,styleQML,styleSLD,useAsDefault,description,owner%11"
2028+ " ) VALUES ("
2029+ " %1,%2,%3,%4,%5,%6,%7,%8,%9,%10%12"
2030+ " )" )
2031+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.database () ) )
2032+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.schema () ) )
2033+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.table () ) )
2034+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.geometryColumn () ) )
2035+ .arg ( QgsMssqlProvider::quotedValue ( styleName.isEmpty () ? dsUri.table () : styleName ) )
2036+ .arg ( QgsMssqlProvider::quotedValue ( qmlStyle ) )
2037+ .arg ( QgsMssqlProvider::quotedValue ( sldStyle ) )
2038+ .arg ( useAsDefault ? " 1" : " 0" )
2039+ .arg ( QgsMssqlProvider::quotedValue ( styleDescription.isEmpty () ? QDateTime::currentDateTime ().toString () : styleDescription ) )
2040+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.username () ) )
2041+ .arg ( uiFileColumn )
2042+ .arg ( uiFileValue );
2043+
2044+ QString checkQuery = QString ( " SELECT styleName"
2045+ " FROM layer_styles"
2046+ " WHERE f_table_catalog=%1"
2047+ " AND f_table_schema=%2"
2048+ " AND f_table_name=%3"
2049+ " AND f_geometry_column=%4"
2050+ " AND styleName=%5" )
2051+ .arg ( QgsMssqlProvider::quotedValue (dsUri.database () ) )
2052+ .arg ( QgsMssqlProvider::quotedValue (dsUri.schema () ) )
2053+ .arg ( QgsMssqlProvider::quotedValue (dsUri.table () ) )
2054+ .arg ( QgsMssqlProvider::quotedValue (dsUri.geometryColumn () ) )
2055+ .arg ( QgsMssqlProvider::quotedValue (styleName.isEmpty () ? dsUri.table () : styleName ) );
2056+
2057+ if ( !query.exec ( checkQuery ) )
2058+ {
2059+ QgsDebugMsg ( query.lastError ().text () );
2060+ QgsDebugMsg (" Check Query failed" );
2061+ return false ;
2062+ }
2063+ if ( query.isActive () && query.next () && query.value ( 0 ).toString () == styleName )
2064+ {
2065+ if ( QMessageBox::question ( nullptr , QObject::tr ( " Save style in database" ),
2066+ QObject::tr ( " A style named \" %1\" already exists in the database for this layer. Do you want to overwrite it?" )
2067+ .arg ( styleName.isEmpty () ? dsUri.table () : styleName ),
2068+ QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
2069+ {
2070+ errCause = QObject::tr ( " Operation aborted. No changes were made in the database" );
2071+ QgsDebugMsg (" User selected not to overwrite styles" );
2072+ return false ;
2073+ }
2074+
2075+ QgsDebugMsg (" Updating styles" );
2076+ sql = QString ( " UPDATE layer_styles "
2077+ " SET useAsDefault=%1"
2078+ " ,styleQML=%2"
2079+ " ,styleSLD=%3"
2080+ " ,description=%4"
2081+ " ,owner=%5"
2082+ " WHERE f_table_catalog=%6"
2083+ " AND f_table_schema=%7"
2084+ " AND f_table_name=%8"
2085+ " AND f_geometry_column=%9"
2086+ " AND styleName=%10" )
2087+ .arg ( useAsDefault ? " 1" : " 0" )
2088+ .arg ( QgsMssqlProvider::quotedValue ( qmlStyle ) )
2089+ .arg ( QgsMssqlProvider::quotedValue ( sldStyle ) )
2090+ .arg ( QgsMssqlProvider::quotedValue ( styleDescription.isEmpty () ? QDateTime::currentDateTime ().toString () : styleDescription ) )
2091+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.username () ) )
2092+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.database () ) )
2093+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.schema () ) )
2094+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.table () ) )
2095+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.geometryColumn () ) )
2096+ .arg ( QgsMssqlProvider::quotedValue ( styleName.isEmpty () ? dsUri.table () : styleName ) );
2097+ }
2098+ if ( useAsDefault )
2099+ {
2100+ QString removeDefaultSql = QString ( " UPDATE layer_styles "
2101+ " SET useAsDefault=0"
2102+ " WHERE f_table_catalog=%1"
2103+ " AND f_table_schema=%2"
2104+ " AND f_table_name=%3"
2105+ " AND f_geometry_column=%4" )
2106+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.database () ) )
2107+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.schema () ) )
2108+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.table () ) )
2109+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.geometryColumn () ) );
2110+ sql = QString ( " %1; %2;" ).arg ( removeDefaultSql, sql );
2111+ }
2112+
2113+ QgsDebugMsg (" Inserting styles" );
2114+ QgsDebugMsg (sql);
2115+ bool execOk = query.exec ( sql );
2116+
2117+ if ( !execOk )
2118+ {
2119+ errCause = QObject::tr ( " Unable to save layer style. It's not possible to insert a new record into the style table. Maybe this is due to table permissions. Please contact your database administrator." );
2120+ }
2121+ return execOk;
2122+ }
2123+
2124+
2125+ QGISEXTERN QString loadStyle ( const QString& uri, QString& errCause )
2126+ {
2127+ QString style;
2128+ QgsDataSourceURI dsUri ( uri );
2129+ // connect to database
2130+ QSqlDatabase mDatabase = QgsMssqlProvider::GetDatabase ( dsUri.service (), dsUri.host (), dsUri.database (), dsUri.username (), dsUri.password () );
2131+
2132+ if ( !QgsMssqlProvider::OpenDatabase ( mDatabase ) )
2133+ {
2134+ QgsDebugMsg ( " Error connecting to database" );
2135+ QgsDebugMsg ( mDatabase .lastError ().text () );
2136+ return QString ();
2137+ }
2138+
2139+ QSqlQuery query = QSqlQuery ( mDatabase );
2140+ query.setForwardOnly ( true );
2141+
2142+ QString selectQmlQuery = QString ( " SELECT top 1 styleQML"
2143+ " FROM layer_styles"
2144+ " WHERE f_table_catalog=%1"
2145+ " AND f_table_schema=%2"
2146+ " AND f_table_name=%3"
2147+ " AND f_geometry_column=%4"
2148+ " ORDER BY useAsDefault desc" )
2149+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.database () ) )
2150+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.schema () ) )
2151+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.table () ) )
2152+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.geometryColumn () ) );
2153+
2154+ if ( !query.exec ( selectQmlQuery ) )
2155+ {
2156+ QgsDebugMsg ( query.lastError ().text () );
2157+ QgsDebugMsg (" Load of Style failed" );
2158+ return QString ();
2159+ }
2160+ if ( query.isActive () && query.next ())
2161+ {
2162+ QString style = query.value ( 0 ).toString ();
2163+ return style;
2164+ }
2165+ return QString ();
2166+ }
2167+
2168+ QGISEXTERN int listStyles ( const QString &uri, QStringList &ids, QStringList &names,
2169+ QStringList &descriptions, QString& errCause )
2170+ {
2171+ QgsDataSourceURI dsUri ( uri );
2172+ // connect to database
2173+ QSqlDatabase mDatabase = QgsMssqlProvider::GetDatabase ( dsUri.service (), dsUri.host (), dsUri.database (), dsUri.username (), dsUri.password () );
2174+
2175+ if ( !QgsMssqlProvider::OpenDatabase ( mDatabase ) )
2176+ {
2177+ QgsDebugMsg ( " Error connecting to database" );
2178+ QgsDebugMsg ( mDatabase .lastError ().text () );
2179+ return -1 ;
2180+ }
2181+
2182+ QSqlQuery query = QSqlQuery ( mDatabase );
2183+ query.setForwardOnly ( true );
2184+
2185+ // check if layer_styles table already exist
2186+ if ( !query.exec ( QString ( " SELECT COUNT(*) FROM information_schema.tables WHERE table_name= N'layer_styles'" ) ) )
2187+ {
2188+ QString msg = query.lastError ().text ();
2189+ errCause = msg;
2190+ QgsDebugMsg ( msg );
2191+ return -1 ;
2192+ }
2193+ if ( query.isActive () && query.next () && query.value ( 0 ).toInt () == 0 )
2194+ {
2195+ QgsDebugMsg ( QObject::tr ( " No styles available on DB, or there is an error connecting to the database." ) );
2196+ return -1 ;
2197+ }
2198+
2199+ QString selectRelatedQuery = QString ( " SELECT id,styleName,description"
2200+ " FROM layer_styles "
2201+ " WHERE f_table_catalog=%1"
2202+ " AND f_table_schema=%2"
2203+ " AND f_table_name=%3"
2204+ " AND f_geometry_column=%4" )
2205+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.database () ) )
2206+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.schema () ) )
2207+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.table () ) )
2208+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.geometryColumn () ) );
2209+ bool queryOk = query.exec (selectRelatedQuery);
2210+ if (!queryOk)
2211+ {
2212+ QgsDebugMsg ( query.lastError ().text () );
2213+ return -1 ;
2214+ }
2215+ int numberOfRelatedStyles = 0 ;
2216+ while ( query.isActive () && query.next ())
2217+ {
2218+ QgsDebugMsg (query.value (1 ).toString ());
2219+ ids.append ( query.value (0 ).toString () );
2220+ names.append ( query.value (1 ).toString () );
2221+ descriptions.append ( query.value (2 ).toString () );
2222+ numberOfRelatedStyles = numberOfRelatedStyles + 1 ;
2223+ }
2224+ QString selectOthersQuery = QString ( " SELECT id,styleName,description"
2225+ " FROM layer_styles "
2226+ " WHERE NOT (f_table_catalog=%1 AND f_table_schema=%2 AND f_table_name=%3 AND f_geometry_column=%4)"
2227+ " ORDER BY update_time DESC" )
2228+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.database () ) )
2229+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.schema () ) )
2230+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.table () ) )
2231+ .arg ( QgsMssqlProvider::quotedValue ( dsUri.geometryColumn () ) );
2232+ QgsDebugMsg (selectOthersQuery);
2233+ queryOk = query.exec (selectOthersQuery);
2234+ if (!queryOk)
2235+ {
2236+ QString msg = query.lastError ().text ();
2237+ QgsDebugMsg ( msg );
2238+ return -1 ;
2239+ }
2240+ QgsDebugMsg (query.isActive () && query.size ());
2241+ while (query.next ())
2242+ {
2243+ ids.append ( query.value (0 ).toString () );
2244+ names.append ( query.value (1 ).toString () );
2245+ descriptions.append ( query.value (2 ).toString () );
2246+ }
2247+ return numberOfRelatedStyles;
2248+ }
2249+ QGISEXTERN QString getStyleById ( const QString& uri, QString styleId, QString& errCause )
2250+ {
2251+ QgsDataSourceURI dsUri ( uri );
2252+ // connect to database
2253+ QSqlDatabase mDatabase = QgsMssqlProvider::GetDatabase ( dsUri.service (), dsUri.host (), dsUri.database (), dsUri.username (), dsUri.password () );
2254+
2255+ if ( !QgsMssqlProvider::OpenDatabase ( mDatabase ) )
2256+ {
2257+ QgsDebugMsg ( " Error connecting to database" );
2258+ QgsDebugMsg ( mDatabase .lastError ().text () );
2259+ return QString ();
2260+ }
2261+
2262+ QSqlQuery query = QSqlQuery ( mDatabase );
2263+ query.setForwardOnly ( true );
2264+
2265+ QString style = " " ;
2266+ QString selectQmlQuery = QString ( " SELECT styleQml FROM layer_styles WHERE id=%1" ).arg ( QgsMssqlProvider::quotedValue ( styleId ) );
2267+ bool queryOk = query.exec (selectQmlQuery);
2268+ if (!queryOk)
2269+ {
2270+ QString msg = query.lastError ().text ();
2271+ QgsDebugMsg ( msg );
2272+ errCause = query.lastError ().text ();
2273+ return QString ( );
2274+ }
2275+ while (query.next ())
2276+ {
2277+ style = query.value (0 ).toString ();
2278+ }
2279+ return style;
2280+ }
0 commit comments