81 |
81 |
mSchemaName = mUri.schema();
|
82 |
82 |
mTableName = mUri.table();
|
83 |
83 |
geometryColumn = mUri.geometryColumn();
|
84 |
|
sqlWhereClause = mUri.sql();
|
85 |
84 |
primaryKey = mUri.keyColumn();
|
86 |
85 |
mUseEstimatedMetadata = mUri.useEstimatedMetadata();
|
87 |
86 |
|
88 |
|
// Keep a schema qualified table name for convenience later on.
|
89 |
|
mSchemaTableName = mUri.quotedTablename();
|
|
87 |
if ( mSchemaName.isEmpty() && mTableName.isEmpty() )
|
|
88 |
{
|
|
89 |
isCustomQuery = true;
|
|
90 |
sqlCustomSelect = mUri.sql();
|
|
91 |
sqlCustomSelect.remove( QRegExp( "\\;.*") );
|
90 |
92 |
|
|
93 |
sqlWhereClause = QString();
|
|
94 |
}
|
|
95 |
else
|
|
96 |
{
|
|
97 |
isCustomQuery = false;
|
|
98 |
sqlWhereClause = mUri.sql();
|
|
99 |
|
|
100 |
// Keep a schema qualified table name for convenience later on.
|
|
101 |
mSchemaTableName = mUri.quotedTablename();
|
|
102 |
}
|
|
103 |
|
91 |
104 |
QgsDebugMsg( "Table name is " + mTableName );
|
92 |
|
QgsDebugMsg( "SQL is " + sqlWhereClause );
|
|
105 |
QgsDebugMsg( "SQL is " + !isCustomQuery ? sqlWhereClause : sqlCustomSelect );
|
93 |
106 |
QgsDebugMsg( "Connection info is " + mUri.connectionInfo() );
|
94 |
107 |
|
95 |
108 |
QgsDebugMsg( "Geometry column is: " + geometryColumn );
|
... | ... | |
104 |
117 |
return;
|
105 |
118 |
}
|
106 |
119 |
|
107 |
|
QgsDebugMsg( "Checking for permissions on the relation" );
|
108 |
|
|
109 |
|
// Check that we can read from the table (i.e., we have
|
110 |
|
// select permission).
|
111 |
|
QString sql = QString( "select * from %1 limit 1" ).arg( mSchemaTableName );
|
112 |
|
Result testAccess = connectionRO->PQexec( sql );
|
113 |
|
if ( PQresultStatus( testAccess ) != PGRES_TUPLES_OK )
|
|
120 |
if ( !checkPermsAndCapabilities() ) // check permissions and set capabilities
|
114 |
121 |
{
|
115 |
|
showMessageBox( tr( "Unable to access relation" ),
|
116 |
|
tr( "Unable to access the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
117 |
|
.arg( mSchemaTableName )
|
118 |
|
.arg( QString::fromUtf8( PQresultErrorMessage( testAccess ) ) )
|
119 |
|
.arg( sql ) );
|
120 |
122 |
valid = false;
|
121 |
123 |
disconnectDb();
|
122 |
124 |
return;
|
123 |
125 |
}
|
124 |
126 |
|
125 |
|
if ( connectionRO->pgVersion() >= 80400 )
|
126 |
|
{
|
127 |
|
sql = QString( "SELECT "
|
128 |
|
"has_table_privilege(%1,'DELETE'),"
|
129 |
|
"has_any_column_privilege(%1,'UPDATE'),"
|
130 |
|
"has_column_privilege(%1,%2,'UPDATE'),"
|
131 |
|
"has_table_privilege(%1,'INSERT'),"
|
132 |
|
"current_schema()" )
|
133 |
|
.arg( quotedValue( mSchemaTableName ) ).arg( quotedValue( geometryColumn ) );
|
134 |
|
}
|
135 |
|
else
|
136 |
|
{
|
137 |
|
sql = QString( "SELECT "
|
138 |
|
"has_table_privilege(%1,'DELETE'),"
|
139 |
|
"has_table_privilege(%1,'UPDATE'),"
|
140 |
|
"has_table_privilege(%1,'UPDATE'),"
|
141 |
|
"has_table_privilege(%1,'INSERT'),"
|
142 |
|
"current_schema()" )
|
143 |
|
.arg( quotedValue( mSchemaTableName ) );
|
144 |
|
}
|
145 |
|
|
146 |
|
testAccess = connectionRO->PQexec( sql );
|
147 |
|
if ( PQresultStatus( testAccess ) != PGRES_TUPLES_OK )
|
148 |
|
{
|
149 |
|
showMessageBox( tr( "Unable to access relation" ),
|
150 |
|
tr( "Unable to determine table access privileges for the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
151 |
|
.arg( mSchemaTableName )
|
152 |
|
.arg( QString::fromUtf8( PQresultErrorMessage( testAccess ) ) )
|
153 |
|
.arg( sql ) );
|
154 |
|
valid = false;
|
155 |
|
disconnectDb();
|
156 |
|
return;
|
157 |
|
}
|
158 |
|
|
159 |
|
// postgres has fast access to features at id (thanks to primary key / unique index)
|
160 |
|
// the latter flag is here just for compatibility
|
161 |
|
enabledCapabilities = QgsVectorDataProvider::SelectAtId | QgsVectorDataProvider::SelectGeometryAtId;
|
162 |
|
|
163 |
|
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 0 ) ) == "t" )
|
164 |
|
{
|
165 |
|
// DELETE
|
166 |
|
enabledCapabilities |= QgsVectorDataProvider::DeleteFeatures;
|
167 |
|
}
|
168 |
|
|
169 |
|
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 1 ) ) == "t" )
|
170 |
|
{
|
171 |
|
// UPDATE
|
172 |
|
enabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues;
|
173 |
|
}
|
174 |
|
|
175 |
|
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 2 ) ) == "t" )
|
176 |
|
{
|
177 |
|
// UPDATE
|
178 |
|
enabledCapabilities |= QgsVectorDataProvider::ChangeGeometries;
|
179 |
|
}
|
180 |
|
|
181 |
|
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 3 ) ) == "t" )
|
182 |
|
{
|
183 |
|
// INSERT
|
184 |
|
enabledCapabilities |= QgsVectorDataProvider::AddFeatures;
|
185 |
|
}
|
186 |
|
|
187 |
|
mCurrentSchema = QString::fromUtf8( PQgetvalue( testAccess, 0, 4 ) );
|
188 |
|
if ( mCurrentSchema == mSchemaName )
|
189 |
|
{
|
190 |
|
mUri.clearSchema();
|
191 |
|
}
|
192 |
|
|
193 |
|
if ( mSchemaName == "" )
|
194 |
|
mSchemaName = mCurrentSchema;
|
195 |
|
|
196 |
|
sql = QString( "SELECT 1 FROM pg_class,pg_namespace WHERE "
|
197 |
|
"pg_class.relnamespace=pg_namespace.oid AND "
|
198 |
|
"pg_get_userbyid(relowner)=current_user AND "
|
199 |
|
"relname=%1 AND nspname=%2" )
|
200 |
|
.arg( quotedValue( mTableName ) )
|
201 |
|
.arg( quotedValue( mSchemaName ) );
|
202 |
|
testAccess = connectionRO->PQexec( sql );
|
203 |
|
if ( PQresultStatus( testAccess ) == PGRES_TUPLES_OK && PQntuples( testAccess ) == 1 )
|
204 |
|
{
|
205 |
|
enabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes;
|
206 |
|
}
|
207 |
|
|
208 |
127 |
if ( !getGeometryDetails() ) // gets srid and geometry type
|
209 |
128 |
{
|
210 |
129 |
// the table is not a geometry table
|
... | ... | |
485 |
404 |
query += "," + fieldExpression( fld );
|
486 |
405 |
}
|
487 |
406 |
|
488 |
|
query += " from " + mSchemaTableName;
|
|
407 |
query += QString(" from %1" ).arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
489 |
408 |
|
490 |
409 |
if ( !whereClause.isEmpty() )
|
491 |
410 |
query += QString( " where %1" ).arg( whereClause );
|
... | ... | |
876 |
795 |
|
877 |
796 |
void QgsPostgresProvider::loadFields()
|
878 |
797 |
{
|
879 |
|
QgsDebugMsg( "Loading fields for table " + mTableName );
|
|
798 |
QString sql;
|
|
799 |
if ( !isCustomQuery )
|
|
800 |
{
|
|
801 |
QgsDebugMsg( "Loading fields for table " + mTableName );
|
880 |
802 |
|
881 |
|
// Get the relation oid for use in later queries
|
882 |
|
QString sql = QString( "SELECT regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
|
883 |
|
Result tresult = connectionRO->PQexec( sql );
|
884 |
|
QString tableoid = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
|
803 |
// Get the relation oid for use in later queries
|
|
804 |
sql = QString( "SELECT regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
|
|
805 |
Result tresult = connectionRO->PQexec( sql );
|
|
806 |
QString tblOid = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
885 |
807 |
|
886 |
|
// Get the table description
|
887 |
|
sql = QString( "SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=0" ).arg( tableoid );
|
888 |
|
tresult = connectionRO->PQexec( sql );
|
889 |
|
if ( PQntuples( tresult ) > 0 )
|
890 |
|
mDataComment = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
|
808 |
// Get the table description
|
|
809 |
sql = QString( "SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=0" ).arg( tblOid );
|
|
810 |
tresult = connectionRO->PQexec( sql );
|
|
811 |
if ( PQntuples( tresult ) > 0 )
|
|
812 |
mDataComment = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
891 |
813 |
|
892 |
|
// Populate the field vector for this layer. The field vector contains
|
893 |
|
// field name, type, length, and precision (if numeric)
|
894 |
|
sql = QString( "select * from %1 limit 0" ).arg( mSchemaTableName );
|
|
814 |
// Populate the field vector for this layer. The field vector contains
|
|
815 |
// field name, type, length, and precision (if numeric)
|
|
816 |
sql = QString( "select * from %1 limit 0" ).arg( mSchemaTableName );
|
|
817 |
}
|
|
818 |
else
|
|
819 |
{
|
|
820 |
sql = QString( "select * from %1 limit 0" ).arg( sqlCustomSelect );
|
|
821 |
}
|
895 |
822 |
|
896 |
823 |
Result result = connectionRO->PQexec( sql );
|
897 |
824 |
|
... | ... | |
909 |
836 |
QString typOid = QString().setNum( fldtyp );
|
910 |
837 |
int fieldModifier = PQfmod( result, i );
|
911 |
838 |
QString fieldComment( "" );
|
|
839 |
int tbloid = PQftable( result, i );
|
912 |
840 |
|
913 |
841 |
sql = QString( "SELECT typname,typtype,typelem,typlen FROM pg_type WHERE oid=%1" ).arg( typOid );
|
914 |
842 |
// just oid; needs more work to support array type
|
... | ... | |
921 |
849 |
QString fieldElem = QString::fromUtf8( PQgetvalue( oidResult, 0, 2 ) );
|
922 |
850 |
int fieldSize = QString::fromUtf8( PQgetvalue( oidResult, 0, 3 ) ).toInt();
|
923 |
851 |
|
924 |
|
sql = QString( "SELECT attnum FROM pg_attribute WHERE attrelid=%1 AND attname=%2" )
|
925 |
|
.arg( tableoid ).arg( quotedValue( fieldName ) );
|
|
852 |
if ( tbloid >= 0 )
|
|
853 |
{
|
|
854 |
QString tableOid = QString().setNum( tbloid );
|
926 |
855 |
|
927 |
|
Result tresult = connectionRO->PQexec( sql );
|
928 |
|
QString attnum = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
|
856 |
sql = QString( "SELECT attnum FROM pg_attribute WHERE attrelid=%1 AND attname=%2" )
|
|
857 |
.arg( tableOid ).arg( quotedValue( fieldName ) );
|
929 |
858 |
|
930 |
|
sql = QString( "SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=%2" )
|
931 |
|
.arg( tableoid ).arg( attnum );
|
|
859 |
Result tresult = connectionRO->PQexec( sql );
|
|
860 |
QString attnum = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
932 |
861 |
|
933 |
|
tresult = connectionRO->PQexec( sql );
|
934 |
|
if ( PQntuples( tresult ) > 0 )
|
935 |
|
fieldComment = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
|
862 |
sql = QString( "SELECT description FROM pg_description WHERE objoid=%1 AND objsubid=%2" )
|
|
863 |
.arg( tableOid ).arg( attnum );
|
936 |
864 |
|
|
865 |
tresult = connectionRO->PQexec( sql );
|
|
866 |
if ( PQntuples( tresult ) > 0 )
|
|
867 |
fieldComment = QString::fromUtf8( PQgetvalue( tresult, 0, 0 ) );
|
|
868 |
}
|
|
869 |
|
937 |
870 |
QVariant::Type fieldType;
|
938 |
871 |
|
939 |
872 |
if ( fieldTType == "b" )
|
... | ... | |
1007 |
940 |
}
|
1008 |
941 |
}
|
1009 |
942 |
|
|
943 |
bool QgsPostgresProvider::checkPermsAndCapabilities()
|
|
944 |
{
|
|
945 |
QgsDebugMsg( "Checking for permissions on the relation" );
|
|
946 |
|
|
947 |
Result testAccess;
|
|
948 |
if ( !isCustomQuery )
|
|
949 |
{
|
|
950 |
// Check that we can read from the table (i.e., we have
|
|
951 |
// select permission).
|
|
952 |
QString sql = QString( "select * from %1 limit 1" ).arg( mSchemaTableName );
|
|
953 |
Result testAccess = connectionRO->PQexec( sql );
|
|
954 |
if ( PQresultStatus( testAccess ) != PGRES_TUPLES_OK )
|
|
955 |
{
|
|
956 |
showMessageBox( tr( "Unable to access relation" ),
|
|
957 |
tr( "Unable to access the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
|
958 |
.arg( mSchemaTableName )
|
|
959 |
.arg( QString::fromUtf8( PQresultErrorMessage( testAccess ) ) )
|
|
960 |
.arg( sql ) );
|
|
961 |
return false;
|
|
962 |
}
|
|
963 |
|
|
964 |
if ( connectionRO->pgVersion() >= 80400 )
|
|
965 |
{
|
|
966 |
sql = QString( "SELECT "
|
|
967 |
"has_table_privilege(%1,'DELETE'),"
|
|
968 |
"has_any_column_privilege(%1,'UPDATE'),"
|
|
969 |
"has_column_privilege(%1,%2,'UPDATE'),"
|
|
970 |
"has_table_privilege(%1,'INSERT'),"
|
|
971 |
"current_schema()" )
|
|
972 |
.arg( quotedValue( mSchemaTableName ) ).arg( quotedValue( geometryColumn ) );
|
|
973 |
}
|
|
974 |
else
|
|
975 |
{
|
|
976 |
sql = QString( "SELECT "
|
|
977 |
"has_table_privilege(%1,'DELETE'),"
|
|
978 |
"has_table_privilege(%1,'UPDATE'),"
|
|
979 |
"has_table_privilege(%1,'UPDATE'),"
|
|
980 |
"has_table_privilege(%1,'INSERT'),"
|
|
981 |
"current_schema()" )
|
|
982 |
.arg( quotedValue( mSchemaTableName ) );
|
|
983 |
}
|
|
984 |
|
|
985 |
testAccess = connectionRO->PQexec( sql );
|
|
986 |
if ( PQresultStatus( testAccess ) != PGRES_TUPLES_OK )
|
|
987 |
{
|
|
988 |
showMessageBox( tr( "Unable to access relation" ),
|
|
989 |
tr( "Unable to determine table access privileges for the %1 relation.\nThe error message from the database was:\n%2.\nSQL: %3" )
|
|
990 |
.arg( mSchemaTableName )
|
|
991 |
.arg( QString::fromUtf8( PQresultErrorMessage( testAccess ) ) )
|
|
992 |
.arg( sql ) );
|
|
993 |
return false;
|
|
994 |
}
|
|
995 |
|
|
996 |
// postgres has fast access to features at id (thanks to primary key / unique index)
|
|
997 |
// the latter flag is here just for compatibility
|
|
998 |
enabledCapabilities = QgsVectorDataProvider::SelectAtId | QgsVectorDataProvider::SelectGeometryAtId;
|
|
999 |
|
|
1000 |
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 0 ) ) == "t" )
|
|
1001 |
{
|
|
1002 |
// DELETE
|
|
1003 |
enabledCapabilities |= QgsVectorDataProvider::DeleteFeatures;
|
|
1004 |
}
|
|
1005 |
|
|
1006 |
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 1 ) ) == "t" )
|
|
1007 |
{
|
|
1008 |
// UPDATE
|
|
1009 |
enabledCapabilities |= QgsVectorDataProvider::ChangeAttributeValues;
|
|
1010 |
}
|
|
1011 |
|
|
1012 |
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 2 ) ) == "t" )
|
|
1013 |
{
|
|
1014 |
// UPDATE
|
|
1015 |
enabledCapabilities |= QgsVectorDataProvider::ChangeGeometries;
|
|
1016 |
}
|
|
1017 |
|
|
1018 |
if ( QString::fromUtf8( PQgetvalue( testAccess, 0, 3 ) ) == "t" )
|
|
1019 |
{
|
|
1020 |
// INSERT
|
|
1021 |
enabledCapabilities |= QgsVectorDataProvider::AddFeatures;
|
|
1022 |
}
|
|
1023 |
|
|
1024 |
mCurrentSchema = QString::fromUtf8( PQgetvalue( testAccess, 0, 4 ) );
|
|
1025 |
if ( mCurrentSchema == mSchemaName )
|
|
1026 |
{
|
|
1027 |
mUri.clearSchema();
|
|
1028 |
}
|
|
1029 |
|
|
1030 |
if ( mSchemaName == "" )
|
|
1031 |
mSchemaName = mCurrentSchema;
|
|
1032 |
|
|
1033 |
sql = QString( "SELECT 1 FROM pg_class,pg_namespace WHERE "
|
|
1034 |
"pg_class.relnamespace=pg_namespace.oid AND "
|
|
1035 |
"pg_get_userbyid(relowner)=current_user AND "
|
|
1036 |
"relname=%1 AND nspname=%2" )
|
|
1037 |
.arg( quotedValue( mTableName ) )
|
|
1038 |
.arg( quotedValue( mSchemaName ) );
|
|
1039 |
testAccess = connectionRO->PQexec( sql );
|
|
1040 |
if ( PQresultStatus( testAccess ) == PGRES_TUPLES_OK && PQntuples( testAccess ) == 1 )
|
|
1041 |
{
|
|
1042 |
enabledCapabilities |= QgsVectorDataProvider::AddAttributes | QgsVectorDataProvider::DeleteAttributes;
|
|
1043 |
}
|
|
1044 |
}
|
|
1045 |
else
|
|
1046 |
{
|
|
1047 |
// Check if the sql is a select query
|
|
1048 |
if ( !sqlCustomSelect.startsWith( "select", Qt::CaseInsensitive ) )
|
|
1049 |
{
|
|
1050 |
QgsDebugMsg( "The custom query is not a select query." );
|
|
1051 |
//TODO show a message by showMessageBox()
|
|
1052 |
return false;
|
|
1053 |
}
|
|
1054 |
|
|
1055 |
// get a new alias for the neasted query
|
|
1056 |
int index = 0;
|
|
1057 |
QString alias;
|
|
1058 |
QRegExp regex;
|
|
1059 |
do
|
|
1060 |
{
|
|
1061 |
alias = QString( "subQuery_%1" ).arg( QString::number( index++ ) );
|
|
1062 |
QString pattern = QString( "(\\\"?)%1\\1" ).arg( QRegExp::escape( alias ) );
|
|
1063 |
regex.setPattern( pattern );
|
|
1064 |
regex.setCaseSensitivity( Qt::CaseInsensitive );
|
|
1065 |
}
|
|
1066 |
while( sqlCustomSelect.contains( regex ) );
|
|
1067 |
|
|
1068 |
// convert the custom query into a neasted query
|
|
1069 |
sqlCustomSelect = QString( "( %1 ) as %2" )
|
|
1070 |
.arg( sqlCustomSelect )
|
|
1071 |
.arg( quotedIdentifier( alias ) );
|
|
1072 |
|
|
1073 |
QString sql = QString( "select * from %1 limit 1" )
|
|
1074 |
.arg( sqlCustomSelect );
|
|
1075 |
|
|
1076 |
testAccess = connectionRO->PQexec( sql );
|
|
1077 |
if ( PQresultStatus( testAccess ) != PGRES_TUPLES_OK )
|
|
1078 |
{
|
|
1079 |
showMessageBox( tr( "Unable execute the query" ),
|
|
1080 |
tr( "Unable to execute the query.\nThe error message from the database was:\n%1.\nSQL: %2" )
|
|
1081 |
.arg( QString::fromUtf8( PQresultErrorMessage( testAccess ) ) )
|
|
1082 |
.arg( sql ) );
|
|
1083 |
return false;
|
|
1084 |
}
|
|
1085 |
|
|
1086 |
enabledCapabilities = QgsVectorDataProvider::SelectAtId | QgsVectorDataProvider::SelectGeometryAtId;
|
|
1087 |
}
|
|
1088 |
|
|
1089 |
return true;
|
|
1090 |
}
|
|
1091 |
|
1010 |
1092 |
QString QgsPostgresProvider::getPrimaryKey()
|
1011 |
1093 |
{
|
1012 |
1094 |
// If we find a database primary key we will set this to true. If it is a column which is serving
|
... | ... | |
1017 |
1099 |
// can be used as a key into the table. Primary keys are always
|
1018 |
1100 |
// unique indices, so we catch them as well.
|
1019 |
1101 |
|
1020 |
|
QString sql = QString( "select indkey from pg_index where indisunique and indrelid=regclass(%1)::oid and indpred is null" )
|
1021 |
|
.arg( quotedValue( mSchemaTableName ) );
|
|
1102 |
QString sql;
|
|
1103 |
if ( !isCustomQuery )
|
|
1104 |
{
|
|
1105 |
sql = QString( "select indkey from pg_index where indisunique and indrelid=regclass(%1)::oid and indpred is null" )
|
|
1106 |
.arg( quotedValue( mSchemaTableName ) );
|
1022 |
1107 |
|
1023 |
|
QgsDebugMsg( "Getting unique index using '" + sql + "'" );
|
|
1108 |
QgsDebugMsg( "Getting unique index using '" + sql + "'" );
|
1024 |
1109 |
|
1025 |
|
Result pk = connectionRO->PQexec( sql );
|
|
1110 |
Result pk = connectionRO->PQexec( sql );
|
1026 |
1111 |
|
1027 |
|
QgsDebugMsg( "Got " + QString::number( PQntuples( pk ) ) + " rows." );
|
|
1112 |
QgsDebugMsg( "Got " + QString::number( PQntuples( pk ) ) + " rows." );
|
1028 |
1113 |
|
1029 |
|
QStringList log;
|
|
1114 |
QStringList log;
|
1030 |
1115 |
|
1031 |
|
// if we got no tuples we ain't got no unique index :)
|
1032 |
|
if ( PQntuples( pk ) == 0 )
|
1033 |
|
{
|
1034 |
|
QgsDebugMsg( "Relation has no unique index -- investigating alternatives" );
|
1035 |
|
|
1036 |
|
// Two options here. If the relation is a table, see if there is
|
1037 |
|
// an oid column that can be used instead.
|
1038 |
|
// If the relation is a view try to find a suitable column to use as
|
1039 |
|
// the primary key.
|
1040 |
|
|
1041 |
|
sql = QString( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" )
|
1042 |
|
.arg( quotedValue( mSchemaTableName ) );
|
1043 |
|
Result tableType = connectionRO->PQexec( sql );
|
1044 |
|
QString type = QString::fromUtf8( PQgetvalue( tableType, 0, 0 ) );
|
1045 |
|
|
1046 |
|
if ( type == "r" ) // the relation is a table
|
|
1116 |
// if we got no tuples we ain't got no unique index :)
|
|
1117 |
if ( PQntuples( pk ) == 0 )
|
1047 |
1118 |
{
|
1048 |
|
QgsDebugMsg( "Relation is a table. Checking to see if it has an oid column." );
|
|
1119 |
QgsDebugMsg( "Relation has no unique index -- investigating alternatives" );
|
1049 |
1120 |
|
1050 |
|
primaryKey = "";
|
|
1121 |
// Two options here. If the relation is a table, see if there is
|
|
1122 |
// an oid column that can be used instead.
|
|
1123 |
// If the relation is a view try to find a suitable column to use as
|
|
1124 |
// the primary key.
|
1051 |
1125 |
|
1052 |
|
// If there is an oid on the table, use that instead,
|
1053 |
|
// otherwise give up
|
1054 |
|
sql = QString( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" )
|
|
1126 |
sql = QString( "SELECT relkind FROM pg_class WHERE oid=regclass(%1)::oid" )
|
1055 |
1127 |
.arg( quotedValue( mSchemaTableName ) );
|
|
1128 |
Result tableType = connectionRO->PQexec( sql );
|
|
1129 |
QString type = QString::fromUtf8( PQgetvalue( tableType, 0, 0 ) );
|
1056 |
1130 |
|
1057 |
|
Result oidCheck = connectionRO->PQexec( sql );
|
1058 |
|
|
1059 |
|
if ( PQntuples( oidCheck ) != 0 )
|
|
1131 |
if ( type == "r" ) // the relation is a table
|
1060 |
1132 |
{
|
1061 |
|
// Could warn the user here that performance will suffer if
|
1062 |
|
// oid isn't indexed (and that they may want to add a
|
1063 |
|
// primary key to the table)
|
1064 |
|
primaryKey = "oid";
|
1065 |
|
primaryKeyType = "int4";
|
1066 |
|
mIsDbPrimaryKey = true;
|
1067 |
|
}
|
1068 |
|
else
|
1069 |
|
{
|
1070 |
|
sql = QString( "SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" )
|
|
1133 |
QgsDebugMsg( "Relation is a table. Checking to see if it has an oid column." );
|
|
1134 |
|
|
1135 |
primaryKey = "";
|
|
1136 |
|
|
1137 |
// If there is an oid on the table, use that instead,
|
|
1138 |
// otherwise give up
|
|
1139 |
sql = QString( "SELECT attname FROM pg_attribute WHERE attname='oid' AND attrelid=regclass(%1)" )
|
1071 |
1140 |
.arg( quotedValue( mSchemaTableName ) );
|
1072 |
|
|
1073 |
|
Result ctidCheck = connectionRO->PQexec( sql );
|
1074 |
|
|
1075 |
|
if ( PQntuples( ctidCheck ) == 1 )
|
|
1141 |
|
|
1142 |
Result oidCheck = connectionRO->PQexec( sql );
|
|
1143 |
|
|
1144 |
if ( PQntuples( oidCheck ) != 0 )
|
1076 |
1145 |
{
|
1077 |
|
sql = QString( "SELECT max(substring(ctid::text from E'\\\\((\\\\d+),\\\\d+\\\\)')::integer) from %1" )
|
1078 |
|
.arg( mSchemaTableName );
|
1079 |
|
|
|
1146 |
// Could warn the user here that performance will suffer if
|
|
1147 |
// oid isn't indexed (and that they may want to add a
|
|
1148 |
// primary key to the table)
|
|
1149 |
primaryKey = "oid";
|
|
1150 |
primaryKeyType = "int4";
|
|
1151 |
mIsDbPrimaryKey = true;
|
|
1152 |
}
|
|
1153 |
else
|
|
1154 |
{
|
|
1155 |
sql = QString( "SELECT attname FROM pg_attribute WHERE attname='ctid' AND attrelid=regclass(%1)" )
|
|
1156 |
.arg( quotedValue( mSchemaTableName ) );
|
|
1157 |
|
1080 |
1158 |
Result ctidCheck = connectionRO->PQexec( sql );
|
|
1159 |
|
1081 |
1160 |
if ( PQntuples( ctidCheck ) == 1 )
|
1082 |
1161 |
{
|
1083 |
|
int id = QString( PQgetvalue( ctidCheck, 0, 0 ) ).toInt();
|
1084 |
|
|
1085 |
|
if ( id < 0x10000 )
|
|
1162 |
sql = QString( "SELECT max(substring(ctid::text from E'\\\\((\\\\d+),\\\\d+\\\\)')::integer) from %1" )
|
|
1163 |
.arg( mSchemaTableName );
|
|
1164 |
|
|
1165 |
Result ctidCheck = connectionRO->PQexec( sql );
|
|
1166 |
if ( PQntuples( ctidCheck ) == 1 )
|
1086 |
1167 |
{
|
1087 |
|
// fallback to ctid
|
1088 |
|
primaryKey = "ctid";
|
1089 |
|
primaryKeyType = "tid";
|
1090 |
|
mIsDbPrimaryKey = true;
|
|
1168 |
int id = QString( PQgetvalue( ctidCheck, 0, 0 ) ).toInt();
|
|
1169 |
|
|
1170 |
if ( id < 0x10000 )
|
|
1171 |
{
|
|
1172 |
// fallback to ctid
|
|
1173 |
primaryKey = "ctid";
|
|
1174 |
primaryKeyType = "tid";
|
|
1175 |
mIsDbPrimaryKey = true;
|
|
1176 |
}
|
1091 |
1177 |
}
|
1092 |
1178 |
}
|
1093 |
1179 |
}
|
1094 |
|
}
|
1095 |
|
|
1096 |
|
if ( primaryKey.isEmpty() )
|
1097 |
|
{
|
1098 |
|
showMessageBox( tr( "No suitable key column in table" ),
|
1099 |
|
tr( "The table has no column suitable for use as a key.\n\n"
|
1100 |
|
"Quantum GIS requires that the table either has a column of type\n"
|
1101 |
|
"int4 with a unique constraint on it (which includes the\n"
|
1102 |
|
"primary key), has a PostgreSQL oid column or has a ctid\n"
|
1103 |
|
"column with a 16bit block number.\n" ) );
|
1104 |
|
}
|
1105 |
|
else
|
1106 |
|
{
|
1107 |
|
mPrimaryKeyDefault = defaultValue( primaryKey ).toString();
|
1108 |
|
if ( mPrimaryKeyDefault.isNull() )
|
|
1180 |
|
|
1181 |
if ( primaryKey.isEmpty() )
|
1109 |
1182 |
{
|
1110 |
|
mPrimaryKeyDefault = QString( "max(%1)+1 from %2.%3" )
|
1111 |
|
.arg( quotedIdentifier( primaryKey ) )
|
1112 |
|
.arg( quotedIdentifier( mSchemaName ) )
|
1113 |
|
.arg( quotedIdentifier( mTableName ) );
|
|
1183 |
showMessageBox( tr( "No suitable key column in table" ),
|
|
1184 |
tr( "The table has no column suitable for use as a key.\n\n"
|
|
1185 |
"Quantum GIS requires that the table either has a column of type\n"
|
|
1186 |
"int4 with a unique constraint on it (which includes the\n"
|
|
1187 |
"primary key), has a PostgreSQL oid column or has a ctid\n"
|
|
1188 |
"column with a 16bit block number.\n" ) );
|
1114 |
1189 |
}
|
|
1190 |
else
|
|
1191 |
{
|
|
1192 |
mPrimaryKeyDefault = defaultValue( primaryKey ).toString();
|
|
1193 |
if ( mPrimaryKeyDefault.isNull() )
|
|
1194 |
{
|
|
1195 |
mPrimaryKeyDefault = QString( "max(%1)+1 from %2.%3" )
|
|
1196 |
.arg( quotedIdentifier( primaryKey ) )
|
|
1197 |
.arg( quotedIdentifier( mSchemaName ) )
|
|
1198 |
.arg( quotedIdentifier( mTableName ) );
|
|
1199 |
}
|
|
1200 |
}
|
1115 |
1201 |
}
|
1116 |
|
}
|
1117 |
|
else if ( type == "v" ) // the relation is a view
|
1118 |
|
{
|
1119 |
|
if ( !primaryKey.isEmpty() )
|
|
1202 |
else if ( type == "v" ) // the relation is a view
|
1120 |
1203 |
{
|
1121 |
|
// check last used candidate
|
1122 |
|
sql = QString( "select pg_type.typname from pg_attribute,pg_type where atttypid=pg_type.oid and attname=%1 and attrelid=regclass(%2)" )
|
1123 |
|
.arg( quotedValue( primaryKey ) ).arg( quotedValue( mSchemaTableName ) );
|
1124 |
|
|
1125 |
|
QgsDebugMsg( "checking candidate: " + sql );
|
1126 |
|
|
1127 |
|
Result result = connectionRO->PQexec( sql );
|
1128 |
|
|
1129 |
|
QString type;
|
1130 |
|
if ( PQresultStatus( result ) == PGRES_TUPLES_OK &&
|
1131 |
|
PQntuples( result ) == 1 )
|
|
1204 |
if ( !primaryKey.isEmpty() )
|
1132 |
1205 |
{
|
1133 |
|
type = PQgetvalue( result, 0, 0 );
|
|
1206 |
// check last used candidate
|
|
1207 |
sql = QString( "select pg_type.typname from pg_attribute,pg_type where atttypid=pg_type.oid and attname=%1 and attrelid=regclass(%2)" )
|
|
1208 |
.arg( quotedValue( primaryKey ) ).arg( quotedValue( mSchemaTableName ) );
|
|
1209 |
|
|
1210 |
QgsDebugMsg( "checking candidate: " + sql );
|
|
1211 |
|
|
1212 |
Result result = connectionRO->PQexec( sql );
|
|
1213 |
|
|
1214 |
QString type;
|
|
1215 |
if ( PQresultStatus( result ) == PGRES_TUPLES_OK &&
|
|
1216 |
PQntuples( result ) == 1 )
|
|
1217 |
{
|
|
1218 |
type = PQgetvalue( result, 0, 0 );
|
|
1219 |
}
|
|
1220 |
|
|
1221 |
// mPrimaryKeyDefault stays null and is retrieved later on demand
|
|
1222 |
|
|
1223 |
if (( type != "int4" && type != "oid" ) ||
|
|
1224 |
!uniqueData( mSchemaName, mTableName, primaryKey ) )
|
|
1225 |
{
|
|
1226 |
primaryKey = "";
|
|
1227 |
}
|
1134 |
1228 |
}
|
1135 |
|
|
1136 |
|
// mPrimaryKeyDefault stays null and is retrieved later on demand
|
1137 |
|
|
1138 |
|
if (( type != "int4" && type != "oid" ) ||
|
1139 |
|
!uniqueData( mSchemaName, mTableName, primaryKey ) )
|
|
1229 |
|
|
1230 |
if ( primaryKey.isEmpty() )
|
1140 |
1231 |
{
|
1141 |
|
primaryKey = "";
|
|
1232 |
parseView();
|
1142 |
1233 |
}
|
1143 |
1234 |
}
|
1144 |
|
|
1145 |
|
if ( primaryKey.isEmpty() )
|
1146 |
|
{
|
1147 |
|
parseView();
|
1148 |
|
}
|
|
1235 |
else
|
|
1236 |
QgsDebugMsg( "Unexpected relation type of '" + type + "'." );
|
1149 |
1237 |
}
|
1150 |
|
else
|
1151 |
|
QgsDebugMsg( "Unexpected relation type of '" + type + "'." );
|
1152 |
|
}
|
1153 |
|
else // have some unique indices on the table. Now choose one...
|
1154 |
|
{
|
1155 |
|
// choose which (if more than one) unique index to use
|
1156 |
|
std::vector<std::pair<QString, QString> > suitableKeyColumns;
|
1157 |
|
for ( int i = 0; i < PQntuples( pk ); ++i )
|
|
1238 |
else // have some unique indices on the table. Now choose one...
|
1158 |
1239 |
{
|
1159 |
|
QString col = QString::fromUtf8( PQgetvalue( pk, i, 0 ) );
|
1160 |
|
QStringList columns = col.split( " ", QString::SkipEmptyParts );
|
1161 |
|
if ( columns.count() == 1 )
|
|
1240 |
// choose which (if more than one) unique index to use
|
|
1241 |
std::vector<std::pair<QString, QString> > suitableKeyColumns;
|
|
1242 |
for ( int i = 0; i < PQntuples( pk ); ++i )
|
1162 |
1243 |
{
|
1163 |
|
// Get the column name and data type
|
1164 |
|
sql = QString( "select attname,pg_type.typname from pg_attribute,pg_type where atttypid=pg_type.oid and attnum=%1 and attrelid=regclass(%2)" )
|
1165 |
|
.arg( col ).arg( quotedValue( mSchemaTableName ) );
|
1166 |
|
Result types = connectionRO->PQexec( sql );
|
1167 |
|
|
1168 |
|
if ( PQntuples( types ) > 0 )
|
|
1244 |
QString col = QString::fromUtf8( PQgetvalue( pk, i, 0 ) );
|
|
1245 |
QStringList columns = col.split( " ", QString::SkipEmptyParts );
|
|
1246 |
if ( columns.count() == 1 )
|
1169 |
1247 |
{
|
1170 |
|
QString columnName = QString::fromUtf8( PQgetvalue( types, 0, 0 ) );
|
1171 |
|
QString columnType = QString::fromUtf8( PQgetvalue( types, 0, 1 ) );
|
1172 |
|
|
1173 |
|
if ( columnType != "int4" )
|
1174 |
|
log.append( tr( "The unique index on column '%1' is unsuitable because Quantum GIS does not currently "
|
1175 |
|
"support non-int4 type columns as a key into the table.\n" ).arg( columnName ) );
|
|
1248 |
// Get the column name and data type
|
|
1249 |
sql = QString( "select attname,pg_type.typname from pg_attribute,pg_type where atttypid=pg_type.oid and attnum=%1 and attrelid=regclass(%2)" )
|
|
1250 |
.arg( col ).arg( quotedValue( mSchemaTableName ) );
|
|
1251 |
Result types = connectionRO->PQexec( sql );
|
|
1252 |
|
|
1253 |
if ( PQntuples( types ) > 0 )
|
|
1254 |
{
|
|
1255 |
QString columnName = QString::fromUtf8( PQgetvalue( types, 0, 0 ) );
|
|
1256 |
QString columnType = QString::fromUtf8( PQgetvalue( types, 0, 1 ) );
|
|
1257 |
|
|
1258 |
if ( columnType != "int4" )
|
|
1259 |
log.append( tr( "The unique index on column '%1' is unsuitable because Quantum GIS does not currently "
|
|
1260 |
"support non-int4 type columns as a key into the table.\n" ).arg( columnName ) );
|
|
1261 |
else
|
|
1262 |
{
|
|
1263 |
mIsDbPrimaryKey = true;
|
|
1264 |
suitableKeyColumns.push_back( std::make_pair( columnName, columnType ) );
|
|
1265 |
}
|
|
1266 |
}
|
1176 |
1267 |
else
|
1177 |
1268 |
{
|
1178 |
|
mIsDbPrimaryKey = true;
|
1179 |
|
suitableKeyColumns.push_back( std::make_pair( columnName, columnType ) );
|
|
1269 |
//QgsDebugMsg( QString("name and type of %3. column of %1.%2 not found").arg(mSchemaName).arg(mTables).arg(col) );
|
1180 |
1270 |
}
|
1181 |
1271 |
}
|
1182 |
1272 |
else
|
1183 |
1273 |
{
|
1184 |
|
//QgsDebugMsg( QString("name and type of %3. column of %1.%2 not found").arg(mSchemaName).arg(mTables).arg(col) );
|
|
1274 |
sql = QString( "select attname from pg_attribute, pg_type where atttypid=pg_type.oid and attnum in (%1) and attrelid=regclass(%2)::oid" )
|
|
1275 |
.arg( col.replace( " ", "," ) )
|
|
1276 |
.arg( quotedValue( mSchemaTableName ) );
|
|
1277 |
|
|
1278 |
Result types = connectionRO->PQexec( sql );
|
|
1279 |
QString colNames;
|
|
1280 |
int numCols = PQntuples( types );
|
|
1281 |
for ( int j = 0; j < numCols; ++j )
|
|
1282 |
{
|
|
1283 |
if ( j == numCols - 1 )
|
|
1284 |
colNames += tr( "and " );
|
|
1285 |
colNames += quotedValue( QString::fromUtf8( PQgetvalue( types, j, 0 ) ) );
|
|
1286 |
if ( j < numCols - 2 )
|
|
1287 |
colNames += ",";
|
|
1288 |
}
|
|
1289 |
|
|
1290 |
log.append( tr( "The unique index based on columns %1 is unsuitable because Quantum GIS does not currently "
|
|
1291 |
"support multiple columns as a key into the table.\n" ).arg( colNames ) );
|
1185 |
1292 |
}
|
1186 |
1293 |
}
|
|
1294 |
|
|
1295 |
// suitableKeyColumns now contains the name of columns (and their
|
|
1296 |
// data type) that
|
|
1297 |
// are suitable for use as a key into the table. If there is
|
|
1298 |
// more than one we need to choose one. For the moment, just
|
|
1299 |
// choose the first in the list.
|
|
1300 |
|
|
1301 |
if ( suitableKeyColumns.size() > 0 )
|
|
1302 |
{
|
|
1303 |
primaryKey = suitableKeyColumns[0].first;
|
|
1304 |
primaryKeyType = suitableKeyColumns[0].second;
|
|
1305 |
}
|
1187 |
1306 |
else
|
1188 |
1307 |
{
|
1189 |
|
sql = QString( "select attname from pg_attribute, pg_type where atttypid=pg_type.oid and attnum in (%1) and attrelid=regclass(%2)::oid" )
|
1190 |
|
.arg( col.replace( " ", "," ) )
|
1191 |
|
.arg( quotedValue( mSchemaTableName ) );
|
1192 |
|
|
1193 |
|
Result types = connectionRO->PQexec( sql );
|
1194 |
|
QString colNames;
|
1195 |
|
int numCols = PQntuples( types );
|
1196 |
|
for ( int j = 0; j < numCols; ++j )
|
|
1308 |
// If there is an oid on the table, use that instead,
|
|
1309 |
// otherwise give up
|
|
1310 |
sql = QString( "select attname from pg_attribute where attname='oid' and attrelid=regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
|
|
1311 |
Result oidCheck = connectionRO->PQexec( sql );
|
|
1312 |
|
|
1313 |
if ( PQntuples( oidCheck ) != 0 )
|
1197 |
1314 |
{
|
1198 |
|
if ( j == numCols - 1 )
|
1199 |
|
colNames += tr( "and " );
|
1200 |
|
colNames += quotedValue( QString::fromUtf8( PQgetvalue( types, j, 0 ) ) );
|
1201 |
|
if ( j < numCols - 2 )
|
1202 |
|
colNames += ",";
|
|
1315 |
primaryKey = "oid";
|
|
1316 |
primaryKeyType = "int4";
|
1203 |
1317 |
}
|
1204 |
|
|
1205 |
|
log.append( tr( "The unique index based on columns %1 is unsuitable because Quantum GIS does not currently "
|
1206 |
|
"support multiple columns as a key into the table.\n" ).arg( colNames ) );
|
|
1318 |
else
|
|
1319 |
{
|
|
1320 |
log.prepend( "There were no columns in the table that were suitable "
|
|
1321 |
"as a qgis key into the table (either a column with a "
|
|
1322 |
"unique index and type int4 or a PostgreSQL oid column.\n" );
|
|
1323 |
}
|
1207 |
1324 |
}
|
1208 |
|
}
|
1209 |
|
|
1210 |
|
// suitableKeyColumns now contains the name of columns (and their
|
1211 |
|
// data type) that
|
1212 |
|
// are suitable for use as a key into the table. If there is
|
1213 |
|
// more than one we need to choose one. For the moment, just
|
1214 |
|
// choose the first in the list.
|
1215 |
|
|
1216 |
|
if ( suitableKeyColumns.size() > 0 )
|
1217 |
|
{
|
1218 |
|
primaryKey = suitableKeyColumns[0].first;
|
1219 |
|
primaryKeyType = suitableKeyColumns[0].second;
|
1220 |
|
}
|
1221 |
|
else
|
1222 |
|
{
|
1223 |
|
// If there is an oid on the table, use that instead,
|
1224 |
|
// otherwise give up
|
1225 |
|
sql = QString( "select attname from pg_attribute where attname='oid' and attrelid=regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
|
1226 |
|
Result oidCheck = connectionRO->PQexec( sql );
|
1227 |
|
|
1228 |
|
if ( PQntuples( oidCheck ) != 0 )
|
|
1325 |
|
|
1326 |
// Either primaryKey has been set by the above code, or it
|
|
1327 |
// hasn't. If not, present some info to the user to give them some
|
|
1328 |
// idea of why not.
|
|
1329 |
if ( primaryKey.isEmpty() )
|
1229 |
1330 |
{
|
1230 |
|
primaryKey = "oid";
|
1231 |
|
primaryKeyType = "int4";
|
|
1331 |
// Give some info to the user about why things didn't work out.
|
|
1332 |
valid = false;
|
|
1333 |
showMessageBox( tr( "Unable to find a key column" ), log );
|
1232 |
1334 |
}
|
1233 |
1335 |
else
|
1234 |
1336 |
{
|
1235 |
|
log.prepend( "There were no columns in the table that were suitable "
|
1236 |
|
"as a qgis key into the table (either a column with a "
|
1237 |
|
"unique index and type int4 or a PostgreSQL oid column.\n" );
|
|
1337 |
mPrimaryKeyDefault = defaultValue( primaryKey ).toString();
|
|
1338 |
if ( mPrimaryKeyDefault.isNull() )
|
|
1339 |
{
|
|
1340 |
mPrimaryKeyDefault = QString( "max(%1)+1 from %2.%3" )
|
|
1341 |
.arg( quotedIdentifier( primaryKey ) )
|
|
1342 |
.arg( quotedIdentifier( mSchemaName ) )
|
|
1343 |
.arg( quotedIdentifier( mTableName ) );
|
|
1344 |
}
|
1238 |
1345 |
}
|
1239 |
1346 |
}
|
1240 |
|
|
1241 |
|
// Either primaryKey has been set by the above code, or it
|
1242 |
|
// hasn't. If not, present some info to the user to give them some
|
1243 |
|
// idea of why not.
|
1244 |
|
if ( primaryKey.isEmpty() )
|
1245 |
|
{
|
1246 |
|
// Give some info to the user about why things didn't work out.
|
1247 |
|
valid = false;
|
1248 |
|
showMessageBox( tr( "Unable to find a key column" ), log );
|
1249 |
|
}
|
1250 |
|
else
|
1251 |
|
{
|
1252 |
|
mPrimaryKeyDefault = defaultValue( primaryKey ).toString();
|
1253 |
|
if ( mPrimaryKeyDefault.isNull() )
|
1254 |
|
{
|
1255 |
|
mPrimaryKeyDefault = QString( "max(%1)+1 from %2.%3" )
|
1256 |
|
.arg( quotedIdentifier( primaryKey ) )
|
1257 |
|
.arg( quotedIdentifier( mSchemaName ) )
|
1258 |
|
.arg( quotedIdentifier( mTableName ) );
|
1259 |
|
}
|
1260 |
|
}
|
1261 |
1347 |
}
|
1262 |
1348 |
|
1263 |
1349 |
if ( !primaryKey.isNull() )
|
... | ... | |
1780 |
1866 |
{
|
1781 |
1867 |
// get the field name
|
1782 |
1868 |
const QgsField &fld = field( index );
|
1783 |
|
QString sql;
|
1784 |
|
if ( sqlWhereClause.isEmpty() )
|
|
1869 |
QString sql = QString( "select min(%1) from %2" )
|
|
1870 |
.arg( quotedIdentifier( fld.name() ) )
|
|
1871 |
.arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
|
1872 |
|
|
1873 |
if ( !sqlWhereClause.isEmpty() )
|
1785 |
1874 |
{
|
1786 |
|
sql = QString( "select min(%1) from %2" )
|
1787 |
|
.arg( quotedIdentifier( fld.name() ) )
|
1788 |
|
.arg( mSchemaTableName );
|
|
1875 |
sql += QString( " where %1" ).arg( sqlWhereClause );
|
1789 |
1876 |
}
|
1790 |
|
else
|
1791 |
|
{
|
1792 |
|
sql = QString( "select min(%1) from %2 where %3" )
|
1793 |
|
.arg( quotedIdentifier( fld.name() ) )
|
1794 |
|
.arg( mSchemaTableName )
|
1795 |
|
.arg( sqlWhereClause );
|
1796 |
|
}
|
|
1877 |
|
1797 |
1878 |
Result rmin = connectionRO->PQexec( sql );
|
1798 |
1879 |
return convertValue( fld.type(), QString::fromUtf8( PQgetvalue( rmin, 0, 0 ) ) );
|
1799 |
1880 |
}
|
... | ... | |
1812 |
1893 |
{
|
1813 |
1894 |
// get the field name
|
1814 |
1895 |
const QgsField &fld = field( index );
|
1815 |
|
QString sql;
|
1816 |
|
if ( sqlWhereClause.isEmpty() )
|
|
1896 |
QString sql = QString( "select distinct %1 from %2" )
|
|
1897 |
.arg( quotedIdentifier( fld.name() ) )
|
|
1898 |
.arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
|
1899 |
|
|
1900 |
if ( !sqlWhereClause.isEmpty() )
|
1817 |
1901 |
{
|
1818 |
|
sql = QString( "select distinct %1 from %2 order by %1" )
|
1819 |
|
.arg( quotedIdentifier( fld.name() ) )
|
1820 |
|
.arg( mSchemaTableName );
|
|
1902 |
sql += QString( " where %1" ).arg( sqlWhereClause );
|
1821 |
1903 |
}
|
1822 |
|
else
|
1823 |
|
{
|
1824 |
|
sql = QString( "select distinct %1 from %2 where %3 order by %1" )
|
1825 |
|
.arg( quotedIdentifier( fld.name() ) )
|
1826 |
|
.arg( mSchemaTableName )
|
1827 |
|
.arg( sqlWhereClause );
|
1828 |
|
}
|
1829 |
1904 |
|
|
1905 |
sql += QString( " order by %1" )
|
|
1906 |
.arg( quotedIdentifier( fld.name() ) );
|
|
1907 |
|
1830 |
1908 |
if ( limit >= 0 )
|
1831 |
1909 |
{
|
1832 |
1910 |
sql += QString( " LIMIT %1" ).arg( limit );
|
... | ... | |
1891 |
1969 |
bool QgsPostgresProvider::parseEnumRange( QStringList& enumValues, const QString& attributeName ) const
|
1892 |
1970 |
{
|
1893 |
1971 |
enumValues.clear();
|
1894 |
|
QString enumRangeSql = QString( "SELECT enum_range(%1) from %2 limit 1" ).arg( quotedIdentifier( attributeName ) ).arg( mSchemaTableName );
|
|
1972 |
QString enumRangeSql = QString( "SELECT enum_range(%1) from %2 limit 1" )
|
|
1973 |
.arg( quotedIdentifier( attributeName ) )
|
|
1974 |
.arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
1895 |
1975 |
Result enumRangeRes = connectionRO->PQexec( enumRangeSql );
|
1896 |
1976 |
if ( PQresultStatus( enumRangeRes ) == PGRES_TUPLES_OK && PQntuples( enumRangeRes ) > 0 )
|
1897 |
1977 |
{
|
... | ... | |
1979 |
2059 |
{
|
1980 |
2060 |
// get the field name
|
1981 |
2061 |
const QgsField &fld = field( index );
|
1982 |
|
QString sql;
|
1983 |
|
if ( sqlWhereClause.isEmpty() )
|
|
2062 |
QString sql = QString( "select max(%1) from %2" )
|
|
2063 |
.arg( quotedIdentifier( fld.name() ) )
|
|
2064 |
.arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
|
2065 |
|
|
2066 |
if ( !sqlWhereClause.isEmpty() )
|
1984 |
2067 |
{
|
1985 |
|
sql = QString( "select max(%1) from %2" )
|
1986 |
|
.arg( quotedIdentifier( fld.name() ) )
|
1987 |
|
.arg( mSchemaTableName );
|
|
2068 |
sql += QString( " where %1" ).arg( sqlWhereClause );
|
1988 |
2069 |
}
|
1989 |
|
else
|
1990 |
|
{
|
1991 |
|
sql = QString( "select max(%1) from %2 where %3" )
|
1992 |
|
.arg( quotedIdentifier( fld.name() ) )
|
1993 |
|
.arg( mSchemaTableName )
|
1994 |
|
.arg( sqlWhereClause );
|
1995 |
|
}
|
|
2070 |
|
1996 |
2071 |
Result rmax = connectionRO->PQexec( sql );
|
1997 |
2072 |
return convertValue( fld.type(), QString::fromUtf8( PQgetvalue( rmax, 0, 0 ) ) );
|
1998 |
2073 |
}
|
... | ... | |
2663 |
2738 |
// a thread the task of getting the full count.
|
2664 |
2739 |
QString sql;
|
2665 |
2740 |
|
2666 |
|
if ( mUseEstimatedMetadata )
|
|
2741 |
if ( !isCustomQuery && mUseEstimatedMetadata )
|
2667 |
2742 |
{
|
2668 |
2743 |
sql = QString( "select reltuples::int from pg_catalog.pg_class where oid=regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
|
2669 |
2744 |
}
|
2670 |
2745 |
else
|
2671 |
2746 |
{
|
2672 |
|
sql = QString( "select count(*) from %1" ).arg( mSchemaTableName );
|
|
2747 |
sql = QString( "select count(*) from %1" ).arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
2673 |
2748 |
|
2674 |
|
if ( sqlWhereClause.length() > 0 )
|
|
2749 |
if ( !sqlWhereClause.isEmpty() )
|
2675 |
2750 |
{
|
2676 |
2751 |
sql += " where " + sqlWhereClause;
|
2677 |
2752 |
}
|
... | ... | |
2696 |
2771 |
QString ext;
|
2697 |
2772 |
|
2698 |
2773 |
// get the extents
|
2699 |
|
if ( mUseEstimatedMetadata || sqlWhereClause.isEmpty() )
|
|
2774 |
if ( !isCustomQuery && ( mUseEstimatedMetadata || sqlWhereClause.isEmpty() ) )
|
2700 |
2775 |
{
|
2701 |
2776 |
// do stats exists?
|
2702 |
2777 |
sql = QString( "SELECT COUNT(*) FROM pg_stats WHERE schemaname=%1 AND tablename=%2 AND attname=%3" )
|
... | ... | |
2727 |
2802 |
{
|
2728 |
2803 |
sql = QString( "select extent(%1) from %2" )
|
2729 |
2804 |
.arg( quotedIdentifier( geometryColumn ) )
|
2730 |
|
.arg( mSchemaTableName );
|
|
2805 |
.arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
2731 |
2806 |
|
2732 |
2807 |
if ( !sqlWhereClause.isEmpty() )
|
2733 |
|
sql += QString( "where %1" ).arg( sqlWhereClause );
|
|
2808 |
sql += QString( " where %1" ).arg( sqlWhereClause );
|
2734 |
2809 |
|
2735 |
2810 |
result = connectionRO->PQexec( sql );
|
2736 |
2811 |
if ( PQresultStatus( result ) != PGRES_TUPLES_OK )
|
... | ... | |
2823 |
2898 |
// version 7.4, binary cursors return data in XDR whereas previous versions
|
2824 |
2899 |
// return data in the endian of the server
|
2825 |
2900 |
|
2826 |
|
QString firstOid = QString( "select regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) );
|
2827 |
|
Result oidResult = connectionRO->PQexec( firstOid );
|
2828 |
|
// get the int value from a "normal" select
|
2829 |
|
QString oidValue = QString::fromUtf8( PQgetvalue( oidResult, 0, 0 ) );
|
|
2901 |
QString oidValue;
|
|
2902 |
QString schemaTableName;
|
2830 |
2903 |
|
|
2904 |
if ( isCustomQuery )
|
|
2905 |
{
|
|
2906 |
QString sql = QString( "select * from %1 limit 0" ).arg( sqlCustomSelect );
|
|
2907 |
Result res = connectionRO->PQexec( sql );
|
|
2908 |
|
|
2909 |
// loop through the returned fields to get a valid oid
|
|
2910 |
// TODO check if we have found a table oid at loop exit
|
|
2911 |
for ( int i = 0; i < PQnfields( res ); i++ )
|
|
2912 |
{
|
|
2913 |
int tableOid = PQftable( res, i );
|
|
2914 |
if ( tableOid >= 0 )
|
|
2915 |
{
|
|
2916 |
oidValue = QString::number( tableOid );
|
|
2917 |
break;
|
|
2918 |
}
|
|
2919 |
}
|
|
2920 |
|
|
2921 |
// get the table name
|
|
2922 |
res = connectionRO->PQexec( QString( "SELECT relname FROM pg_class WHERE oid=%1" ).arg( oidValue ) );
|
|
2923 |
schemaTableName = QString::fromUtf8( PQgetvalue( res, 0, 0 ) );
|
|
2924 |
}
|
|
2925 |
else
|
|
2926 |
{
|
|
2927 |
schemaTableName = mSchemaTableName;
|
|
2928 |
|
|
2929 |
QString firstOid = QString( "select regclass(%1)::oid" ).arg( quotedValue( schemaTableName ) );
|
|
2930 |
Result oidResult = connectionRO->PQexec( firstOid );
|
|
2931 |
// get the int value from a "normal" select
|
|
2932 |
oidValue = QString::fromUtf8( PQgetvalue( oidResult, 0, 0 ) );
|
|
2933 |
}
|
|
2934 |
|
2831 |
2935 |
QgsDebugMsg( "Creating binary cursor" );
|
2832 |
2936 |
|
2833 |
2937 |
// get the same value using a binary cursor
|
2834 |
|
connectionRO->openCursor( "oidcursor", QString( "select regclass(%1)::oid" ).arg( quotedValue( mSchemaTableName ) ) );
|
|
2938 |
connectionRO->openCursor( "oidcursor", QString( "select regclass(%1)::oid" ).arg( quotedValue( schemaTableName ) ) );
|
2835 |
2939 |
|
2836 |
2940 |
QgsDebugMsg( "Fetching a record and attempting to get check endian-ness" );
|
2837 |
2941 |
|
... | ... | |
2861 |
2965 |
valid = false;
|
2862 |
2966 |
QStringList log;
|
2863 |
2967 |
|
2864 |
|
QString sql = QString( "select type,srid from geometry_columns"
|
2865 |
|
" where f_table_name=%1 and f_geometry_column=%2 and f_table_schema=%3" )
|
2866 |
|
.arg( quotedValue( mTableName ) )
|
2867 |
|
.arg( quotedValue( geometryColumn ) )
|
2868 |
|
.arg( quotedValue( mSchemaName ) );
|
|
2968 |
Result result;
|
|
2969 |
QString sql;
|
2869 |
2970 |
|
2870 |
|
QgsDebugMsg( "Getting geometry column: " + sql );
|
|
2971 |
if ( !isCustomQuery )
|
|
2972 |
{
|
|
2973 |
sql = QString( "select type,srid from geometry_columns"
|
|
2974 |
" where f_table_name=%1 and f_geometry_column=%2 and f_table_schema=%3" )
|
|
2975 |
.arg( quotedValue( mTableName ) )
|
|
2976 |
.arg( quotedValue( geometryColumn ) )
|
|
2977 |
.arg( quotedValue( mSchemaName ) );
|
2871 |
2978 |
|
2872 |
|
Result result = connectionRO->PQexec( sql );
|
|
2979 |
QgsDebugMsg( "Getting geometry column: " + sql );
|
2873 |
2980 |
|
2874 |
|
QgsDebugMsg( "geometry column query returned " + QString::number( PQntuples( result ) ) );
|
|
2981 |
Result result = connectionRO->PQexec( sql );
|
2875 |
2982 |
|
2876 |
|
if ( PQntuples( result ) > 0 )
|
2877 |
|
{
|
2878 |
|
fType = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
|
2879 |
|
srid = QString::fromUtf8( PQgetvalue( result, 0, 1 ) );
|
|
2983 |
QgsDebugMsg( "geometry column query returned " + QString::number( PQntuples( result ) ) );
|
|
2984 |
|
|
2985 |
if ( PQntuples( result ) > 0 )
|
|
2986 |
{
|
|
2987 |
fType = QString::fromUtf8( PQgetvalue( result, 0, 0 ) );
|
|
2988 |
srid = QString::fromUtf8( PQgetvalue( result, 0, 1 ) );
|
|
2989 |
}
|
2880 |
2990 |
}
|
2881 |
|
else
|
|
2991 |
|
|
2992 |
if ( srid.isEmpty() || fType.isEmpty() )
|
2882 |
2993 |
{
|
2883 |
2994 |
// Didn't find what we need in the geometry_columns table, so
|
2884 |
2995 |
// get stuff from the relevant column instead. This may (will?)
|
2885 |
2996 |
// fail if there is no data in the relevant table.
|
2886 |
2997 |
sql = QString( "select srid(%1),geometrytype(%1) from %2" )
|
2887 |
2998 |
.arg( quotedIdentifier( geometryColumn ) )
|
2888 |
|
.arg( mSchemaTableName );
|
|
2999 |
.arg( !isCustomQuery ? mSchemaTableName : sqlCustomSelect );
|
2889 |
3000 |
|
2890 |
3001 |
//it is possible that the where clause restricts the feature type
|
2891 |
3002 |
if ( !sqlWhereClause.isEmpty() )
|
... | ... | |
2926 |
3037 |
}
|
2927 |
3038 |
else
|
2928 |
3039 |
{
|
2929 |
|
sql += mSchemaTableName;
|
|
3040 |
sql += !isCustomQuery ? mSchemaTableName : sqlCustomSelect;
|
2930 |
3041 |
}
|
2931 |
3042 |
|
2932 |
|
if ( mUri.sql() != "" )
|
2933 |
|
sql += " where " + mUri.sql();
|
|
3043 |
if ( !sqlWhereClause.isEmpty() )
|
|
3044 |
sql += " where " + sqlWhereClause;
|
2934 |
3045 |
|
2935 |
3046 |
result = connectionRO->PQexec( sql );
|
2936 |
3047 |
|