diff -r 6641e941ef1e -r ff1a9aa48cfd app/django/contrib/gis/db/backend/postgis/creation.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/django/contrib/gis/db/backend/postgis/creation.py Tue Oct 14 16:00:59 2008 +0000 @@ -0,0 +1,224 @@ +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 + +def getstatusoutput(cmd): + "A simpler version of getstatusoutput that works on win32 platforms." + stdin, stdout, stderr = os.popen3(cmd) + output = stdout.read() + if output.endswith('\n'): output = output[:-1] + status = stdin.close() + return status, output + +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." + + # Constructing the necessary SQL to create the database (the DATABASE_USER + # must possess the privileges to create a database) + create_sql = 'CREATE DATABASE %s' % connection.ops.quote_name(db_name) + if settings.DATABASE_USER: + create_sql += ' OWNER %s' % settings.DATABASE_USER + + cursor = connection.cursor() + connection.creation.set_autocommit() + + try: + # Trying to create the database first. + cursor.execute(create_sql) + #print create_sql + except Exception, e: + # Drop and recreate, if necessary. + 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.') + +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. + """ + + # Getting the command-line options for the shell command + options = get_cmd_options(False) + 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_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=False): + "Creates a 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 + if test: + db_name = get_spatial_db(test=True) + _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber) + else: + db_name = get_spatial_db() + _create_with_shell(db_name, verbosity=verbosity, autoclobber=autoclobber) + + # 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 + + # 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)