[GRASS-SVN] r51133 - in grass/trunk: include/vect lib/vector/Vlib

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Mar 21 17:13:05 EDT 2012


Author: martinl
Date: 2012-03-21 14:13:05 -0700 (Wed, 21 Mar 2012)
New Revision: 51133

Modified:
   grass/trunk/include/vect/dig_structs.h
   grass/trunk/lib/vector/Vlib/build_pg.c
   grass/trunk/lib/vector/Vlib/close_pg.c
   grass/trunk/lib/vector/Vlib/open.c
   grass/trunk/lib/vector/Vlib/open_pg.c
   grass/trunk/lib/vector/Vlib/read_pg.c
   grass/trunk/lib/vector/Vlib/write_ogr.c
   grass/trunk/lib/vector/Vlib/write_pg.c
Log:
vlib(pg): implement writing attributes
	  various minor improvements


Modified: grass/trunk/include/vect/dig_structs.h
===================================================================
--- grass/trunk/include/vect/dig_structs.h	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/include/vect/dig_structs.h	2012-03-21 21:13:05 UTC (rev 51133)
@@ -489,6 +489,10 @@
       \brief Feature id
     */
     long fid;
+    /*!
+      \brief Simple feature type (currently used only by PG format)
+    */
+    SF_FeatureType sf_type;
 };
 
 /*!
@@ -617,6 +621,19 @@
       \brief SRS ID
     */
     int      srid;
+
+    /*!
+      \brief Open DB driver when writing attributes
+
+      This driver is open by V2_open_new_pg() and closed by
+      V1_close_pg().
+    */
+    dbDriver *dbdriver;
+
+    /*!
+      \brief Start/Finish transaction
+    */
+    int       inTransaction;
 #ifdef HAVE_POSTGRES
     /*!
       \brief PGconn object (generated by PQconnectdb)

Modified: grass/trunk/lib/vector/Vlib/build_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/build_pg.c	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/lib/vector/Vlib/build_pg.c	2012-03-21 21:13:05 UTC (rev 51133)
@@ -23,7 +23,7 @@
 #include <grass/glocale.h>
 
 #ifdef HAVE_POSTGRES
-#include <libpq-fe.h>
+#include "pg_local_proto.h"
 
 static int build_topo(struct Map_info *, int);
 #endif
@@ -78,7 +78,14 @@
 		    "Unable to build topology."));
 	return 0;
     }
+
+    /* commit transaction block (update mode only) */
+    if (pg_info->inTransaction &&
+	execute(pg_info->conn, "COMMIT") == -1)
+	return -1;
     
+    pg_info->inTransaction = FALSE;
+    
     if (build > GV_BUILD_NONE)
 	G_message(_("Using external data format '%s' (feature type '%s')"),
 		  Vect_get_finfo_format_info(Map),

Modified: grass/trunk/lib/vector/Vlib/close_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/close_pg.c	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/lib/vector/Vlib/close_pg.c	2012-03-21 21:13:05 UTC (rev 51133)
@@ -63,6 +63,11 @@
     
     PQfinish(pg_info->conn);
     
+    /* close DB connection (for atgtributes) */
+    if (pg_info->dbdriver) {
+	db_close_database_shutdown_driver(pg_info->dbdriver);
+    }
+    
     /* free allocated space */
     for (i = 0; i < pg_info->cache.lines_alloc; i++) {
 	Vect_destroy_line_struct(pg_info->cache.lines[i]);

Modified: grass/trunk/lib/vector/Vlib/open.c
===================================================================
--- grass/trunk/lib/vector/Vlib/open.c	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/lib/vector/Vlib/open.c	2012-03-21 21:13:05 UTC (rev 51133)
@@ -839,8 +839,8 @@
 
     Map->open = VECT_OPEN_CODE;
     Map->level = 1;
-    Map->head_only = 0;
-    Map->support_updated = 0;
+    Map->head_only = FALSE;
+    Map->support_updated = FALSE;
     Map->plus.built = GV_BUILD_NONE;
     Map->mode = GV_MODE_RW;
     Map->plus.uplist.do_uplist = FALSE;

Modified: grass/trunk/lib/vector/Vlib/open_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/open_pg.c	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/lib/vector/Vlib/open_pg.c	2012-03-21 21:13:05 UTC (rev 51133)
@@ -111,18 +111,17 @@
 	/* feature type */
 	pg_info->feature_type = ftype_from_string(PQgetvalue(res, 0, 3));
     }
+    PQclear(res);
     
     /* no feature in cache */
     pg_info->cache.fid = -1;
-    
-    PQclear(res);
-    
+
     if (!found) {
 	G_warning(_("Feature table <%s> not found in 'geometry_columns'"),
 		  pg_info->table_name);
 	return -1;
     }
-    
+
     return 0;
 #else
     G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
@@ -467,25 +466,107 @@
 	key_val = G_fread_key_value(fp);
 	fclose(fp);
 	
-	/* spatial index */
+	/* disable spatial index ? */
 	p = G_find_key_value("spatial_index", key_val);
 	if (p && G_strcasecmp(p, "off") == 0)
 	    spatial_index = FALSE;
-	/* primary key */
+	/* disable primary key ? */
 	p = G_find_key_value("primary_key", key_val);
 	if (p && G_strcasecmp(p, "off") == 0)
 	    primary_key = FALSE;
     }
 
