Commit efd1e609 authored by Andrew Godwin's avatar Andrew Godwin
Browse files

Adding 'sqlmigrate' command and quote_parameter to support it.

parent 5ca290f5
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
# encoding: utf8
from __future__ import unicode_literals
from optparse import make_option

from django.core.management.base import BaseCommand, CommandError
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.migrations.executor import MigrationExecutor
from django.db.migrations.loader import AmbiguityError


class Command(BaseCommand):

    option_list = BaseCommand.option_list + (
        make_option('--database', action='store', dest='database',
            default=DEFAULT_DB_ALIAS, help='Nominates a database to create SQL for. '
                'Defaults to the "default" database.'),
        make_option('--backwards', action='store_true', dest='backwards',
            default=False, help='Creates SQL to unapply the migration, rather than to apply it'),
    )

    help = "Prints the SQL statements for the named migration."

    def handle(self, *args, **options):

        # Get the database we're operating from
        db = options.get('database')
        connection = connections[db]

        # Load up an executor to get all the migration data
        executor = MigrationExecutor(connection)

        # Resolve command-line arguments into a migration
        if len(args) != 2:
            raise CommandError("Wrong number of arguments (expecting 'sqlmigrate appname migrationname')")
        else:
            app_label, migration_name = args
            if app_label not in executor.loader.migrated_apps:
                raise CommandError("App '%s' does not have migrations" % app_label)
            try:
                migration = executor.loader.get_migration_by_prefix(app_label, migration_name)
            except AmbiguityError:
                raise CommandError("More than one migration matches '%s' in app '%s'. Please be more specific." % (app_label, migration_name))
            except KeyError:
                raise CommandError("Cannot find a migration matching '%s' from app '%s'. Is it in INSTALLED_APPS?" % (app_label, migration_name))
            targets = [(app_label, migration.name)]

        # Make a plan that represents just the requested migrations and show SQL
        # for it
        plan = [(executor.loader.graph.nodes[targets[0]], options.get("backwards", False))]
        sql_statements = executor.collect_sql(plan)
        for statement in sql_statements:
            self.stdout.write(statement)
+10 −1
Original line number Diff line number Diff line
@@ -521,7 +521,7 @@ class BaseDatabaseWrapper(object):
        """
        raise NotImplementedError

    def schema_editor(self):
    def schema_editor(self, *args, **kwargs):
        "Returns a new instance of this backend's SchemaEditor"
        raise NotImplementedError()

@@ -958,6 +958,15 @@ class BaseDatabaseOperations(object):
        """
        raise NotImplementedError()

    def quote_parameter(self, value):
        """
        Returns a quoted version of the value so it's safe to use in an SQL
        string. This should NOT be used to prepare SQL statements to send to
        the database; it is meant for outputting SQL statements to a file
        or the console for later execution by a developer/DBA.
        """
        raise NotImplementedError()

    def random_function_sql(self):
        """
        Returns an SQL expression that returns a random value.
+7 −2
Original line number Diff line number Diff line
@@ -305,6 +305,11 @@ class DatabaseOperations(BaseDatabaseOperations):
            return name # Quoting once is enough.
        return "`%s`" % name

    def quote_parameter(self, value):
        # Inner import to allow module to fail to load gracefully
        import MySQLdb.converters
        return MySQLdb.escape(value, MySQLdb.converters.conversions)

    def random_function_sql(self):
        return 'RAND()'

@@ -518,9 +523,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
                        table_name, column_name, bad_row[1],
                        referenced_table_name, referenced_column_name))

    def schema_editor(self):
    def schema_editor(self, *args, **kwargs):
        "Returns a new instance of this backend's SchemaEditor"
        return DatabaseSchemaEditor(self)
        return DatabaseSchemaEditor(self, *args, **kwargs)

    def is_usable(self):
        try:
+12 −2
Original line number Diff line number Diff line
@@ -320,6 +320,16 @@ WHEN (new.%(col_name)s IS NULL)
        name = name.replace('%', '%%')
        return name.upper()

    def quote_parameter(self, value):
        if isinstance(value, (datetime.date, datetime.time, datetime.datetime)):
            return "'%s'" % value
        elif isinstance(value, six.string_types):
            return repr(value)
        elif isinstance(value, bool):
            return "1" if value else "0"
        else:
            return str(value)

    def random_function_sql(self):
        return "DBMS_RANDOM.RANDOM"

@@ -628,9 +638,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
                    six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
                raise

    def schema_editor(self):
    def schema_editor(self, *args, **kwargs):
        "Returns a new instance of this backend's SchemaEditor"
        return DatabaseSchemaEditor(self)
        return DatabaseSchemaEditor(self, *args, **kwargs)

    # Oracle doesn't support savepoint commits.  Ignore them.
    def _savepoint_commit(self, sid):
+1 −8
Original line number Diff line number Diff line
@@ -93,11 +93,4 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
        return self.normalize_name(for_name + "_" + suffix)

    def prepare_default(self, value):
        if isinstance(value, (datetime.date, datetime.time, datetime.datetime)):
            return "'%s'" % value
        elif isinstance(value, six.string_types):
            return repr(value)
        elif isinstance(value, bool):
            return "1" if value else "0"
        else:
            return str(value)
        return self.connection.ops.quote_parameter(value)
Loading