@@ -1253,18 +1253,58 @@ def _create_sql_schema(self, frame, table_name, keys=None, dtype=None):
1253
1253
}
1254
1254
1255
1255
1256
+ def _get_unicode_name (name ):
1257
+ try :
1258
+ uname = name .encode ("utf-8" , "strict" ).decode ("utf-8" )
1259
+ except UnicodeError :
1260
+ raise ValueError ("Cannot convert identifier to UTF-8: '%s'" % name )
1261
+ return uname
1262
+
1263
+ def _get_valid_mysql_name (name ):
1264
+ # Filter for unquoted identifiers
1265
+ # See http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
1266
+ uname = _get_unicode_name (name )
1267
+ if not len (uname ):
1268
+ raise ValueError ("Empty table or column name specified" )
1269
+
1270
+ basere = r'[0-9,a-z,A-Z$_]'
1271
+ for c in uname :
1272
+ if not re .match (basere , c ):
1273
+ if not (0x80 < ord (c ) < 0xFFFF ):
1274
+ raise ValueError ("Invalid MySQL identifier '%s'" % uname )
1275
+ if not re .match (r'[^0-9]' , uname ):
1276
+ raise ValueError ('MySQL identifier cannot be entirely numeric' )
1277
+
1278
+ return '`' + uname + '`'
1279
+
1280
+
1281
+ def _get_valid_sqlite_name (name ):
1282
+ # See http://stackoverflow.com/questions/6514274/how-do-you-escape-strings-for-sqlite-table-column-names-in-python
1283
+ # Ensure the string can be encoded as UTF-8.
1284
+ # Ensure the string does not include any NUL characters.
1285
+ # Replace all " with "".
1286
+ # Wrap the entire thing in double quotes.
1287
+
1288
+ uname = _get_unicode_name (name )
1289
+ if not len (uname ):
1290
+ raise ValueError ("Empty table or column name specified" )
1291
+
1292
+ nul_index = uname .find ("\x00 " )
1293
+ if nul_index >= 0 :
1294
+ raise ValueError ('SQLite identifier cannot contain NULs' )
1295
+ return '"' + uname .replace ('"' , '""' ) + '"'
1296
+
1297
+
1256
1298
# SQL enquote and wildcard symbols
1257
- _SQL_SYMB = {
1258
- 'mysql' : {
1259
- 'br_l' : '`' ,
1260
- 'br_r' : '`' ,
1261
- 'wld' : '%s'
1262
- },
1263
- 'sqlite' : {
1264
- 'br_l' : '[' ,
1265
- 'br_r' : ']' ,
1266
- 'wld' : '?'
1267
- }
1299
+ _SQL_WILDCARD = {
1300
+ 'mysql' : '%s' ,
1301
+ 'sqlite' : '?'
1302
+ }
1303
+
1304
+ # Validate and return escaped identifier
1305
+ _SQL_GET_IDENTIFIER = {
1306
+ 'mysql' : _get_valid_mysql_name ,
1307
+ 'sqlite' : _get_valid_sqlite_name ,
1268
1308
}
1269
1309
1270
1310
@@ -1290,18 +1330,17 @@ def _execute_create(self):
1290
1330
def insert_statement (self ):
1291
1331
names = list (map (str , self .frame .columns ))
1292
1332
flv = self .pd_sql .flavor
1293
- br_l = _SQL_SYMB [flv ]['br_l' ] # left val quote char
1294
- br_r = _SQL_SYMB [flv ]['br_r' ] # right val quote char
1295
- wld = _SQL_SYMB [flv ]['wld' ] # wildcard char
1333
+ wld = _SQL_WILDCARD [flv ] # wildcard char
1334
+ escape = _SQL_GET_IDENTIFIER [flv ]
1296
1335
1297
1336
if self .index is not None :
1298
1337
[names .insert (0 , idx ) for idx in self .index [::- 1 ]]
1299
1338
1300
- bracketed_names = [br_l + column + br_r for column in names ]
1339
+ bracketed_names = [escape ( column ) for column in names ]
1301
1340
col_names = ',' .join (bracketed_names )
1302
1341
wildcards = ',' .join ([wld ] * len (names ))
1303
1342
insert_statement = 'INSERT INTO %s (%s) VALUES (%s)' % (
1304
- self .name , col_names , wildcards )
1343
+ escape ( self .name ) , col_names , wildcards )
1305
1344
return insert_statement
1306
1345
1307
1346
def _execute_insert (self , conn , keys , data_iter ):
@@ -1323,29 +1362,28 @@ def _create_table_setup(self):
1323
1362
warnings .warn (_SAFE_NAMES_WARNING )
1324
1363
1325
1364
flv = self .pd_sql .flavor
1365
+ escape = _SQL_GET_IDENTIFIER [flv ]
1326
1366
1327
- br_l = _SQL_SYMB [ flv ][ 'br_l' ] # left val quote char
1328
- br_r = _SQL_SYMB [ flv ][ 'br_r' ] # right val quote char
1367
+ create_tbl_stmts = [ escape ( cname ) + ' ' + ctype
1368
+ for cname , ctype , _ in column_names_and_types ]
1329
1369
1330
- create_tbl_stmts = [(br_l + '%s' + br_r + ' %s' ) % (cname , col_type )
1331
- for cname , col_type , _ in column_names_and_types ]
1332
1370
if self .keys is not None and len (self .keys ):
1333
- cnames_br = "," .join ([br_l + c + br_r for c in self .keys ])
1371
+ cnames_br = "," .join ([escape ( c ) for c in self .keys ])
1334
1372
create_tbl_stmts .append (
1335
1373
"CONSTRAINT {tbl}_pk PRIMARY KEY ({cnames_br})" .format (
1336
1374
tbl = self .name , cnames_br = cnames_br ))
1337
1375
1338
- create_stmts = ["CREATE TABLE " + self .name + " (\n " +
1376
+ create_stmts = ["CREATE TABLE " + escape ( self .name ) + " (\n " +
1339
1377
',\n ' .join (create_tbl_stmts ) + "\n )" ]
1340
1378
1341
1379
ix_cols = [cname for cname , _ , is_index in column_names_and_types
1342
1380
if is_index ]
1343
1381
if len (ix_cols ):
1344
1382
cnames = "_" .join (ix_cols )
1345
- cnames_br = "," .join ([br_l + c + br_r for c in ix_cols ])
1383
+ cnames_br = "," .join ([escape ( c ) for c in ix_cols ])
1346
1384
create_stmts .append (
1347
- "CREATE INDEX ix_{tbl}_{cnames} ON {tbl} ({cnames_br})" . format (
1348
- tbl = self .name , cnames = cnames , cnames_br = cnames_br ) )
1385
+ "CREATE INDEX " + escape ( "ix_" + self . name + "_" + cnames ) +
1386
+ "ON " + escape ( self .name ) + " (" + cnames_br + ")" )
1349
1387
1350
1388
return create_stmts
1351
1389
@@ -1519,19 +1557,23 @@ def to_sql(self, frame, name, if_exists='fail', index=True,
1519
1557
table .insert (chunksize )
1520
1558
1521
1559
def has_table (self , name , schema = None ):
1560
+ escape = _SQL_GET_IDENTIFIER [self .flavor ]
1561
+ esc_name = escape (name )
1562
+ wld = _SQL_WILDCARD [self .flavor ]
1522
1563
flavor_map = {
1523
1564
'sqlite' : ("SELECT name FROM sqlite_master "
1524
- "WHERE type='table' AND name='%s' ;" ) % name ,
1525
- 'mysql' : "SHOW TABLES LIKE '%s' " % name }
1565
+ "WHERE type='table' AND name=%s ;" ) % wld ,
1566
+ 'mysql' : "SHOW TABLES LIKE %s " % wld }
1526
1567
query = flavor_map .get (self .flavor )
1527
1568
1528
- return len (self .execute (query ).fetchall ()) > 0
1569
+ return len (self .execute (query , [ name ,] ).fetchall ()) > 0
1529
1570
1530
1571
def get_table (self , table_name , schema = None ):
1531
1572
return None # not supported in fallback mode
1532
1573
1533
1574
def drop_table (self , name , schema = None ):
1534
- drop_sql = "DROP TABLE %s" % name
1575
+ escape = _SQL_GET_IDENTIFIER [self .flavor ]
1576
+ drop_sql = "DROP TABLE %s" % escape (name )
1535
1577
self .execute (drop_sql )
1536
1578
1537
1579
def _create_sql_schema (self , frame , table_name , keys = None , dtype = None ):
0 commit comments