-    /* begin transaction */
+    /* prepare CREATE TABLE statement */
+    sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s SERIAL",
+	    pg_info->schema_name, pg_info->table_name,
+	    pg_info->fid_column);
+    
+    if (Fi) {
+	/* append attributes */
+	int col, ncols, sqltype, length, ctype;
+	char stmt_col[DB_SQL_MAX];
+	const char *colname;
+	
+	dbString dbstmt;
+	dbHandle  handle;
+	dbDriver *driver;
+	dbCursor  cursor;
+	dbTable  *table;
+	dbColumn *column;
+	  
+	db_init_string(&dbstmt);
+	db_init_handle(&handle);
+	
+	pg_info->dbdriver = driver = db_start_driver(Fi->driver);
+	if (!driver) {
+	    G_warning(_("Unable to start driver <%s>"), Fi->driver);
+	    return -1;
+	}
+	db_set_handle(&handle, Fi->database, NULL);
+	if (db_open_database(driver, &handle) != DB_OK) {
+	    G_warning(_("Unable to open database <%s> by driver <%s>"),
+		      Fi->database, Fi->driver);
+	    db_close_database_shutdown_driver(driver);
+	    pg_info->dbdriver = NULL;
+	    return -1;
+	}
+
+	/* describe table */
+	db_set_string(&dbstmt, "select * from ");
+	db_append_string(&dbstmt, Fi->table);
+	db_append_string(&dbstmt, " where 0 = 1");	
+	
+	if (db_open_select_cursor(driver, &dbstmt,
+				  &cursor, DB_SEQUENTIAL) != DB_OK) {
+	    G_warning(_("Unable to open select cursor: '%s'"),
+		      db_get_string(&dbstmt));
+	    db_close_database_shutdown_driver(driver);
+	    pg_info->dbdriver = NULL;
+	    return -1;
+	}
+	
+	table = db_get_cursor_table(&cursor);
+	ncols = db_get_table_number_of_columns(table);
+	
+	G_debug(3, "copying attributes: driver = %s database = %s table = %s cols = %d",
+		Fi->driver, Fi->database, Fi->table, ncols);
+		
+	for (col = 0; col < ncols; col++) {
+	    column = db_get_table_column(table, col);
+	    colname = db_get_column_name(column);	
+	    sqltype = db_get_column_sqltype(column);
+	    ctype = db_sqltype_to_Ctype(sqltype);
+	    length = db_get_column_length(column);
+	    
+	    G_debug(3, "\tcolumn = %d name = %s type = %d length = %d",
+		    col, colname, sqltype, length);
+	    
+	    if (strcmp(pg_info->fid_column, colname) == 0) {
+		/* skip fid column if exists */
+		G_debug(3, "\t%s skipped", pg_info->fid_column);
+		continue;
+	    }
+	    
+	    /* append column */
+	    sprintf(stmt_col, ",%s %s", colname, db_sqltype_name(sqltype));
+	    strcat(stmt, stmt_col);
+	    if (ctype == DB_C_TYPE_STRING) {
+		/* length only for string columns */
+		sprintf(stmt_col, "(%d)", length);
+		strcat(stmt, stmt_col);
+	    }
+	}
+
+	db_free_string(&dbstmt);
+    }
+    strcat(stmt, ")"); /* close CREATE TABLE statement */
+    
+    /* begin transaction (create table) */
     if (execute(pg_info->conn, "BEGIN") == -1) {
 	return -1;
     }
     
     /* create table */
