Skip to content

Commit

Permalink
Add SQLInsertCompiler stub
Browse files Browse the repository at this point in the history
SQLInsertCompiler.as_sql() is copied from Django, to be customized in the
next commit adding support for inserting JSONField values.
  • Loading branch information
timgraham committed Apr 8, 2023
1 parent 145b190 commit 0cdd7d7
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 0 deletions.
91 changes: 91 additions & 0 deletions django_snowflake/compiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from itertools import chain

from django.db.models.sql import compiler


class SQLInsertCompiler(compiler.SQLInsertCompiler):
def as_sql(self):
# We don't need quote_name_unless_alias() here, since these are all
# going to be column names (so we can avoid the extra overhead).
qn = self.connection.ops.quote_name
opts = self.query.get_meta()
insert_statement = self.connection.ops.insert_statement(
on_conflict=self.query.on_conflict,
)
result = ["%s %s" % (insert_statement, qn(opts.db_table))]
fields = self.query.fields or [opts.pk]
result.append("(%s)" % ", ".join(qn(f.column) for f in fields))

if self.query.fields:
value_rows = [
[
self.prepare_value(field, self.pre_save_val(field, obj))
for field in fields
]
for obj in self.query.objs
]
else:
# An empty object.
value_rows = [
[self.connection.ops.pk_default_value()] for _ in self.query.objs
]
fields = [None]

# Currently the backends just accept values when generating bulk
# queries and generate their own placeholders. Doing that isn't
# necessary and it should be possible to use placeholders and
# expressions in bulk inserts too.
can_bulk = (
not self.returning_fields and self.connection.features.has_bulk_insert
)

placeholder_rows, param_rows = self.assemble_as_sql(fields, value_rows)

on_conflict_suffix_sql = self.connection.ops.on_conflict_suffix_sql(
fields,
self.query.on_conflict,
(f.column for f in self.query.update_fields),
(f.column for f in self.query.unique_fields),
)
if (
self.returning_fields
and self.connection.features.can_return_columns_from_insert
):
if self.connection.features.can_return_rows_from_bulk_insert:
result.append(
self.connection.ops.bulk_insert_sql(fields, placeholder_rows)
)
params = param_rows
else:
result.append("VALUES (%s)" % ", ".join(placeholder_rows[0]))
params = [param_rows[0]]
if on_conflict_suffix_sql:
result.append(on_conflict_suffix_sql)
# Skip empty r_sql to allow subclasses to customize behavior for
# 3rd party backends. Refs #19096.
r_sql, self.returning_params = self.connection.ops.return_insert_columns(
self.returning_fields
)
if r_sql:
result.append(r_sql)
params += [self.returning_params]
return [(" ".join(result), tuple(chain.from_iterable(params)))]

if can_bulk:
result.append(self.connection.ops.bulk_insert_sql(fields, placeholder_rows))
if on_conflict_suffix_sql:
result.append(on_conflict_suffix_sql)
return [(" ".join(result), tuple(p for ps in param_rows for p in ps))]
else:
if on_conflict_suffix_sql:
result.append(on_conflict_suffix_sql)
return [
(" ".join(result + ["VALUES (%s)" % ", ".join(p)]), vals)
for p, vals in zip(placeholder_rows, param_rows)
]


SQLCompiler = compiler.SQLCompiler
SQLDeleteCompiler = compiler.SQLDeleteCompiler
SQLUpdateCompiler = compiler.SQLUpdateCompiler
SQLAggregateCompiler = compiler.SQLAggregateCompiler
1 change: 1 addition & 0 deletions django_snowflake/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class DatabaseOperations(BaseDatabaseOperations):
'BigAutoField': 'NUMBER',
'SmallAutoField': 'NUMBER',
}
compiler_module = 'django_snowflake.compiler'
explain_prefix = 'EXPLAIN USING'

def bulk_insert_sql(self, fields, placeholder_rows):
Expand Down

0 comments on commit 0cdd7d7

Please sign in to comment.