/[thuban]/branches/WIP-pyshapelib-bramz/test/postgissupport.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/test/postgissupport.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2460 - (show annotations)
Wed Dec 15 14:01:04 2004 UTC (20 years, 2 months ago) by bh
Original Path: trunk/thuban/test/postgissupport.py
File MIME type: text/x-python
File size: 28224 byte(s)
(PostgreSQLServer.execute_sql): Extend to
so that it returns a result for select statements.
(PostgreSQLServer.server_version): New.  Return the version of the
server software.
(PostgreSQLServer.require_authentication): The format of
pg_hba.conf has changed between PostgrSQL 7.2 and 7.3.  Check the
server version and generate the file in the correct format

1 # Copyright (C) 2003, 2004 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 #
5 # This program is free software under the GPL (>=v2)
6 # Read the file COPYING coming with the software for details.
7
8 """Support module for tests that use a live PostGIS database"""
9
10 __version__ = "$Revision$"
11 # $Source$
12 # $Id$
13
14 import sys
15 import os
16 import time
17 import popen2
18 import shutil
19 import traceback
20 import re
21
22 import support
23
24 try:
25 import psycopg
26 except ImportError:
27 psycopg = None
28
29 #
30 # Helper code
31 #
32
33 def run_config_script(cmdline):
34 """Run command cmdline and return its stdout or none in case of errors"""
35 pipe = os.popen(cmdline)
36 result = pipe.read()
37 if pipe.close() is not None:
38 raise RuntimeError('Command %r failed' % cmdline)
39 return result
40
41 def run_command(command, outfilename = None):
42 """Run command as a subprocess and send its stdout and stderr to outfile
43
44 The subprocess is run synchroneously so the function returns once
45 the subprocess has termninated. If the process' exit code is not
46 zero raise a RuntimeError.
47
48 If outfilename is None stdout and stderr are still captured but they
49 are ignored and not written to any file.
50 """
51 proc = popen2.Popen4(command)
52 proc.tochild.close()
53 output = proc.fromchild.read()
54 status = proc.wait()
55 if outfilename is not None:
56 outfile = open(outfilename, "w")
57 outfile.write(output)
58 outfile.close()
59 if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
60 if outfilename:
61 message = "see %s" % outfilename
62 else:
63 message = output
64 raise RuntimeError("command %r exited with code %d.\n%s"
65 % (command, status, message))
66
67
68 def run_boolean_command(command):
69 """
70 Run command as a subprocess silently and return whether it ran successfully
71
72 Silently means that all output is captured and ignored. The exit
73 status is true if the command ran successfull, i.e. it terminated by
74 exiting and returned as zero exit code and false other wise
75 """
76 try:
77 run_command(command, None)
78 return 1
79 except RuntimeError:
80 pass
81 return 0
82
83
84 #
85 # PostgreSQL and database
86 #
87
88 class PostgreSQLServer:
89
90 """A PostgreSQL server
91
92 Instances of this class represent a PostgreSQL server with postgis
93 extensions run explicitly for the test cases. Such a server has its
94 own database directory and its own directory for the unix sockets so
95 that it doesn't interfere with any other PostgreSQL server already
96 running on the system.
97 """
98
99 def __init__(self, dbdir, port, postgis_sql, socket_dir):
100 """Initialize the PostgreSQLServer object
101
102 Parameters:
103
104 dbdir -- The directory for the databases
105 port -- The port to use
106 postgis_sql -- The name of the file with the SQL statements to
107 initialize a database for postgis.
108 socket_dir -- The directory for the socket files.
109
110 When connecting to the database server use the port and host
111 instance variables.
112 """
113 self.dbdir = dbdir
114 self.port = port
115 self.postgis_sql = postgis_sql
116 self.socket_dir = socket_dir
117
118 # For the client side the socket directory can be used as the
119 # host if the name starts with a slash.
120 self.host = os.path.abspath(socket_dir)
121
122 # name and password for the admin and an unprivileged user
123 self.admin_name = "postgres"
124 self.admin_password = "postgres"
125 self.user_name = "observer"
126 self.user_password = "telescope"
127
128 # Map db names to db objects
129 self.known_dbs = {}
130
131 def createdb(self):
132 """Create the database in dbdir and start the server.
133
134 First check whether the dbdir already exists and if necessary
135 stop an already running postmaster and remove the dbdir
136 directory completely. Then create a new database cluster in the
137 dbdir and start a postmaster.
138 """
139 if os.path.isdir(self.dbdir):
140 if self.is_running():
141 self.shutdown()
142 shutil.rmtree(self.dbdir)
143 os.mkdir(self.dbdir)
144
145 run_command(["initdb", "-D", self.dbdir, "-U", self.admin_name],
146 os.path.join(self.dbdir, "initdb.log"))
147
148 extra_opts = "-p %d" % self.port
149 if self.socket_dir is not None:
150 extra_opts += " -k %s" % self.socket_dir
151 run_command(["pg_ctl", "-D", self.dbdir,
152 "-l", os.path.join(self.dbdir, "logfile"),
153 "-o", extra_opts, "start"],
154 os.path.join(self.dbdir, "pg_ctl-start.log"))
155 # the -w option of pg_ctl doesn't work properly when the port is
156 # not the default port, so we have to implement waiting for the
157 # server ourselves
158 self.wait_for_postmaster()
159
160 self.alter_user(self.admin_name, self.admin_password)
161 self.create_user(self.user_name, self.user_password)
162
163 def wait_for_postmaster(self):
164 """Return when the database server is running
165
166 Internal method to wait until the postmaster process has been
167 started and is ready for client connections.
168 """
169 max_count = 60
170 count = 0
171 while count < max_count:
172 try:
173 run_command(["psql", "-l", "-p", str(self.port),
174 "-h", self.host, "-U", self.admin_name],
175 os.path.join(self.dbdir, "psql-%d.log" % count))
176 except RuntimeError:
177 pass
178 except:
179 traceback.print_exc()
180 else:
181 break
182 time.sleep(0.5)
183 count += 1
184 else:
185 raise RuntimeError("postmaster didn't start")
186
187 def is_running(self):
188 """Return whether a postmaster process is running on self.dbdir
189
190 This method runs pg_ctl status on the dbdir and returns True if
191 that command succeeds and False otherwise.
192
193 Note that it is possible that this method returns true even if
194 the PostgreSQLServer instance has just been created and
195 createdb() has not been called yet. This can happen, for
196 instance, if the server has been started manually for debugging
197 purposes after a test suite run.
198 """
199 return run_boolean_command(["pg_ctl", "-D", self.dbdir, "status"])
200
201 def shutdown(self):
202 """Stop the postmaster running for self.dbdir"""
203 run_command(["pg_ctl", "-m", "fast", "-D", self.dbdir, "stop"],
204 os.path.join(self.dbdir, "pg_ctl-stop.log"))
205
206 def new_postgis_db(self, dbname, tables = None, reference_systems = None,
207 views = None):
208 """Create and return a new PostGISDatabase object using self as server
209 """
210 db = PostGISDatabase(self, self.postgis_sql, dbname, tables = tables,
211 reference_systems = reference_systems,
212 views = views)
213 db.initdb()
214 self.known_dbs[dbname] = db
215 return db
216
217 def get_static_data_db(self, dbname, tables, reference_systems, views):
218 """Return a PostGISDatabase for a database with the given static data
219
220 If no databasse of the name dbname exists, create a new one via
221 new_postgis_db and upload the data.
222
223 If a database of the name dbname already exists and uses the
224 indicated data, return that. If the already existing db uses
225 different data raise a value error.
226
227 If the database doesn't exist, create a new one via
228 self.new_postgis_db.
229
230 The parameters tables and reference_systems have the same
231 meaning as for new_postgis_db.
232 """
233 db = self.known_dbs.get(dbname)
234 if db is not None:
235 if db.has_data(tables, reference_systems, views):
236 return db
237 raise ValueError("PostGISDatabase named %r doesn't have tables %r"
238 % (dbname, tables))
239 return self.new_postgis_db(dbname, tables, reference_systems, views)
240
241 def get_default_static_data_db(self):
242 dbname = "PostGISStaticTests"
243 srids = [(1, "proj=longlat datum=WGS84")]
244 tables = [
245 # Direct copies of the shapefiles. The shapeids are exactly
246 # the same, except where changed with "gid_offset", of
247 # course. Note that the test implementation requires that
248 # all the landmard tables use an gid_offset of 1000.
249 ("landmarks", os.path.join("..", "Data", "iceland",
250 "cultural_landmark-point.shp"),
251 [("gid_offset", 1000)]),
252 ("political", os.path.join("..", "Data", "iceland",
253 "political.shp")),
254 ("roads", os.path.join("..", "Data", "iceland",
255 "roads-line.shp")),
256
257 # The polygon data as a MULTIPOLYGON geometry type
258 ("political_multi", os.path.join("..", "Data", "iceland",
259 "political.shp"),
260 [("force_wkt_type", "MULTIPOLYGON")]),
261
262 # Copy of landmarks but using an srid != -1
263 ("landmarks_srid", os.path.join("..", "Data", "iceland",
264 "cultural_landmark-point.shp"),
265 [("gid_offset", 1000),
266 ("srid", 1)]),
267
268 # Copy of landmarks with a gid column called "point_id" instead
269 # of "gid" and using an srid != -1.
270 ("landmarks_point_id", os.path.join("..", "Data", "iceland",
271 "cultural_landmark-point.shp"),
272 [("gid_offset", 1000),
273 ("srid", 1),
274 ("gid_column", "point_id")]),
275 ]
276 views = [("v_landmarks", "SELECT * FROM landmarks_point_id")]
277 return self.get_static_data_db(dbname, tables, srids, views)
278
279 def connection_params(self, user):
280 """Return the connection parameters for the given user
281
282 The return value is a dictionary suitable as keyword argument
283 list to PostGISConnection. The user parameter may be either
284 'admin' to connect as admin or 'user' to connect as an
285 unprivileged user.
286 """
287 return {"host": self.host, "port": self.port,
288 "user": getattr(self, user + "_name"),
289 "password": getattr(self, user + "_password")}
290
291 def connection_string(self, user):
292 """Return (part of) the connection string to pass to psycopg.connect
293
294 The string contains host, port, user and password. The user
295 parameter must be either 'admin' or 'user', as for
296 connection_params.
297 """
298 params = []
299 for key, value in self.connection_params(user).items():
300 # FIXME: this doesn't do quiting correctly but that
301 # shouldn't be much of a problem (people shouldn't be using
302 # single quotes in filenames anyway :) )
303 params.append("%s='%s'" % (key, value))
304 return " ".join(params)
305
306 def execute_sql(self, dbname, user, sql):
307 """Execute the sql statament and return a result for SELECT statements
308
309 The user parameter us used as in connection_params. The dbname
310 parameter must be the name of a database in the cluster. The
311 sql parameter is the SQL statement to execute as a string. If
312 the string starts with 'select' (matched case insensitively) the
313 first row of the result will be returned. Otherwise the return
314 value is None.
315 """
316 conn = psycopg.connect("dbname=%s " % dbname
317 + self.connection_string(user))
318 cursor = conn.cursor()
319 cursor.execute(sql)
320 if sql.lower().startswith("select"):
321 row = cursor.fetchone()
322 else:
323 row = None
324 conn.commit()
325 conn.close()
326 return row
327
328 def server_version(self):
329 """Return the server version as a tuple (major, minor, patch)
330
331 Each item in the tuple is an int.
332 """
333 result = self.execute_sql("template1", "admin", "SELECT version();")[0]
334 match = re.match(r"PostgreSQL (\d+\.\d+\.\d+)", result)
335 if match:
336 return tuple(map(int, match.group(1).split(".")))
337 else:
338 raise RutimeError("Cannot determine PostgreSQL server version"
339 " from %r" % result)
340
341 def require_authentication(self, required):
342 """Switch authentication requirements on or off
343
344 When started for the first time no passwords are required. Some
345 tests want to explicitly test whether Thuban's password
346 infrastructure works and switch password authentication on
347 explicitly. When switching it on, there should be a
348 corresponding call to switch it off again in the test case'
349 tearDown method or in a finally: block.
350 """
351 # Starting with PostgreSQL 7.3 the pg_hba.conf file has an
352 # additional column with a username. Query the server version
353 # and generate a file in the correct format.
354 if self.server_version() >= (7, 3):
355 user = "all"
356 else:
357 user = ""
358 if required:
359 contents = "local all %s password\n" % user
360 else:
361 contents = "local all %s trust\n" % user
362 f = open(os.path.join(self.dbdir, "pg_hba.conf"), "w")
363 f.write(contents)
364 f.close()
365 run_command(["pg_ctl", "-D", self.dbdir, "reload"],
366 os.path.join(self.dbdir, "pg_ctl-reload.log"))
367
368
369 def create_user(self, username, password):
370 """Create user username with password in the database"""
371 self.execute_sql("template1", "admin",
372 "CREATE USER %s PASSWORD '%s';" % (username,password))
373
374 def alter_user(self, username, password):
375 """Change the user username's password in the database"""
376 self.execute_sql("template1", "admin",
377 "ALTER USER %s PASSWORD '%s';" % (username,password))
378
379
380 class PostGISDatabase:
381
382 """A PostGIS database in a PostgreSQLServer"""
383
384 def __init__(self, server, postgis_sql, dbname, tables = None,
385 reference_systems = (), views = None):
386 """Initialize the PostGISDatabase
387
388 Parameters:
389
390 server -- The PostgreSQLServer instance containing the
391 database
392
393 postgis_sql -- Filename of the postgis.sql file with the
394 postgis initialization code
395
396 dbname -- The name of the database
397
398 tables -- Optional description of tables to create in the
399 new database. If given it should be a list of
400 (tablename, shapefilename) pairs meaning that a table
401 tablename will be created with the contents of the given
402 shapefile or (tablename, shapefilename, extraargs)
403 triples. The extraargs should be a list of key, value
404 pairs to use as keyword arguments to upload_shapefile.
405
406 reference_systems -- Optional description of spatial
407 reference systems. If given, it should be a sequence of
408 (srid, params) pairs where srid is the srid defined by
409 the proj4 paramter string params. The srid can be given
410 as an extra parameter in the tables list.
411
412 views -- Optional description of views. If given it should
413 be a list of (viewname, select_stmt) pairs where
414 viewname is the name of the view to be created and
415 select_stmt is the select statement to use as the basis.
416 The views will be created after the tables and may refer
417 to them in the select_stmt.
418 """
419 self.server = server
420 self.postgis_sql = postgis_sql
421 self.dbname = dbname
422 self.tables = tables
423 self.views = views
424 if reference_systems:
425 self.reference_systems = reference_systems
426 else:
427 # Make sure that it's a sequence we can iterate over even if
428 # the parameter's None
429 self.reference_systems = ()
430
431 def initdb(self):
432 """Remove the old db directory and create and initialize a new database
433 """
434 run_command(["createdb", "-p", str(self.server.port),
435 "-h", self.server.host, "-U", self.server.admin_name,
436 self.dbname],
437 os.path.join(self.server.dbdir, "createdb.log"))
438 run_command(["createlang", "-p", str(self.server.port),
439 "-h", self.server.host, "-U", self.server.admin_name,
440 "plpgsql", self.dbname],
441 os.path.join(self.server.dbdir, "createlang.log"))
442 # for some reason psql doesn't exit with an error code if the
443 # file given as -f doesn't exist, so we check manually by trying
444 # to open it before we run psql
445 f = open(self.postgis_sql)
446 f.close()
447 del f
448 run_command(["psql", "-f", self.postgis_sql, "-d", self.dbname,
449 "-p", str(self.server.port), "-h", self.server.host,
450 "-U", self.server.admin_name],
451 os.path.join(self.server.dbdir, "psql.log"))
452
453 self.server.execute_sql(self.dbname, "admin",
454 "GRANT SELECT ON geometry_columns TO PUBLIC;")
455 self.server.execute_sql(self.dbname, "admin",
456 "GRANT SELECT ON spatial_ref_sys TO PUBLIC;")
457
458 for srid, params in self.reference_systems:
459 self.server.execute_sql(self.dbname, "admin",
460 "INSERT INTO spatial_ref_sys VALUES"
461 " (%d, '', %d, '', '%s');"
462 % (srid, srid, params))
463 if self.tables is not None:
464 def unpack(item):
465 extra = {"force_wkt_type": None, "gid_offset": 0,
466 "srid": -1}
467 if len(info) == 2:
468 tablename, shapefile = info
469 else:
470 tablename, shapefile, kw = info
471 for key, val in kw:
472 extra[key] = val
473 return tablename, shapefile, extra
474
475 for info in self.tables:
476 tablename, shapefile, kw = unpack(info)
477 upload_shapefile(shapefile, self, tablename, **kw)
478
479 if self.views is not None:
480 for viewname, select_stmt in self.views:
481 self.server.execute_sql(self.dbname, "admin",
482 "CREATE VIEW %s AS %s" % (viewname,
483 select_stmt))
484 self.server.execute_sql(self.dbname, "admin",
485 "GRANT SELECT ON %s TO PUBLIC;"
486 % viewname)
487
488 def has_data(self, tables, reference_systems, views):
489 return (self.tables == tables
490 and self.reference_systems == reference_systems
491 and self.views == views)
492
493
494 def find_postgis_sql():
495 """Return the name of the postgis_sql file
496
497 A postgis installation usually has the postgis_sql file in
498 PostgreSQL's datadir (i.e. the directory where PostgreSQL keeps
499 static files, not the directory containing the databases).
500 Unfortunately there's no way to determine the name of this directory
501 with pg_config so we assume here that it's
502 $bindir/../share/postgresql/.
503 """
504 bindir = run_config_script("pg_config --bindir").strip()
505 return os.path.join(bindir, "..", "share", "postgresql",
506 "contrib", "postgis.sql")
507
508 _postgres_server = None
509 def get_test_server():
510 """Return the test database server object.
511
512 If it doesn't exist yet, create it first.
513
514 The server will use the directory postgis under the temp dir (as
515 defined by support.create_temp_dir()) for the database cluster.
516 Sockets will be created in tempdir.
517 """
518 global _postgres_server
519 if _postgres_server is None:
520 tempdir = support.create_temp_dir()
521 dbdir = os.path.join(tempdir, "postgis")
522 socket_dir = tempdir
523
524 _postgres_server = PostgreSQLServer(dbdir, 6543, find_postgis_sql(),
525 socket_dir = socket_dir)
526 _postgres_server.createdb()
527
528 return _postgres_server
529
530 def shutdown_test_server():
531 """Shutdown the test server if it is running"""
532 global _postgres_server
533 if _postgres_server is not None:
534 _postgres_server.shutdown()
535 _postgres_server = None
536
537
538 def reason_for_not_running_tests():
539 """
540 Determine whether postgis tests can be run and return a reason they can't
541
542 There's no fool-proof way to reliably determine this short of
543 actually running the tests but we try the following here:
544
545 - test whether pg_ctl --help can be run successfully
546 - test whether the postgis_sql can be opened
547 The name of the postgis_sql file is determined by find_postgis_sql()
548 - psycopg can be imported successfully.
549 """
550 # run_command currently uses Popen4 which is not available under
551 # Windows, for example.
552 if not hasattr(popen2, "Popen4"):
553 return "Can't run PostGIS test because popen2.Popen4 does not exist"
554
555 try:
556 run_command(["pg_ctl", "--help"], None)
557 except RuntimeError:
558 return "Can't run PostGIS tests because pg_ctl fails"
559
560 try:
561 postgis_sql = find_postgis_sql()
562 except:
563 return "Can't run PostGIS tests because postgis.sql can't be found"
564
565 try:
566 f = open(postgis_sql)
567 f.close()
568 except:
569 return "Can't run PostGIS tests because postgis.sql can't be opened"
570
571 # The test for psycopg was already done when this module was
572 # imported so we only have to check whether it was successful
573 if psycopg is None:
574 return "Can't run PostGIS tests because psycopg can't be imported"
575
576 return ""
577
578
579 _cannot_run_postgis_tests = None
580 def skip_if_no_postgis():
581 global _cannot_run_postgis_tests
582 if _cannot_run_postgis_tests is None:
583 _cannot_run_postgis_tests = reason_for_not_running_tests()
584 if _cannot_run_postgis_tests:
585 raise support.SkipTest(_cannot_run_postgis_tests)
586
587 def skip_if_addgeometrycolumn_does_not_use_quote_ident():
588 """Skip a test if the AddGeometryColumn function doesn't use quote_ident
589
590 If the AddGeometryColumn function doesn't use quote_ident it doesn't
591 support unusual table or column names properly, that is, it will
592 fail with errors for names that contain spaces or double quotes.
593
594 The test performed by this function is a bit simplistic because it
595 only tests whether the string 'quote_ident' occurs anywhere in the
596 postgis.sql file. This will hopefully work because when this was
597 fixed in postgis CVS AddGeometryColumn was the first function to use
598 quote_ident.
599 """
600 f = file(find_postgis_sql())
601 content = f.read()
602 f.close()
603 if content.find("quote_ident") < 0:
604 raise support.SkipTest("AddGeometryColumn doesn't use quote_ident")
605
606 def coords_to_point(coords):
607 """Return string with a WKT representation of the point in coords"""
608 x, y = coords[0]
609 return "POINT(%r %r)" % (x, y)
610
611 def coords_to_polygon(coords):
612 """Return string with a WKT representation of the polygon in coords"""
613 poly = []
614 for ring in coords:
615 poly.append(", ".join(["%r %r" % p for p in ring]))
616 return "POLYGON((%s))" % "), (".join(poly)
617
618 def coords_to_multilinestring(coords):
619 """Return string with a WKT representation of the arc in coords"""
620 poly = []
621 for ring in coords:
622 poly.append(", ".join(["%r %r" % p for p in ring]))
623 return "MULTILINESTRING((%s))" % "), (".join(poly)
624
625 def coords_to_multipolygon(coords):
626 """Return string with a WKT representation of the polygon in coords"""
627 poly = []
628 for ring in coords:
629 poly.append(", ".join(["%r %r" % p for p in ring]))
630 return "MULTIPOLYGON(((%s)))" % ")), ((".join(poly)
631
632 wkt_converter = {
633 "POINT": coords_to_point,
634 "MULTILINESTRING": coords_to_multilinestring,
635 "POLYGON": coords_to_polygon,
636 "MULTIPOLYGON": coords_to_multipolygon,
637 }
638
639 def upload_shapefile(filename, db, tablename, force_wkt_type = None,
640 gid_offset = 0, gid_column = "gid", srid = -1):
641 """Upload a shapefile into a new database table
642
643 Parameters:
644
645 filename -- The name of the shapefile
646
647 db -- The PostGISDatabase instance representing the database
648
649 tablename -- The name of the table to create and into which the data
650 is to be inserted
651
652 force_wkt_type -- If given the real WKT geometry type to use instead
653 of the default that would be chosen based on the type of
654 the shapefile
655
656 gid_offset -- A number to add to the shapeid to get the value for
657 the gid column (default 0)
658
659 gid_column -- The name of the column with the shape ids. Default
660 'gid'. If None, no gid column will be created. The
661 name is directly used in SQL statements, so if it
662 contains unusualy characters the caller should provide
663 a suitable quoted string.
664
665 srid -- The srid of the spatial references system used by the table
666 and the data
667 """
668 import dbflib, shapelib
669
670 # We build this map here because we need shapelib which can only be
671 # imported after support.initthuban has been called which we can't
672 # easily do in this module because it's imported by support.
673 shp_to_wkt = {
674 shapelib.SHPT_POINT: "POINT",
675 shapelib.SHPT_ARC: "MULTILINESTRING",
676 shapelib.SHPT_POLYGON: "POLYGON",
677 }
678
679 server = db.server
680 dbname = db.dbname
681 conn = psycopg.connect("dbname=%s " % dbname
682 + db.server.connection_string("admin"))
683 cursor = conn.cursor()
684
685 shp = shapelib.ShapeFile(filename)
686 dbf = dbflib.DBFFile(filename)
687 typemap = {dbflib.FTString: "VARCHAR",
688 dbflib.FTInteger: "INTEGER",
689 dbflib.FTDouble: "DOUBLE PRECISION"}
690
691 insert_formats = []
692 if gid_column:
693 insert_formats.append("%(gid)s")
694
695 fields = []
696 fields_decl = []
697 if gid_column:
698 fields.append(gid_column)
699 fields_decl.append("%s INT" % gid_column)
700 for i in range(dbf.field_count()):
701 ftype, name, width, prec = dbf.field_info(i)
702 fields.append(name)
703 fields_decl.append("%s %s" % (name, typemap[ftype]))
704 insert_formats.append("%%(%s)s" % name)
705 stmt = "CREATE TABLE %s (\n %s\n);" % (tablename,
706 ",\n ".join(fields_decl))
707 cursor.execute(stmt)
708 #print stmt
709
710 numshapes, shapetype, mins, maxs = shp.info()
711 wkttype = shp_to_wkt[shapetype]
712 if force_wkt_type:
713 wkttype = force_wkt_type
714 convert = wkt_converter[wkttype]
715
716 cursor.execute("select AddGeometryColumn('%(dbname)s',"
717 "'%(tablename)s', 'the_geom', %(srid)d, '%(wkttype)s', 2);"
718 % locals())
719 fields.append("the_geom")
720 insert_formats.append("GeometryFromText(%(the_geom)s, %(srid)d)")
721
722 insert = ("INSERT INTO %s (%s) VALUES (%s)"
723 % (tablename, ", ".join(fields), ", ".join(insert_formats)))
724
725 for i in range(numshapes):
726 data = dbf.read_record(i)
727 data["tablename"] = tablename
728 if gid_column:
729 data["gid"] = i + gid_offset
730 data["srid"] = srid
731 data["the_geom"] = convert(shp.read_object(i).vertices())
732 #print insert % data
733 cursor.execute(insert, data)
734
735 cursor.execute("GRANT SELECT ON %s TO PUBLIC;" % tablename)
736
737 conn.commit()

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26