-    sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s SERIAL)",
-	    pg_info->schema_name, pg_info->table_name,
-	    pg_info->fid_column);
     G_debug(2, "SQL: %s", stmt);
     if (execute(pg_info->conn, stmt) == -1) {
 	execute(pg_info->conn, "ROLLBACK");
@@ -549,7 +630,7 @@
 	}
     }
     
-    /* close transaction */
+    /* close transaction (create table) */
     if (execute(pg_info->conn, "COMMIT") == -1) {
 	return -1;
     }

Modified: grass/trunk/lib/vector/Vlib/read_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/read_pg.c	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/lib/vector/Vlib/read_pg.c	2012-03-21 21:13:05 UTC (rev 51133)
@@ -219,8 +219,7 @@
 #ifdef HAVE_POSTGRES
     long fid;
     int  ipart, type;
-    static SF_FeatureType sf_type;
-    
+        
     struct Format_info_pg     *pg_info;
     
     pg_info = &(Map->fInfo.pg);
@@ -241,25 +240,30 @@
     
     /* read feature to cache if necessary */
     if (pg_info->cache.fid != fid) {
-	G_debug(4, "read feature (fid = %ld) to cache", fid);
-	sf_type = get_feature(pg_info, fid);
+	int type;
 	
-	if (sf_type == SF_NONE) {
+	G_debug(3, "read (%s) feature (fid = %ld) to cache",
+		pg_info->table_name, fid);
+	get_feature(pg_info, fid);
+	
+	if (pg_info->cache.sf_type == SF_NONE) {
 	    G_warning(_("Feature %d without geometry skipped"), fid);
 	    return -1;
 	}
-
-	if ((int) sf_type < 0) /* -1 || - 2 */
-	    return (int) sf_type;
+	
+	type = (int) pg_info->cache.sf_type;
+	if (type < 0) /* -1 || - 2 */
+	    return type;
     }
     
     /* get data from cache */
-    if (sf_type == SF_POINT || sf_type == SF_LINESTRING)
+    if (pg_info->cache.sf_type == SF_POINT ||
+	pg_info->cache.sf_type == SF_LINESTRING)
 	ipart = 0;
     else 
 	ipart = pg_info->offset.array[offset + 1];
     type  = pg_info->cache.lines_types[ipart];
-    G_debug(4, "read feature part: %d -> type = %d",
+    G_debug(3, "read feature part: %d -> type = %d",
 	    ipart, type);    
 	
     if (line_p)
@@ -394,7 +398,6 @@
 {
     char *data;
     char stmt[DB_SQL_MAX];
-    SF_FeatureType ftype;
 
     if (!pg_info->geom_column) {
 	G_warning(_("No geometry column defined"));
@@ -468,7 +471,7 @@
     }
     data = (char *)PQgetvalue(pg_info->res, pg_info->next_line, 0);
     
-    ftype = cache_feature(data, FALSE, &(pg_info->cache), NULL);
+    pg_info->cache.sf_type = cache_feature(data, FALSE, &(pg_info->cache), NULL);
     if (fid < 0) {
 	pg_info->cache.fid = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
 	pg_info->next_line++;
@@ -490,7 +493,7 @@
 	    return -1;
     }
     
-    return ftype;
+    return pg_info->cache.sf_type;
 }
 
 /*!
@@ -567,7 +570,7 @@
     unsigned char *wkb_data;
     unsigned int wkb_flags;
     SF_FeatureType ftype;
-
+    
     /* reset cache */
     cache->lines_num = 0;
     cache->fid       = -1;

Modified: grass/trunk/lib/vector/Vlib/write_ogr.c
===================================================================
--- grass/trunk/lib/vector/Vlib/write_ogr.c	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/lib/vector/Vlib/write_ogr.c	2012-03-21 21:13:05 UTC (rev 51133)
@@ -383,6 +383,9 @@
 		OGR_F_SetFieldString(Ogr_feature, ogrfieldnum,
 				     db_get_string(&dbstring));
 		break;
+	    default:
+		G_warning(_("Unsupported column type %d"), ctype);
+		break;
 	    }
 	}
     }

Modified: grass/trunk/lib/vector/Vlib/write_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/write_pg.c	2012-03-21 21:12:19 UTC (rev 51132)
+++ grass/trunk/lib/vector/Vlib/write_pg.c	2012-03-21 21:13:05 UTC (rev 51133)
@@ -38,8 +38,11 @@
 					int, int*);
 static unsigned char *polygon_to_wkb(int, const struct line_pnts *,
 				     int, int*);
