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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2459 - (hide annotations)
Wed Dec 15 11:12:11 2004 UTC (20 years, 2 months ago) by bh
Original Path: trunk/thuban/test/postgissupport.py
File MIME type: text/x-python
File size: 26955 byte(s)
(PostgreSQLServer.is_running): Fix typo
in the doc string and rephrase it a little.

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