|
@@ -1,515 +0,0 @@
|
1
|
|
-# -*- coding: utf-8 -*-
|
2
|
|
-import os
|
3
|
|
-import re
|
4
|
|
-import logging
|
5
|
|
-
|
6
|
|
-import sqlalchemy as sqla
|
7
|
|
-from sqlalchemy.ext.compiler import compiles
|
8
|
|
-from django.conf import settings
|
9
|
|
-
|
10
|
|
-from Database.sqlalter import *
|
11
|
|
-
|
12
|
|
-#Logger config
|
13
|
|
-#logger.getLogger().setLevel('WARNING')
|
14
|
|
-logger = logging.getLogger('lodel2.Database.sqlwrapper')
|
15
|
|
-logger.setLevel('CRITICAL')
|
16
|
|
-logger.propagate = False
|
17
|
|
-#To be able to use dango confs
|
18
|
|
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Lodel.settings")
|
19
|
|
-
|
20
|
|
-class SqlWrapper(object):
|
21
|
|
- """ A wrapper class to sqlalchemy
|
22
|
|
-
|
23
|
|
- Usefull to provide a standart API
|
24
|
|
-
|
25
|
|
- __Note__ : This class is not thread safe (sqlAlchemy connections are not). Create a new instance of the class to use in different threads or use SqlWrapper::copy
|
26
|
|
- """
|
27
|
|
- ENGINES = {'mysql': {
|
28
|
|
- 'driver': 'pymysql',
|
29
|
|
- 'encoding': 'utf8'
|
30
|
|
- },
|
31
|
|
- 'postgresql': {
|
32
|
|
- 'driver': 'psycopg2',
|
33
|
|
- 'encoding': 'utf8',
|
34
|
|
- },
|
35
|
|
- 'sqlite': {
|
36
|
|
- 'driver': 'pysqlite',
|
37
|
|
- 'encoding': 'utf8',
|
38
|
|
- },
|
39
|
|
- }
|
40
|
|
-
|
41
|
|
- ##Configuration dict alias for class access
|
42
|
|
- config=settings.LODEL2SQLWRAPPER
|
43
|
|
-
|
44
|
|
- ##Wrapper instance list
|
45
|
|
- wrapinstance = dict()
|
46
|
|
-
|
47
|
|
- def __init__(self, name=None, alchemy_logs=None, read_db = "default", write_db = "default"):
|
48
|
|
- """ Instanciate a new SqlWrapper
|
49
|
|
- @param name str: The wrapper name
|
50
|
|
- @param alchemy_logs bool: If true activate sqlalchemy logger
|
51
|
|
- @param read_db str: The name of the db conf
|
52
|
|
- @param write_db str: The name of the db conf
|
53
|
|
-
|
54
|
|
- @todo Better use of name (should use self.cfg['wrapper'][name] to get engines configs
|
55
|
|
- @todo Is it a really good idea to store instance in class scope ? Maybe not !!
|
56
|
|
- """
|
57
|
|
-
|
58
|
|
- self.sqlalogging = False if alchemy_logs == None else bool(alchemy_logs)
|
59
|
|
-
|
60
|
|
- if name == None:
|
61
|
|
- self.name = read_db+':'+write_db
|
62
|
|
- else:
|
63
|
|
- self.name = name
|
64
|
|
-
|
65
|
|
- self.r_dbconf = read_db
|
66
|
|
- self.w_dbconf = write_db
|
67
|
|
-
|
68
|
|
- self._checkConf() #raise if errors in configuration
|
69
|
|
-
|
70
|
|
- if self.name in self.__class__.wrapinstance:
|
71
|
|
- logger.warning("A SqlWrapper with the name "+self.name+" allready exist. Replacing the old one by the new one")
|
72
|
|
- SqlWrapper.wrapinstance[self.name] = self
|
73
|
|
-
|
74
|
|
- #Engine and wrapper initialisation
|
75
|
|
- self.r_engine = self._getEngine(True, self.sqlalogging)
|
76
|
|
- self.w_engine = self._getEngine(False, self.sqlalogging)
|
77
|
|
- self.r_conn = None
|
78
|
|
- self.w_conn = None
|
79
|
|
-
|
80
|
|
-
|
81
|
|
- self.metadata = None #TODO : use it to load all db schema in 1 request and don't load it each table instanciation
|
82
|
|
- self.meta_crea = None
|
83
|
|
-
|
84
|
|
- logger.debug("New wrapper instance : <"+self.name+" read:"+str(self.r_engine)+" write:"+str(self.w_engine))
|
85
|
|
- pass
|
86
|
|
-
|
87
|
|
- @classmethod
|
88
|
|
- def getEngine(c, ename = 'default', logs = None):
|
89
|
|
- return c(read_db = ename, write_db = ename, alchemy_logs = logs).r_engine
|
90
|
|
-
|
91
|
|
- @property
|
92
|
|
- def cfg(self):
|
93
|
|
- """ Return the SqlWrapper.config dict """
|
94
|
|
- return self.__class__.config;
|
95
|
|
- @property
|
96
|
|
- def _engines_cfg(self):
|
97
|
|
- return self.__class__.ENGINES;
|
98
|
|
-
|
99
|
|
- @property
|
100
|
|
- def meta(self):
|
101
|
|
- if self.metadata == None:
|
102
|
|
- self.renewMetaData()
|
103
|
|
- return self.metadata
|
104
|
|
-
|
105
|
|
- def renewMetaData(self):
|
106
|
|
- """ (Re)load the database schema """
|
107
|
|
- self.metadata = sqla.MetaData(bind=self.r_engine)
|
108
|
|
- self.metadata.reflect()
|
109
|
|
-
|
110
|
|
- @property
|
111
|
|
- def rconn(self):
|
112
|
|
- """ Return the read connection
|
113
|
|
- @warning Do not store the connection, call this method each time you need it
|
114
|
|
- """
|
115
|
|
- return self._getConnection(True)
|
116
|
|
- @property
|
117
|
|
- def wconn(self):
|
118
|
|
- """ Return the write connection
|
119
|
|
- @warning Do not store the connection, call this method each time you need it
|
120
|
|
- """
|
121
|
|
- return self._getConnection(False)
|
122
|
|
-
|
123
|
|
- def _getConnection(self, read):
|
124
|
|
- """ Return an opened connection
|
125
|
|
- @param read bool: If true return the reading connection
|
126
|
|
- @return A sqlAlchemy db connection
|
127
|
|
- @private
|
128
|
|
- """
|
129
|
|
- if read:
|
130
|
|
- r = self.r_conn
|
131
|
|
- else:
|
132
|
|
- r = self.w_conn
|
133
|
|
-
|
134
|
|
- if r == None:
|
135
|
|
- #Connection not yet opened
|
136
|
|
- self.connect(read)
|
137
|
|
- r = self._getConnection(read) #TODO : Un truc plus safe/propre qu'un appel reccursif ?
|
138
|
|
- return r
|
139
|
|
-
|
140
|
|
-
|
141
|
|
- def connect(self, read = None):
|
142
|
|
- """ Open a connection to a database
|
143
|
|
- @param read bool|None: If None connect both, if True only connect the read side (False the write side)
|
144
|
|
- @return None
|
145
|
|
- """
|
146
|
|
- if read or read == None:
|
147
|
|
- if self.r_conn != None:
|
148
|
|
- logger.debug(' SqlWrapper("'+self.name+'") Unable to connect, already connected')
|
149
|
|
- else:
|
150
|
|
- self.r_conn = self.r_engine.connect()
|
151
|
|
-
|
152
|
|
- if not read or read == None:
|
153
|
|
- if self.w_conn != None:
|
154
|
|
- logger.debug(' SqlWrapper("'+self.name+'") Unable to connect, already connected')
|
155
|
|
- else:
|
156
|
|
- self.w_conn = self.w_engine.connect()
|
157
|
|
-
|
158
|
|
- def disconnect(self, read = None):
|
159
|
|
- """ Close a connection to a database
|
160
|
|
- @param read bool|None: If None disconnect both, if True only connect the read side (False the write side)
|
161
|
|
- @return None
|
162
|
|
- """
|
163
|
|
- if read or read == None:
|
164
|
|
- if self.r_conn == None:
|
165
|
|
- logger.info('Unable to close read connection : connection not opened')
|
166
|
|
- else:
|
167
|
|
- self.r_conn.close()
|
168
|
|
- self.r_conn = None
|
169
|
|
-
|
170
|
|
- if not read or read == None:
|
171
|
|
- if self.r_conn == None:
|
172
|
|
- logger.info('Unable to close write connection : connection not opened')
|
173
|
|
- else:
|
174
|
|
- self.w_conn.close()
|
175
|
|
- self.w_conn = None
|
176
|
|
-
|
177
|
|
- def reconnect(self, read = None):
|
178
|
|
- """ Close and reopen a connection to a database
|
179
|
|
- @param read bool|None: If None disconnect both, if True only connect the read side (False the write side)
|
180
|
|
- @return None
|
181
|
|
- """
|
182
|
|
- self.disconnect(read)
|
183
|
|
- self.connect(read)
|
184
|
|
-
|
185
|
|
- @classmethod
|
186
|
|
- def reconnectAll(c, read = None):
|
187
|
|
- """ Reconnect all the wrappers
|
188
|
|
- @static
|
189
|
|
- """
|
190
|
|
- for wname in c.wrapinstance:
|
191
|
|
- c.wrapinstance[wname].reconnect(read)
|
192
|
|
-
|
193
|
|
- def Table(self, tname):
|
194
|
|
- """ Instanciate a new SqlAlchemy Table
|
195
|
|
- @param tname str: The table name
|
196
|
|
- @return A new instance of SqlAlchemy::Table
|
197
|
|
- """
|
198
|
|
- if not isinstance(tname, str):
|
199
|
|
- return TypeError('Excepting a <class str> but got a '+str(type(tname)))
|
200
|
|
- #return sqla.Table(tname, self.meta, autoload_with=self.r_engine, autoload=True)
|
201
|
|
- return sqla.Table(tname, self.meta)
|
202
|
|
-
|
203
|
|
- def _getEngine(self, read=True, sqlalogging = None):
|
204
|
|
- """ Return a sqlalchemy engine
|
205
|
|
- @param read bool: If True return the read engine, else
|
206
|
|
- return the write one
|
207
|
|
- @return a sqlachemy engine instance
|
208
|
|
-
|
209
|
|
- @todo Put the check on db config in SqlWrapper.checkConf()
|
210
|
|
- """
|
211
|
|
- #Loading confs
|
212
|
|
- cfg = self.cfg['db'][self.r_dbconf if read else self.w_dbconf]
|
213
|
|
-
|
214
|
|
- edata = self._engines_cfg[cfg['ENGINE']] #engine infos
|
215
|
|
- conn_str = ""
|
216
|
|
-
|
217
|
|
- if cfg['ENGINE'] == 'sqlite':
|
218
|
|
- #Sqlite connection string
|
219
|
|
- conn_str = '%s+%s:///%s'%( cfg['ENGINE'],
|
220
|
|
- edata['driver'],
|
221
|
|
- cfg['NAME'])
|
222
|
|
- else:
|
223
|
|
- #Mysql and Postgres connection string
|
224
|
|
- user = cfg['USER']
|
225
|
|
- user += (':'+cfg['PASSWORD'] if 'PASSWORD' in cfg else '')
|
226
|
|
-
|
227
|
|
- if 'HOST' not in cfg:
|
228
|
|
- logger.info('Not HOST in configuration, using localhost')
|
229
|
|
- host = 'localhost'
|
230
|
|
- else:
|
231
|
|
- host = cfg['HOST']
|
232
|
|
-
|
233
|
|
- host += (':'+cfg['PORT'] if 'PORT' in cfg else '')
|
234
|
|
-
|
235
|
|
- conn_str = '%s+%s://'%(cfg['ENGINE'], edata['driver'])
|
236
|
|
- conn_str += '%s@%s/%s'%(user,host,cfg['NAME'])
|
237
|
|
-
|
238
|
|
-
|
239
|
|
- ret = sqla.create_engine(conn_str, encoding=edata['encoding'], echo=self.sqlalogging)
|
240
|
|
-
|
241
|
|
- logger.debug("Getting engine :"+str(ret))
|
242
|
|
-
|
243
|
|
- return ret
|
244
|
|
-
|
245
|
|
- @classmethod
|
246
|
|
- def getWrapper(c, name):
|
247
|
|
- """ Return a wrapper instance from a wrapper name
|
248
|
|
- @param name str: The wrapper name
|
249
|
|
- @return a SqlWrapper instance
|
250
|
|
-
|
251
|
|
- @throw KeyError
|
252
|
|
- """
|
253
|
|
- if name not in c.wrapinstance:
|
254
|
|
- raise KeyError("No wrapper named '"+name+"' exists")
|
255
|
|
- return c.wrapinstance[name]
|
256
|
|
-
|
257
|
|
- def _checkConf(self):
|
258
|
|
- """ Class method that check the configuration
|
259
|
|
-
|
260
|
|
- Configuration looks like
|
261
|
|
- - db (mandatory)
|
262
|
|
- - ENGINE (mandatory)
|
263
|
|
- - NAME (mandatory)
|
264
|
|
- - USER
|
265
|
|
- - PASSWORD
|
266
|
|
- - engines (mandatory)
|
267
|
|
- - driver (mandatory)
|
268
|
|
- - encoding (mandatory)
|
269
|
|
- - dbread (mandatory if no default db)
|
270
|
|
- - dbwrite (mandatory if no default db)
|
271
|
|
- """
|
272
|
|
- err = []
|
273
|
|
- if 'db' not in self.cfg:
|
274
|
|
- err.append('Missing "db" in configuration')
|
275
|
|
- else:
|
276
|
|
- for dbname in [self.r_dbconf, self.w_dbconf]:
|
277
|
|
- if dbname not in self.cfg['db']:
|
278
|
|
- err.append('Missing "'+dbname+'" db configuration')
|
279
|
|
- else:
|
280
|
|
- db = self.cfg['db'][dbname]
|
281
|
|
- if db['ENGINE'] not in self._engines_cfg:
|
282
|
|
- err.append('Unknown engine "'+db['ENGINE']+'"')
|
283
|
|
- elif db['ENGINE'] != 'sqlite' and 'USER' not in db:
|
284
|
|
- err.append('Missing "User" in configuration of database "'+dbname+'"')
|
285
|
|
- if 'NAME' not in db:
|
286
|
|
- err.append('Missing "NAME" in database "'+dbname+'"')
|
287
|
|
-
|
288
|
|
- if len(err)>0:
|
289
|
|
- err_str = "\n"
|
290
|
|
- for e in err:
|
291
|
|
- err_str += "\t\t"+e+"\n"
|
292
|
|
- raise NameError('Configuration errors in LODEL2SQLWRAPPER:'+err_str)
|
293
|
|
-
|
294
|
|
- def dropAll(self):
|
295
|
|
- """ Drop ALL tables from the database """
|
296
|
|
- if not settings.DEBUG:
|
297
|
|
- logger.critical("Trying to drop all tables but we are not in DEBUG !!!")
|
298
|
|
- raise RuntimeError("Trying to drop all tables but we are not in DEBUG !!!")
|
299
|
|
- meta = sqla.MetaData(bind=self.w_engine)
|
300
|
|
- meta.reflect()
|
301
|
|
- meta.drop_all()
|
302
|
|
- pass
|
303
|
|
-
|
304
|
|
- def createAllFromConf(self, schema):
|
305
|
|
- """ Create a bunch of tables from a schema
|
306
|
|
- @param schema list: A list of table schema
|
307
|
|
- @see SqlWrapper::createTable()
|
308
|
|
- """
|
309
|
|
- self.meta_crea = sqla.MetaData()
|
310
|
|
-
|
311
|
|
- logger.info("Running function createAllFromConf")
|
312
|
|
- for i,table in enumerate(schema):
|
313
|
|
- if not isinstance(table, dict):
|
314
|
|
- raise TypeError("Excepted a list of dict but got a "+str(type(schema))+" in the list")
|
315
|
|
- self.createTable(**table)
|
316
|
|
-
|
317
|
|
- self.meta_crea.create_all(bind = self.w_engine)
|
318
|
|
- logger.info("All tables created")
|
319
|
|
- self.meta_crea = None
|
320
|
|
- self.renewMetaData()
|
321
|
|
- pass
|
322
|
|
-
|
323
|
|
- def createTable(self, name, columns, **kw):
|
324
|
|
- """ Create a table
|
325
|
|
- @param name str: The table name
|
326
|
|
- @param columns list: A list of columns description dict
|
327
|
|
- @param extra dict: Extra arguments for table creation
|
328
|
|
- @see SqlWrapper::createColumn()
|
329
|
|
- """
|
330
|
|
-
|
331
|
|
- if self.meta_crea == None:
|
332
|
|
- self.meta_crea = sqla.MetaData()
|
333
|
|
- crea_now = True
|
334
|
|
- else:
|
335
|
|
- crea_now = False
|
336
|
|
-
|
337
|
|
- if not isinstance(name, str):
|
338
|
|
- raise TypeError("<class str> excepted for table name, but got "+type(name))
|
339
|
|
-
|
340
|
|
- #if not 'mysql_engine' in kw and self.w_engine.dialect.name == 'mysql':
|
341
|
|
- # kw['mysql_engine'] = 'InnoDB'
|
342
|
|
-
|
343
|
|
- res = sqla.Table(name, self.meta_crea, **kw)
|
344
|
|
- for i,col in enumerate(columns):
|
345
|
|
- res.append_column(self.createColumn(**col))
|
346
|
|
-
|
347
|
|
- if crea_now:
|
348
|
|
- self.meta_crea.create_all(self.w_engine)
|
349
|
|
- logger.debug("Table '"+name+"' created")
|
350
|
|
-
|
351
|
|
- pass
|
352
|
|
-
|
353
|
|
- def createColumn(self, **kwargs):
|
354
|
|
- """ Create a Column
|
355
|
|
-
|
356
|
|
- Accepte named parameters :
|
357
|
|
- - name : The column name
|
358
|
|
- - type : see SqlWrapper::_strToSqlAType()
|
359
|
|
- - extra : a dict like { "primarykey":True, "nullable":False, "default":"test"...}
|
360
|
|
- @param **kwargs
|
361
|
|
- """
|
362
|
|
- if not 'name' in kwargs or ('type' not in kwargs and 'type_' not in kwargs):
|
363
|
|
- pass#ERROR
|
364
|
|
-
|
365
|
|
- #Converting parameters
|
366
|
|
- if 'type_' not in kwargs and 'type' in kwargs:
|
367
|
|
- kwargs['type_'] = self._strToSqlAType(kwargs['type'])
|
368
|
|
- del kwargs['type']
|
369
|
|
-
|
370
|
|
- if 'extra' in kwargs:
|
371
|
|
- #put the extra keys in kwargs
|
372
|
|
- for exname in kwargs['extra']:
|
373
|
|
- kwargs[exname] = kwargs['extra'][exname]
|
374
|
|
- del kwargs['extra']
|
375
|
|
-
|
376
|
|
- if 'foreignkey' in kwargs:
|
377
|
|
- #Instanciate a fk
|
378
|
|
- fk = sqla.ForeignKey(kwargs['foreignkey'])
|
379
|
|
- del kwargs['foreignkey']
|
380
|
|
- else:
|
381
|
|
- fk = None
|
382
|
|
-
|
383
|
|
- if 'primarykey' in kwargs:
|
384
|
|
- #renaming primary_key in primarykey in kwargs
|
385
|
|
- kwargs['primary_key'] = kwargs['primarykey']
|
386
|
|
- del kwargs['primarykey']
|
387
|
|
-
|
388
|
|
- res = sqla.Column(**kwargs)
|
389
|
|
-
|
390
|
|
- if fk != None:
|
391
|
|
- res.append_foreign_key(fk)
|
392
|
|
-
|
393
|
|
- #logger.debug("Column '"+kwargs['name']+"' created")
|
394
|
|
- return res
|
395
|
|
-
|
396
|
|
- def _strToSqlAType(self, strtype):
|
397
|
|
- """ Convert a string to an sqlAlchemy column type """
|
398
|
|
-
|
399
|
|
- if 'VARCHAR' in strtype:
|
400
|
|
- return self._strToVarchar(strtype)
|
401
|
|
- else:
|
402
|
|
- try:
|
403
|
|
- return getattr(sqla, strtype)
|
404
|
|
- except AttributeError:
|
405
|
|
- raise NameError("Unknown type '"+strtype+"'")
|
406
|
|
- pass
|
407
|
|
-
|
408
|
|
- def _strToVarchar(self, vstr):
|
409
|
|
- """ Convert a string like 'VARCHAR(XX)' (with XX an integer) to a SqlAlchemy varchar type"""
|
410
|
|
- check_length = re.search(re.compile('VARCHAR\(([\d]+)\)', re.IGNORECASE), vstr)
|
411
|
|
- column_length = int(check_length.groups()[0]) if check_length else None
|
412
|
|
- return sqla.VARCHAR(length=column_length)
|
413
|
|
-
|
414
|
|
-
|
415
|
|
- def dropColumn(self, tname, colname):
|
416
|
|
- """ Drop a column from a table
|
417
|
|
- @param tname str|sqlalchemy.Table: The table name or a Table object
|
418
|
|
- @param colname str|sqlalchemy.Column: The column name or a column object
|
419
|
|
- @return None
|
420
|
|
- """
|
421
|
|
- if tname not in self.meta.tables: #Useless ?
|
422
|
|
- raise NameError("The table '"+tname+"' dont exist")
|
423
|
|
- table = self.Table(tname)
|
424
|
|
- col = sqla.Column(colname)
|
425
|
|
-
|
426
|
|
- ddl = DropColumn(table, col)
|
427
|
|
- sql = ddl.compile(dialect=self.w_engine.dialect)
|
428
|
|
- sql = str(sql)
|
429
|
|
- logger.debug("Executing SQL : '"+sql+"'")
|
430
|
|
- ret = bool(self.w_engine.execute(sql))
|
431
|
|
-
|
432
|
|
- self.renewMetaData()
|
433
|
|
- return ret
|
434
|
|
-
|
435
|
|
-
|
436
|
|
- def addColumn(self, tname, colname, coltype):
|
437
|
|
- """ Add a column to a table
|
438
|
|
- @param tname str: The table name
|
439
|
|
- @param colname str: The column name
|
440
|
|
- @param coltype str: The new column type
|
441
|
|
-
|
442
|
|
- @return True if query success False if it fails
|
443
|
|
- """
|
444
|
|
- if tname not in self.meta.tables: #Useless ?
|
445
|
|
- raise NameError("The table '"+tname+"' dont exist")
|
446
|
|
- table = self.Table(tname)
|
447
|
|
- newcol = self.createColumn(name=colname, type_ = coltype)
|
448
|
|
-
|
449
|
|
- ddl = AddColumn(table, newcol)
|
450
|
|
- sql = ddl.compile(dialect=self.w_engine.dialect)
|
451
|
|
- sql = str(sql)
|
452
|
|
- logger.debug("Executing SQL : '"+sql+"'")
|
453
|
|
- ret = bool(self.wconn.execute(sql))
|
454
|
|
-
|
455
|
|
- self.renewMetaData()
|
456
|
|
- return ret
|
457
|
|
-
|
458
|
|
- ## AddColumnObject
|
459
|
|
- #
|
460
|
|
- # Adds a column from a SQLAlchemy Column Object
|
461
|
|
- #
|
462
|
|
- # @param tname str: Name of the table in which to add the column
|
463
|
|
- # @param column Column: Column object to add to the table
|
464
|
|
- # @return True if query is successful, False if it fails
|
465
|
|
- def addColumnObject(self, tname, column):
|
466
|
|
- if tname not in self.meta.tables:
|
467
|
|
- raise NameError("The table '%s' doesn't exist" % tname)
|
468
|
|
- table = self.Table(tname)
|
469
|
|
-
|
470
|
|
- ddl = AddColumn(table, column)
|
471
|
|
- sql = ddl.compile(dialect=self.w_engine.dialect)
|
472
|
|
- sql = str(sql)
|
473
|
|
- logger.debug("Executing SQL : '%s'" % sql)
|
474
|
|
- ret = bool(self.wconn.execute(sql))
|
475
|
|
- self.renewMetaData()
|
476
|
|
- return ret
|
477
|
|
-
|
478
|
|
- def alterColumn(self, tname, colname, col_newtype):
|
479
|
|
- """ Change the type of a column
|
480
|
|
- @param tname str: The table name
|
481
|
|
- @param colname str: The column name
|
482
|
|
- @param col_newtype str: The column new type
|
483
|
|
-
|
484
|
|
- @return True if query successs False if it fails
|
485
|
|
- """
|
486
|
|
-
|
487
|
|
- if tname not in self.meta.tables: #Useless ?
|
488
|
|
- raise NameError("The table '"+tname+"' dont exist")
|
489
|
|
-
|
490
|
|
- col = self.createColumn(name=colname, type_=col_newtype)
|
491
|
|
- table = self.Table(tname)
|
492
|
|
-
|
493
|
|
- ddl = AlterColumn(table, newcol)
|
494
|
|
- sql = ddl.compile(dialect=self.w_engine.dialect)
|
495
|
|
- sql = str(sql)
|
496
|
|
- logger.debug("Executing SQL : '"+sql+"'")
|
497
|
|
- ret = bool(self.wconn.execute(sql))
|
498
|
|
-
|
499
|
|
- self.renewMetaData()
|
500
|
|
- return ret
|
501
|
|
-
|
502
|
|
- def _debug__printSchema(self):
|
503
|
|
- """ Debug function to print the db schema """
|
504
|
|
- print(self.meta)
|
505
|
|
- for tname in self.meta.tables:
|
506
|
|
- self._debug__printTable(tname)
|
507
|
|
-
|
508
|
|
- def _debug__printTable(self, tname):
|
509
|
|
- t = self.meta.tables[tname]
|
510
|
|
- tstr = 'Table : "'+tname+'" :\n'
|
511
|
|
- for c in t.c:
|
512
|
|
- tstr += '\t\t"'+c.name+'"('+str(c.type)+') \n'
|
513
|
|
- print(tstr)
|
514
|
|
-
|
515
|
|
-
|