-static int write_feature(const struct Format_info_pg *, 
-			 int, const struct line_pnts *, int, int);
+static int write_feature(struct Format_info_pg *, 
+			 int, const struct line_pnts *, int,
+			 int, const struct field_info *);
+static char *build_insert_stmt(const struct Format_info_pg *, const char *,
+			       int, const struct field_info *);
 #endif
 
 /*!
@@ -92,7 +95,8 @@
 	    return -1;
     }
     
-    cat = -1; /* no attributes to be written */
+    Fi = NULL; /* no attributes to be written */
+    cat = -1;
     if (cats->n_cats > 0 && Vect_get_num_dblinks(Map) > 0) {
 	/* check for attributes */
 	Fi = Vect_get_dblink(Map, 0);
@@ -156,20 +160,14 @@
 	}
     }
 
-    if (execute(pg_info->conn, "BEGIN") == -1)
-	return -1;
-    
     /* write feature's geometry and fid */
     if (-1 == write_feature(pg_info, type, points,
-			    Vect_is_3d(Map) ? WITH_Z : WITHOUT_Z, cat))
+			    Vect_is_3d(Map) ? WITH_Z : WITHOUT_Z,
+			    cat, Fi)) {
+	execute(pg_info->conn, "ROLLBACK");
 	return -1;
+    }
 
-    /* write attributes */
-    /* ? */
-    
-    if (execute(pg_info->conn, "COMMIT") == -1)
-	return -1;
-    
     /* update offset array */
     if (offset_info->array_num >= offset_info->array_alloc) {
 	offset_info->array_alloc += 1000;
@@ -263,21 +261,23 @@
     G_debug(3, "V1_delete_line_pg(), offset = %lu -> fid = %ld",
 	    (unsigned long) offset, fid);
 
-    if (execute(pg_info->conn, "BEGIN") == -1)
-	return -1;
-
+    if (!pg_info->inTransaction) {
+	/* start transaction */
+	pg_info->inTransaction = TRUE;
+	if (execute(pg_info->conn, "BEGIN") == -1)
+	    return -1;
+    }
+    
     sprintf(stmt, "DELETE FROM %s WHERE %s = %ld",
 	    pg_info->table_name, pg_info->fid_column, fid);
     G_debug(2, "SQL: %s", stmt);
     
     if (execute(pg_info->conn, stmt) == -1) {
 	G_warning(_("Unable to delete feature"));
+	execute(pg_info->conn, "ROLLBACK");
 	return -1;
     }
     
-    if (execute(pg_info->conn, "COMMIT") == -1)
-	return -1;
-
     return 0;
 #else
     G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
@@ -544,6 +544,7 @@
 
     return wkb_data;
 }
+
 /*!
   \brief Insert feature into table
 
@@ -551,14 +552,15 @@
   \param type feature type (GV_POINT, GV_LINE, ...)
   \param points pointer to line_pnts struct
   \param with_z WITH_Z for 3D data
-  \param fid feature id
+  \param cat category number (-1 for no category)
+  \param Fi pointer to field_info (attributes to copy, NULL for no attributes)
 
   \return -1 on error
   \retirn 0 on success
 */
-int write_feature(const struct Format_info_pg *pg_info,
+int write_feature(struct Format_info_pg *pg_info,
 		  int type, const struct line_pnts *points, int with_z,
-		  int fid)
+		  int cat, const struct field_info *Fi)
 {
     int   byte_order, nbytes, nsize;
     unsigned int sf_type;
@@ -641,16 +643,19 @@
     G_free(hex_data);
 
     /* build INSERT statement */
-    stmt = NULL;
-    G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
-	       "('%s'::GEOMETRY)",
-	       pg_info->schema_name, pg_info->table_name, 
-	       pg_info->geom_column, text_data);
+    stmt = build_insert_stmt(pg_info, text_data, cat, Fi);
     G_debug(2, "SQL: %s", stmt);
     
+    if (!pg_info->inTransaction) {
+	/* start transaction */
+	pg_info->inTransaction = TRUE;
+	if (execute(pg_info->conn, "BEGIN") == -1)
+	    return -1;
+    }
+    
     if (execute(pg_info->conn, stmt) == -1) {
-	/* close transaction */
-	execute(pg_info->conn, "COMMIT");
+	/* rollback transaction */
+	execute(pg_info->conn, "ROLLBACK");
 	return -1;
     }
 
@@ -660,4 +665,139 @@
     
     return 0;
 }
