@@ -1239,18 +1239,58 @@ def _create_sql_schema(self, frame, table_name, keys=None):
1239
1239
}
1240
1240
1241
1241
1242
+ def _get_unicode_name (name ):
1243
+ try :
1244
+ uname = name .encode ("utf-8" , "strict" ).decode ("utf-8" )
1245
+ except UnicodeError :
1246
+ raise ValueError ("Cannot convert identifier to UTF-8: '%s'" % name )
1247
+ return uname
1248
+
1249
+ def _get_valid_mysql_name (name ):
1250
+ # Filter for unquoted identifiers
1251
+ # See http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
1252
+ uname = _get_unicode_name (name )
1253
+ if not len (uname ):
1254
+ raise ValueError ("Empty table or column name specified" )
1255
+
1256
+ basere = r'[0-9,a-z,A-Z$_]'
1257
+ for c in uname :
1258
+ if not re .match (basere , c ):
1259
+ if not (0x80 < ord (c ) < 0xFFFF ):
1260
+ raise ValueError ("Invalid MySQL identifier '%s'" % uname )
1261
+ if not re .match (r'[^0-9]' , uname ):
1262
+ raise ValueError ('MySQL identifier cannot be entirely numeric' )
1263
+
1264
+ return '`' + uname + '`'
1265
+
1266
+
1267
+ def _get_valid_sqlite_name (name ):
1268
+ # See http://stackoverflow.com/questions/6514274/how-do-you-escape-strings-for-sqlite-table-column-names-in-python
1269
+ # Ensure the string can be encoded as UTF-8.
1270
+ # Ensure the string does not include any NUL characters.
1271
+ # Replace all " with "".
1272
+ # Wrap the entire thing in double quotes.
1273
+
1274
+ uname = _get_unicode_name (name )
1275
+ if not len (uname ):
1276
+ raise ValueError ("Empty table or column name specified" )
1277
+
1278
+ nul_index = uname .find ("\x00 " )
1279
+ if nul_index >= 0 :
1280
+ raise ValueError ('SQLite identifier cannot contain NULs' )
1281
+ return '"' + uname .replace ('"' , '""' ) + '"'
1282
+
1283
+
1242
1284
# SQL enquote and wildcard symbols
1243
- _SQL_SYMB = {
1244
- 'mysql' : {
1245
- 'br_l' : '`' ,
1246
- 'br_r' : '`' ,
1247
- 'wld' : '%s'
1248
- },
1249
- 'sqlite' : {
1250
- 'br_l' : '[' ,
1251
- 'br_r' : ']' ,
1252
- 'wld' : '?'
1253
- }
1285
+ _SQL_WILDCARD = {
1286
+ 'mysql' : '%s' ,
1287
+ 'sqlite' : '?'
1288
+ }
1289
+
1290
+ # Validate and return escaped identifier
1291
+ _SQL_GET_IDENTIFIER = {
1292
+ 'mysql' : _get_valid_mysql_name ,
1293
+ 'sqlite' : _get_valid_sqlite_name ,
1254
1294
}
1255
1295
1256
1296
@@ -1276,18 +1316,17 @@ def _execute_create(self):
1276
1316
def insert_statement (self ):
1277
1317
names = list (map (str , self .frame .columns ))
1278
1318
flv = self .pd_sql .flavor
1279
- br_l = _SQL_SYMB [flv ]['br_l' ] # left val quote char
1280
- br_r = _SQL_SYMB [flv ]['br_r' ] # right val quote char
1281
- wld = _SQL_SYMB [flv ]['wld' ] # wildcard char
1319
+ wld = _SQL_WILDCARD [flv ] # wildcard char
1320
+ escape = _SQL_GET_IDENTIFIER [flv ]
1282
1321
1283
1322
if self .index is not None :
1284
1323
[names .insert (0 , idx ) for idx in self .index [::- 1 ]]
1285
1324
1286
- bracketed_names = [br_l + column + br_r for column in names ]
1325
+ bracketed_names = [escape ( column ) for column in names ]
1287
1326
col_names = ',' .join (bracketed_names )
1288
1327
wildcards = ',' .join ([wld ] * len (names ))
1289
1328
insert_statement = 'INSERT INTO %s (%s) VALUES (%s)' % (
1290
- self .name , col_names , wildcards )
1329
+ escape ( self .name ) , col_names , wildcards )
1291
1330
return insert_statement
1292
1331
1293
1332
def _execute_insert (self , conn , keys , data_iter ):
@@ -1309,29 +1348,28 @@ def _create_table_setup(self):
1309
1348
warnings .warn (_SAFE_NAMES_WARNING )
1310
1349
1311
1350
flv = self .pd_sql .flavor
1351
+ escape = _SQL_GET_IDENTIFIER [flv ]
1312
1352
1313
- br_l = _SQL_SYMB [ flv ][ 'br_l' ] # left val quote char
1314
- br_r = _SQL_SYMB [ flv ][ 'br_r' ] # right val quote char
1353
+ create_tbl_stmts = [ escape ( cname ) + ' ' + ctype
1354
+ for cname , ctype , _ in column_names_and_types ]
1315
1355
1316
- create_tbl_stmts = [(br_l + '%s' + br_r + ' %s' ) % (cname , col_type )
1317
- for cname , col_type , _ in column_names_and_types ]
1318
1356
if self .keys is not None and len (self .keys ):
1319
- cnames_br = "," .join ([br_l + c + br_r for c in self .keys ])
1357
+ cnames_br = "," .join ([escape ( c ) for c in self .keys ])
1320
1358
create_tbl_stmts .append (
1321
1359
"CONSTRAINT {tbl}_pk PRIMARY KEY ({cnames_br})" .format (
1322
1360
tbl = self .name , cnames_br = cnames_br ))
1323
1361
1324
- create_stmts = ["CREATE TABLE " + self .name + " (\n " +
1362
+ create_stmts = ["CREATE TABLE " + escape ( self .name ) + " (\n " +
1325
1363
',\n ' .join (create_tbl_stmts ) + "\n )" ]
1326
1364
1327
1365
ix_cols = [cname for cname , _ , is_index in column_names_and_types
1328
1366
if is_index ]
1329
1367
if len (ix_cols ):
1330
1368
cnames = "_" .join (ix_cols )
1331
- cnames_br = "," .join ([br_l + c + br_r for c in ix_cols ])
1369
+ cnames_br = "," .join ([escape ( c ) for c in ix_cols ])
1332
1370
create_stmts .append (
1333
- "CREATE INDEX ix_{tbl}_{cnames} ON {tbl} ({cnames_br})" . format (
1334
- tbl = self .name , cnames = cnames , cnames_br = cnames_br ) )
1371
+ "CREATE INDEX " + escape ( "ix_" + self . name + "_" + cnames ) +
1372
+ "ON " + escape ( self .name ) + " (" + cnames_br + ")" )
1335
1373
1336
1374
return create_stmts
1337
1375
@@ -1505,19 +1543,23 @@ def to_sql(self, frame, name, if_exists='fail', index=True,
1505
1543
table .insert (chunksize )
1506
1544
1507
1545
def has_table (self , name , schema = None ):
1546
+ escape = _SQL_GET_IDENTIFIER [self .flavor ]
1547
+ esc_name = escape (name )
1548
+ wld = _SQL_WILDCARD [self .flavor ]
1508
1549
flavor_map = {
1509
1550
'sqlite' : ("SELECT name FROM sqlite_master "
1510
- "WHERE type='table' AND name='%s' ;" ) % name ,
1511
- 'mysql' : "SHOW TABLES LIKE '%s' " % name }
1551
+ "WHERE type='table' AND name=%s ;" ) % wld ,
1552
+ 'mysql' : "SHOW TABLES LIKE %s " % wld }
1512
1553
query = flavor_map .get (self .flavor )
1513
1554
1514
- return len (self .execute (query ).fetchall ()) > 0
1555
+ return len (self .execute (query , [ name ,] ).fetchall ()) > 0
1515
1556
1516
1557
def get_table (self , table_name , schema = None ):
1517
1558
return None # not supported in fallback mode
1518
1559
1519
1560
def drop_table (self , name , schema = None ):
1520
- drop_sql = "DROP TABLE %s" % name
1561
+ escape = _SQL_GET_IDENTIFIER [self .flavor ]
1562
+ drop_sql = "DROP TABLE %s" % escape (name )
1521
1563
self .execute (drop_sql )
1522
1564
1523
1565
def _create_sql_schema (self , frame , table_name , keys = None ):
0 commit comments