Project

General

Profile

1 2211 aaronmk
# SQL code generation
2
3 2748 aaronmk
import copy
4 2276 aaronmk
import operator
5 2568 aaronmk
import re
6 2653 aaronmk
import UserDict
7 2953 aaronmk
import warnings
8 2276 aaronmk
9 2667 aaronmk
import dicts
10 2953 aaronmk
import exc
11 2701 aaronmk
import iters
12
import lists
13 2360 aaronmk
import objects
14 2222 aaronmk
import strings
15 2227 aaronmk
import util
16 2211 aaronmk
17 2587 aaronmk
##### Names
18 2499 aaronmk
19 2608 aaronmk
identifier_max_len = 63 # works for both PostgreSQL and MySQL
20 2587 aaronmk
21 2932 aaronmk
def concat(str_, suffix):
22 2609 aaronmk
    '''Preserves version so that it won't be truncated off the string, leading
23
    to collisions.'''
24 2613 aaronmk
    # Preserve version
25 2990 aaronmk
    match = re.match(r'^(.*?)((?:(?:#\d+)?\)?)*(?:\.\w+)?)$', str_)
26 2985 aaronmk
    if match:
27
        str_, old_suffix = match.groups()
28
        suffix = old_suffix+suffix
29 2613 aaronmk
30 2932 aaronmk
    return strings.concat(str_, suffix, identifier_max_len)
31 2587 aaronmk
32 2932 aaronmk
def truncate(str_): return concat(str_, '')
33 2842 aaronmk
34 2575 aaronmk
def is_safe_name(name):
35 2583 aaronmk
    '''A name is safe *and unambiguous* if it:
36
    * contains only *lowercase* word (\w) characters
37
    * doesn't start with a digit
38
    * contains "_", so that it's not a keyword
39 2984 aaronmk
    '''
40
    return re.match(r'^(?=.*_)(?!\d)[^\WA-Z]+$', name)
41 2568 aaronmk
42 2499 aaronmk
def esc_name(name, quote='"'):
43
    return quote + name.replace(quote, quote+quote) + quote
44
        # doubling an embedded quote escapes it in both PostgreSQL and MySQL
45
46 2513 aaronmk
def clean_name(name): return name.replace('"', '').replace('`', '')
47
48 2659 aaronmk
##### General SQL code objects
49 2219 aaronmk
50 2349 aaronmk
class MockDb:
51 2503 aaronmk
    def esc_value(self, value): return strings.repr_no_u(value)
52 2349 aaronmk
53 2499 aaronmk
    def esc_name(self, name): return esc_name(name)
54 2859 aaronmk
55
    def col_info(self, col):
56
        return TypedCol(col.name, '<type>', CustomCode('<default>'), True)
57
58 2349 aaronmk
mockDb = MockDb()
59
60 2514 aaronmk
class BasicObject(objects.BasicObject):
61
    def __init__(self, value): self.value = value
62
63
    def __str__(self): return clean_name(strings.repr_no_u(self))
64
65 2659 aaronmk
##### Unparameterized code objects
66
67 2514 aaronmk
class Code(BasicObject):
68 2658 aaronmk
    def to_str(self, db): raise NotImplementedError()
69 2349 aaronmk
70 2514 aaronmk
    def __repr__(self): return self.to_str(mockDb)
71 2211 aaronmk
72 2269 aaronmk
class CustomCode(Code):
73 2256 aaronmk
    def __init__(self, str_): self.str_ = str_
74
75
    def to_str(self, db): return self.str_
76
77 2815 aaronmk
def as_Code(value, db=None):
78
    '''
79
    @param db If set, runs db.std_code() on the value.
80
    '''
81
    if util.is_str(value):
82
        if db != None: value = db.std_code(value)
83
        return CustomCode(value)
84 2659 aaronmk
    else: return Literal(value)
85
86 2540 aaronmk
class Expr(Code):
87
    def __init__(self, expr): self.expr = expr
88
89
    def to_str(self, db): return '('+self.expr.to_str(db)+')'
90
91 2335 aaronmk
##### Literal values
92
93 2216 aaronmk
class Literal(Code):
94 2211 aaronmk
    def __init__(self, value): self.value = value