+
+/*!
+  \brief Build INSERT statement to insert new feature to the table
+
+  \param pg_info pointer to Format_info_pg structure
+  \param cat category number (or -1 for no category)
+  \param Fi pointer to field_info structure (NULL for no attributes)
+
+  \return allocated string with INSERT statement
+*/
+char *build_insert_stmt(const struct Format_info_pg *pg_info,
+			const char *geom_data,
+			int cat, const struct field_info *Fi)
+{
+    char *stmt, buf[DB_SQL_MAX];
+    
+    stmt = NULL;
+    if (Fi && cat > -1) {
+	int col, ncol, more;
+	int sqltype, ctype;
+	char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
+	char *str_val;
+	
+	const char *colname;
+	
+	dbString  dbstmt;
+	dbCursor  cursor;
+	dbTable  *table;
+	dbColumn *column;
+	dbValue  *value;
+	
+	db_init_string(&dbstmt);
+	buf_val[0] = '\0';
+	
+	/* read & set attributes */
+	sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key,
+		cat);
+	G_debug(4, "SQL: %s", buf);
+	db_set_string(&dbstmt, buf);
+
+	/* prepare INSERT statement */
+	sprintf(buf, "INSERT INTO \"%s\".\"%s\" (%s",
+		pg_info->schema_name, pg_info->table_name, 
+		pg_info->geom_column);
+	
+	/* select data */
+	if (db_open_select_cursor(pg_info->dbdriver, &dbstmt,
+				  &cursor, DB_SEQUENTIAL) != DB_OK) {
+	    G_warning(_("Unable to select attributes for category %d"),
+		      cat);
+	}
+	else {
+	    if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
+		G_warning(_("Unable to fetch data from table <%s>"),
+			  Fi->table);
+	    }
+	 
+	    if (!more) {
+		G_warning(_("No database record for category %d, "
+			    "no attributes will be written"), cat);
+	    }
+	    else {
+		table = db_get_cursor_table(&cursor);
+		ncol = db_get_table_number_of_columns(table);
+
+		for (col = 0; col < ncol; col++) {
+		    column = db_get_table_column(table, col);
+		    colname = db_get_column_name(column);
+	
+		    /* skip fid column */
+		    if (strcmp(pg_info->fid_column, colname) == 0)
+			continue;
+		    
+		    /* -> columns */
+		    sprintf(buf_tmp, ",%s", colname);
+		    strcat(buf, buf_tmp);
+		    
+		    /* -> values */
+		    value = db_get_column_value(column);
+		    	/* for debug only */
+		    db_convert_column_value_to_string(column, &dbstmt);	
+		    G_debug(2, "col %d : val = %s", col,
+			    db_get_string(&dbstmt));
+
+		    sqltype = db_get_column_sqltype(column);
+		    ctype = db_sqltype_to_Ctype(sqltype);
+
+		    /* prevent writing NULL values */
+		    if (!db_test_value_isnull(value)) {
+			switch (ctype) {
+			case DB_C_TYPE_INT:
+			    sprintf(buf_tmp, ",%d", db_get_value_int(value));
+			    break;
+			case DB_C_TYPE_DOUBLE:
+			    sprintf(buf_tmp, ",%.14f", db_get_value_double(value));
+			    break;
+			case DB_C_TYPE_STRING:
+			    str_val = G_store(db_get_value_string(value));
+			    G_str_to_sql(str_val);
+			    sprintf(buf_tmp, ",'%s'", str_val);
+			    G_free(str_val);
+			    break;
+			case DB_C_TYPE_DATETIME:
+			    db_convert_column_value_to_string(column,
+							      &dbstmt);
+			    sprintf(buf_tmp, ",%s", db_get_string(&dbstmt));
+			    break;
+			default:
+			    G_warning(_("Unsupported column type %d"), ctype);
+			    sprintf(buf_tmp, ",NULL");
+			    break;
+			}
+		    }
+		    else {
+			sprintf(buf_tmp, ",NULL");
+		    }
+		    strcat(buf_val, buf_tmp);
+		}
+		
+		G_asprintf(&stmt, "%s) VALUES ('%s'::GEOMETRY%s)",
+			   buf, geom_data, buf_val);
+	    }
+	}
+    }
+
+    if (!stmt) {
+	/* no attributes */
+	G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
+		   "('%s'::GEOMETRY)",
+		   pg_info->schema_name, pg_info->table_name, 
+		   pg_info->geom_column, geom_data);
+    }
+
+    return stmt;
+}
 #endif



More information about the grass-commit mailing list