@@ -38,7 +38,7 @@ def _is_sqlalchemy_engine(con):
3838 try :
3939 import sqlalchemy
4040 _SQLALCHEMY_INSTALLED = True
41-
41+
4242 from distutils .version import LooseVersion
4343 ver = LooseVersion (sqlalchemy .__version__ )
4444 # For sqlalchemy versions < 0.8.2, the BIGINT type is recognized
@@ -47,7 +47,7 @@ def _is_sqlalchemy_engine(con):
4747 if ver < '0.8.2' :
4848 from sqlalchemy import BigInteger
4949 from sqlalchemy .ext .compiler import compiles
50-
50+
5151 @compiles (BigInteger , 'sqlite' )
5252 def compile_big_int_sqlite (type_ , compiler , ** kw ):
5353 return 'INTEGER'
@@ -145,7 +145,7 @@ def _safe_fetch(cur):
145145 if not isinstance (result , list ):
146146 result = list (result )
147147 return result
148- except Exception as e : # pragma: no cover
148+ except Exception as e : # pragma: no cover
149149 excName = e .__class__ .__name__
150150 if excName == 'OperationalError' :
151151 return []
@@ -187,7 +187,7 @@ def tquery(sql, con=None, cur=None, retry=True):
187187 con .commit ()
188188 except Exception as e :
189189 excName = e .__class__ .__name__
190- if excName == 'OperationalError' : # pragma: no cover
190+ if excName == 'OperationalError' : # pragma: no cover
191191 print ('Failed to commit, may need to restart interpreter' )
192192 else :
193193 raise
@@ -199,7 +199,7 @@ def tquery(sql, con=None, cur=None, retry=True):
199199 if result and len (result [0 ]) == 1 :
200200 # python 3 compat
201201 result = list (lzip (* result )[0 ])
202- elif result is None : # pragma: no cover
202+ elif result is None : # pragma: no cover
203203 result = []
204204
205205 return result
@@ -253,8 +253,8 @@ def uquery(sql, con=None, cur=None, retry=True, params=None):
253253#------------------------------------------------------------------------------
254254#--- Read and write to DataFrames
255255
256- def read_sql_table (table_name , con , index_col = None , coerce_float = True ,
257- parse_dates = None , columns = None ):
256+ def read_sql_table (table_name , con , schema = None , index_col = None ,
257+ coerce_float = True , parse_dates = None , columns = None ):
258258 """Read SQL database table into a DataFrame.
259259
260260 Given a table name and an SQLAlchemy engine, returns a DataFrame.
@@ -266,6 +266,9 @@ def read_sql_table(table_name, con, index_col=None, coerce_float=True,
266266 Name of SQL table in database
267267 con : SQLAlchemy engine
268268 Sqlite DBAPI connection mode not supported
269+ schema : string, default None
270+ Name of SQL schema in database to query (if database flavor supports this).
271+ If None, use default schema (default).
269272 index_col : string, optional
270273 Column to set as index
271274 coerce_float : boolean, default True
@@ -298,7 +301,7 @@ def read_sql_table(table_name, con, index_col=None, coerce_float=True,
298301 "SQLAlchemy engines." )
299302 import sqlalchemy
300303 from sqlalchemy .schema import MetaData
301- meta = MetaData (con )
304+ meta = MetaData (con , schema = schema )
302305 try :
303306 meta .reflect (only = [table_name ])
304307 except sqlalchemy .exc .InvalidRequestError :
@@ -437,8 +440,8 @@ def read_sql(sql, con, index_col=None, coerce_float=True, params=None,
437440 coerce_float = coerce_float , parse_dates = parse_dates )
438441
439442
440- def to_sql (frame , name , con , flavor = 'sqlite' , if_exists = 'fail' , index = True ,
441- index_label = None , chunksize = None ):
443+ def to_sql (frame , name , con , flavor = 'sqlite' , schema = None , if_exists = 'fail' ,
444+ index = True , index_label = None , chunksize = None ):
442445 """
443446 Write records stored in a DataFrame to a SQL database.
444447
@@ -455,6 +458,9 @@ def to_sql(frame, name, con, flavor='sqlite', if_exists='fail', index=True,
455458 The flavor of SQL to use. Ignored when using SQLAlchemy engine.
456459 'mysql' is deprecated and will be removed in future versions, but it
457460 will be further supported through SQLAlchemy engines.
461+ schema : string, default None
462+ Name of SQL schema in database to write to (if database flavor supports
463+ this). If None, use default schema (default).
458464 if_exists : {'fail', 'replace', 'append'}, default 'fail'
459465 - fail: If table exists, do nothing.
460466 - replace: If table exists, drop it, recreate it, and insert data.
@@ -473,18 +479,19 @@ def to_sql(frame, name, con, flavor='sqlite', if_exists='fail', index=True,
473479 if if_exists not in ('fail' , 'replace' , 'append' ):
474480 raise ValueError ("'{0}' is not valid for if_exists" .format (if_exists ))
475481
476- pandas_sql = pandasSQL_builder (con , flavor = flavor )
482+ pandas_sql = pandasSQL_builder (con , schema = schema , flavor = flavor )
477483
478484 if isinstance (frame , Series ):
479485 frame = frame .to_frame ()
480486 elif not isinstance (frame , DataFrame ):
481487 raise NotImplementedError
482488
483489 pandas_sql .to_sql (frame , name , if_exists = if_exists , index = index ,
484- index_label = index_label , chunksize = chunksize )
490+ index_label = index_label , schema = schema ,
491+ chunksize = chunksize )
485492
486493
487- def has_table (table_name , con , flavor = 'sqlite' ):
494+ def has_table (table_name , con , flavor = 'sqlite' , schema = None ):
488495 """
489496 Check if DataBase has named table.
490497
@@ -500,12 +507,15 @@ def has_table(table_name, con, flavor='sqlite'):
500507 The flavor of SQL to use. Ignored when using SQLAlchemy engine.
501508 'mysql' is deprecated and will be removed in future versions, but it
502509 will be further supported through SQLAlchemy engines.
510+ schema : string, default None
511+ Name of SQL schema in database to write to (if database flavor supports
512+ this). If None, use default schema (default).
503513
504514 Returns
505515 -------
506516 boolean
507517 """
508- pandas_sql = pandasSQL_builder (con , flavor = flavor )
518+ pandas_sql = pandasSQL_builder (con , flavor = flavor , schema = schema )
509519 return pandas_sql .has_table (table_name )
510520
511521table_exists = has_table
@@ -515,15 +525,15 @@ def has_table(table_name, con, flavor='sqlite'):
515525 "and will be removed in future versions. "
516526 "MySQL will be further supported with SQLAlchemy engines." )
517527
518- def pandasSQL_builder (con , flavor = None , meta = None , is_cursor = False ):
528+ def pandasSQL_builder (con , flavor = None , schema = None , meta = None , is_cursor = False ):
519529 """
520530 Convenience function to return the correct PandasSQL subclass based on the
521531 provided parameters
522532 """
523533 # When support for DBAPI connections is removed,
524534 # is_cursor should not be necessary.
525535 if _is_sqlalchemy_engine (con ):
526- return PandasSQLAlchemy (con , meta = meta )
536+ return PandasSQLAlchemy (con , schema = schema , meta = meta )
527537 else :
528538 if flavor == 'mysql' :
529539 warnings .warn (_MYSQL_WARNING , FutureWarning )
@@ -540,24 +550,26 @@ class PandasSQLTable(PandasObject):
540550 """
541551 # TODO: support for multiIndex
542552 def __init__ (self , name , pandas_sql_engine , frame = None , index = True ,
543- if_exists = 'fail' , prefix = 'pandas' , index_label = None ):
553+ if_exists = 'fail' , prefix = 'pandas' , index_label = None ,
554+ schema = None ):
544555 self .name = name
545556 self .pd_sql = pandas_sql_engine
546557 self .prefix = prefix
547558 self .frame = frame
548559 self .index = self ._index_name (index , index_label )
560+ self .schema = schema
549561
550562 if frame is not None :
551563 # We want to write a frame
552- if self .pd_sql .has_table (self .name ):
564+ if self .pd_sql .has_table (self .name , self . schema ):
553565 if if_exists == 'fail' :
554566 raise ValueError ("Table '%s' already exists." % name )
555567 elif if_exists == 'replace' :
556- self .pd_sql .drop_table (self .name )
568+ self .pd_sql .drop_table (self .name , self . schema )
557569 self .table = self ._create_table_statement ()
558570 self .create ()
559571 elif if_exists == 'append' :
560- self .table = self .pd_sql .get_table (self .name )
572+ self .table = self .pd_sql .get_table (self .name , self . schema )
561573 if self .table is None :
562574 self .table = self ._create_table_statement ()
563575 else :
@@ -568,13 +580,13 @@ def __init__(self, name, pandas_sql_engine, frame=None, index=True,
568580 self .create ()
569581 else :
570582 # no data provided, read-only mode
571- self .table = self .pd_sql .get_table (self .name )
583+ self .table = self .pd_sql .get_table (self .name , self . schema )
572584
573585 if self .table is None :
574586 raise ValueError ("Could not init table '%s'" % name )
575587
576588 def exists (self ):
577- return self .pd_sql .has_table (self .name )
589+ return self .pd_sql .has_table (self .name , self . schema )
578590
579591 def sql_schema (self ):
580592 from sqlalchemy .schema import CreateTable
@@ -709,7 +721,7 @@ def _create_table_statement(self):
709721 columns = [Column (name , typ )
710722 for name , typ in column_names_and_types ]
711723
712- return Table (self .name , self .pd_sql .meta , * columns )
724+ return Table (self .name , self .pd_sql .meta , * columns , schema = self . schema )
713725
714726 def _harmonize_columns (self , parse_dates = None ):
715727 """ Make a data_frame's column type align with an sql_table
@@ -830,11 +842,11 @@ class PandasSQLAlchemy(PandasSQL):
830842 using SQLAlchemy to handle DataBase abstraction
831843 """
832844
833- def __init__ (self , engine , meta = None ):
845+ def __init__ (self , engine , schema = None , meta = None ):
834846 self .engine = engine
835847 if not meta :
836848 from sqlalchemy .schema import MetaData
837- meta = MetaData (self .engine )
849+ meta = MetaData (self .engine , schema = schema )
838850
839851 self .meta = meta
840852
@@ -843,9 +855,10 @@ def execute(self, *args, **kwargs):
843855 return self .engine .execute (* args , ** kwargs )
844856
845857 def read_table (self , table_name , index_col = None , coerce_float = True ,
846- parse_dates = None , columns = None ):
858+ parse_dates = None , columns = None , schema = None ):
847859
848- table = PandasSQLTable (table_name , self , index = index_col )
860+ table = PandasSQLTable (
861+ table_name , self , index = index_col , schema = schema )
849862 return table .read (coerce_float = coerce_float ,
850863 parse_dates = parse_dates , columns = columns )
851864
@@ -868,26 +881,31 @@ def read_sql(self, sql, index_col=None, coerce_float=True,
868881 return data_frame
869882
870883 def to_sql (self , frame , name , if_exists = 'fail' , index = True ,
871- index_label = None , chunksize = None ):
884+ index_label = None , schema = None , chunksize = None ):
872885 table = PandasSQLTable (
873886 name , self , frame = frame , index = index , if_exists = if_exists ,
874- index_label = index_label )
887+ index_label = index_label , schema = schema )
875888 table .insert (chunksize )
876889
877890 @property
878891 def tables (self ):
879892 return self .meta .tables
880893
881- def has_table (self , name ):
882- return self .engine .has_table (name )
894+ def has_table (self , name , schema = None ):
895+ return self .engine .has_table (name , schema or self . meta . schema )
883896
884- def get_table (self , table_name ):
885- return self .meta .tables .get (table_name )
897+ def get_table (self , table_name , schema = None ):
898+ schema = schema or self .meta .schema
899+ if schema :
900+ return self .meta .tables .get ('.' .join ([schema , table_name ]))
901+ else :
902+ return self .meta .tables .get (table_name )
886903
887- def drop_table (self , table_name ):
888- if self .engine .has_table (table_name ):
889- self .meta .reflect (only = [table_name ])
890- self .get_table (table_name ).drop ()
904+ def drop_table (self , table_name , schema = None ):
905+ schema = schema or self .meta .schema
906+ if self .engine .has_table (table_name , schema ):
907+ self .meta .reflect (only = [table_name ], schema = schema )
908+ self .get_table (table_name , schema ).drop ()
891909 self .meta .clear ()
892910
893911 def _create_sql_schema (self , frame , table_name ):
@@ -1113,7 +1131,7 @@ def _fetchall_as_list(self, cur):
11131131 return result
11141132
11151133 def to_sql (self , frame , name , if_exists = 'fail' , index = True ,
1116- index_label = None , chunksize = None ):
1134+ index_label = None , schema = None , chunksize = None ):
11171135 """
11181136 Write records stored in a DataFrame to a SQL database.
11191137
@@ -1133,7 +1151,7 @@ def to_sql(self, frame, name, if_exists='fail', index=True,
11331151 index_label = index_label )
11341152 table .insert (chunksize )
11351153
1136- def has_table (self , name ):
1154+ def has_table (self , name , schema = None ):
11371155 flavor_map = {
11381156 'sqlite' : ("SELECT name FROM sqlite_master "
11391157 "WHERE type='table' AND name='%s';" ) % name ,
@@ -1142,10 +1160,10 @@ def has_table(self, name):
11421160
11431161 return len (self .execute (query ).fetchall ()) > 0
11441162
1145- def get_table (self , table_name ):
1163+ def get_table (self , table_name , schema = None ):
11461164 return None # not supported in Legacy mode
11471165
1148- def drop_table (self , name ):
1166+ def drop_table (self , name , schema = None ):
11491167 drop_sql = "DROP TABLE %s" % name
11501168 self .execute (drop_sql )
11511169
0 commit comments