95 2213 aaronmk
96
    def to_str(self, db): return db.esc_value(self.value)
97 2211 aaronmk
98 2400 aaronmk
def as_Value(value):
99
    if isinstance(value, Code): return value
100
    else: return Literal(value)
101
102 2216 aaronmk
def is_null(value): return isinstance(value, Literal) and value.value == None
103
104 2711 aaronmk
##### Derived elements
105
106
src_self = object() # tells Col that it is its own source column
107
108
class Derived(Code):
109
    def __init__(self, srcs):
110 2712 aaronmk
        '''An element which was derived from some other element(s).
111 2711 aaronmk
        @param srcs See self.set_srcs()
112
        '''
113
        self.set_srcs(srcs)
114
115 2713 aaronmk
    def set_srcs(self, srcs, overwrite=True):
116 2711 aaronmk
        '''
117
        @param srcs (self_type...)|src_self The element(s) this is derived from
118
        '''
119 2713 aaronmk
        if not overwrite and self.srcs != (): return # already set
120
121 2711 aaronmk
        if srcs == src_self: srcs = (self,)
122
        srcs = tuple(srcs) # make Col hashable
123
        self.srcs = srcs
124
125
    def _compare_on(self):
126
        compare_on = self.__dict__.copy()
127
        del compare_on['srcs'] # ignore
128
        return compare_on
129
130
def cols_srcs(cols): return lists.uniqify(iters.flatten((v.srcs for v in cols)))
131
132 2335 aaronmk
##### Tables
133
134 2712 aaronmk
class Table(Derived):
135
    def __init__(self, name, schema=None, srcs=()):
136 2211 aaronmk
        '''
137
        @param schema str|None (for no schema)
138 2712 aaronmk
        @param srcs (Table...)|src_self See Derived.set_srcs()
139 2211 aaronmk
        '''
140 2712 aaronmk
        Derived.__init__(self, srcs)
141
142 2843 aaronmk
        name = truncate(name)
143
144 2211 aaronmk
        self.name = name
145
        self.schema = schema
146
147 2348 aaronmk
    def to_str(self, db):
148
        str_ = ''
149
        if self.schema != None: str_ += db.esc_name(self.schema)+'.'
150
        str_ += db.esc_name(self.name)
151
        return str_
152 2336 aaronmk
153
    def to_Table(self): return self
154 2211 aaronmk
155 2835 aaronmk
def is_underlying_table(table):
156
    return isinstance(table, Table) and table.to_Table() is table
157 2832 aaronmk
158 2902 aaronmk
class NoUnderlyingTableException(Exception): pass
159
160
def underlying_table(table):
161
    table = remove_table_rename(table)
162
    if not is_underlying_table(table): raise NoUnderlyingTableException
163
    return table
164
165 2776 aaronmk
def as_Table(table, schema=None):
166 2270 aaronmk
    if table == None or isinstance(table, Code): return table
167 2776 aaronmk
    else: return Table(table, schema)
168 2219 aaronmk
169 2707 aaronmk
def suffixed_table(table, suffix): return Table(table.name+suffix, table.schema)
170
171 2336 aaronmk
class NamedTable(Table):
172
    def __init__(self, name, code, cols=None):
173
        Table.__init__(self, name)
174
175
        if not isinstance(code, Code): code = Table(code)
176 2741 aaronmk
        if not isinstance(code, (Table, FunctionCall, Expr)): code = Expr(code)
177 2742 aaronmk
        if cols != None: cols = map(to_name_only_col, cols)
178 2336 aaronmk
179
        self.code = code
180
        self.cols = cols
181
182
    def to_str(self, db):
183 2467 aaronmk
        str_ = self.code.to_str(db)+'\nAS '+Table.to_str(self, db)
184 2742 aaronmk
        if self.cols != None:
185
            str_ += ' ('+(', '.join((c.to_str(db) for c in self.cols)))+')'
186 2336 aaronmk
        return str_
187
188
    def to_Table(self): return Table(self.name)
189
190 2753 aaronmk
def remove_table_rename(table):
191
    if isinstance(table, NamedTable): table = table.code
