diff -r 000000000000 -r 0d40e90630ef web/lib/django/contrib/gis/db/backend/postgis/creation.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/django/contrib/gis/db/backend/postgis/creation.py Wed Jan 20 00:34:04 2010 +0100 @@ -0,0 +1,231 @@ +import os, re, sys + +from django.conf import settings +from django.core.management import call_command +from django.db import connection +from django.db.backends.creation import TEST_DATABASE_PREFIX +from django.contrib.gis.db.backend.util import getstatusoutput + +def create_lang(db_name, verbosity=1): + "Sets up the pl/pgsql language on the given database." + + # Getting the command-line options for the shell command + options = get_cmd_options(db_name) + + # Constructing the 'createlang' command. + createlang_cmd = 'createlang %splpgsql' % options + if verbosity >= 1: print createlang_cmd + + # Must have database super-user privileges to execute createlang -- it must + # also be in your path. + status, output = getstatusoutput(createlang_cmd) + + # Checking the status of the command, 0 => execution successful + if status: + raise Exception("Error executing 'plpgsql' command: %s\n" % output) + +def _create_with_cursor(db_name, verbosity=1, autoclobber=False): + "Creates database with psycopg2 cursor." + qn = connection.ops.quote_name + + # Constructing the necessary SQL to create the database. + create_sql = 'CREATE DATABASE %s' % qn(db_name) + + # If there's a template database for PostGIS set, then use it. + if hasattr(settings, 'POSTGIS_TEMPLATE'): + create_sql += ' TEMPLATE %s' % qn(settings.POSTGIS_TEMPLATE) + + # The DATABASE_USER must possess the privileges to create a spatial database. + if settings.DATABASE_USER: + create_sql += ' OWNER %s' % qn(settings.DATABASE_USER) + + cursor = connection.cursor() + connection.creation.set_autocommit() + + try: + # Trying to create the database first. + cursor.execute(create_sql) + except Exception, e: + if 'already exists' in e.pgerror.lower(): + # Database already exists, drop and recreate if user agrees. + if not autoclobber: + confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name) + if autoclobber or confirm == 'yes': + if verbosity >= 1: print 'Destroying old spatial database...' + drop_db(db_name) + if verbosity >= 1: print 'Creating new spatial database...' + cursor.execute(create_sql) + else: + raise Exception('Spatial database creation canceled.') + else: + raise Exception('Spatial database creation failed: "%s"' % e.pgerror.strip()) + +created_regex = re.compile(r'^createdb: database creation failed: ERROR: database ".+" already exists') +def _create_with_shell(db_name, verbosity=1, autoclobber=False): + """ + If no spatial database already exists, then using a cursor will not work. + Thus, a `createdb` command will be issued through the shell to bootstrap + creation of the spatial database. + + TODO: Actually allow this method to be used without a spatial database + in place first. + """ + # Getting the command-line options for the shell command + options = get_cmd_options(False) + if hasattr(settings, 'POSTGIS_TEMPLATE'): + options += '-T %s ' % settings.POSTGIS_TEMPlATE + + create_cmd = 'createdb -O %s %s%s' % (settings.DATABASE_USER, options, db_name) + if verbosity >= 1: print create_cmd + + # Attempting to create the database. + status, output = getstatusoutput(create_cmd) + + if status: + if created_regex.match(output): + if not autoclobber: + confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name) + if autoclobber or confirm == 'yes': + if verbosity >= 1: print 'Destroying old spatial database...' + drop_cmd = 'dropdb %s%s' % (options, db_name) + status, output = getstatusoutput(drop_cmd) + if status != 0: + raise Exception('Could not drop database %s: %s' % (db_name, output)) + if verbosity >= 1: print 'Creating new spatial database...' + status, output = getstatusoutput(create_cmd) + if status != 0: + raise Exception('Could not create database after dropping: %s' % output) + else: + raise Exception('Spatial Database Creation canceled.') + else: + raise Exception('Unknown error occurred in creating database: %s' % output) + +def create_test_spatial_db(verbosity=1, autoclobber=False, interactive=False): + "Creates a test spatial database based on the settings." + + # Making sure we're using PostgreSQL and psycopg2 + if settings.DATABASE_ENGINE != 'postgresql_psycopg2': + raise Exception('Spatial database creation only supported postgresql_psycopg2 platform.') + + # Getting the spatial database name + db_name = get_spatial_db(test=True) + _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber) + + # If a template database is used, then don't need to do any of the following. + if not hasattr(settings, 'POSTGIS_TEMPLATE'): + # Creating the db language, does not need to be done on NT platforms + # since the PostGIS installer enables this capability. + if os.name != 'nt': + create_lang(db_name, verbosity=verbosity) + + # Now adding in the PostGIS routines. + load_postgis_sql(db_name, verbosity=verbosity) + + if verbosity >= 1: print 'Creation of spatial database %s successful.' % db_name + + # Closing the connection + connection.close() + settings.DATABASE_NAME = db_name + connection.settings_dict["DATABASE_NAME"] = db_name + can_rollback = connection.creation._rollback_works() + settings.DATABASE_SUPPORTS_TRANSACTIONS = can_rollback + connection.settings_dict["DATABASE_SUPPORTS_TRANSACTIONS"] = can_rollback + + # Syncing the database + call_command('syncdb', verbosity=verbosity, interactive=interactive) + +def drop_db(db_name=False, test=False): + """ + Drops the given database (defaults to what is returned from + get_spatial_db()). All exceptions are propagated up to the caller. + """ + if not db_name: db_name = get_spatial_db(test=test) + cursor = connection.cursor() + cursor.execute('DROP DATABASE %s' % connection.ops.quote_name(db_name)) + +def get_cmd_options(db_name): + "Obtains the command-line PostgreSQL connection options for shell commands." + # The db_name parameter is optional + options = '' + if db_name: + options += '-d %s ' % db_name + if settings.DATABASE_USER: + options += '-U %s ' % settings.DATABASE_USER + if settings.DATABASE_HOST: + options += '-h %s ' % settings.DATABASE_HOST + if settings.DATABASE_PORT: + options += '-p %s ' % settings.DATABASE_PORT + return options + +def get_spatial_db(test=False): + """ + Returns the name of the spatial database. The 'test' keyword may be set + to return the test spatial database name. + """ + if test: + if settings.TEST_DATABASE_NAME: + test_db_name = settings.TEST_DATABASE_NAME + else: + test_db_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + return test_db_name + else: + if not settings.DATABASE_NAME: + raise Exception('must configure DATABASE_NAME in settings.py') + return settings.DATABASE_NAME + +def load_postgis_sql(db_name, verbosity=1): + """ + This routine loads up the PostGIS SQL files lwpostgis.sql and + spatial_ref_sys.sql. + """ + # Getting the path to the PostGIS SQL + try: + # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the + # PostGIS SQL files are located. This is especially useful on Win32 + # platforms since the output of pg_config looks like "C:/PROGRA~1/..". + sql_path = settings.POSTGIS_SQL_PATH + except AttributeError: + status, sql_path = getstatusoutput('pg_config --sharedir') + if status: + sql_path = '/usr/local/share' + + # The PostGIS SQL post-creation files. + lwpostgis_file = os.path.join(sql_path, 'lwpostgis.sql') + srefsys_file = os.path.join(sql_path, 'spatial_ref_sys.sql') + if not os.path.isfile(lwpostgis_file): + raise Exception('Could not find PostGIS function definitions in %s' % lwpostgis_file) + if not os.path.isfile(srefsys_file): + raise Exception('Could not find PostGIS spatial reference system definitions in %s' % srefsys_file) + + # Getting the psql command-line options, and command format. + options = get_cmd_options(db_name) + cmd_fmt = 'psql %s-f "%%s"' % options + + # Now trying to load up the PostGIS functions + cmd = cmd_fmt % lwpostgis_file + if verbosity >= 1: print cmd + status, output = getstatusoutput(cmd) + if status: + raise Exception('Error in loading PostGIS lwgeometry routines.') + + # Now trying to load up the Spatial Reference System table + cmd = cmd_fmt % srefsys_file + if verbosity >= 1: print cmd + status, output = getstatusoutput(cmd) + if status: + raise Exception('Error in loading PostGIS spatial_ref_sys table.') + + # Setting the permissions because on Windows platforms the owner + # of the spatial_ref_sys and geometry_columns tables is always + # the postgres user, regardless of how the db is created. + if os.name == 'nt': set_permissions(db_name) + +def set_permissions(db_name): + """ + Sets the permissions on the given database to that of the user specified + in the settings. Needed specifically for PostGIS on Win32 platforms. + """ + cursor = connection.cursor() + user = settings.DATABASE_USER + cursor.execute('ALTER TABLE geometry_columns OWNER TO %s' % user) + cursor.execute('ALTER TABLE spatial_ref_sys OWNER TO %s' % user)