Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/dml.py: 54%

48 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1from ... import exc 

2from ... import util 

3from ...sql.base import _exclusive_against 

4from ...sql.base import _generative 

5from ...sql.base import ColumnCollection 

6from ...sql.dml import Insert as StandardInsert 

7from ...sql.elements import ClauseElement 

8from ...sql.expression import alias 

9from ...util.langhelpers import public_factory 

10 

11 

12__all__ = ("Insert", "insert") 

13 

14 

15class Insert(StandardInsert): 

16 """MySQL-specific implementation of INSERT. 

17 

18 Adds methods for MySQL-specific syntaxes such as ON DUPLICATE KEY UPDATE. 

19 

20 The :class:`~.mysql.Insert` object is created using the 

21 :func:`sqlalchemy.dialects.mysql.insert` function. 

22 

23 .. versionadded:: 1.2 

24 

25 """ 

26 

27 stringify_dialect = "mysql" 

28 inherit_cache = False 

29 

30 @property 

31 def inserted(self): 

32 """Provide the "inserted" namespace for an ON DUPLICATE KEY UPDATE 

33 statement 

34 

35 MySQL's ON DUPLICATE KEY UPDATE clause allows reference to the row 

36 that would be inserted, via a special function called ``VALUES()``. 

37 This attribute provides all columns in this row to be referenceable 

38 such that they will render within a ``VALUES()`` function inside the 

39 ON DUPLICATE KEY UPDATE clause. The attribute is named ``.inserted`` 

40 so as not to conflict with the existing 

41 :meth:`_expression.Insert.values` method. 

42 

43 .. tip:: The :attr:`_mysql.Insert.inserted` attribute is an instance 

44 of :class:`_expression.ColumnCollection`, which provides an 

45 interface the same as that of the :attr:`_schema.Table.c` 

46 collection described at :ref:`metadata_tables_and_columns`. 

47 With this collection, ordinary names are accessible like attributes 

48 (e.g. ``stmt.inserted.some_column``), but special names and 

49 dictionary method names should be accessed using indexed access, 

50 such as ``stmt.inserted["column name"]`` or 

51 ``stmt.inserted["values"]``. See the docstring for 

52 :class:`_expression.ColumnCollection` for further examples. 

53 

54 .. seealso:: 

55 

56 :ref:`mysql_insert_on_duplicate_key_update` - example of how 

57 to use :attr:`_expression.Insert.inserted` 

58 

59 """ 

60 return self.inserted_alias.columns 

61 

62 @util.memoized_property 

63 def inserted_alias(self): 

64 return alias(self.table, name="inserted") 

65 

66 @_generative 

67 @_exclusive_against( 

68 "_post_values_clause", 

69 msgs={ 

70 "_post_values_clause": "This Insert construct already " 

71 "has an ON DUPLICATE KEY clause present" 

72 }, 

73 ) 

74 def on_duplicate_key_update(self, *args, **kw): 

75 r""" 

76 Specifies the ON DUPLICATE KEY UPDATE clause. 

77 

78 :param \**kw: Column keys linked to UPDATE values. The 

79 values may be any SQL expression or supported literal Python 

80 values. 

81 

82 .. warning:: This dictionary does **not** take into account 

83 Python-specified default UPDATE values or generation functions, 

84 e.g. those specified using :paramref:`_schema.Column.onupdate`. 

85 These values will not be exercised for an ON DUPLICATE KEY UPDATE 

86 style of UPDATE, unless values are manually specified here. 

87 

88 :param \*args: As an alternative to passing key/value parameters, 

89 a dictionary or list of 2-tuples can be passed as a single positional 

90 argument. 

91 

92 Passing a single dictionary is equivalent to the keyword argument 

93 form:: 

94 

95 insert().on_duplicate_key_update({"name": "some name"}) 

96 

97 Passing a list of 2-tuples indicates that the parameter assignments 

98 in the UPDATE clause should be ordered as sent, in a manner similar 

99 to that described for the :class:`_expression.Update` 

100 construct overall 

101 in :ref:`tutorial_parameter_ordered_updates`:: 

102 

103 insert().on_duplicate_key_update( 

104 [("name", "some name"), ("value", "some value")]) 

105 

106 .. versionchanged:: 1.3 parameters can be specified as a dictionary 

107 or list of 2-tuples; the latter form provides for parameter 

108 ordering. 

109 

110 

111 .. versionadded:: 1.2 

112 

113 .. seealso:: 

114 

115 :ref:`mysql_insert_on_duplicate_key_update` 

116 

117 """ 

118 if args and kw: 

119 raise exc.ArgumentError( 

120 "Can't pass kwargs and positional arguments simultaneously" 

121 ) 

122 

123 if args: 

124 if len(args) > 1: 

125 raise exc.ArgumentError( 

126 "Only a single dictionary or list of tuples " 

127 "is accepted positionally." 

128 ) 

129 values = args[0] 

130 else: 

131 values = kw 

132 

133 inserted_alias = getattr(self, "inserted_alias", None) 

134 self._post_values_clause = OnDuplicateClause(inserted_alias, values) 

135 

136 

137insert = public_factory( 

138 Insert, ".dialects.mysql.insert", ".dialects.mysql.Insert" 

139) 

140 

141 

142class OnDuplicateClause(ClauseElement): 

143 __visit_name__ = "on_duplicate_key_update" 

144 

145 _parameter_ordering = None 

146 

147 stringify_dialect = "mysql" 

148 

149 def __init__(self, inserted_alias, update): 

150 self.inserted_alias = inserted_alias 

151 

152 # auto-detect that parameters should be ordered. This is copied from 

153 # Update._proces_colparams(), however we don't look for a special flag 

154 # in this case since we are not disambiguating from other use cases as 

155 # we are in Update.values(). 

156 if isinstance(update, list) and ( 

157 update and isinstance(update[0], tuple) 

158 ): 

159 self._parameter_ordering = [key for key, value in update] 

160 update = dict(update) 

161 

162 if isinstance(update, dict): 

163 if not update: 

164 raise ValueError( 

165 "update parameter dictionary must not be empty" 

166 ) 

167 elif isinstance(update, ColumnCollection): 

168 update = dict(update) 

169 else: 

170 raise ValueError( 

171 "update parameter must be a non-empty dictionary " 

172 "or a ColumnCollection such as the `.c.` collection " 

173 "of a Table object" 

174 ) 

175 self.update = update