192
    return table
193
194 2335 aaronmk
##### Columns
195
196 2711 aaronmk
class Col(Derived):
197 2701 aaronmk
    def __init__(self, name, table=None, srcs=()):
198 2211 aaronmk
        '''
199
        @param table Table|None (for no table)
200 2711 aaronmk
        @param srcs (Col...)|src_self See Derived.set_srcs()
201 2211 aaronmk
        '''
202 2711 aaronmk
        Derived.__init__(self, srcs)
203
204 2843 aaronmk
        name = truncate(name)
205 2241 aaronmk
        if util.is_str(table): table = Table(table)
206 2211 aaronmk
        assert table == None or isinstance(table, Table)
207
208
        self.name = name
209
        self.table = table
210
211 2989 aaronmk
    def to_str(self, db, for_str=False):
212 2933 aaronmk
        str_ = db.esc_name(self.name)
213 2989 aaronmk
        if for_str: str_ = clean_name(str_)
214 2933 aaronmk
        if self.table != None:
215 2989 aaronmk
            table = self.table.to_Table()
216
            if for_str: str_ = concat(str(table), '.'+str_)
217
            else: str_ = table.to_str(db)+'.'+str_
218 2211 aaronmk
        return str_
219 2314 aaronmk
220 2989 aaronmk
    def __str__(self): return self.to_str(mockDb, for_str=True)
221 2933 aaronmk
222 2314 aaronmk
    def to_Col(self): return self
223 2211 aaronmk
224 2767 aaronmk
def is_table_col(col): return isinstance(col, Col) and col.table != None
225 2393 aaronmk
226 2563 aaronmk
def as_Col(col, table=None, name=None):
227
    '''
228
    @param name If not None, any non-Col input will be renamed using NamedCol.
229
    '''
230
    if name != None:
231
        col = as_Value(col)
232
        if not isinstance(col, Col): col = NamedCol(name, col)
233 2333 aaronmk
234
    if isinstance(col, Code): return col
235 2260 aaronmk
    else: return Col(col, table)
236
237 2750 aaronmk
def with_default_table(col, table, overwrite=False):
238 2747 aaronmk
    col = as_Col(col)
239 2750 aaronmk
    if not isinstance(col, NamedCol) and (overwrite or col.table == None):
240 2748 aaronmk
        col = copy.copy(col) # don't modify input!
241
        col.table = table
242 2747 aaronmk
    return col
243
244 2744 aaronmk
def set_cols_table(table, cols):
245
    table = as_Table(table)
246
247
    for i, col in enumerate(cols):
248
        col = cols[i] = as_Col(col)
249
        col.table = table
250
251 2401 aaronmk
def to_name_only_col(col, check_table=None):
252
    col = as_Col(col)
253 2579 aaronmk
    if not isinstance(col, Col): return col
254 2401 aaronmk
255
    if check_table != None:
256
        table = col.table
257
        assert table == None or table == check_table
258
    return Col(col.name)
259
260 2323 aaronmk
class NamedCol(Col):
261 2229 aaronmk
    def __init__(self, name, code):
262 2310 aaronmk
        Col.__init__(self, name)
263
264 2229 aaronmk
        if not isinstance(code, Code): code = Literal(code)
265
266
        self.code = code
267
268
    def to_str(self, db):
269 2310 aaronmk
        return self.code.to_str(db)+' AS '+Col.to_str(self, db)
270 2314 aaronmk
271
    def to_Col(self): return Col(self.name)
272 2229 aaronmk
273 2462 aaronmk
def remove_col_rename(col):
274
    if isinstance(col, NamedCol): col = col.code
275
    return col
276
277 2830 aaronmk
def underlying_col(col):
278
    col = remove_col_rename(col)
279 2849 aaronmk
    if not isinstance(col, Col): raise NoUnderlyingTableException
280
281 2902 aaronmk
    return Col(col.name, underlying_table(col.table), col.srcs)
282 2830 aaronmk
283 2703 aaronmk
def wrap(wrap_func, value):
284
    '''Wraps a value, propagating any column renaming to the returned value.'''
285
    if isinstance(value, NamedCol):
