summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES10
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py25
-rw-r--r--lib/sqlalchemy/dialects/postgresql/psycopg2.py1
-rw-r--r--lib/sqlalchemy/engine/default.py2
-rw-r--r--lib/sqlalchemy/sql/compiler.py3
-rw-r--r--test/dialect/test_postgresql.py22
-rw-r--r--test/orm/inheritance/test_query.py5
7 files changed, 50 insertions, 18 deletions
diff --git a/CHANGES b/CHANGES
index fad467e8a..8682440d5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -235,6 +235,16 @@ CHANGES
just to treat that one non-existent use case.
[ticket:1279]
+ - Inserting NULL into a primary key + foreign key column
+ will allow the "not null constraint" error to raise,
+ not an attempt to execute a nonexistent "col_id_seq"
+ sequence. [ticket:1516]
+
+ - autoincrement SELECT statements, i.e. those which
+ select from a procedure that modifies rows, now work
+ with server-side cursor mode (the named cursor isn't
+ used for such statements.)
+
- mysql
- all the _detect_XXX() functions now run once underneath
dialect.initialize()
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index 321601a83..7f9cc889a 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -333,32 +333,29 @@ class PGDDLCompiler(compiler.DDLCompiler):
class PGDefaultRunner(base.DefaultRunner):
- def __init__(self, context):
- base.DefaultRunner.__init__(self, context)
- # craete cursor which won't conflict with a server-side cursor
- self.cursor = context._connection.connection.cursor()
-
+
def get_column_default(self, column, isinsert=True):
if column.primary_key:
- # pre-execute passive defaults on primary keys
if (isinstance(column.server_default, schema.DefaultClause) and
column.server_default.arg is not None):
+
+ # pre-execute passive defaults on primary key columns
return self.execute_string("select %s" % column.server_default.arg)
- elif (isinstance(column.type, sqltypes.Integer) and column.autoincrement) \
+
+ elif column is column.table._autoincrement_column \
and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)):
+
+ # execute the sequence associated with a SERIAL primary key column.
+ # for non-primary-key SERIAL, the ID just generates server side.
sch = column.table.schema
- # TODO: this has to build into the Sequence object so we can get the quoting
- # logic from it
+
if sch is not None:
exc = "select nextval('\"%s\".\"%s_%s_seq\"')" % (sch, column.table.name, column.name)
else:
exc = "select nextval('\"%s_%s_seq\"')" % (column.table.name, column.name)
- if self.dialect.supports_unicode_statements:
- return self.execute_string(exc)
- else:
- return self.execute_string(exc.encode(self.dialect.encoding))
-
+ return self.execute_string(exc)
+
return super(PGDefaultRunner, self).get_column_default(column)
def visit_sequence(self, seq):
diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
index 973bacd06..a09697e79 100644
--- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py
+++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
@@ -70,6 +70,7 @@ class PostgreSQL_psycopg2ExecutionContext(default.DefaultExecutionContext):
# TODO: coverage for server side cursors + select.for_update()
is_server_side = \
self.dialect.server_side_cursors and \
+ not self.should_autocommit and \
((self.compiled and isinstance(self.compiled.statement, expression.Selectable)
and not getattr(self.compiled.statement, 'for_update', False)) \
or \
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 935d1e087..78164e459 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -254,8 +254,8 @@ class DefaultExecutionContext(base.ExecutionContext):
else:
self.statement = statement
self.isinsert = self.isupdate = self.isdelete = False
- self.cursor = self.create_cursor()
self.should_autocommit = self.should_autocommit_text(statement)
+ self.cursor = self.create_cursor()
else:
# no statement. used for standalone ColumnDefault execution.
self.statement = self.compiled = None
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 78529ee6d..7bd0c1b05 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -844,8 +844,7 @@ class SQLCompiler(engine.Compiled):
self.dialect.supports_sequences or
not isinstance(c.default, schema.Sequence)
)
- ) or \
- self.dialect.preexecute_autoincrement_sequences:
+ ) or self.dialect.preexecute_autoincrement_sequences:
values.append((c, self._create_crud_bind_param(c, None)))
self.prefetch.append(c)
diff --git a/test/dialect/test_postgresql.py b/test/dialect/test_postgresql.py
index e1c351a93..c27e24e43 100644
--- a/test/dialect/test_postgresql.py
+++ b/test/dialect/test_postgresql.py
@@ -131,6 +131,28 @@ class InsertTest(TestBase, AssertsExecutionResults):
ins.execute({'x':"five"}, {'x':"seven"})
assert table.select().execute().fetchall() == [(1, 'five'), (2, 'seven')]
+ def test_foreignkey_missing_insert(self):
+ t1 = Table('t1', metadata,
+ Column('id', Integer, primary_key=True)
+ )
+ t2 = Table('t2', metadata,
+ Column('id', Integer, ForeignKey('t1.id'), primary_key=True)
+ )
+ metadata.create_all()
+
+ # want to ensure that
+ # "null value in column "id" violates not-null constraint" is raised (IntegrityError on psycoopg2,
+ # but ProgrammingError on pg8000),
+ # and not "ProgrammingError: (ProgrammingError) relation "t2_id_seq" does not exist".
+ # the latter corresponds to autoincrement behavior, which is not the case
+ # here due to the foreign key.
+ for eng in [
+ engines.testing_engine(options={'implicit_returning':False}),
+ engines.testing_engine(options={'implicit_returning':True}),
+ ]:
+ assert_raises_message(exc.DBAPIError, "violates not-null constraint", eng.execute, t2.insert())
+
+
def test_sequence_insert(self):
table = Table('testtable', metadata,
Column('id', Integer, Sequence('my_seq'), primary_key=True),
diff --git a/test/orm/inheritance/test_query.py b/test/orm/inheritance/test_query.py
index daf8bf3bd..ee2d7f0e5 100644
--- a/test/orm/inheritance/test_query.py
+++ b/test/orm/inheritance/test_query.py
@@ -367,7 +367,10 @@ def _produce_test(select_type):
)
eq_(
- sess.query(Manager.name, Paperwork.description).join((Paperwork, Manager.person_id==Paperwork.person_id)).all(),
+ sess.query(Manager.name, Paperwork.description).
+ join((Paperwork, Manager.person_id==Paperwork.person_id)).
+ order_by(Paperwork.paperwork_id).
+ all(),
[(u'pointy haired boss', u'review #1'), (u'dogbert', u'review #2'), (u'dogbert', u'review #3')]
)