OracleBranch: django-oracle-rev5046.diff
File django-oracle-rev5046.diff, 83.7 KB (added by , 18 years ago) |
---|
-
django/db/backends/mysql/base.py
131 131 self.server_version = tuple([int(x) for x in m.groups()]) 132 132 return self.server_version 133 133 134 135 136 137 138 134 139 supports_constraints = True 140 141 135 142 136 143 def quote_name(name): 137 144 if name.startswith("`") and name.endswith("`"): … … 164 171 sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) 165 172 return sql 166 173 174 175 176 167 177 def get_limit_offset_sql(limit, offset=None): 168 178 sql = "LIMIT " 169 179 if offset and offset != 0: … … 185 195 def get_pk_default_value(): 186 196 return "DEFAULT" 187 197 198 199 200 201 202 203 204 205 206 188 207 def get_sql_flush(style, tables, sequences): 189 208 """Return a list of SQL statements required to remove all data from 190 209 all tables in the database (without actually removing the tables -
django/db/backends/ado_mssql/base.py
88 88 self.connection.close() 89 89 self.connection = None 90 90 91 92 93 94 95 91 96 supports_constraints = True 97 98 92 99 93 100 def quote_name(name): 94 101 if name.startswith('[') and name.endswith(']'): … … 116 123 if lookup_type=='day': 117 124 return "Convert(datetime, Convert(varchar(12), %s))" % field_name 118 125 126 127 128 119 129 def get_limit_offset_sql(limit, offset=None): 120 130 # TODO: This is a guess. Make sure this is correct. 121 131 sql = "LIMIT %s" % limit … … 138 148 def get_pk_default_value(): 139 149 return "DEFAULT" 140 150 151 152 153 154 155 156 157 158 159 160 161 162 141 163 def get_sql_flush(style, tables, sequences): 142 164 """Return a list of SQL statements required to remove all data from 143 165 all tables in the database (without actually removing the tables -
django/db/backends/postgresql/base.py
104 104 self.connection.close() 105 105 self.connection = None 106 106 107 108 109 110 111 107 112 supports_constraints = True 113 114 108 115 109 116 def quote_name(name): 110 117 if name.startswith('"') and name.endswith('"'): … … 137 144 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 138 145 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 139 146 147 148 149 140 150 def get_limit_offset_sql(limit, offset=None): 141 151 sql = "LIMIT %s" % limit 142 152 if offset and offset != 0: … … 158 168 def get_pk_default_value(): 159 169 return "DEFAULT" 160 170 171 172 173 174 175 176 177 178 179 161 180 def get_sql_flush(style, tables, sequences): 162 181 """Return a list of SQL statements required to remove all data from 163 182 all tables in the database (without actually removing the tables -
django/db/backends/sqlite3/base.py
99 99 def convert_query(self, query, num_params): 100 100 return query % tuple("?" * num_params) 101 101 102 103 104 105 106 102 107 supports_constraints = False 108 109 103 110 104 111 def quote_name(name): 105 112 if name.startswith('"') and name.endswith('"'): … … 131 138 # sqlite doesn't support DATE_TRUNC, so we fake it as above. 132 139 return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) 133 140 141 142 143 134 144 def get_limit_offset_sql(limit, offset=None): 135 145 sql = "LIMIT %s" % limit 136 146 if offset and offset != 0: … … 152 162 def get_pk_default_value(): 153 163 return "NULL" 154 164 165 166 167 168 169 170 171 172 173 155 174 def get_sql_flush(style, tables, sequences): 156 175 """Return a list of SQL statements required to remove all data from 157 176 all tables in the database (without actually removing the tables -
django/db/backends/util.py
1 1 import datetime 2 2 3 from time import time 3 4 4 5 class CursorDebugWrapper(object): … … 92 93 def rev_typecast_boolean(obj, d): 93 94 return obj and '1' or '0' 94 95 96 97 98 99 100 101 102 103 104 105 95 106 ################################################################################## 96 107 # Helper functions for dictfetch* for databases that don't natively support them # 97 108 ################################################################################## -
django/db/backends/oracle/base.py
4 4 Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ 5 5 """ 6 6 7 7 8 from django.db.backends import util 8 9 try: 9 10 import cx_Oracle as Database 10 11 except ImportError, e: 11 12 from django.core.exceptions import ImproperlyConfigured 12 13 raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e 14 15 13 16 17 14 18 DatabaseError = Database.Error 15 19 16 20 try: … … 30 34 return self.connection is not None 31 35 32 36 def cursor(self): 33 from django.conf import settings34 37 if not self._valid_connection(): 35 38 if len(settings.DATABASE_HOST.strip()) == 0: 36 39 settings.DATABASE_HOST = 'localhost' … … 40 43 else: 41 44 conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME) 42 45 self.connection = Database.connect(conn_string, **self.options) 43 return FormatStylePlaceholderCursor(self.connection) 46 cursor = FormatStylePlaceholderCursor(self.connection) 47 # default arraysize of 1 is highly sub-optimal 48 cursor.arraysize = 100 49 # set oracle date to ansi date format 50 cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'") 51 cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") 52 if settings.DEBUG: 53 return util.CursorDebugWrapper(cursor, self) 54 return cursor 44 55 45 56 def _commit(self): 46 57 if self.connection is not None: 47 self.connection.commit()58 self.connection.commit() 48 59 49 60 def _rollback(self): 50 61 if self.connection is not None: 51 try: 52 self.connection.rollback() 53 except Database.NotSupportedError: 54 pass 62 return self.connection.rollback() 55 63 56 64 def close(self): 57 65 if self.connection is not None: 58 66 self.connection.close() 59 67 self.connection = None 60 68 69 70 71 72 73 61 74 supports_constraints = True 75 76 62 77 63 78 class FormatStylePlaceholderCursor(Database.Cursor): 64 79 """ … … 66 81 This fixes it -- but note that if you want to use a literal "%s" in a query, 67 82 you'll need to use "%%s". 68 83 """ 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 69 102 def execute(self, query, params=None): 70 if params is None: params = [] 71 query = self.convert_arguments(query, len(params)) 103 query, params = self._rewrite_args(query, params) 72 104 return Database.Cursor.execute(self, query, params) 73 105 74 106 def executemany(self, query, params=None): 75 if params is None: params = [] 76 query = self.convert_arguments(query, len(params[0])) 107 query, params = self._rewrite_args(query, params) 77 108 return Database.Cursor.executemany(self, query, params) 78 109 79 def convert_arguments(self, query, num_params):80 # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders.81 args = [':arg' for i in range(num_params)]82 return query % tuple(args)83 84 110 def quote_name(name): 85 return name 111 # SQL92 requires delimited (quoted) names to be case-sensitive. When 112 # not quoted, Oracle has case-insensitive behavior for identifiers, but 113 # always defaults to uppercase. 114 # We simplify things by making Oracle identifiers always uppercase. 115 if not name.startswith('"') and not name.endswith('"'): 116 name = '"%s"' % util.truncate_name(name.upper(), get_max_name_length()) 117 return name.upper() 86 118 87 119 dictfetchone = util.dictfetchone 88 120 dictfetchmany = util.dictfetchmany 89 121 dictfetchall = util.dictfetchall 90 122 91 123 def get_last_insert_id(cursor, table_name, pk_name): 92 query = "SELECT %s_sq.currval from dual" % table_name93 cursor.execute( query)124 125 cursor.execute() 94 126 return cursor.fetchone()[0] 95 127 96 128 def get_date_extract_sql(lookup_type, table_name): 97 129 # lookup_type is 'year', 'month', 'day' 98 # http:// www.psoug.org/reference/date_func.html130 # http:// 99 131 return "EXTRACT(%s FROM %s)" % (lookup_type, table_name) 100 132 101 133 def get_date_trunc_sql(lookup_type, field_name): 102 return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, field_name) 134 # lookup_type is 'year', 'month', 'day' 135 # Oracle uses TRUNC() for both dates and numbers. 136 # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151 137 if lookup_type == 'day': 138 sql = 'TRUNC(%s)' % (field_name,) 139 else: 140 sql = "TRUNC(%s, '%s')" % (field_name, lookup_type) 141 return sql 103 142 143 144 145 104 146 def get_limit_offset_sql(limit, offset=None): 105 147 # Limits and offset are too complicated to be handled here. 106 # Instead, they are handled in django/db/ query.py.107 pass148 # Instead, they are handled in django/db/query.py. 149 108 150 109 151 def get_random_function_sql(): 110 152 return "DBMS_RANDOM.RANDOM" … … 116 158 raise NotImplementedError 117 159 118 160 def get_drop_foreignkey_sql(): 119 return "DROP FOREIGN KEY"161 return "DROP " 120 162 121 163 def get_pk_default_value(): 122 164 return "DEFAULT" 123 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 124 212 def get_sql_flush(style, tables, sequences): 125 213 """Return a list of SQL statements required to remove all data from 126 214 all tables in the database (without actually removing the tables 127 215 themselves) and put the database in an empty 'initial' state 128 216 """ 129 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 130 # TODO - SQL not actually tested against Oracle yet! 131 # TODO - autoincrement indices reset required? See other get_sql_flush() implementations 132 sql = ['%s %s;' % \ 133 (style.SQL_KEYWORD('TRUNCATE'), 217 # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 218 # 'TRUNCATE z;'... style SQL statements 219 if tables: 220 # Oracle does support TRUNCATE, but it seems to get us into 221 # FK referential trouble, whereas DELETE FROM table works. 222 sql = ['%s %s %s;' % \ 223 (style.SQL_KEYWORD('DELETE'), 224 style.SQL_KEYWORD('FROM'), 134 225 style.SQL_FIELD(quote_name(table)) 135 226 ) for table in tables] 227 228 229 230 231 232 233 234 235 236 237 136 238 239 240 241 242 137 243 def get_sql_sequence_reset(style, model_list): 138 244 "Returns a list of the SQL statements to reset sequences for the given models." 139 # No sequence reset required 140 return [] 245 from django.db import models 246 output = [] 247 query = _get_sequence_reset_sql() 248 for model in model_list: 249 for f in model._meta.fields: 250 if isinstance(f, models.AutoField): 251 sequence_name = get_sequence_name(model._meta.db_table) 252 output.append(query % {'sequence':sequence_name, 253 'table':model._meta.db_table}) 254 break # Only one AutoField is allowed per model, so don't bother continuing. 255 for f in model._meta.many_to_many: 256 sequence_name = get_sequence_name(f.m2m_db_table()) 257 output.append(query % {'sequence':sequence_name, 258 'table':f.m2m_db_table()}) 259 return output 141 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 142 501 OPERATOR_MAPPING = { 143 502 'exact': '= %s', 144 'iexact': ' LIKE %s',145 'contains': 'LIKE %s',146 'icontains': 'LIKE %s',503 'iexact': '', 504 'contains': , 505 'icontains': , 147 506 'gt': '> %s', 148 507 'gte': '>= %s', 149 508 'lt': '< %s', 150 509 'lte': '<= %s', 151 'startswith': 'LIKE %s',152 'endswith': 'LIKE %s',153 'istartswith': 'LIKE %s',154 'iendswith': 'LIKE %s',510 'startswith': , 511 'endswith': , 512 'istartswith': , 513 'iendswith': , 155 514 } -
django/db/backends/oracle/client.py
2 2 import os 3 3 4 4 def runshell(): 5 args = '' 6 args += settings.DATABASE_USER 5 dsn = settings.DATABASE_USER 7 6 if settings.DATABASE_PASSWORD: 8 args += "/%s" % settings.DATABASE_PASSWORD 9 args += "@%s" % settings.DATABASE_NAME 10 os.execvp('sqlplus', args) 7 dsn += "/%s" % settings.DATABASE_PASSWORD 8 if settings.DATABASE_NAME: 9 dsn += "@%s" % settings.DATABASE_NAME 10 args = ["sqlplus", "-L", dsn] 11 os.execvp("sqlplus", args) -
django/db/backends/oracle/introspection.py
1 1 2 import re 3 2 4 5 3 6 foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") 4 7 5 8 def get_table_list(cursor): 6 9 "Returns a list of table names in the current database." 7 10 cursor.execute("SELECT TABLE_NAME FROM USER_TABLES") 8 return [row[0] for row in cursor.fetchall()]11 return [row[0] for row in cursor.fetchall()] 9 12 10 13 def get_table_description(cursor, table_name): 11 return table_name 14 "Returns a description of the table, with the DB-API cursor.description interface." 15 cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % quote_name(table_name)) 16 return cursor.description 12 17 13 18 def _name_to_index(cursor, table_name): 14 19 """ … … 22 27 Returns a dictionary of {field_index: (field_index_other_table, other_table)} 23 28 representing all relationships to the given table. Indexes are 0-based. 24 29 """ 25 raise NotImplementedError 30 cursor.execute(""" 31 SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1 32 FROM user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb, 33 user_tab_cols ta, user_tab_cols tb 34 WHERE user_constraints.table_name = %s AND 35 ta.table_name = %s AND 36 ta.column_name = ca.column_name AND 37 ca.table_name = %s AND 38 user_constraints.constraint_name = ca.constraint_name AND 39 user_constraints.r_constraint_name = cb.constraint_name AND 40 cb.table_name = tb.table_name AND 41 cb.column_name = tb.column_name AND 42 ca.position = cb.position""", [table_name, table_name, table_name]) 26 43 44 45 46 47 48 27 49 def get_indexes(cursor, table_name): 28 50 """ 29 51 Returns a dictionary of fieldname -> infodict for the given table, … … 31 53 {'primary_key': boolean representing whether it's the primary key, 32 54 'unique': boolean representing whether it's a unique index} 33 55 """ 34 raise NotImplementedError 56 # This query retrieves each index on the given table, including the 57 # first associated field name 58 # "We were in the nick of time; you were in great peril!" 59 sql = """ 60 WITH primarycols AS ( 61 SELECT user_cons_columns.table_name, user_cons_columns.column_name, 1 AS PRIMARYCOL 62 FROM user_cons_columns, user_constraints 63 WHERE user_cons_columns.constraint_name = user_constraints.constraint_name AND 64 user_constraints.constraint_type = 'P' AND 65 user_cons_columns.table_name = %s), 66 uniquecols AS ( 67 SELECT user_ind_columns.table_name, user_ind_columns.column_name, 1 AS UNIQUECOL 68 FROM user_indexes, user_ind_columns 69 WHERE uniqueness = 'UNIQUE' AND 70 user_indexes.index_name = user_ind_columns.index_name AND 71 user_ind_columns.table_name = %s) 72 SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL 73 FROM (SELECT column_name FROM primarycols UNION SELECT column_name FROM 74 uniquecols) allcols, 75 primarycols, uniquecols 76 WHERE allcols.column_name = primarycols.column_name (+) AND 77 allcols.column_name = uniquecols.column_name (+) 78 """ 79 cursor.execute(sql, [table_name, table_name]) 80 indexes = {} 81 for row in cursor.fetchall(): 82 # row[1] (idx.indkey) is stored in the DB as an array. It comes out as 83 # a string of space-separated integers. This designates the field 84 # indexes (1-based) of the fields that have indexes on the table. 85 # Here, we skip any indexes across multiple fields. 86 indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]} 87 return indexes 35 88 36 # Maps type codes to Django Field types.89 # Maps type s to Django Field types. 37 90 DATA_TYPES_REVERSE = { 38 16: 'BooleanField', 39 21: 'SmallIntegerField', 40 23: 'IntegerField', 41 25: 'TextField', 42 869: 'IPAddressField', 43 1043: 'CharField', 44 1082: 'DateField', 45 1083: 'TimeField', 46 1114: 'DateTimeField', 47 1184: 'DateTimeField', 48 1266: 'TimeField', 49 1700: 'FloatField', 91 cx_Oracle.CLOB: 'TextField', 92 cx_Oracle.DATETIME: 'DateTimeField', 93 cx_Oracle.FIXED_CHAR: 'CharField', 94 cx_Oracle.NCLOB: 'TextField', 95 cx_Oracle.NUMBER: 'FloatField', 96 cx_Oracle.STRING: 'TextField', 97 cx_Oracle.TIMESTAMP: 'DateTimeField', 50 98 } -
django/db/backends/oracle/creation.py
1 2 3 4 5 6 7 1 8 DATA_TYPES = { 2 'AutoField': 'number(38)',3 'BooleanField': 'number(1)',4 'CharField': 'varchar2(%(maxlength)s)',5 'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)',6 'DateField': 'date',7 'DateTimeField': 'date',8 'FileField': 'varchar2(100)',9 'FilePathField': 'varchar2(100)',10 'FloatField': 'number(%(max_digits)s, %(decimal_places)s)',11 'ImageField': 'varchar2(100)',12 'IntegerField': 'integer',13 'IPAddressField': 'char(15)',9 'AutoField': )', 10 'BooleanField': )', 11 'CharField': 2(%(maxlength)s)', 12 'CommaSeparatedIntegerField': 2(%(maxlength)s)', 13 'DateField': ', 14 'DateTimeField': ', 15 'FileField': 2(100)', 16 'FilePathField': 2(100)', 17 'FloatField': (%(max_digits)s, %(decimal_places)s)', 18 'ImageField': 2(100)', 19 'IntegerField': ', 20 'IPAddressField': (15)', 14 21 'ManyToManyField': None, 15 'NullBooleanField': 'integer', 16 'OneToOneField': 'integer', 17 'PhoneNumberField': 'varchar(20)', 18 'PositiveIntegerField': 'integer', 19 'PositiveSmallIntegerField': 'smallint', 20 'SlugField': 'varchar(50)', 21 'SmallIntegerField': 'smallint', 22 'TextField': 'long', 23 'TimeField': 'timestamp', 24 'USStateField': 'varchar(2)', 22 'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', 23 'OneToOneField': 'NUMBER(11)', 24 'PhoneNumberField': 'VARCHAR2(20)', 25 'PositiveIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', 26 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)', 27 'SlugField': 'VARCHAR2(50)', 28 'SmallIntegerField': 'NUMBER(11)', 29 'TextField': 'NCLOB', 30 'TimeField': 'TIMESTAMP', 31 'URLField': 'VARCHAR2(200)', 32 'USStateField': 'CHAR(2)', 25 33 } 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 -
django/db/backends/postgresql_psycopg2/base.py
72 72 self.connection.close() 73 73 self.connection = None 74 74 75 76 77 78 79 75 80 supports_constraints = True 81 82 76 83 77 84 def quote_name(name): 78 85 if name.startswith('"') and name.endswith('"'): … … 97 104 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 98 105 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 99 106 107 108 109 100 110 def get_limit_offset_sql(limit, offset=None): 101 111 sql = "LIMIT %s" % limit 102 112 if offset and offset != 0: … … 118 128 def get_pk_default_value(): 119 129 return "DEFAULT" 120 130 131 132 133 134 135 136 137 138 139 121 140 def get_sql_flush(style, tables, sequences): 122 141 """Return a list of SQL statements required to remove all data from 123 142 all tables in the database (without actually removing the tables -
django/db/backends/dummy/base.py
27 27 pass # close() 28 28 29 29 supports_constraints = False 30 30 31 quote_name = complain 31 32 dictfetchone = complain 32 33 dictfetchmany = complain -
django/db/models/base.py
210 210 record_exists = True 211 211 if pk_set: 212 212 # Determine whether a record with the primary key already exists. 213 cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \213 cursor.execute("SELECT " % \ 214 214 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) 215 215 # If it does already exist, do an UPDATE. 216 if cursor.fetchone() :216 if cursor.fetchone(): 217 217 db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks] 218 218 if db_values: 219 219 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ -
django/db/models/options.py
13 13 14 14 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 15 15 'unique_together', 'permissions', 'get_latest_by', 16 'order_with_respect_to', 'app_label' )16 'order_with_respect_to', 'app_label') 17 17 18 18 class Options(object): 19 19 def __init__(self, meta): … … 27 27 self.object_name, self.app_label = None, None 28 28 self.get_latest_by = None 29 29 self.order_with_respect_to = None 30 30 31 self.admin = None 31 32 self.meta = meta 32 33 self.pk = None … … 59 60 del self.meta 60 61 61 62 def _prepare(self, model): 63 64 62 65 if self.order_with_respect_to: 63 66 self.order_with_respect_to = self.get_field(self.order_with_respect_to) 64 67 self.ordering = ('_order',) … … 73 76 # If the db_table wasn't provided, use the app_label + module_name. 74 77 if not self.db_table: 75 78 self.db_table = "%s_%s" % (self.app_label, self.module_name) 79 80 76 81 77 82 def add_field(self, field): 78 83 # Insert the given field in the order in which it was created, using -
django/db/models/fields/__init__.py
70 70 core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 71 71 prepopulate_from=None, unique_for_date=None, unique_for_month=None, 72 72 unique_for_year=None, validator_list=None, choices=None, radio_admin=None, 73 help_text='', db_column=None ):73 help_text='', db_column=None): 74 74 self.name = name 75 75 self.verbose_name = verbose_name 76 76 self.primary_key = primary_key … … 87 87 self.radio_admin = radio_admin 88 88 self.help_text = help_text 89 89 self.db_column = db_column 90 90 91 91 92 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 92 93 self.db_index = db_index … … 161 162 162 163 def get_db_prep_save(self, value): 163 164 "Returns field's value prepared for saving into a database." 165 166 167 168 164 169 return value 165 170 166 171 def get_db_prep_lookup(self, lookup_type, value): … … 528 533 def get_db_prep_save(self, value): 529 534 # Casts dates into string format for entry into database. 530 535 if value is not None: 531 # MySQL will throw a warning if microseconds are given, because it532 # doesn't supportmicroseconds.533 if settings.DATABASE_ENGINE == 'mysql'and hasattr(value, 'microsecond'):536 # MySQL 537 # microseconds. 538 if settings.DATABASE_ENGINE and hasattr(value, 'microsecond'): 534 539 value = value.replace(microsecond=0) 535 540 value = str(value) 536 541 return Field.get_db_prep_save(self, value) 537 542 538 543 def get_db_prep_lookup(self, lookup_type, value): 544 545 546 547 539 548 if lookup_type == 'range': 540 549 value = [str(v) for v in value] 541 550 else: … … 808 817 Field.__init__(self, verbose_name, name, **kwargs) 809 818 810 819 def get_db_prep_lookup(self, lookup_type, value): 820 821 822 823 824 825 826 827 811 828 if lookup_type == 'range': 812 value = [ str(v) for v in value]829 value = [(v) for v in value] 813 830 else: 814 value = str(value)831 value = (value) 815 832 return Field.get_db_prep_lookup(self, lookup_type, value) 816 833 817 834 def pre_save(self, model_instance, add): … … 827 844 if value is not None: 828 845 # MySQL will throw a warning if microseconds are given, because it 829 846 # doesn't support microseconds. 830 if settings.DATABASE_ENGINE == 'mysql'and hasattr(value, 'microsecond'):847 if settings.DATABASE_ENGINE and hasattr(value, 'microsecond'): 831 848 value = value.replace(microsecond=0) 849 850 851 852 853 854 855 832 856 value = str(value) 833 857 return Field.get_db_prep_save(self, value) 834 858 -
django/db/models/fields/related.py
335 335 (target_col_name, self.join_table, source_col_name, 336 336 target_col_name, ",".join(['%s'] * len(new_ids))), 337 337 [self._pk_val] + list(new_ids)) 338 if cursor.rowcount is not None and cursor.rowcount != 0: 339 existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) 340 else: 341 existing_ids = set() 338 existing_ids = set([row[0] for row in cursor.fetchall()]) 342 339 343 340 # Add the ones that aren't there already 344 341 for obj_id in (new_ids - existing_ids): -
django/db/models/query.py
4 4 from django.db.models import signals 5 5 from django.dispatch import dispatcher 6 6 from django.utils.datastructures import SortedDict 7 8 7 9 import operator 8 10 import re 9 11 … … 77 79 else: 78 80 return backend.quote_name(word) 79 81 80 class QuerySet(object):82 class QuerySet(object): 81 83 "Represents a lazy database lookup for a set of objects" 82 84 def __init__(self, model=None): 83 85 self.model = model … … 181 183 182 184 cursor = connection.cursor() 183 185 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 186 184 187 fill_cache = self._select_related 185 index_end = len(self.model._meta.fields) 188 fields = self.model._meta.fields 189 index_end = len(fields) 190 has_resolve_columns = hasattr(self, 'resolve_columns') 186 191 while 1: 187 192 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 188 193 if not rows: 189 194 raise StopIteration 190 195 for row in rows: 196 197 191 198 if fill_cache: 192 199 obj, index_end = get_cached_row(klass=self.model, row=row, 193 200 index_start=0, max_depth=self._max_related_depth) … … 551 558 552 559 return select, " ".join(sql), params 553 560 561 562 563 564 565 566 554 567 class ValuesQuerySet(QuerySet): 555 568 def __init__(self, *args, **kwargs): 556 569 super(ValuesQuerySet, self).__init__(*args, **kwargs) … … 566 579 567 580 # self._fields is a list of field names to fetch. 568 581 if self._fields: 569 columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 570 field_names = self._fields 582 fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 571 583 else: # Default to all fields. 572 columns = [f.column for f in self.model._meta.fields] 573 field_names = [f.attname for f in self.model._meta.fields] 584 fields = self.model._meta.fields 585 columns = [f.column for f in fields] 586 field_names = [f.attname for f in fields] 574 587 575 588 select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 576 589 cursor = connection.cursor() 577 590 cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 591 592 578 593 while 1: 579 594 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 580 595 if not rows: 581 596 raise StopIteration 582 597 for row in rows: 598 599 583 600 yield dict(zip(field_names, row)) 584 601 585 602 def _clone(self, klass=None, **kwargs): … … 590 607 class DateQuerySet(QuerySet): 591 608 def iterator(self): 592 609 from django.db.backends.util import typecast_timestamp 610 593 611 self._order_by = () # Clear this because it'll mess things up otherwise. 594 612 if self._field.null: 595 613 self._where.append('%s.%s IS NOT NULL' % \ 596 614 (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 597 598 615 try: 599 616 select, sql, params = self._get_sql_clause() 600 617 except EmptyResultSet: 601 618 raise StopIteration 602 619 603 sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 620 table_name = backend.quote_name(self.model._meta.db_table) 621 field_name = backend.quote_name(self._field.column) 622 623 if backend.allows_group_by_ordinal: 624 group_by = '1' 625 else: 626 group_by = backend.get_date_trunc_sql(self._kind, 627 '%s.%s' % (table_name, field_name)) 628 629 sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 604 630 (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 605 backend.quote_name(self._field.column))), sql, self._order)631 backend.quote_name(self._field.column))), sql, self._order) 606 632 cursor = connection.cursor() 607 633 cursor.execute(sql, params) 608 # We have to manually run typecast_timestamp(str()) on the results, because609 # MySQL doesn't automatically cast the result of date functions as datetime610 # objects -- MySQL returns the values as strings, instead.611 return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]612 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 613 654 def _clone(self, klass=None, **kwargs): 614 655 c = super(DateQuerySet, self)._clone(klass, **kwargs) 615 656 c._field = self._field … … 716 757 if table_prefix.endswith('.'): 717 758 table_prefix = backend.quote_name(table_prefix[:-1])+'.' 718 759 field_name = backend.quote_name(field_name) 760 761 762 763 764 765 766 767 719 768 try: 720 return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 769 return format % (table_prefix, field_name, 770 backend.OPERATOR_MAPPING[lookup_type] % cast_sql) 721 771 except KeyError: 722 772 pass 723 773 if lookup_type == 'in': -
django/core/management.py
55 55 56 56 def _get_installed_models(table_list): 57 57 "Gets a set of all models that are installed, given a list of existing tables" 58 from django.db import models58 from django.db import models 59 59 all_models = [] 60 60 for app in models.get_apps(): 61 61 for model in models.get_models(app): 62 62 all_models.append(model) 63 return set([m for m in all_models if m._meta.db_table in table_list]) 63 if backend.uses_case_insensitive_names: 64 converter = str.upper 65 else: 66 converter = lambda x: x 67 return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)]) 64 68 65 69 def _get_table_list(): 66 70 "Gets a list of all db tables that are physically installed." … … 104 108 def get_sql_create(app): 105 109 "Returns a list of the CREATE TABLE SQL statements for the given app." 106 110 from django.db import get_creation_module, models 111 107 112 data_types = get_creation_module().DATA_TYPES 108 113 109 114 if not data_types: … … 173 178 rel_field = f 174 179 data_type = f.get_internal_type() 175 180 col_type = data_types[data_type] 181 176 182 if col_type is not None: 177 183 # Make the definition (e.g. 'foo VARCHAR(30)') for this field. 178 184 field_output = [style.SQL_FIELD(backend.quote_name(f.column)), 179 185 style.SQL_COLTYPE(col_type % rel_field.__dict__)] 180 186 field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 181 if f.unique :187 if f.unique: 182 188 field_output.append(style.SQL_KEYWORD('UNIQUE')) 183 189 if f.primary_key: 184 190 field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 191 192 193 194 185 195 if f.rel: 186 196 if f.rel.to in known_models: 187 197 field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ … … 205 215 full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(backend.quote_name(opts.db_table)) + ' ('] 206 216 for i, line in enumerate(table_output): # Combine and add commas. 207 217 full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) 208 full_statement.append(');') 218 full_statement.append(')') 219 if opts.db_tablespace and backend.supports_tablespaces: 220 full_statement.append(backend.get_tablespace_sql(opts.db_tablespace)) 221 full_statement.append(';') 209 222 final_output.append('\n'.join(full_statement)) 210 223 224 225 226 227 228 229 230 211 231 return final_output, pending_references 212 232 213 233 def _get_sql_for_pending_references(model, pending_references): … … 215 235 Get any ALTER TABLE statements to add constraints after the fact. 216 236 """ 217 237 from django.db import backend, get_creation_module 238 218 239 data_types = get_creation_module().DATA_TYPES 219 240 220 241 final_output = [] … … 227 248 r_col = f.column 228 249 table = opts.db_table 229 250 col = opts.get_field(f.rel.field_name).column 230 # For MySQL, r_name must be unique in the first 64 characters. 231 # So we are careful with character usage here. 232 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 251 r_name = '%s_refs_%s_%s_%s' % (r_col, col, r_table, table) 233 252 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 234 (backend.quote_name(r_table), r_name,253 (backend.quote_name(r_table), , 235 254 backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col), 236 255 backend.get_deferrable_sql())) 237 256 del pending_references[model] … … 247 266 final_output = [] 248 267 for f in opts.many_to_many: 249 268 if not isinstance(f.rel, GenericRel): 269 270 271 272 273 250 274 table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ 251 275 style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] 252 table_output.append(' %s %s %s ,' % \276 table_output.append(' %s %s %s,' % \ 253 277 (style.SQL_FIELD(backend.quote_name('id')), 254 278 style.SQL_COLTYPE(data_types['AutoField']), 255 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'))) 279 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), 280 tablespace_sql)) 256 281 table_output.append(' %s %s %s %s (%s)%s,' % \ 257 282 (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 258 283 style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__), … … 267 292 style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)), 268 293 style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), 269 294 backend.get_deferrable_sql())) 270 table_output.append(' %s (%s, %s) ' % \295 table_output.append(' %s (%s, %s)' % \ 271 296 (style.SQL_KEYWORD('UNIQUE'), 272 297 style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 273 style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())))) 274 table_output.append(');') 298 style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), 299 tablespace_sql)) 300 table_output.append(')') 301 if opts.db_tablespace and backend.supports_tablespaces: 302 # f.db_tablespace is only for indices, so ignore its value here. 303 table_output.append(backend.get_tablespace_sql(opts.db_tablespace)) 304 table_output.append(';') 275 305 final_output.append('\n'.join(table_output)) 306 307 308 309 310 311 312 276 313 return final_output 277 314 278 315 def get_sql_delete(app): 279 316 "Returns a list of the DROP TABLE SQL statements for the given app." 280 317 from django.db import backend, connection, models, get_introspection_module 318 281 319 introspection = get_introspection_module() 282 320 283 321 # This should work even if a connection isn't available … … 291 329 table_names = introspection.get_table_list(cursor) 292 330 else: 293 331 table_names = [] 332 333 334 335 294 336 295 337 output = [] 296 338 … … 300 342 references_to_delete = {} 301 343 app_models = models.get_models(app) 302 344 for model in app_models: 303 if cursor and model._meta.db_tablein table_names:345 if cursor and in table_names: 304 346 # The table exists, so it needs to be dropped 305 347 opts = model._meta 306 348 for f in opts.fields: … … 310 352 to_delete.add(model) 311 353 312 354 for model in app_models: 313 if cursor and model._meta.db_tablein table_names:355 if cursor and in table_names: 314 356 # Drop the table now 315 357 output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 316 358 style.SQL_TABLE(backend.quote_name(model._meta.db_table)))) … … 320 362 col = f.column 321 363 r_table = model._meta.db_table 322 364 r_col = model._meta.get_field(f.rel.field_name).column 365 323 366 output.append('%s %s %s %s;' % \ 324 367 (style.SQL_KEYWORD('ALTER TABLE'), 325 368 style.SQL_TABLE(backend.quote_name(table)), 326 369 style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()), 327 style.SQL_FIELD( backend.quote_name('%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))))))370 style.SQL_FIELD())))) 328 371 del references_to_delete[model] 372 373 329 374 330 375 # Output DROP TABLE statements for many-to-many tables. 331 376 for model in app_models: 332 377 opts = model._meta 333 378 for f in opts.many_to_many: 334 if cursor and f.m2m_db_table() in table_names:379 if cursor and ) in table_names: 335 380 output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), 336 381 style.SQL_TABLE(backend.quote_name(f.m2m_db_table())))) 382 383 337 384 385 338 386 app_label = app_models[0]._meta.app_label 339 387 340 388 # Close database connection explicitly, in case this output is being piped … … 429 477 def get_sql_indexes_for_model(model): 430 478 "Returns the CREATE INDEX SQL statements for a single model" 431 479 from django.db import backend 480 432 481 output = [] 433 482 434 483 for f in model._meta.fields: 435 if f.db_index :484 if f.db_index: 436 485 unique = f.unique and 'UNIQUE ' or '' 486 487 488 489 490 437 491 output.append( 438 492 style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ 439 493 style.SQL_TABLE(backend.quote_name('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \ 440 494 style.SQL_KEYWORD('ON') + ' ' + \ 441 495 style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ 442 "(%s);" % style.SQL_FIELD(backend.quote_name(f.column)) 496 "(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \ 497 "%s;" % tablespace_sql 443 498 ) 444 499 return output 445 500 … … 463 518 464 519 def syncdb(verbosity=1, interactive=True): 465 520 "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 466 from django.db import connection, transaction, models, get_creation_module521 from django.db import connection, transaction, models, get_creation_module 467 522 from django.conf import settings 468 523 469 524 disable_termcolors() … … 486 541 # Get a list of all existing database tables, 487 542 # so we know what needs to be added. 488 543 table_list = _get_table_list() 544 545 546 547 489 548 490 549 # Get a list of already installed *models* so that references work right. 491 550 seen_models = _get_installed_models(table_list) … … 500 559 # Create the model's database table, if it doesn't already exist. 501 560 if verbosity >= 2: 502 561 print "Processing %s.%s model" % (app_name, model._meta.object_name) 503 if model._meta.db_tablein table_list:562 if in table_list: 504 563 continue 505 564 sql, references = _get_sql_model_create(model, seen_models) 506 565 seen_models.add(model) … … 512 571 print "Creating table %s" % model._meta.db_table 513 572 for statement in sql: 514 573 cursor.execute(statement) 515 table_list.append( model._meta.db_table)574 table_list.append() 516 575 517 576 # Create the m2m tables. This must be done after all tables have been created 518 577 # to ensure that all referred tables will exist. … … 830 889 except NotImplementedError: 831 890 indexes = {} 832 891 for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)): 833 att_name = row[0] 892 att_name = row[0] 834 893 comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 835 894 extra_params = {} # Holds Field parameters such as 'db_column'. 836 895 … … 1621 1680 if not mod_list: 1622 1681 parser.print_usage_and_exit() 1623 1682 if action not in NO_SQL_TRANSACTION: 1624 print style.SQL_KEYWORD("BEGIN;") 1683 from django.db import backend 1684 if backend.get_start_transaction_sql(): 1685 print style.SQL_KEYWORD(backend.get_start_transaction_sql()) 1625 1686 for mod in mod_list: 1626 1687 if action == 'reset': 1627 1688 output = action_mapping[action](mod, options.interactive) -
django/contrib/admin/models.py
9 9 10 10 class LogEntryManager(models.Manager): 11 11 def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''): 12 e = self.model(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message)12 e = self.model(None, None, user_id, content_type_id, , object_repr[:200], action_flag, change_message) 13 13 e.save() 14 14 15 15 class LogEntry(models.Model): -
django/test/utils.py
1 1 import sys, time 2 2 from django.conf import settings 3 from django.db import connection, transaction, backend3 from django.db import connection, 4 4 from django.core import management 5 5 from django.dispatch import dispatcher 6 6 from django.test import signals … … 44 44 connection.connection.set_isolation_level(0) 45 45 46 46 def create_test_db(verbosity=1, autoclobber=False): 47 48 49 50 51 52 47 53 if verbosity >= 1: 48 54 print "Creating test database..." 49 55 # If we're using SQLite, it's more convenient to test against an … … 92 98 cursor = connection.cursor() 93 99 94 100 def destroy_test_db(old_database_name, verbosity=1): 101 102 103 104 105 106 95 107 # Unless we're using SQLite, remove the test database to clean up after 96 108 # ourselves. Connect to the previous database (not the test database) 97 109 # to do so, because it's not allowed to delete a database while being -
docs/settings.txt
245 245 Default: ``''`` (Empty string) 246 246 247 247 Which database backend to use. Either ``'postgresql_psycopg2'``, 248 ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'`` or249 ``' ado_mssql'``.248 ``'postgresql'``, ``'mysql'``, ``'mysql_old'``, ``'sqlite3'`` 249 ``'ado_mssql'``. 250 250 251 251 DATABASE_HOST 252 252 ------------- -
docs/faq.txt
301 301 302 302 If you want to use Django with a database, which is probably the case, you'll 303 303 also need a database engine. PostgreSQL_ is recommended, because we're 304 PostgreSQL fans, and MySQL_ and `SQLite 3`_ are also supported.304 PostgreSQL fans, and MySQL__ are also supported. 305 305 306 306 .. _Python: http://www.python.org/ 307 307 .. _Apache 2: http://httpd.apache.org/ … … 310 310 .. _PostgreSQL: http://www.postgresql.org/ 311 311 .. _MySQL: http://www.mysql.com/ 312 312 .. _`SQLite 3`: http://www.sqlite.org/ 313 313 314 314 315 Do I lose anything by using Python 2.3 versus newer Python versions, such as Python 2.5? 315 316 ---------------------------------------------------------------------------------------- -
docs/install.txt
62 62 63 63 * If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher. 64 64 65 66 65 67 .. _PostgreSQL: http://www.postgresql.org/ 66 68 .. _MySQL: http://www.mysql.com/ 67 69 .. _Django's ticket system: http://code.djangoproject.com/report/1 … … 71 73 .. _SQLite: http://www.sqlite.org/ 72 74 .. _pysqlite: http://initd.org/tracker/pysqlite 73 75 .. _MySQL backend: ../databases/ 76 74 77 75 78 Remove any old versions of Django 76 79 ================================= -
tests/modeltests/datatypes/models.py
Property changes on: tests/modeltests/datatypes ___________________________________________________________________ Name: svn:ignore + *.pyc Property changes on: tests/modeltests/datatypes/__init__.py ___________________________________________________________________ Name: svn:eol-style + native
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60