286
        return NamedCol(value.name, wrap_func(value.code))
287
    else: return wrap_func(value)
288
289 2667 aaronmk
class ColDict(dicts.DictProxy):
290 2564 aaronmk
    '''A dict that automatically makes inserted entries Col objects'''
291
292 2645 aaronmk
    def __init__(self, db, keys_table, dict_={}):
293 2667 aaronmk
        dicts.DictProxy.__init__(self, {})
294
295 2645 aaronmk
        keys_table = as_Table(keys_table)
296
297 2642 aaronmk
        self.db = db
298 2641 aaronmk
        self.table = keys_table
299 2653 aaronmk
        self.update(dict_) # after setting vars because __setitem__() needs them
300 2641 aaronmk
301 2667 aaronmk
    def copy(self): return ColDict(self.db, self.table, self.inner.copy())
302 2655 aaronmk
303 2667 aaronmk
    def __getitem__(self, key):
304
        return dicts.DictProxy.__getitem__(self, self._key(key))
305 2653 aaronmk
306 2564 aaronmk
    def __setitem__(self, key, value):
307 2642 aaronmk
        key = self._key(key)
308 2819 aaronmk
        if value == None: value = self.db.col_info(key).default
309 2667 aaronmk
        dicts.DictProxy.__setitem__(self, key, as_Col(value, name=key.name))
310 2564 aaronmk
311 2641 aaronmk
    def _key(self, key): return as_Col(key, self.table)
312 2564 aaronmk
313 2524 aaronmk
##### Functions
314
315 2912 aaronmk
Function = Table
316 2911 aaronmk
as_Function = as_Table
317
318 2691 aaronmk
class InternalFunction(CustomCode): pass
319
320 2941 aaronmk
class NamedArg(NamedCol):
321
    def __init__(self, name, value):
322
        NamedCol.__init__(self, name, value)
323
324
    def to_str(self, db):
325
        return Col.to_str(self, db)+' := '+self.code.to_str(db)
326
327 2524 aaronmk
class FunctionCall(Code):
328 2941 aaronmk
    def __init__(self, function, *args, **kw_args):
329 2524 aaronmk
        '''
330 2690 aaronmk
        @param args [Code|literal-value...] The function's arguments
331 2524 aaronmk
        '''
332
        if not isinstance(function, Code): function = Function(function)
333 2941 aaronmk
        def filter_(arg): return remove_col_rename(as_Value(arg))
334
        args = map(filter_, args)
335
        args += [NamedArg(k, filter_(v)) for k, v in kw_args.iteritems()]
336 2524 aaronmk
337
        self.function = function
338
        self.args = args
339
340
    def to_str(self, db):
341
        args_str = ', '.join((v.to_str(db) for v in self.args))
342
        return self.function.to_str(db)+'('+args_str+')'
343
344 2533 aaronmk
def wrap_in_func(function, value):
345
    '''Wraps a value inside a function call.
346
    Propagates any column renaming to the returned value.
347
    '''
348 2703 aaronmk
    return wrap(lambda v: FunctionCall(function, v), value)
349 2533 aaronmk
350 2561 aaronmk
def unwrap_func_call(func_call, check_name=None):
351
    '''Unwraps any function call to its first argument.
352
    Also removes any column renaming.
353
    '''
354
    func_call = remove_col_rename(func_call)
355
    if not isinstance(func_call, FunctionCall): return func_call
356
357
    if check_name != None:
358
        name = func_call.function.name
359
        assert name == None or name == check_name
360
    return func_call.args[0]
361
362 2986 aaronmk
##### Casts
363
364
class Cast(FunctionCall):
365
    def __init__(self, type_, value):
366
        value = as_Value(value)
367
368
        self.type_ = type_
369
        self.value = value
370
371
    def to_str(self, db):
372
        return 'CAST('+self.value.to_str(db)+' AS '+self.type_+')'
373
374 2335 aaronmk
##### Conditions
375 2259 aaronmk
376 2398 aaronmk
class ColValueCond(Code):
377
    def __init__(self, col, value):
378
        value = as_ValueCond(value)
379
380
        self.col = col
