|
10 | 10 | __version__ = '1.4.4' |
11 | 11 | __all__ = [ |
12 | 12 | 'Flavor', 'Table', 'Values', 'Literal', 'Column', 'Grouping', 'Conflict', |
| 13 | + 'Matched', 'MatchedUpdate', 'MatchedDelete', |
| 14 | + 'NotMatched', 'NotMatchedInsert', |
13 | 15 | 'Rollup', 'Cube', 'Excluded', 'Join', 'Asc', 'Desc', 'NullsFirst', |
14 | 16 | 'NullsLast', 'format2numeric'] |
15 | 17 |
|
@@ -1075,6 +1077,207 @@ def params(self): |
1075 | 1077 | return tuple(p) |
1076 | 1078 |
|
1077 | 1079 |
|
| 1080 | +class Merge(WithQuery): |
| 1081 | + __slots__ = ('_target', '_source', '_condition', '_whens') |
| 1082 | + |
| 1083 | + def __init__(self, target, source, condition, *whens, **kwargs): |
| 1084 | + self._target = None |
| 1085 | + self._source = None |
| 1086 | + self._condition = None |
| 1087 | + self._whens = None |
| 1088 | + self.target = target |
| 1089 | + self.source = source |
| 1090 | + self.condition = condition |
| 1091 | + self.whens = whens |
| 1092 | + super().__init__(**kwargs) |
| 1093 | + |
| 1094 | + @property |
| 1095 | + def target(self): |
| 1096 | + return self._target |
| 1097 | + |
| 1098 | + @target.setter |
| 1099 | + def target(self, value): |
| 1100 | + assert isinstance(value, Table) |
| 1101 | + self._target = value |
| 1102 | + |
| 1103 | + @property |
| 1104 | + def source(self): |
| 1105 | + return self._source |
| 1106 | + |
| 1107 | + @source.setter |
| 1108 | + def source(self, value): |
| 1109 | + assert isinstance(value, (Table, SelectQuery, Values)) |
| 1110 | + self._source = value |
| 1111 | + |
| 1112 | + @property |
| 1113 | + def condition(self): |
| 1114 | + return self._condition |
| 1115 | + |
| 1116 | + @condition.setter |
| 1117 | + def condition(self, value): |
| 1118 | + assert isinstance(value, Expression) |
| 1119 | + self._condition = value |
| 1120 | + |
| 1121 | + @property |
| 1122 | + def whens(self): |
| 1123 | + return self._whens |
| 1124 | + |
| 1125 | + @whens.setter |
| 1126 | + def whens(self, value): |
| 1127 | + assert all(isinstance(w, Matched) for w in value) |
| 1128 | + self._whens = tuple(value) |
| 1129 | + |
| 1130 | + def __str__(self): |
| 1131 | + with AliasManager(): |
| 1132 | + if isinstance(self.source, (Select, Values)): |
| 1133 | + source = '(%s)' % self.source |
| 1134 | + else: |
| 1135 | + source = self.source |
| 1136 | + if self.condition: |
| 1137 | + condition = 'ON %s' % self.condition |
| 1138 | + else: |
| 1139 | + condition = '' |
| 1140 | + return (self._with_str() |
| 1141 | + + 'MERGE INTO %s AS "%s" ' % (self.target, self.target.alias) |
| 1142 | + + 'USING %s AS "%s" ' % (source, self.source.alias) |
| 1143 | + + condition + ' ' + ' '.join(map(str, self.whens))) |
| 1144 | + |
| 1145 | + @property |
| 1146 | + def params(self): |
| 1147 | + p = [] |
| 1148 | + p.extend(self._with_params()) |
| 1149 | + if isinstance(self.source, (SelectQuery, Values)): |
| 1150 | + p.extend(self.source.params) |
| 1151 | + if self.condition: |
| 1152 | + p.extend(self.condition.params) |
| 1153 | + for match in self.whens: |
| 1154 | + p.extend(match.params) |
| 1155 | + return tuple(p) |
| 1156 | + |
| 1157 | + |
| 1158 | +class Matched(object): |
| 1159 | + __slots__ = ('_condition',) |
| 1160 | + _when = 'MATCHED' |
| 1161 | + |
| 1162 | + def __init__(self, condition=None): |
| 1163 | + self._condition = None |
| 1164 | + self.condition = condition |
| 1165 | + |
| 1166 | + @property |
| 1167 | + def condition(self): |
| 1168 | + return self._condition |
| 1169 | + |
| 1170 | + @condition.setter |
| 1171 | + def condition(self, value): |
| 1172 | + if value is not None: |
| 1173 | + assert isinstance(value, Expression) |
| 1174 | + self._condition = value |
| 1175 | + |
| 1176 | + def _then_str(self): |
| 1177 | + return 'DO NOTHING' |
| 1178 | + |
| 1179 | + def __str__(self): |
| 1180 | + if self.condition is not None: |
| 1181 | + condition = ' AND ' + str(self.condition) |
| 1182 | + else: |
| 1183 | + condition = '' |
| 1184 | + return 'WHEN ' + self._when + condition + ' THEN ' + self._then_str() |
| 1185 | + |
| 1186 | + @property |
| 1187 | + def params(self): |
| 1188 | + p = [] |
| 1189 | + if self.condition: |
| 1190 | + p.extend(self.condition.params) |
| 1191 | + return tuple(p) |
| 1192 | + |
| 1193 | + |
| 1194 | +class _MatchedValues(Matched): |
| 1195 | + __slots__ = ('_columns', '_values') |
| 1196 | + |
| 1197 | + def __init__(self, columns, values, **kwargs): |
| 1198 | + self._columns = columns |
| 1199 | + self._values = values |
| 1200 | + self.columns = columns |
| 1201 | + self.values = values |
| 1202 | + super().__init__(**kwargs) |
| 1203 | + |
| 1204 | + @property |
| 1205 | + def columns(self): |
| 1206 | + return self._columns |
| 1207 | + |
| 1208 | + @columns.setter |
| 1209 | + def columns(self, value): |
| 1210 | + assert all(isinstance(col, Column) for col in value) |
| 1211 | + self._columns = value |
| 1212 | + |
| 1213 | + |
| 1214 | +class MatchedUpdate(_MatchedValues, Matched): |
| 1215 | + __slots__ = () |
| 1216 | + |
| 1217 | + @property |
| 1218 | + def values(self): |
| 1219 | + return self._values |
| 1220 | + |
| 1221 | + @values.setter |
| 1222 | + def values(self, value): |
| 1223 | + self._values = value |
| 1224 | + |
| 1225 | + def _then_str(self): |
| 1226 | + columns = [c.column_name for c in self.columns] |
| 1227 | + return 'UPDATE SET ' + ', '.join( |
| 1228 | + '%s = %s' % (c, Update._format(v)) |
| 1229 | + for c, v in zip(columns, self.values)) |
| 1230 | + |
| 1231 | + @property |
| 1232 | + def params(self): |
| 1233 | + p = list(super().params) |
| 1234 | + for value in self.values: |
| 1235 | + if isinstance(value, (Expression, Select)): |
| 1236 | + p.extend(value.params) |
| 1237 | + else: |
| 1238 | + p.append(value) |
| 1239 | + return tuple(p) |
| 1240 | + |
| 1241 | + |
| 1242 | +class MatchedDelete(Matched): |
| 1243 | + __slots__ = () |
| 1244 | + |
| 1245 | + def _then_str(self): |
| 1246 | + return 'DELETE' |
| 1247 | + |
| 1248 | + |
| 1249 | +class NotMatched(Matched): |
| 1250 | + __slots__ = () |
| 1251 | + _when = 'NOT MATCHED' |
| 1252 | + |
| 1253 | + |
| 1254 | +class NotMatchedInsert(_MatchedValues, NotMatched): |
| 1255 | + __slots__ = () |
| 1256 | + |
| 1257 | + @property |
| 1258 | + def values(self): |
| 1259 | + return self._values |
| 1260 | + |
| 1261 | + @values.setter |
| 1262 | + def values(self, value): |
| 1263 | + self._values = Values([value]) |
| 1264 | + |
| 1265 | + def _then_str(self): |
| 1266 | + columns = ', '.join(c.column_name for c in self.columns) |
| 1267 | + columns = '(' + columns + ')' |
| 1268 | + if self.values is None: |
| 1269 | + values = ' DEFAULT VALUES ' |
| 1270 | + else: |
| 1271 | + values = ' ' + str(self.values) |
| 1272 | + return 'INSERT ' + columns + values |
| 1273 | + |
| 1274 | + @property |
| 1275 | + def params(self): |
| 1276 | + p = list(super().params) |
| 1277 | + p.extend(self.values.params) |
| 1278 | + return tuple(p) |
| 1279 | + |
| 1280 | + |
1078 | 1281 | class CombiningQuery(FromItem, SelectQuery): |
1079 | 1282 | __slots__ = ('queries', 'all_') |
1080 | 1283 | _operator = '' |
@@ -1161,6 +1364,9 @@ def delete(self, only=False, using=None, where=None, returning=None, |
1161 | 1364 | return Delete(self, only=only, using=using, where=where, |
1162 | 1365 | returning=returning, with_=with_) |
1163 | 1366 |
|
| 1367 | + def merge(self, source, condition, *whens, with_=None): |
| 1368 | + return Merge(self, source, condition, *whens, with_=with_) |
| 1369 | + |
1164 | 1370 |
|
1165 | 1371 | class _Excluded(Table): |
1166 | 1372 | def __init__(self): |
|
0 commit comments