3636# -----------------------------------------------------------------------------
3737
3838from __future__ import division
39- from future .utils import viewitems
39+ from future .utils import viewitems , viewvalues
4040from os .path import join
4141from functools import partial
42+ from collections import defaultdict
4243from copy import deepcopy
4344
4445import pandas as pd
4849
4950from qiita_db .exceptions import (QiitaDBUnknownIDError , QiitaDBColumnError ,
5051 QiitaDBNotImplementedError ,
52+ QiitaDBExecutionError ,
5153 QiitaDBDuplicateHeaderError )
5254from qiita_db .base import QiitaObject
5355from qiita_db .sql_connection import SQLConnectionHandler
@@ -313,8 +315,8 @@ def handler(x):
313315 " in template %d" %
314316 (key , self ._id , self ._md_template .id ))
315317
316- def __setitem__ (self , column , value ):
317- r """Sets the metadata value for the category `column`
318+ def add_setitem_queries (self , column , value , conn_handler , queue ):
319+ """Adds the SQL queries needed to set a value to the provided queue
318320
319321 Parameters
320322 ----------
@@ -323,16 +325,16 @@ def __setitem__(self, column, value):
323325 value : str
324326 The value to set. This is expected to be a str on the assumption
325327 that psycopg2 will cast as necessary when updating.
328+ conn_handler : SQLConnectionHandler
329+ The connection handler object connected to the DB
330+ queue : str
331+ The queue where the SQL statements will be added
326332
327333 Raises
328334 ------
329335 QiitaDBColumnError
330336 If the column does not exist in the table
331- ValueError
332- If the value type does not match the one in the DB
333337 """
334- conn_handler = SQLConnectionHandler ()
335-
336338 # try dynamic tables
337339 sql = """SELECT EXISTS (
338340 SELECT column_name
@@ -367,27 +369,58 @@ def __setitem__(self, column, value):
367369 raise QiitaDBColumnError ("Column %s does not exist in %s" %
368370 (column , self ._dynamic_table ))
369371
372+ conn_handler .add_to_queue (queue , sql , (value , self ._id ))
373+
374+ def __setitem__ (self , column , value ):
375+ r"""Sets the metadata value for the category `column`
376+
377+ Parameters
378+ ----------
379+ column : str
380+ The column to update
381+ value : str
382+ The value to set. This is expected to be a str on the assumption
383+ that psycopg2 will cast as necessary when updating.
384+
385+ Raises
386+ ------
387+ ValueError
388+ If the value type does not match the one in the DB
389+ """
390+ conn_handler = SQLConnectionHandler ()
391+ queue_name = "set_item_%s" % self ._id
392+ conn_handler .create_queue (queue_name )
393+
394+ self .add_setitem_queries (column , value , conn_handler , queue_name )
395+
370396 try :
371- conn_handler .execute ( sql , ( value , self . _id ) )
372- except Exception as e :
397+ conn_handler .execute_queue ( queue_name )
398+ except QiitaDBExecutionError as e :
373399 # catching error so we can check if the error is due to different
374400 # column type or something else
401+ type_lookup = defaultdict (lambda : 'varchar' )
402+ type_lookup [int ] = 'integer'
403+ type_lookup [float ] = 'float8'
404+ type_lookup [str ] = 'varchar'
405+ value_type = type_lookup [type (value )]
406+
407+ sql = """SELECT udt_name
408+ FROM information_schema.columns
409+ WHERE column_name = %s
410+ AND table_schema = 'qiita'
411+ AND (table_name = %s OR table_name = %s)"""
375412 column_type = conn_handler .execute_fetchone (
376- """SELECT data_type
377- FROM information_schema.columns
378- WHERE column_name=%s AND table_schema='qiita'
379- """ , (column ,))[0 ]
380- value_type = type (value ).__name__
413+ sql , (column , self ._table , self ._dynamic_table ))
381414
382415 if column_type != value_type :
383416 raise ValueError (
384417 'The new value being added to column: "{0}" is "{1}" '
385418 '(type: "{2}"). However, this column in the DB is of '
386419 'type "{3}". Please change the value in your updated '
387- 'template or reprocess your sample template.' .format (
420+ 'template or reprocess your template.' .format (
388421 column , value , value_type , column_type ))
389- else :
390- raise e
422+
423+ raise e
391424
392425 def __delitem__ (self , key ):
393426 r"""Removes the sample with sample id `key` from the database
@@ -1180,12 +1213,53 @@ def update_category(self, category, samples_and_values):
11801213 QiitaDBColumnError
11811214 If the column does not exist in the table. This is implicit, and
11821215 can be thrown by the contained Samples.
1216+ ValueError
1217+ If one of the new values cannot be inserted in the DB due to
1218+ different types
11831219 """
11841220 if not set (self .keys ()).issuperset (samples_and_values ):
11851221 missing = set (self .keys ()) - set (samples_and_values )
11861222 table_name = self ._table_name (self .study_id )
11871223 raise QiitaDBUnknownIDError (missing , table_name )
11881224
1225+ conn_handler = SQLConnectionHandler ()
1226+ queue_name = "update_category_%s_%s" % (self ._id , category )
1227+ conn_handler .create_queue (queue_name )
1228+
11891229 for k , v in viewitems (samples_and_values ):
11901230 sample = self [k ]
1191- sample [category ] = v
1231+ sample .add_setitem_queries (category , v , conn_handler , queue_name )
1232+
1233+ try :
1234+ conn_handler .execute_queue (queue_name )
1235+ except QiitaDBExecutionError as e :
1236+ # catching error so we can check if the error is due to different
1237+ # column type or something else
1238+ type_lookup = defaultdict (lambda : 'varchar' )
1239+ type_lookup [int ] = 'integer'
1240+ type_lookup [float ] = 'float8'
1241+ type_lookup [str ] = 'varchar'
1242+ value_types = set (type_lookup [type (value )]
1243+ for value in viewvalues (samples_and_values ))
1244+
1245+ sql = """SELECT udt_name
1246+ FROM information_schema.columns
1247+ WHERE column_name = %s
1248+ AND table_schema = 'qiita'
1249+ AND (table_name = %s OR table_name = %s)"""
1250+ column_type = conn_handler .execute_fetchone (
1251+ sql , (category , self ._table , self ._table_name (self ._id )))
1252+
1253+ if any ([column_type != vt for vt in value_types ]):
1254+ value_str = ', ' .join (
1255+ [str (value ) for value in viewvalues (samples_and_values )])
1256+ value_types_str = ', ' .join (value_types )
1257+
1258+ raise ValueError (
1259+ 'The new values being added to column: "%s" are "%s" '
1260+ '(types: "%s"). However, this column in the DB is of '
1261+ 'type "%s". Please change the values in your updated '
1262+ 'template or reprocess your template.'
1263+ % (category , value_str , value_types_str , column_type ))
1264+
1265+ raise e
0 commit comments