381
        self.value = value
382
383
    def to_str(self, db): return self.value.to_str(db, self.col)
384
385 2577 aaronmk
def combine_conds(conds, keyword=None):
386
    '''
387
    @param keyword The keyword to add before the conditions, if any
388
    '''
389
    str_ = ''
390
    if keyword != None:
391
        if conds == []: whitespace = ''
392
        elif len(conds) == 1: whitespace = ' '
393
        else: whitespace = '\n'
394
        str_ += keyword+whitespace
395
396
    str_ += '\nAND '.join(conds)
397
    return str_
398
399 2398 aaronmk
##### Condition column comparisons
400
401 2514 aaronmk
class ValueCond(BasicObject):
402 2213 aaronmk
    def __init__(self, value):
403 2858 aaronmk
        value = remove_col_rename(as_Value(value))
404 2213 aaronmk
405
        self.value = value
406 2214 aaronmk
407 2216 aaronmk
    def to_str(self, db, left_value):
408 2214 aaronmk
        '''
409 2216 aaronmk
        @param left_value The Code object that the condition is being applied on
410 2214 aaronmk
        '''
411
        raise NotImplemented()
412 2228 aaronmk
413 2514 aaronmk
    def __repr__(self): return self.to_str(mockDb, '<left_value>')
414 2211 aaronmk
415
class CompareCond(ValueCond):
416
    def __init__(self, value, operator='='):
417 2222 aaronmk
        '''
418
        @param operator By default, compares NULL values literally. Use '~=' or
419
            '~!=' to pass NULLs through.
420
        '''
421 2211 aaronmk
        ValueCond.__init__(self, value)
422
        self.operator = operator
423
424 2216 aaronmk
    def to_str(self, db, left_value):
425 2858 aaronmk
        left_value = remove_col_rename(as_Col(left_value))
426 2216 aaronmk
427 2222 aaronmk
        right_value = self.value
428
429
        # Parse operator
430 2216 aaronmk
        operator = self.operator
431 2222 aaronmk
        passthru_null_ref = [False]
432
        operator = strings.remove_prefix('~', operator, passthru_null_ref)
433
        neg_ref = [False]
434
        operator = strings.remove_prefix('!', operator, neg_ref)
435 2844 aaronmk
        equals = operator.endswith('=') # also includes <=, >=
436 2222 aaronmk
437 2825 aaronmk
        # Handle nullable columns
438
        check_null = False
439 2844 aaronmk
        if not passthru_null_ref[0]: # NULLs compare equal
440 2857 aaronmk
            try: left_value = ensure_not_null(db, left_value)
441 2844 aaronmk
            except ensure_not_null_excs: # fall back to alternate method
442
                check_null = equals and isinstance(right_value, Col)
443 2837 aaronmk
            else:
444 2857 aaronmk
                if isinstance(left_value, EnsureNotNull):
445
                    right_value = ensure_not_null(db, right_value,
446
                        left_value.type) # apply same function to both sides
447 2825 aaronmk
448 2844 aaronmk
        if equals and is_null(right_value): operator = 'IS'
449
450 2825 aaronmk
        left = left_value.to_str(db)
451
        right = right_value.to_str(db)
452
453 2222 aaronmk
        # Create str
454
        str_ = left+' '+operator+' '+right
455 2825 aaronmk
        if check_null:
456 2578 aaronmk
            str_ = '('+str_+' OR ('+left+' IS NULL AND '+right+' IS NULL))'
457
        if neg_ref[0]: str_ = 'NOT '+str_
458 2222 aaronmk
        return str_
459 2216 aaronmk
460 2260 aaronmk
# Tells as_ValueCond() to assume a non-ValueCond is a literal value
461
assume_literal = object()
462
463
def as_ValueCond(value, default_table=assume_literal):
464
    if not isinstance(value, ValueCond):
465
        if default_table is not assume_literal:
466 2748 aaronmk
            value = with_default_table(value, default_table)
467 2260 aaronmk
        return CompareCond(value)
468 2216 aaronmk
    else: return value
469 2219 aaronmk
470 2335 aaronmk
##### Joins
471
472 2352 aaronmk
join_same = object() # tells Join the left and right columns have the same name
473 2260 aaronmk
474 2353 aaronmk
# Tells Join the left and right columns have the same name and are never NULL
475
join_same_not_null = object()
476
477 2260 aaronmk
filter_out = object() # tells Join to filter out rows that match the join
478
479 2514 aaronmk
class Join(BasicObject):
480 2746 aaronmk
    def __init__(self, table, mapping={}, type_=None):
481 2260 aaronmk
        '''
482
        @param mapping dict(right_table_col=left_table_col, ...)
483 2352 aaronmk
            * if left_table_col is join_same: left_table_col = right_table_col
484 2353 aaronmk
              * Note that right_table_col must be a string
485
            * if left_table_col is join_same_not_null:
486
              left_table_col = right_table_col and both have NOT NULL constraint
487
              * Note that right_table_col must be a string
488 2260 aaronmk
        @param type_ None (for plain join)|str (e.g. 'LEFT')|filter_out
489
            * filter_out: equivalent to 'LEFT' with the query filtered by
490
              `table_pkey IS NULL` (indicating no match)
491
        '''
492
        if util.is_str(table): table = Table(table)
493
        assert type_ == None or util.is_str(type_) or type_ is filter_out
494
495
        self.table = table
496
        self.mapping = mapping
497
        self.type_ = type_
498
499 2749 aaronmk
    def to_str(self, db, left_table_):
500 2260 aaronmk
        def join(entry):
501
            '''Parses non-USING joins'''
502
            right_table_col, left_table_col = entry
503
504 2353 aaronmk
            # Switch order (right_table_col is on the left in the comparison)
505
            left = right_table_col
506
            right = left_table_col
507 2749 aaronmk
            left_table = self.table
508
            right_table = left_table_
509 2353 aaronmk
510 2747 aaronmk
            # Parse left side
511 2748 aaronmk
            left = with_default_table(left, left_table)
512 2747 aaronmk
513 2260 aaronmk
            # Parse special values
514 2747 aaronmk
            left_on_right = Col(left.name, right_table)
515
            if right is join_same: right = left_on_right
516 2353 aaronmk
            elif right is join_same_not_null:
517 2747 aaronmk
                right = CompareCond(left_on_right, '~=')
518 2260 aaronmk
519 2747 aaronmk
            # Parse right side
520 2353 aaronmk
            right = as_ValueCond(right, right_table)
521 2747 aaronmk
522
            return right.to_str(db, left)
523 2260 aaronmk
524 2265 aaronmk
        # Create join condition
525
        type_ = self.type_
526 2276 aaronmk
        joins = self.mapping
527 2746 aaronmk
        if joins == {}: join_cond = None
528
        elif type_ is not filter_out and reduce(operator.and_,
529 2460 aaronmk
            (v is join_same_not_null for v in joins.itervalues())):
530 2260 aaronmk
            # all cols w/ USING, so can use simpler USING syntax
531 2747 aaronmk
            cols = map(to_name_only_col, joins.iterkeys())
532
            join_cond = 'USING ('+(', '.join((c.to_str(db) for c in cols)))+')'
533 2757 aaronmk
        else: join_cond = combine_conds(map(join, joins.iteritems()), 'ON')
534 2260 aaronmk
535 2757 aaronmk
        if isinstance(self.table, NamedTable): whitespace = '\n'
536
        else: whitespace = ' '
537
538 2260 aaronmk
        # Create join
539
        if type_ is filter_out: type_ = 'LEFT'
540 2266 aaronmk
        str_ = ''
541
        if type_ != None: str_ += type_+' '
542 2757 aaronmk
        str_ += 'JOIN'+whitespace+self.table.to_str(db)
543
        if join_cond != None: str_ += whitespace+join_cond
544 2266 aaronmk
        return str_
545 2349 aaronmk
546 2514 aaronmk
    def __repr__(self): return self.to_str(mockDb, '<left_table>')
547 2424 aaronmk
548
##### Value exprs
549
550 2737 aaronmk
default = CustomCode('DEFAULT')
551
552 2424 aaronmk
row_count = CustomCode('count(*)')
553 2674 aaronmk
554 2850 aaronmk
# See <http://www.postgresql.org/docs/8.3/static/datatype-numeric.html>
555 2958 aaronmk
null_sentinels = {
556
    'character varying': r'\N',
557
    'double precision': 'NaN',
558
    'integer': 2147483647,
559
    'text': r'\N',
560
    'timestamp with time zone': 'infinity'
561
}
562 2692 aaronmk
563 2850 aaronmk
class EnsureNotNull(FunctionCall):
564
    def __init__(self, value, type_):
565 2870 aaronmk
        FunctionCall.__init__(self, InternalFunction('COALESCE'), as_Col(value),
566 2988 aaronmk
            Cast(type_, null_sentinels[type_]))
567 2850 aaronmk
568
        self.type = type_
569
570 2737 aaronmk
##### Table exprs
571
572
class Values(Code):
573
    def __init__(self, values):
574 2739 aaronmk
        '''
575
        @param values [...]|[[...], ...] Can be one or multiple rows.
576
        '''
577
        rows = values
578
        if len(values) >= 1 and not lists.is_seq(values[0]): # only one row
579
            rows = [values]
580
        for i, row in enumerate(rows):
581
            rows[i] = map(remove_col_rename, map(as_Value, row))
582 2737 aaronmk
583 2739 aaronmk
        self.rows = rows
584 2737 aaronmk
585
    def to_str(self, db):
586 2739 aaronmk
        def row_str(row):
587
            return '('+(', '.join((v.to_str(db) for v in row)))+')'
588
        return 'VALUES '+(', '.join(map(row_str, self.rows)))
589 2737 aaronmk
590 2740 aaronmk
def NamedValues(name, cols, values):
591 2745 aaronmk
    '''
592
    @post `cols` will be changed to Col objects with the table set to `name`.
593
    '''
594 2834 aaronmk
    table = NamedTable(name, Values(values), cols)
595
    set_cols_table(table, cols)
596
    return table
597 2740 aaronmk
598 2674 aaronmk
##### Database structure
599
600
class TypedCol(Col):
601 2871 aaronmk
    def __init__(self, name, type_, default=None, nullable=True,
602
        constraints=None):
603 2818 aaronmk
        assert default == None or isinstance(default, Code)
604
605 2674 aaronmk
        Col.__init__(self, name)
606
607
        self.type = type_
608 2818 aaronmk
        self.default = default
609
        self.nullable = nullable
610 2871 aaronmk
        self.constraints = constraints
611 2674 aaronmk
612 2818 aaronmk
    def to_str(self, db):
613
        str_ = Col.to_str(self, db)+' '+self.type
614
        if not self.nullable: str_ += ' NOT NULL'
615
        if self.default != None: str_ += ' DEFAULT '+self.default.to_str(db)
616 2871 aaronmk
        if self.constraints != None: str_ += ' '+self.constraints
617 2818 aaronmk
        return str_
618 2674 aaronmk
619
    def to_Col(self): return Col(self.name)
620 2822 aaronmk
621 2840 aaronmk
ensure_not_null_excs = (NoUnderlyingTableException, KeyError)
622
623 2851 aaronmk
def ensure_not_null(db, col, type_=None):
624 2840 aaronmk
    '''
625 2855 aaronmk
    @param col If type_ is not set, must have an underlying column.
626 2851 aaronmk
    @param type_ If set, overrides the underlying column's type.
627 2840 aaronmk
    @return EnsureNotNull|Col
628
    @throws ensure_not_null_excs
629
    '''
630 2855 aaronmk
    nullable = True
631
    try: typed_col = db.col_info(underlying_col(col))
632
    except NoUnderlyingTableException:
633
        if type_ == None: raise
634
    else:
635
        if type_ == None: type_ = typed_col.type
636
        nullable = typed_col.nullable
637
638 2953 aaronmk
    if nullable:
639
        try: col = EnsureNotNull(col, type_)
640
        except KeyError, e:
641
            # Warn of no null sentinel for type, even if caller catches error
642
            warnings.warn(UserWarning(exc.str_(e)))
643
            raise
644
645 2840 aaronmk
    return col