├── MANIFEST.in ├── cx_OracleParser ├── __init__.py ├── full │ ├── __init__.py │ ├── Statements.py │ ├── Processor.py │ └── Grammar.py ├── simple │ ├── __init__.py │ ├── Statements.py │ ├── Grammar.py │ └── Processor.py └── Parser.py ├── .gitignore ├── LICENSE.txt ├── cx_OracleObject ├── __init__.py ├── Statements.py ├── Utils.py ├── Environment.py ├── Describer.py └── Object.py ├── README.txt ├── test ├── TestParser.py └── TestSimpleParser.py ├── setup.py ├── cx_ExportData.py ├── cx_ImportData.py ├── cx_PatchCommands.py ├── cx_OracleDebugger.py ├── HISTORY.txt ├── cx_OracleEx.py └── cx_OracleUtils.py /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include *.txt 3 | -------------------------------------------------------------------------------- /cx_OracleParser/__init__.py: -------------------------------------------------------------------------------- 1 | from .Parser import * 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__ 3 | MANIFEST 4 | build 5 | dist 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthony-tuininga/cx_PyOracleLib/HEAD/LICENSE.txt -------------------------------------------------------------------------------- /cx_OracleParser/full/__init__.py: -------------------------------------------------------------------------------- 1 | from .Grammar import * 2 | from .Processor import * 3 | 4 | -------------------------------------------------------------------------------- /cx_OracleParser/simple/__init__.py: -------------------------------------------------------------------------------- 1 | from .Grammar import * 2 | from .Processor import * 3 | 4 | -------------------------------------------------------------------------------- /cx_OracleObject/__init__.py: -------------------------------------------------------------------------------- 1 | from .Environment import * 2 | from .Describer import * 3 | from .Utils import * 4 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | cx_PyOracleLib 2 | -------------- 3 | This project contains a number of Python modules that are used by a number of 4 | projects, in particular the cx_OracleTools project, and as such they are 5 | handled independently, rather than bundled with a distribution of the 6 | dependent project. 7 | 8 | It is released under a free software license, see LICENSE.txt for more 9 | details. 10 | 11 | -------------------------------------------------------------------------------- /test/TestParser.py: -------------------------------------------------------------------------------- 1 | import cx_OptionParser 2 | import cx_OracleParser 3 | 4 | parser = cx_OptionParser.OptionParser("TestParser") 5 | parser.AddOption("--production-name", default = "file", 6 | help = "the name of the production to look for") 7 | parser.AddArgument("fileName", required = True, 8 | help = "the name of the file to parse") 9 | options = parser.Parse() 10 | 11 | parser = cx_OracleParser.Parser() 12 | try: 13 | for statement in parser.Parse(open(options.fileName).read(), "dummy", 14 | productionName = options.productionName): 15 | print(statement) 16 | except cx_OracleParser.ParsingFailed as value: 17 | print("Parsing failed at position:", value.arguments["pos"]) 18 | print("Remaining string:", value.arguments["remainingString"]) 19 | 20 | -------------------------------------------------------------------------------- /test/TestSimpleParser.py: -------------------------------------------------------------------------------- 1 | import cx_OptionParser 2 | import cx_OracleParser 3 | 4 | parser = cx_OptionParser.OptionParser("TestParser") 5 | parser.AddOption("--production-name", default = "file", 6 | help = "the name of the production to look for") 7 | parser.AddArgument("fileName", required = True, 8 | help = "the name of the file to parse") 9 | options = parser.Parse() 10 | 11 | parser = cx_OracleParser.SimpleParser() 12 | try: 13 | for statement in parser.Parse(open(options.fileName).read(), 14 | "dummy", productionName = options.productionName): 15 | print(statement) 16 | except cx_OracleParser.ParsingFailed as value: 17 | print("Parsing failed at position:", value.arguments["pos"]) 18 | print("Remaining string:", value.arguments["remainingString"]) 19 | 20 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Distutils script for cx_PyOracleLib. 2 | 3 | To install: 4 | python setup.py install 5 | 6 | """ 7 | 8 | from distutils.core import setup 9 | 10 | modules = [ 11 | "cx_ExportData", 12 | "cx_ImportData", 13 | "cx_OracleDebugger", 14 | "cx_OracleEx", 15 | "cx_OracleUtils", 16 | "cx_PatchCommands" 17 | ] 18 | 19 | packages = [ 20 | "cx_OracleObject", 21 | "cx_OracleParser", 22 | "cx_OracleParser.full", 23 | "cx_OracleParser.simple" 24 | ] 25 | 26 | 27 | setup( 28 | name = "cx_PyOracleLib", 29 | version = "3.0", 30 | description = "Set of Python modules for handling Oracle databases", 31 | license = "See LICENSE.txt", 32 | long_description = "Set of Python modules for handling Oracle " + \ 33 | "databases and used by a number of projects, " + \ 34 | "in particular the cx_OracleTools project", 35 | author = "Anthony Tuininga", 36 | author_email = "anthony.tuininga@gmail.com", 37 | url = "https://github.com/anthony-tuininga/cx_PyOracleLib", 38 | py_modules = modules, 39 | packages = packages) 40 | 41 | -------------------------------------------------------------------------------- /cx_OracleParser/Parser.py: -------------------------------------------------------------------------------- 1 | """Submodule for handling the parsing of files containing Oracle objects.""" 2 | 3 | import cx_Exceptions 4 | import cx_OracleParser.simple 5 | import cx_OracleParser.full 6 | import cx_Parser 7 | 8 | __all__ = [ "Parser", "SimpleParser", "ParsingFailed" ] 9 | 10 | 11 | class ParsingFailed(cx_Exceptions.BaseException): 12 | message = "Parsing failed at character %(pos)s" 13 | 14 | 15 | class Parser(object): 16 | 17 | def __init__(self): 18 | self.parser = cx_Parser.Parser(cx_OracleParser.full.GRAMMAR, 19 | cx_OracleParser.full.Processor()) 20 | 21 | def Parse(self, string, owner = None, productionName = "file"): 22 | self.parser.processor.owner = owner 23 | remainingString, results = self.parser.Parse(string, productionName) 24 | if results is None or remainingString: 25 | pos = len(string) - len(remainingString) 26 | raise ParsingFailed(pos = pos, string = string, owner = owner, 27 | productionName = productionName, 28 | remainingString = remainingString) 29 | return results 30 | 31 | 32 | class SimpleParser(Parser): 33 | 34 | def __init__(self): 35 | self.parser = cx_Parser.Parser(cx_OracleParser.simple.GRAMMAR, 36 | cx_OracleParser.simple.Processor()) 37 | 38 | def IterParse(self, string, owner = None): 39 | lineNumber = 1 40 | productionName = "statement" 41 | self.parser.processor.owner = owner 42 | while string: 43 | remainingString, results = self.parser.Parse(string, 44 | productionName) 45 | pos = len(string) - len(remainingString) 46 | if results is None: 47 | raise ParsingFailed(pos = pos, string = string, 48 | owner = self.parser.processor.owner, 49 | productionName = productionName, 50 | lineNumber = lineNumber, 51 | remainingString = remainingString) 52 | statement, = results 53 | statement.lineNumber += lineNumber - 1 54 | lineNumber += len(string[:pos].splitlines()) 55 | string = remainingString 56 | yield statement 57 | 58 | -------------------------------------------------------------------------------- /cx_ExportData.py: -------------------------------------------------------------------------------- 1 | """Module for use in exporting data to a file.""" 2 | 3 | import cx_Logging 4 | import cx_Oracle 5 | import pickle 6 | import sys 7 | 8 | # define constant for pickle protocol 9 | BINARY = 1 10 | 11 | class Exporter: 12 | """Export data from a database in a cross platform manner.""" 13 | 14 | def __init__(self, outFile, cursor, reportPoint, prefix = ""): 15 | self.outFile = outFile 16 | self.cursor = cursor 17 | self.cursor.numbersAsStrings = True 18 | self.reportPoint = reportPoint 19 | self.prefix = prefix 20 | 21 | def __ExportTableHeader(self, tableName): 22 | """Export the table header to the file.""" 23 | cx_Logging.Trace("%sExporting table %s...", self.prefix, tableName) 24 | self.cursor.execute("select * from " + tableName) 25 | columns = [(r[0], self.__StringRepOfType(r[1], r[2])) \ 26 | for r in self.cursor.description] 27 | pickle.dump(tableName, self.outFile, BINARY) 28 | pickle.dump(columns, self.outFile, BINARY) 29 | 30 | def __ExportTableRows(self, rowsToSkip, rowLimit): 31 | """Export the rows in the table to the file.""" 32 | numRows = 0 33 | format = self.prefix + " %d rows exported." 34 | cursor = self.cursor 35 | outFile = self.outFile 36 | reportPoint = self.reportPoint 37 | for row in cursor: 38 | numRows += 1 39 | if numRows > rowLimit: 40 | numRows -= 1 41 | break 42 | elif numRows > rowsToSkip: 43 | pickle.dump(row, outFile, BINARY) 44 | if reportPoint is not None and numRows % reportPoint == 0: 45 | cx_Logging.Trace(format, numRows) 46 | if reportPoint is None or numRows == 0 or numRows % reportPoint != 0: 47 | cx_Logging.Trace(format, numRows) 48 | pickle.dump(None, outFile, BINARY) 49 | 50 | def __StringRepOfType(self, dataType, displaySize): 51 | """Return the string representation of the type.""" 52 | if dataType == cx_Oracle.NUMBER: 53 | return "STRING,%d" % displaySize 54 | for stringRep in ("BINARY", "STRING", "ROWID", "FIXED_CHAR", "NCHAR", 55 | "FIXED_NCHAR"): 56 | if getattr(cx_Oracle, stringRep) == dataType: 57 | return "%s,%d" % (stringRep, displaySize) 58 | for stringRep in ("BLOB", "CLOB", "NCLOB", "DATETIME", "LONG_BINARY", 59 | "LONG_STRING", "TIMESTAMP"): 60 | if getattr(cx_Oracle, stringRep) == dataType: 61 | return stringRep 62 | raise Exception("Unsupported type: %s!" % dataType) 63 | 64 | def ExportTable(self, tableName, rowsToSkip = None, rowLimit = None): 65 | """Export the data in the table to the file.""" 66 | if rowsToSkip is None: 67 | rowsToSkip = 0 68 | if rowLimit is None: 69 | rowLimit = sys.maxsize 70 | self.__ExportTableHeader(tableName) 71 | self.__ExportTableRows(rowsToSkip, rowLimit) 72 | 73 | def FinalizeExport(self): 74 | """Finalize the export.""" 75 | pickle.dump(None, self.outFile, BINARY) 76 | 77 | def TablesInSchema(self): 78 | """Return a list of tables found in the schema.""" 79 | self.cursor.execute(""" 80 | select table_name 81 | from user_tables 82 | where temporary = 'N'""") 83 | return [n for n, in self.cursor.fetchall()] 84 | 85 | -------------------------------------------------------------------------------- /cx_ImportData.py: -------------------------------------------------------------------------------- 1 | """Defines class for importing data from an export file.""" 2 | 3 | import pickle 4 | import cx_Logging 5 | import cx_Oracle 6 | import os 7 | import sys 8 | 9 | class Importer: 10 | """Handles importing data from the file.""" 11 | 12 | def __init__(self, connection): 13 | self.connection = connection 14 | self.cursor = connection.cursor() 15 | self.inFile = None 16 | self.reportPoint = None 17 | self.reportFunc = self.ReportProgress 18 | self.commitPoint = None 19 | 20 | def __iter__(self): 21 | return self 22 | 23 | def DataInTable(self): 24 | """Return a list of the data stored in the table.""" 25 | rows = [] 26 | while True: 27 | row = pickle.load(self.inFile) 28 | if row is None: 29 | break 30 | rows.append(row) 31 | return rows 32 | 33 | def ImportTable(self): 34 | """Import the data into the table and return the number of rows 35 | imported.""" 36 | numRows = 0 37 | rowsToInsert = [] 38 | while True: 39 | row = pickle.load(self.inFile) 40 | if row is None: 41 | break 42 | rowsToInsert.append(row) 43 | numRows += 1 44 | commit = (self.commitPoint is not None \ 45 | and numRows % self.commitPoint == 0) 46 | if commit or len(rowsToInsert) == self.cursor.arraysize: 47 | self.cursor.executemany(None, rowsToInsert) 48 | rowsToInsert = [] 49 | if commit: 50 | self.connection.commit() 51 | if self.reportPoint and numRows % self.reportPoint == 0: 52 | self.reportFunc(numRows) 53 | if rowsToInsert: 54 | self.cursor.executemany(None, rowsToInsert) 55 | self.connection.commit() 56 | if not self.reportPoint or numRows == 0 \ 57 | or numRows % self.reportPoint != 0: 58 | self.reportFunc(numRows) 59 | return numRows 60 | 61 | def __next__(self): 62 | """Return the next table name to process.""" 63 | tableName = pickle.load(self.inFile) 64 | if tableName is None: 65 | raise StopIteration 66 | columnNames = [] 67 | bindVarNames = [] 68 | bindVars = [] 69 | columns = pickle.load(self.inFile) 70 | for name, dataType in columns: 71 | dataSize = 0 72 | if "," in dataType: 73 | dataType, dataSize = dataType.split(",") 74 | dataSize = int(dataSize) 75 | dataType = getattr(cx_Oracle, dataType) 76 | columnNames.append(name) 77 | bindVars.append(self.cursor.var(dataType, dataSize)) 78 | bindVarNames.append(":%s" % len(bindVars)) 79 | sql = "insert into %s (%s) values (%s)" % \ 80 | (tableName, ",".join(columnNames), ",".join(bindVarNames)) 81 | self.cursor.prepare(sql) 82 | self.cursor.setinputsizes(*bindVars) 83 | return tableName, columnNames 84 | 85 | def OpenFile(self, fileName): 86 | """Open the file for importing.""" 87 | if fileName == "-": 88 | self.inFile = sys.stdin 89 | self.inFileSize = None 90 | else: 91 | self.inFile = open(fileName, "rb") 92 | self.inFileSize = os.stat(fileName).st_size * 1.0 93 | 94 | def ReportProgress(self, numRows): 95 | """Report progress on the import.""" 96 | if self.inFileSize is not None: 97 | percent = (self.inFile.tell() / self.inFileSize) * 100 98 | cx_Logging.Trace(" %d rows imported (%.0f%% of file).", 99 | numRows, percent) 100 | else: 101 | cx_Logging.Trace(" %d rows imported.", numRows) 102 | 103 | def SkipTable(self): 104 | """Skip the import of the table.""" 105 | while pickle.load(self.inFile): 106 | pass 107 | 108 | -------------------------------------------------------------------------------- /cx_PatchCommands.py: -------------------------------------------------------------------------------- 1 | """Module for use in patching databases by a number of means.""" 2 | 3 | import cx_Exceptions 4 | import cx_Logging 5 | import cx_Oracle 6 | import cx_OracleEx 7 | import cx_OracleParser 8 | import datetime 9 | import os 10 | import sys 11 | 12 | class Processor(object): 13 | 14 | def __init__(self, connection, onErrorContinue = False): 15 | self.connection = connection 16 | self.onErrorContinue = onErrorContinue 17 | 18 | def _LogCommand(self, command): 19 | separator = "-" * 66 20 | message = command.GetLogMessage() 21 | cx_Logging.Trace("%s", separator) 22 | cx_Logging.Trace("%s", message) 23 | cx_Logging.Trace("%s", separator) 24 | if sys.stdout.isatty() and not sys.stderr.isatty(): 25 | now = datetime.datetime.today() 26 | print(now.strftime("%Y/%m/%d %H:%M:%S"), message) 27 | sys.stdout.flush() 28 | 29 | def ProcessCommand(self, command): 30 | self._LogCommand(command) 31 | command.Process(self) 32 | 33 | def ProcessFile(self, fileName): 34 | name, extension = os.path.splitext(fileName) 35 | if not extension: 36 | extension = ".sql" 37 | fileName += extension 38 | cls = CommandBase.classByExtension.get(extension and extension.lower()) 39 | if cls is None: 40 | cls = ExecuteSQLCommands 41 | command = cls(fileName) 42 | self.ProcessCommand(command) 43 | 44 | 45 | class CommandMetaClass(type): 46 | 47 | def __init__(cls, name, bases, classDict): 48 | super(CommandMetaClass, cls).__init__(name, bases, classDict) 49 | if cls.extension is not None: 50 | cls.classByExtension[cls.extension] = cls 51 | 52 | 53 | class CommandBase(object, metaclass = CommandMetaClass): 54 | classByExtension = {} 55 | extension = None 56 | 57 | def __init__(self, fileName): 58 | self.fileName = fileName 59 | 60 | def GetLogMessage(self): 61 | return "Executing commands in %s." % self.fileName 62 | 63 | 64 | class ExecuteSQLCommands(CommandBase): 65 | extension = ".sql" 66 | 67 | def Process(self, processor): 68 | connection = processor.connection 69 | cursor = connection.cursor() 70 | cursor.execute("select user from dual") 71 | user, = cursor.fetchone() 72 | parser = cx_OracleParser.SimpleParser() 73 | sql = open(self.fileName).read() 74 | connectStatementClass = parser.parser.processor.ConnectStatement 75 | try: 76 | for statement in parser.IterParse(sql, user): 77 | if isinstance(statement, connectStatementClass): 78 | connection = cx_OracleEx.Connection(statement.user, 79 | statement.password or connection.password, 80 | statement.dsn or connection.dsn) 81 | cursor = connection.cursor() 82 | cx_Logging.Trace("%s", statement.GetLogMessage(cursor)) 83 | parser.parser.processor.owner = statement.user 84 | else: 85 | try: 86 | statement.Process(cursor) 87 | except cx_Exceptions.BaseException as error: 88 | lineNumber = statement.lineNumber 89 | if isinstance(error, cx_OracleEx.DatabaseException) \ 90 | and error.dbErrorOffset is not None: 91 | offset = error.dbErrorOffset 92 | lineNumber += statement.sql[:offset].count("\n") 93 | cx_Logging.Error("Error at line %s", lineNumber) 94 | if not processor.onErrorContinue: 95 | raise 96 | cx_Logging.Error("%s", error.message) 97 | except cx_OracleParser.ParsingFailed as value: 98 | cx_Logging.Error("Parsing failed at line %s (%s...)", 99 | value.arguments["lineNumber"], 100 | value.arguments["remainingString"][:100]) 101 | 102 | -------------------------------------------------------------------------------- /cx_OracleDebugger.py: -------------------------------------------------------------------------------- 1 | """Return messages from a DBMS pipe.""" 2 | 3 | import cx_Exceptions 4 | import cx_Logging 5 | import cx_Oracle 6 | 7 | class CannotReceiveMessage(cx_Exceptions.BaseException): 8 | message = "Unable to receive message: return value is %(returnValue)d" 9 | 10 | 11 | class UnhandledProtocol(cx_Exceptions.BaseException): 12 | message = 'Unhandled protocol "%(protocol)s"' 13 | 14 | 15 | class UnhandledService(cx_Exceptions.BaseException): 16 | message = 'Unhandled service "%(serviceName)s"' 17 | 18 | 19 | def Enable(connection, pipeName = None): 20 | """Enable debugging messages on the given pipe.""" 21 | cursor = connection.cursor() 22 | if pipeName is None: 23 | pipeName = cursor.callfunc("dbms_pipe.unique_session_name", 24 | cx_Oracle.STRING) 25 | cursor.callproc("pkg_Debug.Enable", (pipeName,)) 26 | cx_Logging.Debug("logging messages to pipe %s", pipeName) 27 | return pipeName 28 | 29 | 30 | def LogMessages(connection, pipeName): 31 | """Log messages using the cx_Logging module.""" 32 | cx_Logging.Debug("logging messages from pipe %s", pipeName) 33 | debugConnection = cx_Oracle.Connection(connection.username, 34 | connection.password, connection.tnsentry, threaded = True) 35 | for message in MessageGenerator(debugConnection, pipeName): 36 | cx_Logging.Trace("%s", message) 37 | 38 | 39 | def MessageGenerator(connection, pipeName): 40 | """Generator function which returns messages as they are received and 41 | terminates only when a shutdown request is received.""" 42 | 43 | # prepare a cursor for acquiring the service 44 | serviceCursor = connection.cursor() 45 | serviceCursor.prepare(""" 46 | begin 47 | :returnValue := dbms_pipe.receive_message(:pipeName); 48 | dbms_pipe.unpack_message(:protocol); 49 | dbms_pipe.unpack_message(:returnPipeName); 50 | dbms_pipe.unpack_message(:serviceName); 51 | end;""") 52 | serviceBindVars = serviceCursor.setinputsizes( 53 | pipeName = cx_Oracle.STRING, 54 | returnValue = cx_Oracle.NUMBER, 55 | protocol = cx_Oracle.STRING, 56 | returnPipeName = cx_Oracle.STRING, 57 | serviceName = cx_Oracle.STRING) 58 | pipeNameVar = serviceBindVars["pipeName"] 59 | pipeNameVar.setvalue(0, pipeName) 60 | returnValueVar = serviceBindVars["returnValue"] 61 | protocolVar = serviceBindVars["protocol"] 62 | serviceNameVar = serviceBindVars["serviceName"] 63 | 64 | # prepare a cursor for logging messages 65 | loggingCursor = connection.cursor() 66 | loggingCursor.prepare(""" 67 | begin 68 | dbms_pipe.unpack_message(:lastPartOfMessage); 69 | dbms_pipe.unpack_message(:message); 70 | end;""") 71 | loggingBindVars = loggingCursor.setinputsizes( 72 | lastPartOfMessage = cx_Oracle.NUMBER, 73 | message = cx_Oracle.STRING) 74 | lastPartOfMessageVar = loggingBindVars["lastPartOfMessage"] 75 | messageVar = loggingBindVars["message"] 76 | 77 | # process logging requests until a shutdown request is found 78 | messageParts = [] 79 | while True: 80 | serviceCursor.execute(None) 81 | returnValue = returnValueVar.getvalue() 82 | if returnValue: 83 | raise CannotReceiveMessage(returnValue = returnValue) 84 | serviceName = serviceNameVar.getvalue() 85 | if serviceName == "Shutdown": 86 | yield "*** TERMINATING ***" 87 | break 88 | if serviceName != "LogMessage": 89 | raise UnhandledService(serviceName = serviceName) 90 | protocol = protocolVar.getvalue() 91 | if protocol != "2": 92 | raise UnhandledProtocol(protocol = protocol) 93 | loggingCursor.execute(None) 94 | part = messageVar.getvalue() 95 | if part is not None: 96 | messageParts.append(part) 97 | if lastPartOfMessageVar.getvalue(): 98 | yield "".join(messageParts) 99 | messageParts = [] 100 | 101 | 102 | def Shutdown(connection, pipeName): 103 | """Shutdown debugging messages on the given pipe.""" 104 | cursor = connection.cursor() 105 | cursor.callproc("pkg_Debug.Shutdown", (pipeName,)) 106 | 107 | -------------------------------------------------------------------------------- /cx_OracleParser/simple/Statements.py: -------------------------------------------------------------------------------- 1 | """Module defining statements supported by parser.""" 2 | 3 | import cx_Logging 4 | import cx_OracleUtils 5 | 6 | class Statement(object): 7 | message = None 8 | 9 | def __init__(self, sql, lineNumber): 10 | self.sql = sql 11 | self.lineNumber = lineNumber 12 | self.terminator = ";\n\n" 13 | 14 | def __repr__(self): 15 | return "<%s>" % self.__class__.__name__ 16 | 17 | def Execute(self, cursor): 18 | cursor.execute(self.sql) 19 | 20 | def GetLogMessage(self, cursor): 21 | return self.message 22 | 23 | def Process(self, cursor): 24 | self.Execute(cursor) 25 | message = self.GetLogMessage(cursor) 26 | if message is not None: 27 | cx_Logging.Trace("%s", message) 28 | 29 | def Sql(self): 30 | return self.sql + self.terminator 31 | 32 | 33 | class ObjectStatement(Statement): 34 | 35 | def __init__(self, sql, lineNumber, type, name, owner = None): 36 | super(ObjectStatement, self).__init__(sql, lineNumber) 37 | self.type = type 38 | self.name = name 39 | self.owner = owner 40 | 41 | def __repr__(self): 42 | if self.owner is None: 43 | return "<%s %s (%s)>" % \ 44 | (self.__class__.__name__, self.name, self.type.upper()) 45 | return "<%s %s.%s (%s)>" % \ 46 | (self.__class__.__name__, self.owner, self.name, 47 | self.type.upper()) 48 | 49 | def GetLogMessage(self, cursor): 50 | if self.owner is None or self.type in ("user", "public synonym"): 51 | return "%s %s %s." % \ 52 | (self.type.capitalize(), self.name, self.action) 53 | return "%s %s.%s %s." % \ 54 | (self.type.capitalize(), self.owner, self.name, self.action) 55 | 56 | 57 | class DMLStatement(ObjectStatement): 58 | 59 | def __init__(self, sql, lineNumber, owner, name): 60 | super(ObjectStatement, self).__init__(sql, lineNumber) 61 | self.owner = owner 62 | self.name = name 63 | 64 | def __repr__(self): 65 | return "<%s %s.%s>" % (self.__class__.__name__, self.owner, self.name) 66 | 67 | def GetLogMessage(self, cursor): 68 | rowsAffected = cursor.rowcount 69 | modifier = "row" 70 | if rowsAffected != 1: 71 | modifier = "rows" 72 | if self.owner is None and self.name is None: 73 | objectName = "anonymous view" 74 | else: 75 | objectName = "%s.%s" % (self.owner, self.name) 76 | return "%s %s %s in %s." % \ 77 | (self.action.capitalize(), rowsAffected, modifier, objectName) 78 | 79 | 80 | class AlterObjectStatement(ObjectStatement): 81 | action = "altered" 82 | 83 | 84 | class AnonymousPlsqlBlock(Statement): 85 | message = "PL/SQL procedure successfully completed." 86 | 87 | 88 | class CommentStatement(Statement): 89 | message = "Comment created." 90 | 91 | 92 | class CommitStatement(Statement): 93 | message = "Commit point reached." 94 | 95 | def Execute(self, cursor): 96 | cursor.connection.commit() 97 | 98 | 99 | class ConnectStatement(Statement): 100 | 101 | def __init__(self, sql, lineNumber, user, password = None, dsn = None): 102 | super(ConnectStatement, self).__init__(sql, lineNumber) 103 | self.user = user 104 | self.password = password 105 | self.dsn = dsn 106 | 107 | def GetLogMessage(self, cursor): 108 | return "Connected to %s" % self.user 109 | 110 | 111 | class CreateObjectStatement(ObjectStatement): 112 | action = "created" 113 | 114 | def __init__(self, *args, **kwargs): 115 | super(CreateObjectStatement, self).__init__(*args, **kwargs) 116 | if self.type in ("package", "package body", "trigger", "type"): 117 | self.terminator = "\n/\n\n" 118 | 119 | def Execute(self, cursor): 120 | sql = self.sql 121 | lines = sql.splitlines() 122 | if lines[0].endswith("wrapped"): 123 | sql = sql + "\n" 124 | cursor.execute(sql) 125 | cursor = cx_OracleUtils.PrepareErrorsCursor(cursor.connection) 126 | cx_OracleUtils.CheckForErrors(cursor, self.owner, self.name, 127 | self.type.upper(), self.action + " with", self.lineNumber - 1) 128 | 129 | 130 | class CreateConstraintStatement(CreateObjectStatement): 131 | 132 | def __init__(self, sql, lineNumber, type, owner, name, tableName): 133 | super(CreateConstraintStatement, self).__init__(sql, lineNumber, type, 134 | name, owner) 135 | self.tableName = tableName 136 | 137 | 138 | class DropObjectStatement(ObjectStatement): 139 | action = "dropped" 140 | 141 | 142 | class DeleteStatement(DMLStatement): 143 | action = "deleted" 144 | 145 | 146 | class GrantStatement(Statement): 147 | message = "Privilege(s) granted." 148 | 149 | 150 | class InsertStatement(DMLStatement): 151 | action = "inserted" 152 | 153 | 154 | class RenameObjectStatement(ObjectStatement): 155 | action = "renamed" 156 | 157 | def __init__(self, sql, lineNumber, name): 158 | super(RenameObjectStatement, self).__init__(sql, lineNumber, "object", 159 | name) 160 | 161 | 162 | class RevokeStatement(Statement): 163 | message = "Privilege(s) revoked." 164 | 165 | 166 | class RollbackStatement(Statement): 167 | message = "Rolled back." 168 | 169 | def Execute(self, cursor): 170 | cursor.connection.rollback() 171 | 172 | 173 | class TruncateObjectStatement(ObjectStatement): 174 | action = "truncated" 175 | 176 | def __init__(self, sql, lineNumber, owner, name): 177 | super(TruncateObjectStatement, self).__init__(sql, lineNumber, "table", 178 | name, owner) 179 | 180 | 181 | class UpdateStatement(DMLStatement): 182 | action = "Updated" 183 | 184 | -------------------------------------------------------------------------------- /cx_OracleParser/simple/Grammar.py: -------------------------------------------------------------------------------- 1 | """Grammar for parsing SQL as defined by Oracle.""" 2 | 3 | GRAMMAR = """ 4 | 5 | # tokens 6 | := '--' 7 | := [ \t\r\n] / comment 8 | := "'" 9 | := '"' 10 | := '/*' 11 | := '*/' 12 | := '\r\n' / '\n' / '\r' 13 | := ';' 14 | := [+-] 15 | := [0-9] 16 | := [Ee] 17 | := [A-Za-z] 18 | := '.' 19 | := ',' 20 | := '/' 21 | := '@' 22 | := [-A-Za-z0-9/*+=,.|()<>_:!%"@$#] 23 | := literal / CHAR+ / WS+ 24 | := '(' 25 | 26 | # object type keywords 27 | KW_bitmap := c"bitmap" 28 | KW_body := c"body" 29 | KW_check := c"check" 30 | KW_context := c"context" 31 | KW_foreign := c"foreign" 32 | KW_function := c"function" 33 | KW_global := c"global" 34 | KW_index := c"index" 35 | KW_key := c"key" 36 | KW_package := c"package" 37 | KW_primary := c"primary" 38 | KW_procedure := c"procedure" 39 | KW_public := c"public" 40 | KW_role := c"role" 41 | KW_sequence := c"sequence" 42 | KW_synonym := c"synonym" 43 | KW_table := c"table" 44 | KW_temporary := c"temporary" 45 | KW_trigger := c"trigger" 46 | KW_type := c"type" 47 | KW_unique := c"unique" 48 | KW_user := c"user" 49 | KW_view := c"view" 50 | KW_wrapped := c"wrapped" 51 | 52 | # keywords 53 | := c"add" 54 | := c"alter" 55 | := c"as" 56 | := c"begin" 57 | := c"comment" 58 | := c"commit" 59 | := c"connect" 60 | := c"constraint" 61 | := c"create" 62 | := c"declare" 63 | := c"delete" 64 | := c"drop" 65 | := c"from" 66 | := c"grant" 67 | := c"into" 68 | := c"insert" 69 | := c"or" 70 | := c"rename" 71 | := c"replace" 72 | := c"revoke" 73 | := c"rollback" 74 | := c"truncate" 75 | := c"update" 76 | 77 | # comments 78 | dash_comment := DASHES, -CR*, CR 79 | slash_comment := COMMENT_START, -COMMENT_END*, COMMENT_END 80 | comment := (dash_comment / slash_comment) 81 | 82 | # literals 83 | string_literal := (STRING_DELIM, -STRING_DELIM*, STRING_DELIM)+ 84 | integer_literal := SIGN?, DIGIT+ 85 | float_literal := SIGN?, DIGIT*, PERIOD?, DIGIT+, (EXPONENT, SIGN?, DIGIT+)? 86 | literal := (string_literal / float_literal / integer_literal) 87 | 88 | # identifiers 89 | unquoted_identifier := LETTER, [a-zA-Z0-9_$#-]* 90 | quoted_identifier := NAME_DELIM, [a-zA-Z0-9_$#.]+, NAME_DELIM 91 | >identifier< := quoted_identifier / unquoted_identifier 92 | qualified_identifier := identifier, (PERIOD, identifier)? 93 | 94 | # common clauses 95 | := token*, SEMICOLON 96 | := CR, WS*, SLASH, (?-CR, WS)*, CR 97 | := (?-complex_statement_terminator, 98 | (token / SEMICOLON))*, complex_statement_terminator 99 | 100 | # object types 101 | >index_type< := ((KW_bitmap / KW_unique), WS+)?, KW_index 102 | >synonym_type< := (KW_public, WS+)?, KW_synonym 103 | >table_type< := (KW_global, WS+, KW_temporary, WS+)?, KW_table 104 | simple_object_type := KW_context / index_type / KW_role / KW_sequence / 105 | synonym_type / table_type / KW_user / KW_view 106 | complex_object_type := KW_function / (KW_package, (WS+, KW_body)?) / 107 | KW_procedure / KW_trigger / (KW_type, (WS+, KW_body)?) 108 | constraint_type := KW_check / (KW_foreign, WS+, KW_key) / 109 | (KW_primary, WS+, KW_key) / KW_unique 110 | >object_type< := simple_object_type / complex_object_type 111 | 112 | # statements 113 | anonymous_plsql_block := (KW_declare / KW_begin), complex_statement_ender 114 | comment_statement := KW_comment, simple_statement_ender 115 | commit_statement := KW_commit, simple_statement_ender 116 | connect_statement := KW_connect, WS+, identifier, 117 | (SLASH, identifier, (AT, identifier)?)? 118 | create_constraint_statement := KW_alter, WS+, KW_table, WS+, 119 | qualified_identifier, WS+, KW_add, WS+, KW_constraint, WS+, 120 | identifier, WS+, constraint_type, simple_statement_ender 121 | create_object_statement := KW_create, WS+, (KW_or, WS+, KW_replace, WS+)?, 122 | (simple_object_type, WS+, qualified_identifier, simple_statement_ender) / 123 | (complex_object_type, WS+, qualified_identifier, complex_statement_ender) 124 | alter_object_statement := KW_alter, WS+, object_type, WS+, 125 | qualified_identifier, simple_statement_ender 126 | delete_statement := KW_delete, WS+, (KW_from, WS+)?, qualified_identifier, 127 | simple_statement_ender 128 | drop_object_statement := KW_drop, WS+, object_type, WS+, 129 | qualified_identifier, simple_statement_ender 130 | grant_statement := KW_grant, simple_statement_ender 131 | insert_statement := KW_insert, WS+, KW_into, WS+, qualified_identifier, 132 | simple_statement_ender 133 | rename_statement := KW_rename, WS+, identifier, simple_statement_ender 134 | revoke_statement := KW_revoke, simple_statement_ender 135 | rollback_statement := KW_rollback, simple_statement_ender 136 | truncate_statement := KW_truncate, WS+, KW_table, WS+, 137 | qualified_identifier, simple_statement_ender 138 | update_statement := KW_update, WS+, (qualified_identifier / LPAREN), 139 | simple_statement_ender 140 | 141 | # all possible statements 142 | >sql_statement< := create_object_statement / create_constraint_statement / 143 | alter_object_statement / drop_object_statement / comment_statement / 144 | commit_statement / rollback_statement / grant_statement / 145 | revoke_statement / connect_statement / insert_statement / 146 | update_statement / delete_statement / rename_statement / 147 | truncate_statement / anonymous_plsql_block 148 | 149 | statement := WS*, sql_statement, WS* 150 | file := (WS*, sql_statement)*, WS* 151 | 152 | """ 153 | 154 | -------------------------------------------------------------------------------- /cx_OracleObject/Statements.py: -------------------------------------------------------------------------------- 1 | """Define statements for retrieving the data for each of the types.""" 2 | 3 | CONSTRAINTS = """ 4 | select 5 | o.owner, 6 | o.constraint_name, 7 | o.constraint_type, 8 | o.table_name, 9 | o.search_condition, 10 | o.r_owner, 11 | o.r_constraint_name, 12 | o.delete_rule, 13 | o.deferred, 14 | o.deferrable 15 | from %(p_ViewPrefix)s_constraints o 16 | %(p_WhereClause)s 17 | and exists 18 | ( select 1 19 | from %(p_ViewPrefix)s_tables 20 | where owner = o.owner 21 | and table_name = o.table_name 22 | ) 23 | and (o.generated = 'USER NAME' or o.constraint_type in ('P', 'U')) 24 | order by decode(o.constraint_type, 'P', 1, 'U', 2, 'R', 3, 'C', 4), 25 | o.owner, o.constraint_name""" 26 | 27 | CONTEXTS = """ 28 | select 29 | namespace, 30 | schema, 31 | package, 32 | type 33 | from dba_context o 34 | %(p_WhereClause)s 35 | order by namespace""" 36 | 37 | INDEXES_ANY = """ 38 | select 39 | o.owner, 40 | o.index_name, 41 | o.table_name, 42 | o.tablespace_name, 43 | o.uniqueness, 44 | o.initial_extent, 45 | o.next_extent, 46 | o.min_extents, 47 | o.max_extents, 48 | o.pct_increase, 49 | o.index_type, 50 | o.partitioned, 51 | o.temporary, 52 | o.compression, 53 | o.prefix_length, 54 | o.ityp_owner, 55 | o.ityp_name, 56 | o.parameters 57 | from %(p_ViewPrefix)s_indexes o 58 | %(p_WhereClause)s 59 | and o.index_type in ('NORMAL', 'NORMAL/REV', 'IOT - TOP', 'BITMAP', 60 | 'FUNCTION-BASED NORMAL', 'FUNCTION-BASED NORMAL/REV', 61 | 'DOMAIN')""" 62 | 63 | INDEXES = INDEXES_ANY + """ 64 | and not exists 65 | ( select 1 66 | from %(p_ViewPrefix)s_constraints 67 | where owner = o.owner 68 | and table_name = o.table_name 69 | and constraint_name = o.index_name 70 | ) 71 | order by o.owner, o.index_name""" 72 | 73 | INDEX_PARTITIONS = """ 74 | select 75 | o.index_owner, 76 | o.partition_name, 77 | o.high_value, 78 | o.tablespace_name, 79 | o.initial_extent, 80 | o.next_extent, 81 | o.min_extent, 82 | o.max_extent, 83 | o.pct_increase 84 | from %(p_ViewPrefix)s_ind_partitions o 85 | %(p_WhereClause)s 86 | order by o.partition_position""" 87 | 88 | LIBRARIES = """ 89 | select 90 | o.owner, 91 | o.library_name, 92 | o.file_spec 93 | from %(p_ViewPrefix)s_libraries o 94 | %(p_WhereClause)s 95 | order by o.owner, o.library_name""" 96 | 97 | LOBS = """ 98 | select 99 | o.owner, 100 | o.column_name, 101 | o.table_name, 102 | o.segment_name, 103 | o.in_row 104 | from %(p_ViewPrefix)s_lobs o 105 | %(p_WhereClause)s 106 | order by o.column_name""" 107 | 108 | ROLES = """ 109 | select 110 | o.role, 111 | o.password_required 112 | from dba_roles o 113 | %(p_WhereClause)s 114 | order by o.role""" 115 | 116 | SEQUENCES = """ 117 | select 118 | o.sequence_owner, 119 | o.sequence_name, 120 | to_char(min_value), 121 | to_char(max_value), 122 | to_char(increment_by), 123 | cycle_flag, 124 | order_flag, 125 | to_char(cache_size), 126 | to_char(last_number) 127 | from %(p_ViewPrefix)s_sequences o 128 | %(p_WhereClause)s 129 | order by o.sequence_owner, o.sequence_name""" 130 | 131 | SYNONYMS = """ 132 | select 133 | o.owner, 134 | o.synonym_name, 135 | o.table_owner, 136 | o.table_name, 137 | o.db_link 138 | from %(p_ViewPrefix)s_synonyms o 139 | %(p_WhereClause)s 140 | order by decode(o.owner, 'PUBLIC', 0, 1), o.owner, o.synonym_name""" 141 | 142 | TABLES = """ 143 | select 144 | o.owner, 145 | o.table_name, 146 | o.tablespace_name, 147 | o.initial_extent, 148 | o.next_extent, 149 | o.min_extents, 150 | o.max_extents, 151 | o.pct_increase, 152 | o.temporary, 153 | o.partitioned, 154 | o.duration, 155 | o.iot_type 156 | from %(p_ViewPrefix)s_tables o 157 | %(p_WhereClause)s 158 | and secondary = 'N' 159 | order by o.owner, o.table_name""" 160 | 161 | TABLE_PARTITIONS = """ 162 | select 163 | o.table_owner, 164 | o.partition_name, 165 | o.high_value, 166 | o.tablespace_name, 167 | o.initial_extent, 168 | o.next_extent, 169 | o.min_extent, 170 | o.max_extent, 171 | o.pct_increase 172 | from %(p_ViewPrefix)s_tab_partitions o 173 | %(p_WhereClause)s 174 | order by o.partition_position""" 175 | 176 | TRIGGERS = """ 177 | select 178 | o.owner, 179 | o.trigger_name, 180 | o.table_name, 181 | o.description, 182 | o.when_clause, 183 | o.action_type, 184 | o.trigger_body 185 | from %(p_ViewPrefix)s_triggers o 186 | %(p_WhereClause)s 187 | order by o.owner, o.trigger_name""" 188 | 189 | USERS = """ 190 | select 191 | o.username, 192 | o.default_tablespace, 193 | o.temporary_tablespace 194 | from dba_users o 195 | %(p_WhereClause)s 196 | order by o.username""" 197 | 198 | VIEWS = """ 199 | select 200 | o.owner, 201 | o.view_name, 202 | o.text 203 | from %(p_ViewPrefix)s_views o 204 | %(p_WhereClause)s 205 | order by o.owner, o.view_name""" 206 | 207 | -------------------------------------------------------------------------------- /cx_OracleParser/simple/Processor.py: -------------------------------------------------------------------------------- 1 | """Module for processing dependencies in SQL.""" 2 | 3 | import cx_Parser 4 | 5 | from . import Statements 6 | 7 | __all__ = [ "Processor" ] 8 | 9 | class Processor(cx_Parser.DispatchProcessor): 10 | AlterObjectStatement = Statements.AlterObjectStatement 11 | AnonymousPlsqlBlock = Statements.AnonymousPlsqlBlock 12 | CommentStatement = Statements.CommentStatement 13 | CommitStatement = Statements.CommitStatement 14 | ConnectStatement = Statements.ConnectStatement 15 | CreateConstraintStatement = Statements.CreateConstraintStatement 16 | CreateObjectStatement = Statements.CreateObjectStatement 17 | DeleteStatement = Statements.DeleteStatement 18 | DropObjectStatement = Statements.DropObjectStatement 19 | GrantStatement = Statements.GrantStatement 20 | InsertStatement = Statements.InsertStatement 21 | RenameObjectStatement = Statements.RenameObjectStatement 22 | RevokeStatement = Statements.RevokeStatement 23 | RollbackStatement = Statements.RollbackStatement 24 | TruncateObjectStatement = Statements.TruncateObjectStatement 25 | UpdateStatement = Statements.UpdateStatement 26 | 27 | def __init__(self, initialOwner = None): 28 | self.owner = initialOwner 29 | 30 | def _merge_keywords(self, children): 31 | keywords = [t[3:] for t, st, e, c in children] 32 | return " ".join(keywords) 33 | 34 | def _statement_obj(self, cls, sql, start, end, *args): 35 | lineNumber = sql[:start].count("\n") + 1 36 | sql = sql[start:end - 1].strip() 37 | if sql.endswith("/"): 38 | sql = sql[:-1].strip() 39 | return cls(sql, lineNumber, *args) 40 | 41 | def anonymous_plsql_block(self, sql, tag, start, end, children): 42 | return self._statement_obj(self.AnonymousPlsqlBlock, sql, start, end) 43 | 44 | def comment_statement(self, sql, tag, start, end, children): 45 | return self._statement_obj(self.CommentStatement, sql, start, end) 46 | 47 | def commit_statement(self, sql, tag, start, end, children): 48 | return self._statement_obj(self.CommitStatement, sql, start, end) 49 | 50 | def complex_object_type(self, sql, tag, start, end, children): 51 | return self._merge_keywords(children) 52 | 53 | def connect_statement(self, sql, tag, start, end, children): 54 | parts = self.DispatchList(sql, children) 55 | return self._statement_obj(self.ConnectStatement, sql, start, end, 56 | *parts) 57 | 58 | def constraint_type(self, sql, tag, start, end, children): 59 | return self._merge_keywords(children) 60 | 61 | def create_constraint_statement(self, sql, tag, start, end, children): 62 | owner, tableName = self.Dispatch(sql, children[1]) 63 | name = self.Dispatch(sql, children[2]) 64 | type = self.Dispatch(sql, children[3]) 65 | if " " not in type: 66 | type += " constraint" 67 | return self._statement_obj(self.CreateConstraintStatement, sql, start, 68 | end, type, owner, name, tableName) 69 | 70 | def alter_object_statement(self, sql, tag, start, end, children): 71 | type = self.Dispatch(sql, children[0]) 72 | owner, name = self.Dispatch(sql, children[1]) 73 | return self._statement_obj(self.AlterObjectStatement, sql, start, end, 74 | type, name, owner) 75 | 76 | def create_object_statement(self, sql, tag, start, end, children): 77 | type = self.Dispatch(sql, children[0]) 78 | owner, name = self.Dispatch(sql, children[1]) 79 | return self._statement_obj(self.CreateObjectStatement, sql, start, end, 80 | type, name, owner) 81 | 82 | def delete_statement(self, sql, tag, start, end, children): 83 | owner, name = self.Dispatch(sql, children[0]) 84 | return self._statement_obj(self.DeleteStatement, sql, start, end, 85 | owner, name) 86 | 87 | def drop_object_statement(self, sql, tag, start, end, children): 88 | type = self.Dispatch(sql, children[0]) 89 | owner, name = self.Dispatch(sql, children[1]) 90 | return self._statement_obj(self.DropObjectStatement, sql, start, end, 91 | type, name, owner) 92 | 93 | def grant_statement(self, sql, tag, start, end, children): 94 | return self._statement_obj(self.GrantStatement, sql, start, end) 95 | 96 | def insert_statement(self, sql, tag, start, end, children): 97 | owner, name = self.Dispatch(sql, children[0]) 98 | return self._statement_obj(self.InsertStatement, sql, start, end, 99 | owner, name) 100 | 101 | def qualified_identifier(self, sql, tag, start, end, children): 102 | identifiers = self.DispatchList(sql, children) 103 | if len(identifiers) == 2: 104 | owner, name = identifiers 105 | else: 106 | owner = self.owner 107 | name, = identifiers 108 | return (owner, name) 109 | 110 | def quoted_identifier(self, sql, tag, start, end, children): 111 | return sql[start + 1:end - 1] 112 | 113 | def rename_statement(self, sql, tag, start, end, children): 114 | name = self.Dispatch(sql, children[0]) 115 | return self._statement_obj(self.RenameObjectStatement, sql, start, end, 116 | name) 117 | 118 | def revoke_statement(self, sql, tag, start, end, children): 119 | return self._statement_obj(self.RevokeStatement, sql, start, end) 120 | 121 | def rollback_statement(self, sql, tag, start, end, children): 122 | return self._statement_obj(self.RollbackStatement, sql, start, end) 123 | 124 | def simple_object_type(self, sql, tag, start, end, children): 125 | return self._merge_keywords(children) 126 | 127 | def truncate_statement(self, sql, tag, start, end, children): 128 | owner, name = self.Dispatch(sql, children[1]) 129 | return self._statement_obj(self.TruncateObjectStatement, sql, start, 130 | end, owner, name) 131 | 132 | def unquoted_identifier(self, sql, tag, start, end, children): 133 | return sql[start:end].upper() 134 | 135 | def update_statement(self, sql, tag, start, end, children): 136 | if len(children) > 0: 137 | owner, name = self.Dispatch(sql, children[0]) 138 | else: 139 | owner = name = None 140 | return self._statement_obj(self.UpdateStatement, sql, start, end, 141 | owner, name) 142 | 143 | -------------------------------------------------------------------------------- /cx_OracleObject/Utils.py: -------------------------------------------------------------------------------- 1 | """Defines utility functions.""" 2 | 3 | import cx_Exceptions 4 | import sys 5 | 6 | __all__ = [ "OrderObjects" ] 7 | 8 | def ClausesForOutput(clauses, firstString, restString, joinString): 9 | """Return a list of clauses suitable for output in a SQL statement.""" 10 | if not clauses: 11 | return "" 12 | joinString = joinString + "\n" + restString 13 | return firstString + joinString.join(clauses) 14 | 15 | def DependenciesOfInterest(key, objectsOfInterest, dependencies, 16 | dependenciesOfInterest): 17 | """Return a list of dependencies on objects of interest.""" 18 | if key in dependencies: 19 | for refKey in dependencies[key]: 20 | if refKey in objectsOfInterest: 21 | dependenciesOfInterest[refKey] = None 22 | else: 23 | DependenciesOfInterest(refKey, objectsOfInterest, dependencies, 24 | dependenciesOfInterest) 25 | 26 | def OrderObjects(objects, dependencies): 27 | """Put the objects in the order necessary for creation without errors.""" 28 | 29 | # initialize the mapping that indicates which items this object depends on 30 | iDependOn = {} 31 | dependsOnMe = {} 32 | for key in objects: 33 | iDependOn[key] = {} 34 | dependsOnMe[key] = {} 35 | 36 | # populate a mapping which indicates all of the dependencies for an object 37 | mappedDependencies = {} 38 | for owner, name, type, refOwner, refName, refType in dependencies: 39 | key = (owner, name, type) 40 | refKey = (refOwner, refName, refType) 41 | subDict = mappedDependencies.get(key) 42 | if subDict is None: 43 | subDict = mappedDependencies[key] = {} 44 | subDict[refKey] = None 45 | 46 | # now populate the mapping that indicates which items this object depends on 47 | # note that an object may depend on an object which is not in the list of 48 | # interest, but it itself depends on an object which is in the list so the 49 | # chain of dependencies is traversed until no objects of interest are found 50 | for key in iDependOn: 51 | refKeys = {} 52 | DependenciesOfInterest(key, iDependOn, mappedDependencies, refKeys) 53 | for refKey in refKeys: 54 | iDependOn[key][refKey] = None 55 | dependsOnMe[refKey][key] = None 56 | 57 | # order the items until no more items left 58 | outputObjs = {} 59 | orderedObjs = [] 60 | while iDependOn: 61 | 62 | # acquire a list of items which do not depend on anything 63 | references = {} 64 | keysToOutput = {} 65 | for key, value in list(iDependOn.items()): 66 | if not value: 67 | owner, name, type = key 68 | if owner not in keysToOutput: 69 | keysToOutput[owner] = [] 70 | keysToOutput[owner].append(key) 71 | del iDependOn[key] 72 | else: 73 | for refKey in value: 74 | owner, name, type = refKey 75 | if owner not in references: 76 | references[owner] = 0 77 | references[owner] += 1 78 | 79 | # detect a circular reference and avoid an infinite loop 80 | if not keysToOutput: 81 | keys = list(iDependOn.keys()) 82 | keys.sort() 83 | for key in keys: 84 | print("%s.%s (%s)" % key, file = sys.stderr) 85 | refKeys = list(iDependOn[key].keys()) 86 | refKeys.sort() 87 | for refKey in refKeys: 88 | print(" %s.%s (%s)" % refKey, file = sys.stderr) 89 | raise CircularReferenceDetected() 90 | 91 | # for each owner that has something to describe 92 | while keysToOutput: 93 | 94 | # determine the owner with the most references 95 | outputOwner = "" 96 | maxReferences = 0 97 | keys = list(references.keys()) 98 | keys.sort() 99 | for key in keys: 100 | value = references[key] 101 | if value > maxReferences and key in keysToOutput: 102 | maxReferences = value 103 | outputOwner = key 104 | if not outputOwner: 105 | for key in keysToOutput: 106 | outputOwner = key 107 | break 108 | 109 | # remove this owner from the list 110 | keys = keysToOutput[outputOwner] 111 | del keysToOutput[outputOwner] 112 | if outputOwner in references: 113 | del references[outputOwner] 114 | 115 | # process this list, removing dependencies and adding additional 116 | # objects 117 | tempKeys = keys 118 | keys = [] 119 | while tempKeys: 120 | nextKeys = [] 121 | tempKeys.sort() 122 | for key in tempKeys: 123 | refKeys = list(dependsOnMe[key].keys()) 124 | refKeys.sort() 125 | for refKey in dependsOnMe[key]: 126 | del iDependOn[refKey][key] 127 | if not iDependOn[refKey]: 128 | owner, name, type = refKey 129 | if owner == outputOwner: 130 | del iDependOn[refKey] 131 | nextKeys.append(refKey) 132 | elif owner in keysToOutput: 133 | del iDependOn[refKey] 134 | keysToOutput[owner].append(refKey) 135 | keys += tempKeys 136 | tempKeys = nextKeys 137 | 138 | # output the list of objects that have their dependencies satisfied 139 | for key in keys: 140 | if key not in outputObjs: 141 | orderedObjs.append(key) 142 | outputObjs[key] = None 143 | 144 | # return the ordered list 145 | return orderedObjs 146 | 147 | def SetOptions(obj, options): 148 | """Set values from the options on the command line.""" 149 | if options: 150 | for attribute in dir(options): 151 | if attribute.startswith("_"): 152 | continue 153 | if hasattr(obj, attribute): 154 | value = getattr(options, attribute) 155 | if isinstance(value, list): 156 | value = [s for v in value for s in v.split(",")] 157 | setattr(obj, attribute, value) 158 | 159 | def SizeForOutput(size): 160 | """Return the size suitable for output in a SQL statement. Note that a 161 | negative size is assumed to be unlimited.""" 162 | if size < 0: 163 | return "unlimited" 164 | kilobytes, remainder = divmod(size, 1024) 165 | if not remainder: 166 | megabytes, remainder = divmod(kilobytes, 1024) 167 | if not remainder: 168 | return "%gm" % megabytes 169 | else: 170 | return "%gk" % kilobytes 171 | else: 172 | return "%g" % size 173 | 174 | 175 | class CircularReferenceDetected(cx_Exceptions.BaseException): 176 | message = "Circular reference detected!" 177 | 178 | -------------------------------------------------------------------------------- /HISTORY.txt: -------------------------------------------------------------------------------- 1 | Changes from 2.5 to 3.0 2 | 1) Added support for Python 3. 3 | 2) Added support for cast expressions as requested by Alex Vanderwoude. 4 | 3) Added support for index organized tables. 5 | 4) Added the characters $ and # to the list of valid characters since they are 6 | part of identifiers which may be found in PL/SQL code. 7 | 5) Added support for defining the terminator that is used for completing a 8 | statement so that the SQL can be written back out again if desired. Thanks 9 | to Micah Friesen for the patch. 10 | 6) Added support for wapped packages which aren't quite as nicely formed as 11 | their source equivalents -- in particular the semicolon is not present. 12 | 7) Added support for updating anonymous views. 13 | 8) Added support for the cache clause of the create sequence statement as 14 | requested by Micah Friesen. 15 | 9) Allow nothing to follow the dashes that constitute a line comment; thanks to 16 | Micah Friesen for discovering this. 17 | 10) Added support for NCLOB type columns. 18 | 11) Ensure that users and public synonyms do not have an owner added to them 19 | since they do not have owners. 20 | 12) As requested by Robert Ritchie, show the row that failed instead of having 21 | to work your way through the list of all rows which could be considerable. 22 | 13) Added support for a range clause as requested by Micah Friesen. 23 | 14) Added support for columns declared with the syntax varchar(6 char) instead 24 | of varchar2(6). 25 | 26 | Changes from 2.4 to 2.5 27 | 1) Use exception classes rather than string exceptions everywhere. 28 | 2) Added support for specifying "as sysdba" and "as sysoper" in the connection 29 | string and set the necessary privileges when connecting. 30 | 3) Added support for getting constant representations for binary data, CLOB 31 | and BLOB values. 32 | 4) Added support for Oracle context objects. 33 | 5) Fixed support for interval data types. 34 | 6) In cx_OracleParser, added a simple SQL parser and used that to replace the 35 | module cx_SQL. 36 | 7) In cx_OracleParser, added support for additional SQL statements or options 37 | on existing statements. 38 | 8) In cx_OracleObject, constraints on objects that have been dropped are now 39 | ignored. 40 | 9) In cx_OracleObject, trailing lines are no longer stripped from the source 41 | that is returned from dba_source as wrapped code sometimes requires the 42 | blank line in Oracle 10g as reported by Robert Ritchie. 43 | 10) In cx_OracleObject, added support for option --name-file which is the same 44 | as --name except that the names are listed in the specified file, one per 45 | line; roles can also be included if the names refer to roles. 46 | 11) In cx_OracleObject, always set the owner when an object is exported so 47 | that referenced constraints are prefixed with connect statements as 48 | needed. 49 | 12) In cx_OracleObject, added support when describing views to include the 50 | column names in the SQL for the view. 51 | 13) In cx_OracleObject, added support for domain indexes. 52 | 14) In cx_OracleObject, added support for Oracle 11.2. 53 | 15) In cx_OracleObject, added support for using flashback queries when 54 | performing describes, either by timestamp or by SCN. 55 | 16) In cx_OracleEx, added method for getting the current date from the 56 | database. 57 | 17) In cx_OracleEx, added methods for performing simple insert, update and 58 | delete statements based on the data passed into those methods. 59 | 18) In cx_OracleEx, show the number of rows successfully processed before an 60 | error occurs as requested by Gordon den Otter. 61 | 19) In cx_OracleEx, added method that can be used as an output type handler 62 | for cx_Oracle that returns all strings as Unicode strings. 63 | 20) Added module cx_PatchCommands which enables the use of the simple SQL 64 | parser for processing SQL statements and reporting more useful information 65 | than is reported by SQL*Plus. 66 | 21) Dropped module cx_RecordSet which has been replaced by more advanced code 67 | in the ceDatabase module in the cx_PyGenLib project. 68 | 22) Dropped module cx_CursorCache which is not used and only contained a few 69 | lines of code anyway. 70 | 71 | Changes from 2.3 to 2.4 72 | 1) In cx_ExportData and cx_ImportData, use cx_Logging to output messages 73 | rather than write directly to stderr. 74 | 2) In cx_ImportData, added phrase "(n% of file)" to the reporting message when 75 | possible as requested by Don Reid. 76 | 3) In cx_ImportData, replaced use of executemanyprepared() with executemany() 77 | and bind arrays instead of dictionaries which actually improves performance 78 | by about 20-25% in some cases. 79 | 4) In cx_OracleDebugger, fixed case of message attribute. 80 | 5) In cx_OracleObject, added support for describing comments on tables and 81 | columns. 82 | 6) In cx_OracleObject, eliminate identical grants from the output. 83 | 7) In cx_OracleObject, export the roles before the users as the reason the 84 | roles are included is because they are administered by the users and the 85 | grants will by definition fail. 86 | 8) In cx_OracleObject, create the directory before any exporting actually 87 | takes place in order to handle the situation when no objects end up being 88 | exported, as requested by Micah Friesen. 89 | 9) In cx_OracleParser, added support for a select for update statement 90 | without a where clause. 91 | 10) In cx_OracleUtils, ignore invalid objects of type "UNDEFINED" which will 92 | fail to compile anyway. 93 | 11) In cx_SQL, added support for compiling comment statements. 94 | 12) In cx_SQL, added additional case statement to handle the modify constraint 95 | clause. 96 | 13) cx_Oracle 4.2 is now required. 97 | 14) Added new module cx_OracleEx which supports logging of Oracle statements 98 | and has a few additional convenient methods. 99 | 15) Removed module cx_DumpData which was only used by DumpData in the 100 | cx_OracleTools project. 101 | 102 | Changes from 2.2 to 2.3 103 | 1) Added new module cx_OracleDebugger which allows for logging of messages 104 | using dbms_pipe which was originally exclusively done in DbDebugger, a 105 | part of the cx_OracleTools package. 106 | 2) Added support for limiting the number of rows exported and skipping a 107 | certain number of rows first in cx_ExportData. 108 | 3) Removed special code in cx_ExportData and cx_ImportData for handling 109 | dates since they can be pickled directly now. 110 | 4) Added support for exporting timestamp columns in cx_ExportData. 111 | 5) Improved Oracle SQL and PL/SQL parser in cx_OracleParser. 112 | 6) Added ability to enable roles when connecting to a database using the 113 | utility function in cx_OracleUtils. 114 | 7) Added support for describing index organized tables in cx_OracleObject. 115 | 8) Added support for describing compressed keys in indexes in cx_OracleObject. 116 | 9) Added support for describing users and roles in cx_OracleObject. 117 | 10) Added support for describing trigters which call stored procedures instead 118 | of executing PL/SQL blocks in cx_OracleObject. 119 | 11) Fixed bug in display of size of unicode columns in cx_OracleObject. 120 | 12) Fixed bug in display of integer columns in cx_OracleObject. 121 | 13) Fixed bug in cx_OracleObject which was not describing generated primary 122 | key and unique constraints. 123 | 14) Ensured that the "&" character is not dumped directly in cx_DumpData so 124 | that the "set define off" command is not required before running the 125 | generated script through SQL*Plus. 126 | 15) Fixed messages that were generated by cx_SQL when constraints were enabled 127 | or disabled. 128 | 16) Fixed some spelling mistakes. 129 | 130 | Changes from 2.1 to 2.2 131 | 1) Added partial support for describing partitioned tables. 132 | 2) Added support for describing constraints directly. 133 | 3) Added utility function which will prompt for a password when one is 134 | missing in a connect string and then return the modified connect string. 135 | 4) Added support for case sensitivity when describing objects. 136 | 5) Improved the grammar for parsing Oracle code. 137 | 6) Added support for exporting CLOB and BLOB columns directly. 138 | 7) Do not use the column "table_owner" on dba_synonyms if the value is null. 139 | This is a rather rare situation but raises an exception which discovered. 140 | 8) Add checking for circular dependencies to avoid infinite loops. 141 | 142 | Changes for 2.1 143 | 1) First introduced to the public. 144 | 145 | -------------------------------------------------------------------------------- /cx_OracleEx.py: -------------------------------------------------------------------------------- 1 | """Define extensions to the cx_Oracle module.""" 2 | 3 | import cx_Exceptions 4 | import cx_Logging 5 | import cx_Oracle 6 | import sys 7 | 8 | class Connection(cx_Oracle.Connection): 9 | BFILE = cx_Oracle.BFILE 10 | BINARY = cx_Oracle.BINARY 11 | BLOB = cx_Oracle.BLOB 12 | CLOB = cx_Oracle.CLOB 13 | NCLOB = cx_Oracle.NCLOB 14 | DATETIME = cx_Oracle.DATETIME 15 | FIXED_CHAR = cx_Oracle.FIXED_CHAR 16 | LONG_BINARY = cx_Oracle.LONG_BINARY 17 | LONG_STRING = cx_Oracle.LONG_STRING 18 | NUMBER = cx_Oracle.NUMBER 19 | ROWID = cx_Oracle.ROWID 20 | STRING = cx_Oracle.STRING 21 | TIMESTAMP = cx_Oracle.TIMESTAMP 22 | Date = cx_Oracle.Date 23 | Timestamp = cx_Oracle.Timestamp 24 | trimMessage = logSql = True 25 | 26 | def cursor(self): 27 | cursor = Cursor(self) 28 | cursor.arraysize = 50 29 | return cursor 30 | 31 | def DeleteRow(self, tableName, **args): 32 | """Delete a row from a table.""" 33 | whereClauses = ["%s = :%s" % (n, n) for n in args] 34 | statement = "delete from %s where %s" % \ 35 | (tableName, " and ".join(whereClauses)) 36 | cursor = self.cursor() 37 | cursor.execute(statement, **args) 38 | 39 | def ExceptionHandler(self, excType, excValue, excTraceback): 40 | if excType is None or excValue is None \ 41 | or not isinstance(excValue, cx_Oracle.DatabaseError): 42 | exc = cx_Exceptions.GetExceptionInfo(excType, excValue, 43 | excTraceback) 44 | else: 45 | exc = DatabaseException(excType, excValue, excTraceback, 46 | self.trimMessage) 47 | exc.arguments["connection"] = repr(self) 48 | return exc 49 | 50 | def GetCurrentDate(self): 51 | """Return the current date according to the database.""" 52 | cursor = self.cursor() 53 | cursor.execute("select sysdate from dual") 54 | value, = cursor.fetchone() 55 | return value 56 | 57 | def InsertRow(self, tableName, **args): 58 | """Insert a row into the table.""" 59 | names = list(args.keys()) 60 | bindNames = [":%s" % n for n in names] 61 | statement = "insert into %s (%s) values (%s)" % \ 62 | (tableName, ",".join(names), ",".join(bindNames)) 63 | cursor = self.cursor() 64 | cursor.execute(statement, **args) 65 | 66 | def IsValidOracleName(self, name): 67 | """Return true if the name is valid for use within Oracle.""" 68 | cursor = cx_Oracle.Cursor(self) 69 | try: 70 | cursor.execute("select 1 as %s from dual %s" % (name, name)) 71 | return True 72 | except: 73 | return False 74 | 75 | def UpdateRow(self, tableName, *whereNames, **args): 76 | """Update a row in the table.""" 77 | setClauses = ["%s = :%s" % (n, n) for n in args \ 78 | if n not in whereNames] 79 | whereClauses = ["%s = :%s" % (n, n) for n in whereNames] 80 | statement = "update %s set %s where %s" % \ 81 | (tableName, ",".join(setClauses), " and ".join(whereClauses)) 82 | cursor = self.cursor() 83 | cursor.execute(statement, **args) 84 | 85 | 86 | class Cursor(cx_Oracle.Cursor): 87 | 88 | def blob(self, _value): 89 | """Return a BLOB variable containing the given value.""" 90 | var = self.var(self.connection.BLOB) 91 | var.setvalue(0, _value) 92 | return var 93 | 94 | def clob(self, _value): 95 | """Return a CLOB variable containing the given value.""" 96 | var = self.var(self.connection.CLOB) 97 | var.setvalue(0, _value) 98 | return var 99 | 100 | def execute(self, _sql, _args = None, **_kwargs): 101 | """Wrap the execute so that unhandled exceptions are handled.""" 102 | if _args is None: 103 | _args = _kwargs 104 | try: 105 | if self.connection.logSql \ 106 | and cx_Logging.Debug("SQL\n%s", _sql or self.statement): 107 | if isinstance(_args, dict): 108 | _output = [(k, v) for k, v in _args.items() \ 109 | if not k.endswith("_")] 110 | _output.sort() 111 | else: 112 | _output = enumerate(_args) 113 | _output = [" %s => %r" % (n, v) for n, v in _output] 114 | if _output: 115 | cx_Logging.Debug("BIND VARIABLES\n%s", "\n".join(_output)) 116 | return cx_Oracle.Cursor.execute(self, _sql, _args) 117 | except: 118 | exc = self.connection.ExceptionHandler(*sys.exc_info()) 119 | exc.details.append("SQL: %s" % _sql or self.statement) 120 | exc.details.append("Bind Variables:") 121 | if isinstance(_args, dict): 122 | _output = [(k, v) for k, v in _args.items() \ 123 | if not k.endswith("_")] 124 | _output.sort() 125 | else: 126 | _output = enumerate(_args) 127 | for name, value in _output: 128 | exc.details.append(" %s -> %r" % (name, value)) 129 | raise exc 130 | 131 | def executeandfetch(self, _sql, _args = None, **_kwargs): 132 | """Execute the statement and return the cursor for iterating.""" 133 | if _args is None: 134 | _args = _kwargs 135 | self.execute(_sql, _args) 136 | return self 137 | 138 | def executeandfetchall(self, _sql, _args = None, **_kwargs): 139 | """Execute the statement and return all of the rows from the cursor.""" 140 | if _args is None: 141 | _args = _kwargs 142 | self.execute(_sql, _args) 143 | return self.fetchall() 144 | 145 | def executeandfetchone(self, _sql, _args = None, **_kwargs): 146 | """Execute the statement and return one and only one row. If no rows 147 | are found, the NoDataFound exception is raised. If too many rows 148 | are found, the TooManyRows exception is raised.""" 149 | if _args is None: 150 | _args = _kwargs 151 | self.execute(_sql, _args) 152 | rows = self.fetchall() 153 | if len(rows) == 0: 154 | raise cx_Exceptions.NoDataFound() 155 | elif len(rows) > 1: 156 | raise cx_Exceptions.TooManyRows(numRows = len(rows)) 157 | return rows[0] 158 | 159 | def executemany(self, _sql, _args): 160 | try: 161 | if self.connection.logSql \ 162 | and cx_Logging.Debug("SQL\n%s", _sql or self.statement): 163 | _output = [" %s" % (r,) for r in _args] 164 | cx_Logging.Debug("ROWS (%s):\n%s", len(_output), 165 | "\n".join(_output)) 166 | return cx_Oracle.Cursor.executemany(self, _sql, _args) 167 | except: 168 | exc = self.connection.ExceptionHandler(*sys.exc_info()) 169 | exc.details.append("SQL: %s" % _sql or self.statement) 170 | if self.rowcount > -1 and self.rowcount < len(_args): 171 | exc.details.append("FAILED ROW: %s" % (_args[self.rowcount],)) 172 | exc.details.append("ROWS (%s, %s before error):" % \ 173 | (len(_args), self.rowcount)) 174 | for row in _args: 175 | exc.details.append(" %s" % (row,)) 176 | raise exc 177 | 178 | def nclob(self, _value): 179 | """Return a NCLOB variable containing the given value.""" 180 | var = self.var(self.connection.NCLOB) 181 | var.setvalue(0, _value) 182 | return var 183 | 184 | 185 | class DatabaseException(cx_Exceptions.BaseException): 186 | dbErrorCode = None 187 | dbErrorOffset = None 188 | 189 | def __init__(self, excType, excValue, excTraceback, trimMessage = True): 190 | cx_Exceptions.BaseException.__init__(self) 191 | self._FormatException(excType, excValue, excTraceback) 192 | self.message = str(excValue) 193 | error, = excValue.args 194 | if not isinstance(error, str): 195 | self.dbErrorCode = error.code 196 | try: 197 | self.dbErrorOffset = error.offset 198 | except AttributeError: 199 | pass 200 | if trimMessage and self.message.startswith("ORA-"): 201 | pos = self.message.find("ORA-", 1) 202 | if pos > 0: 203 | self.message = self.message[11:pos].rstrip() 204 | 205 | -------------------------------------------------------------------------------- /cx_OracleObject/Environment.py: -------------------------------------------------------------------------------- 1 | """Defines the environment for describing Oracle objects.""" 2 | 3 | import cx_Exceptions 4 | 5 | from . import Object 6 | from . import Statements 7 | from . import Utils 8 | 9 | __all__ = [ "Environment" ] 10 | 11 | class Environment(object): 12 | """Defines environment for describing Oracle objects.""" 13 | sourceTypes = { 14 | "FUNCTION" : Object.StoredProcWithPrivileges, 15 | "PACKAGE" : Object.StoredProcWithBody, 16 | "PACKAGE BODY" : Object.StoredProc, 17 | "PROCEDURE" : Object.StoredProcWithPrivileges, 18 | "TYPE" : Object.StoredProcWithBody, 19 | "TYPE BODY" : Object.StoredProc, 20 | "VIEW" : Object.ViewNoRetrieve 21 | } 22 | constraintTypes = [ 23 | "CONSTRAINT", 24 | "PRIMARY KEY", 25 | "UNIQUE CONSTRAINT", 26 | "FOREIGN KEY", 27 | "CHECK CONSTRAINT" 28 | ] 29 | 30 | def __init__(self, connection, options): 31 | self.connection = connection 32 | self.useDbaViews = False 33 | self.maxLongSize = None 34 | self.wantViewColumns = False 35 | self.wantQuotas = True 36 | self.asOfTimestamp = None 37 | self.asOfScn = None 38 | Utils.SetOptions(self, options) 39 | self.cursors = {} 40 | self.cachedObjects = {} 41 | self.namesForOutput = {} 42 | if self.asOfTimestamp is not None: 43 | cursor = connection.cursor() 44 | cursor.execute(""" 45 | begin 46 | dbms_flashback.enable_at_time(%s); 47 | end;""" % self.asOfTimestamp) 48 | elif self.asOfScn is not None: 49 | cursor = connection.cursor() 50 | cursor.callproc("dbms_flashback.enable_at_system_change_number", 51 | (self.asOfScn,)) 52 | 53 | def CacheObject(self, obj): 54 | """Cache the object for later retrieval.""" 55 | self.cachedObjects[(obj.owner, obj.name)] = obj 56 | 57 | def CachedObject(self, owner, name): 58 | """Returns the cached object or None if not cached.""" 59 | return self.cachedObjects.get((owner, name)) 60 | 61 | def Cursor(self, tag = None): 62 | """Return a cursor which is cached for better performance.""" 63 | isPrepared = cursor = self.cursors.get(tag) 64 | if cursor is None: 65 | cursor = self.connection.cursor() 66 | if tag: 67 | self.cursors[tag] = cursor 68 | return cursor, isPrepared 69 | 70 | def NameForOutput(self, name): 71 | """Return the name to be used for output.""" 72 | nameForOutput = self.namesForOutput.get(name) 73 | if nameForOutput is None: 74 | if name.isupper() and self.connection.IsValidOracleName(name): 75 | nameForOutput = name.lower() 76 | else: 77 | nameForOutput = '"%s"' % name 78 | self.namesForOutput[name] = nameForOutput 79 | return nameForOutput 80 | 81 | def ObjectByType(self, owner, name, type): 82 | """Return an object of the correct type.""" 83 | if type in self.sourceTypes: 84 | return self.sourceTypes[type](self, owner, name, type) 85 | whereClause = "where o.owner = :owner and " 86 | if type == "TABLE": 87 | whereClause += "o.table_name = :name" 88 | statement = Statements.TABLES 89 | objectFunction = Object.Table 90 | elif type == "INDEX": 91 | whereClause += "o.index_name = :name" 92 | statement = Statements.INDEXES 93 | objectFunction = Object.Index 94 | elif type == "TRIGGER": 95 | whereClause += "o.trigger_name = :name" 96 | statement = Statements.TRIGGERS 97 | objectFunction = Object.Trigger 98 | elif type == "SYNONYM": 99 | whereClause += "o.synonym_name = :name" 100 | statement = Statements.SYNONYMS 101 | objectFunction = Object.Synonym 102 | elif type == "SEQUENCE": 103 | whereClause = "where o.sequence_owner = :owner " + \ 104 | "and o.sequence_name = :name" 105 | statement = Statements.SEQUENCES 106 | objectFunction = Object.Sequence 107 | elif type == "CONTEXT": 108 | whereClause = "where :owner = 'SYS' and namespace = :name" 109 | statement = Statements.CONTEXTS 110 | objectFunction = Object.Context 111 | elif type == "LIBRARY": 112 | whereClause += "o.library_name = :name" 113 | statement = Statements.LIBRARIES 114 | objectFunction = Object.Library 115 | elif type in self.constraintTypes: 116 | whereClause += "o.constraint_name = :name" 117 | statement = Statements.CONSTRAINTS 118 | objectFunction = Object.Constraint 119 | else: 120 | raise DescribeNotSupported(type = type) 121 | for object in Object.ObjectIterator(self, "Default_%s" % type, 122 | statement, whereClause, objectFunction, 123 | owner = owner, name = name): 124 | return object 125 | 126 | def ObjectExists(self, owner, name, type): 127 | """Returns a boolean indicating if the object exists.""" 128 | if type in self.constraintTypes: 129 | cursor, isPrepared = self.Cursor("ConstraintExists") 130 | if not isPrepared: 131 | cursor.prepare(""" 132 | select count(*) 133 | from %s_constraints 134 | where owner = :owner 135 | and constraint_name = :name""" % \ 136 | self.ViewPrefix()) 137 | cursor.execute(None, 138 | owner = owner, 139 | name = name) 140 | else: 141 | cursor, isPrepared = self.Cursor("ObjectExists") 142 | if not isPrepared: 143 | cursor.prepare(""" 144 | select count(*) 145 | from %s_objects 146 | where owner = :owner 147 | and object_name = :name 148 | and object_type = :objType""" % self.ViewPrefix()) 149 | cursor.execute(None, 150 | owner = owner, 151 | name = name, 152 | objType = type) 153 | count, = cursor.fetchone() 154 | return (count > 0) 155 | 156 | def ObjectInfo(self, name): 157 | """Return the owner, name and type of the object if found and raise 158 | an exception if an object cannot be found; the first attempt is 159 | with the case as provided; the second attempt is with the name 160 | uppercased; the third attempt (if not fully qualified) is to 161 | attempt to find a public synonym with the given name.""" 162 | isFullyQualified = "." in name 163 | if isFullyQualified: 164 | owner, name = name.split(".") 165 | else: 166 | cursor = self.connection.cursor() 167 | cursor.execute("select user from dual") 168 | owner, = cursor.fetchone() 169 | type = self.ObjectType(owner, name) 170 | if type is not None: 171 | return (owner, name, type) 172 | owner = owner.upper() 173 | name = name.upper() 174 | type = self.ObjectType(owner, name) 175 | if type is not None: 176 | return (owner, name, type) 177 | if not isFullyQualified: 178 | cursor = self.connection.cursor() 179 | cursor.execute(""" 180 | select 181 | table_owner, 182 | table_name 183 | from %s_synonyms 184 | where owner = 'PUBLIC' 185 | and synonym_name = :name""" % self.ViewPrefix(), 186 | name = name) 187 | row = cursor.fetchone() 188 | if row is not None: 189 | owner, name = row 190 | type = self.ObjectType(owner, name) 191 | if type is not None: 192 | return (owner, name, type) 193 | raise ObjectNotFound(owner = self.NameForOutput(owner), 194 | name = self.NameForOutput(name)) 195 | 196 | def ObjectType(self, owner, name): 197 | """Return the type of the object or None if no such object exists.""" 198 | cursor = self.connection.cursor() 199 | cursor.execute(""" 200 | select object_type 201 | from %s_objects 202 | where owner = :owner 203 | and object_name = :name 204 | and subobject_name is null 205 | and instr(object_type, 'BODY') = 0""" % self.ViewPrefix(), 206 | owner = owner, 207 | name = name) 208 | row = cursor.fetchone() 209 | if row is not None: 210 | return row[0] 211 | 212 | def ServerVersion(self): 213 | """Return a 2-tuple of the major and minor version of the server.""" 214 | versionParts = [int(s) for s in self.connection.version.split(".")] 215 | return tuple(versionParts[:2]) 216 | 217 | def ViewPrefix(self): 218 | """Return the view prefix for use in the queries.""" 219 | if self.useDbaViews: 220 | return "dba" 221 | return "all" 222 | 223 | 224 | class DescribeNotSupported(cx_Exceptions.BaseException): 225 | message = "Describing objects of type '%(type)s' is not supported." 226 | 227 | 228 | class ObjectNotFound(cx_Exceptions.BaseException): 229 | message = "Object %(owner)s.%(name)s not found." 230 | 231 | -------------------------------------------------------------------------------- /cx_OracleParser/full/Statements.py: -------------------------------------------------------------------------------- 1 | """Module defining statements supported by parser. Note that statements here 2 | are treated as objects so that they can be put in order.""" 3 | 4 | import cx_Parser 5 | 6 | __all__ = [ "CheckConstraint", "ForeignKey", "Grant", "Index", "Package", 7 | "PackageBody", "PrimaryKey", "PublicSynonym", "Revoke", "Role", 8 | "Sequence", "StandaloneSelect", "Synonym", "Table", "Trigger", 9 | "Type", "UniqueConstraint", "User", "View" ] 10 | 11 | 12 | class Statement(object): 13 | """Base class for all statements.""" 14 | 15 | def __init__(self, sql, directory, owner, name, type): 16 | self.sql = sql 17 | self.owner = owner 18 | self.name = name 19 | self.type = type 20 | key = (owner, name) 21 | if key not in directory: 22 | directory[key] = self 23 | 24 | def __repr__(self): 25 | if self.owner is not None: 26 | return "<%s %s.%s>" % \ 27 | (self.__class__.__name__, self.owner, self.name) 28 | return "<%s>" % self.__class__.__name__ 29 | 30 | 31 | class Constraint(Statement): 32 | """Base class for all constraints.""" 33 | 34 | def __init__(self, sql, directory, owner, name, type, tableName): 35 | Statement.__init__(self, sql, directory, owner, name, type) 36 | self.tableName = tableName 37 | 38 | def DependsOn(self): 39 | return [ (self.owner, self.tableName, "TABLE") ] 40 | 41 | 42 | class SourceObject(Statement): 43 | """Base class for all "source" objects.""" 44 | 45 | def __init__(self, sql, directory, owner, name, type, references): 46 | Statement.__init__(self, sql, directory, owner, name, type) 47 | self.references = references 48 | self.directory = directory 49 | 50 | def DependsOn(self): 51 | """Get the dependencies for the object. If enough components are 52 | available, the directory is searched for an object with the 53 | owner and name given; if that is not found, the current owner 54 | and name are attempted and finally, a public synonym is 55 | attempted.""" 56 | dependencies = [] 57 | for ref in self.references: 58 | if len(ref) == 3: 59 | depObj = self.directory.get(ref[:2]) 60 | elif len(ref) == 2: 61 | depObj = self.directory.get(ref) 62 | if depObj is None: 63 | depObj = self.directory.get((self.owner, ref[0])) 64 | elif len(ref) == 1: 65 | depObj = self.directory.get((self.owner, ref[0])) 66 | if depObj is not None and depObj is not self: 67 | depTuple = (depObj.owner, depObj.name, depObj.type) 68 | if depTuple not in dependencies: 69 | dependencies.append(depTuple) 70 | return dependencies 71 | 72 | 73 | class SynonymBase(Statement): 74 | """Base class for synonyms.""" 75 | 76 | def __init__(self, sql, directory, owner, type, name, refName): 77 | Statement.__init__(self, sql, directory, owner, name, type) 78 | if len(refName) == 1: 79 | self.refOwner = owner 80 | self.refObjectName, = refName 81 | else: 82 | self.refOwner, self.refObjectName = refName 83 | 84 | def DependsOn(self): 85 | return [] 86 | 87 | 88 | class CheckConstraint(Constraint): 89 | """Class for check constraints.""" 90 | 91 | def __init__(self, sql, directory, owner, name, tableName): 92 | Constraint.__init__(self, sql, directory, owner, name, 93 | "CHECK CONSTRAINT", tableName) 94 | 95 | 96 | class ForeignKey(Constraint): 97 | """Class for foreign keys.""" 98 | 99 | def __init__(self, sql, directory, owner, name, tableName, 100 | referencedTable): 101 | Constraint.__init__(self, sql, directory, owner, name, "FOREIGN KEY", 102 | tableName) 103 | if len(referencedTable) == 1: 104 | self.refOwner = owner 105 | self.refTableName, = referencedTable 106 | else: 107 | self.refOwner, self.refTableName = referencedTable 108 | 109 | def DependsOn(self): 110 | return Constraint.DependsOn(self) + \ 111 | [ (self.refOwner, self.refTableName, "TABLE") ] 112 | 113 | 114 | class Grant(object): 115 | """Class for grants.""" 116 | 117 | def __init__(self, sql, privileges, grantees, objectOwner = None, 118 | objectName = None): 119 | self.sql = sql 120 | self.privileges = privileges 121 | self.grantees = grantees 122 | self.objectOwner = objectOwner 123 | self.objectName = objectName 124 | 125 | def __repr__(self): 126 | if self.objectOwner is None: 127 | return "<%s>" % self.__class__.__name__ 128 | return "<%s on %s.%s>" % \ 129 | (self.__class__.__name__, self.objectOwner, self.objectName) 130 | 131 | 132 | class Index(Statement): 133 | """Class for indexes.""" 134 | 135 | def __init__(self, sql, directory, owner, name, tableName): 136 | Statement.__init__(self, sql, directory, owner, name, "INDEX") 137 | self.tableName = tableName 138 | 139 | def DependsOn(self): 140 | return [ (self.owner, self.tableName, "TABLE") ] 141 | 142 | 143 | class Package(SourceObject): 144 | """Class for packages.""" 145 | 146 | def __init__(self, sql, directory, owner, name, references, identifiers): 147 | SourceObject.__init__(self, sql, directory, owner, name, "PACKAGE", 148 | references) 149 | self.identifiers = identifiers 150 | 151 | 152 | class PackageBody(SourceObject): 153 | """Class for package bodies.""" 154 | 155 | def __init__(self, sql, directory, owner, name, references): 156 | SourceObject.__init__(self, sql, directory, owner, name, 157 | "PACKAGE BODY", references) 158 | 159 | def DependsOn(self): 160 | return [(self.owner, self.name, "PACKAGE")] + \ 161 | SourceObject.DependsOn(self) 162 | 163 | 164 | class PrimaryKey(Constraint): 165 | """Class for primary keys.""" 166 | 167 | def __init__(self, sql, directory, owner, name, tableName): 168 | Constraint.__init__(self, sql, directory, owner, name, "PRIMARY KEY", 169 | tableName) 170 | 171 | 172 | class PublicSynonym(SynonymBase): 173 | """Class for public synonyms.""" 174 | 175 | def __init__(self, sql, directory, name, refName): 176 | SynonymBase.__init__(self, sql, directory, "PUBLIC", "PUBLIC SYNONYM", 177 | name, refName) 178 | 179 | 180 | class Revoke(object): 181 | """Class for revokes.""" 182 | 183 | def __init__(self, sql, privileges, grantees, objectOwner = None, 184 | objectName = None): 185 | self.sql = sql 186 | self.privileges = privileges 187 | self.grantees = grantees 188 | self.objectOwner = objectOwner 189 | self.objectName = objectName 190 | 191 | def __repr__(self): 192 | if self.objectOwner is None: 193 | return "<%s>" % self.__class__.__name__ 194 | return "<%s on %s.%s>" % \ 195 | (self.__class__.__name__, self.objectOwner, self.objectName) 196 | 197 | 198 | class Role(Statement): 199 | """Class for roles.""" 200 | 201 | def __init__(self, sql, directory, name): 202 | Statement.__init__(self, sql, directory, "SYSTEM", name, "ROLE") 203 | 204 | def __repr__(self): 205 | return "<%s %s>" % (self.__class__.__name__, self.name) 206 | 207 | def DependsOn(self): 208 | return [] 209 | 210 | 211 | class Sequence(Statement): 212 | """Class for tables.""" 213 | 214 | def __init__(self, sql, directory, owner, name): 215 | Statement.__init__(self, sql, directory, owner, name, "SEQUENCE") 216 | 217 | def DependsOn(self): 218 | return [] 219 | 220 | 221 | class StandaloneSelect(SourceObject): 222 | """Class for standalone select statements.""" 223 | 224 | def __init__(self, sql, directory, references): 225 | SourceObject.__init__(self, sql, directory, None, None, "SELECT", 226 | references) 227 | 228 | 229 | class Synonym(SynonymBase): 230 | """Class for synonyms.""" 231 | 232 | def __init__(self, sql, directory, owner, name, refName): 233 | SynonymBase.__init__(self, sql, directory, owner, "SYNONYM", name, 234 | refName) 235 | 236 | 237 | class Table(Statement): 238 | """Class for tables.""" 239 | 240 | def __init__(self, sql, directory, owner, name): 241 | Statement.__init__(self, sql, directory, owner, name, "TABLE") 242 | 243 | def DependsOn(self): 244 | return [] 245 | 246 | 247 | class Trigger(Statement): 248 | """Class for triggers.""" 249 | 250 | def __init__(self, sql, directory, owner, name): 251 | Statement.__init__(self, sql, directory, owner, name, "TRIGGER") 252 | 253 | def DependsOn(self): 254 | return [] 255 | 256 | 257 | class Type(SourceObject): 258 | """Class for types.""" 259 | 260 | def __init__(self, sql, directory, owner, name, references): 261 | SourceObject.__init__(self, sql, directory, owner, name, "TYPE", 262 | references) 263 | 264 | class UniqueConstraint(Constraint): 265 | """Class for unique constraints.""" 266 | 267 | def __init__(self, sql, directory, owner, name, tableName): 268 | Constraint.__init__(self, sql, directory, owner, name, 269 | "UNIQUE CONSTRAINT", tableName) 270 | 271 | class User(Statement): 272 | """Class for users.""" 273 | 274 | def __init__(self, sql, directory, name): 275 | Statement.__init__(self, sql, directory, "SYSTEM", name, "USER") 276 | 277 | def __repr__(self): 278 | return "<%s %s>" % (self.__class__.__name__, self.name) 279 | 280 | def DependsOn(self): 281 | return [] 282 | 283 | 284 | class View(SourceObject): 285 | """Class for views.""" 286 | 287 | def __init__(self, sql, directory, owner, name, references): 288 | SourceObject.__init__(self, sql, directory, owner, name, "VIEW", 289 | references) 290 | 291 | -------------------------------------------------------------------------------- /cx_OracleUtils.py: -------------------------------------------------------------------------------- 1 | """Define utility functions for use with Oracle.""" 2 | 3 | import datetime 4 | import getpass 5 | import cx_Exceptions 6 | import cx_Logging 7 | import cx_OptionParser 8 | import cx_Oracle 9 | import cx_OracleEx 10 | import os 11 | import string 12 | import sys 13 | 14 | class CompilationErrors(cx_Exceptions.BaseException): 15 | message = "%(type)s %(name)s %(fragment)s compilation errors." 16 | 17 | 18 | def CheckForErrors(cursor, objectOwner, objectName, objectType, errorFragment, 19 | baseLineNo = 0, logPrefix = ""): 20 | """Check the object for errors, and if any exist, print them.""" 21 | cursor.execute(None, 22 | owner = objectOwner, 23 | name = objectName, 24 | type = objectType) 25 | errors = cursor.fetchall() 26 | if errors: 27 | cx_Logging.Error("%s***** ERROR *****", logPrefix) 28 | for line, position, text in errors: 29 | cx_Logging.Error("%s%s/%s\t%s", logPrefix, int(line + baseLineNo), 30 | int(position), text) 31 | raise CompilationErrors(type = objectType.capitalize(), 32 | name = objectName.upper(), fragment = errorFragment) 33 | 34 | def Connect(connectString, rolesToEnable = None): 35 | """Connect to the database prompting for the password if necessary.""" 36 | pos = connectString.find(" as sysdba") 37 | if pos < 0: 38 | pos = connectString.find(" as sysoper") 39 | if pos < 0: 40 | mode = 0 41 | else: 42 | mode = cx_Oracle.SYSOPER 43 | connectString = connectString[:pos] 44 | else: 45 | mode = cx_Oracle.SYSDBA 46 | connectString = connectString[:pos] 47 | connectString = GetConnectString(connectString) 48 | connection = cx_OracleEx.Connection(connectString, mode = mode) 49 | if rolesToEnable is not None: 50 | cursor = connection.cursor() 51 | cursor.callproc("dbms_session.set_role", (rolesToEnable,)) 52 | connection.trimMessage = False 53 | return connection 54 | 55 | 56 | def GetConnectString(connectString): 57 | """Return the connect string, modified to include a password, which is 58 | prompted for, if necessary.""" 59 | if "/" not in connectString: 60 | prompt = "Password for %s: " % connectString 61 | sys.stderr.write(prompt) 62 | password = getpass.getpass("") 63 | pos = connectString.find("@") 64 | if pos < 0: 65 | pos = len(connectString) 66 | connectString = connectString[:pos] + "/" + password + \ 67 | connectString[pos:] 68 | return connectString 69 | 70 | 71 | def QuotedString(value): 72 | """Return the value quoted as needed.""" 73 | return "'%s'" % value.replace("'", "''") 74 | 75 | 76 | def GetConstantRepr(value, binaryData = False): 77 | """Return the value represented as an Oracle constant.""" 78 | if value is None: 79 | return "null" 80 | elif isinstance(value, cx_Oracle.LOB): 81 | value = value.read() 82 | if isinstance(value, (int, float)): 83 | return str(value) 84 | elif isinstance(value, str): 85 | if binaryData: 86 | parts = [] 87 | while value: 88 | part = value[:35] 89 | value = value[35:] 90 | chars = [hex(ord(c))[2:].zfill(2) for c in part] 91 | parts.append("'%s'" % "".join(chars)) 92 | return " ||\n ".join(parts) 93 | else: 94 | parts = [] 95 | lastPos = 0 96 | for i, char in enumerate(value): 97 | if not char.isalnum() and char != " " \ 98 | and char not in string.punctuation: 99 | temp = value[lastPos:i] 100 | lastPos = i + 1 101 | if temp: 102 | parts.append(QuotedString(temp)) 103 | parts.append("chr(%s)" % ord(char)) 104 | temp = value[lastPos:] 105 | if temp: 106 | parts.append(QuotedString(temp)) 107 | return " || ".join(parts) 108 | elif isinstance(value, datetime.datetime): 109 | return "to_date('%s', 'YYYY-MM-DD HH24:MI:SS')" % \ 110 | value.strftime("%Y-%m-%d %H:%M:%S") 111 | elif isinstance(value, datetime.date): 112 | return "to_date('%s', 'YYYY-MM-DD')" % value.strftime("%Y-%m-%d") 113 | raise "Cannot convert %r to an Oracle constant representation." % value 114 | 115 | 116 | def GetObjectInfo(connection, objectName, useDbaViews = False): 117 | """Return information about the object. The object is first searched in a 118 | case sensitive fashion and if that fails upshifts the name and tries 119 | again.""" 120 | if "." in objectName: 121 | owner, name = objectName.split(".") 122 | owner = owner.upper() 123 | else: 124 | owner = connection.username.upper() 125 | name = objectName 126 | if useDbaViews: 127 | prefix = "dba" 128 | else: 129 | prefix = "all" 130 | cursor = connection.cursor() 131 | cursor.execute(""" 132 | select object_type 133 | from %s_objects 134 | where owner = :owner 135 | and object_name = :name 136 | and subobject_name is null 137 | and instr(object_type, 'BODY') = 0""" % prefix, 138 | owner = owner, 139 | name = name) 140 | row = cursor.fetchone() 141 | if row is not None: 142 | return owner, name, row[0] 143 | elif not name.isupper(): 144 | return GetObjectInfo(connection, objectName.upper(), useDbaViews) 145 | 146 | 147 | def IdentifierRepr(identifier): 148 | """Return the representation of an identifier. In Oracle this means that 149 | if the identifier is all uppercase, it can be returned as is; otherwise, 150 | double quotes must be placed around it.""" 151 | if identifier.isupper(): 152 | return identifier 153 | return '"%s"' % identifier 154 | 155 | 156 | def PrepareErrorsCursor(connection, viewPrefix = "all"): 157 | """Prepare a cursor for retrieving errors from the database.""" 158 | cursor = connection.cursor() 159 | cursor.prepare(""" 160 | select 161 | line, 162 | position, 163 | text 164 | from %s_errors 165 | where owner = :owner 166 | and name = :name 167 | and type = :type 168 | order by sequence""" % viewPrefix) 169 | cursor.setinputsizes(owner = connection.STRING, 170 | name = connection.STRING, type = connection.STRING) 171 | return cursor 172 | 173 | 174 | def RecompileInvalidObjects(connection, includeSchemas, excludeSchemas = [], 175 | raiseError = True, logPrefix = "", connectAsOwner = False): 176 | """Recompile all invalid objects in the schemas requested.""" 177 | 178 | # determine whether or not to use dba views or not 179 | if len(includeSchemas) == 1 and not excludeSchemas \ 180 | and connection.username.upper() == includeSchemas[0]: 181 | singleSchema = True 182 | viewPrefix = "all" 183 | else: 184 | singleSchema = False 185 | viewPrefix = "dba" 186 | 187 | # prepare a cursor to determine if object is still invalid 188 | invalidCursor = connection.cursor() 189 | invalidCursor.prepare(""" 190 | select count(*) 191 | from %s_objects 192 | where owner = :owner 193 | and object_name = :name 194 | and object_type = :type 195 | and status = 'INVALID'""" % viewPrefix) 196 | invalidCursor.setinputsizes(owner = connection.STRING, 197 | name = connection.STRING, type = connection.STRING) 198 | 199 | # prepare a cursor to determine the errors for stored source 200 | errorsCursor = PrepareErrorsCursor(connection, viewPrefix) 201 | 202 | # fetch all of the invalid objects 203 | numErrors = 0 204 | numCompiled = 0 205 | compileCursor = connection.cursor() 206 | cursor = connection.cursor() 207 | cursor.arraysize = 25 208 | cursor.execute(""" 209 | select 210 | owner, 211 | object_name, 212 | object_type 213 | from %s_objects 214 | where status = 'INVALID' 215 | and object_type != 'UNDEFINED' 216 | order by owner""" % viewPrefix) 217 | for owner, name, type in cursor.fetchall(): 218 | 219 | # ignore if this schema should be ignored 220 | if includeSchemas and owner not in includeSchemas: 221 | continue 222 | if excludeSchemas and owner in excludeSchemas: 223 | continue 224 | 225 | # ignore if prior compiles have made this object valid 226 | invalidCursor.execute(None, 227 | owner = owner, 228 | name = name, 229 | type = type) 230 | invalid, = invalidCursor.fetchone() 231 | if not invalid: 232 | continue 233 | 234 | # perform compile 235 | numCompiled += 1 236 | if singleSchema: 237 | compileName = name 238 | else: 239 | compileName = "%s.%s" % (owner, name) 240 | cx_Logging.Trace("%sCompiling %s (%s)...", logPrefix, compileName, 241 | type) 242 | parts = type.lower().split() 243 | statement = "alter " + parts[0] + " " + compileName + " compile" 244 | if len(parts) > 1: 245 | statement += " " + parts[1] 246 | if connectAsOwner and connection.username.upper() != owner: 247 | connection = cx_OracleEx.Connection(owner, connection.password, 248 | connection.dsn) 249 | compileCursor = connection.cursor() 250 | compileCursor.execute(statement) 251 | try: 252 | CheckForErrors(errorsCursor, owner, name, type, "has", 253 | logPrefix = logPrefix) 254 | except: 255 | if raiseError: 256 | raise 257 | numErrors += 1 258 | 259 | # all done 260 | if numErrors: 261 | cx_Logging.Trace("%sAll objects compiled: %s error(s).", logPrefix, 262 | numErrors) 263 | elif numCompiled: 264 | cx_Logging.Trace("%sAll objects compiled successfully.", logPrefix) 265 | else: 266 | cx_Logging.Trace("%sNo invalid objects to compile.", logPrefix) 267 | 268 | 269 | def SchemaOption(name = "schema"): 270 | """Option for specifying the schema to connect to.""" 271 | return cx_OptionParser.Option("--%s" % name, required = 1, 272 | default = os.environ.get("ORA_USERID"), metavar = "SCHEMA", 273 | help = "use this connect string to connect to the database") 274 | 275 | 276 | def WhereClause(columnName, compareValue, nullable, equals): 277 | """Return a where clause.""" 278 | if equals and nullable: 279 | clause = "(%(columnName)s = %(compareValue)s or " + \ 280 | "%(columnName)s is null and %(compareValue)s is null)" 281 | elif equals: 282 | clause = "%(columnName)s = %(compareValue)s" 283 | elif nullable: 284 | clause = "(%(columnName)s != %(compareValue)s or " + \ 285 | "%(columnName)s is null and " + \ 286 | "%(compareValue)s is not null or " + \ 287 | "%(columnName)s is not null and %(compareValue)s is null)" 288 | else: 289 | clause = "%(columnName)s != %(compareValue)s" 290 | args = dict(columnName = IdentifierRepr(columnName), 291 | compareValue = compareValue) 292 | return clause % args 293 | 294 | -------------------------------------------------------------------------------- /cx_OracleObject/Describer.py: -------------------------------------------------------------------------------- 1 | """Defines classes for describing objects.""" 2 | 3 | import os 4 | import sys 5 | 6 | from . import Object, Statements, Utils 7 | 8 | __all__ = [ "Describer", "Exporter", "FileNameForObject" ] 9 | 10 | class Describer(object): 11 | """Base class for describing objects.""" 12 | 13 | def __init__(self, environment, options, outFile = None): 14 | self.environment = environment 15 | self.wantGrants = True 16 | self.wantComments = True 17 | self.wantTablespace = True 18 | self.wantStorage = True 19 | self.wantSequenceValues = False 20 | self.wantRelated = False 21 | self.wantForeignKeys = False 22 | self.wantSynonyms = False 23 | self.wantTriggers = False 24 | self.mergeGrants = True 25 | self.nameOnly = False 26 | self.includeRoles = False 27 | self.includeUsers = False 28 | self.includeContexts = False 29 | self.includedObjects = None 30 | self.onlyIf = None 31 | self.objectTypes = [] 32 | self.schemas = [] 33 | if outFile is None: 34 | self.outFile = sys.stdout 35 | else: 36 | self.outFile = outFile 37 | Utils.SetOptions(self, options) 38 | fileName = getattr(options, "nameFile", None) 39 | if fileName is not None: 40 | self.schemas = [s.strip() for s in open(fileName)] 41 | if not self.schemas: 42 | cursor = environment.connection.cursor() 43 | cursor.execute("select user from dual") 44 | schema, = cursor.fetchone() 45 | self.schemas = [schema] 46 | 47 | def __FetchObjectsToInclude(self): 48 | """Populate the dictionary of objects to include in the export.""" 49 | whereClause = "where owner %s and %s" % \ 50 | (self.schemasClause, self.onlyIf) 51 | if self.objectTypes: 52 | clauses = ["'%s'" % t for t in self.objectTypes] 53 | whereClause += " and object_type in (%s)" % ",".join(clauses) 54 | sql = "select owner, object_name from %s_objects %s" % \ 55 | (self.environment.ViewPrefix(), whereClause) 56 | cursor, isPrepared = self.environment.Cursor() 57 | cursor.execute(sql) 58 | self.includedObjects = dict.fromkeys(cursor) 59 | 60 | def __GetObjectTypes(self): 61 | return self.__objectTypes 62 | 63 | def __GetSchemas(self): 64 | return self.__schemas 65 | 66 | def __SetObjectTypes(self, objectTypes): 67 | self.__objectTypes = [s.replace("_", " ").upper() for s in objectTypes] 68 | 69 | def __SetSchemas(self, schemas): 70 | self.__schemas = [s.upper() for s in schemas] 71 | self.__schemas.sort() 72 | if len(self.__schemas) == 1: 73 | self.currentOwner, = self.__schemas 74 | self.schemasClause = "= '%s'" % self.currentOwner 75 | else: 76 | self.currentOwner = None 77 | schemas = ["'%s'" % s for s in self.schemas] 78 | self.schemasClause = "in (%s)" % ",".join(schemas) 79 | 80 | objectTypes = property(__GetObjectTypes, __SetObjectTypes, None, 81 | "the list of object types to describe") 82 | 83 | schemas = property(__GetSchemas, __SetSchemas, None, 84 | "the list of schemas to describe") 85 | 86 | def ExportAllObjects(self): 87 | """Export all objects for the chosen schemas.""" 88 | if self.onlyIf is not None: 89 | self.__FetchObjectsToInclude() 90 | if self.includeRoles: 91 | self.ExportRoles() 92 | if self.includeUsers: 93 | self.ExportUsers() 94 | if self.includeContexts: 95 | self.ExportContexts() 96 | if self.TypeIncluded("SYNONYM"): 97 | self.ExportSynonyms() 98 | if self.TypeIncluded("SEQUENCE"): 99 | self.ExportSequences() 100 | if self.TypeIncluded("TABLE"): 101 | self.ExportTables() 102 | if not self.wantRelated: 103 | self.ExportConstraints() 104 | self.ExportIndexes() 105 | if self.TypeIncluded("VIEW"): 106 | self.ExportViews() 107 | if self.SourceTypes(): 108 | self.ExportSource() 109 | if not self.wantTriggers and self.TypeIncluded("TRIGGER"): 110 | self.ExportTriggers() 111 | 112 | def ExportConstraints(self): 113 | """Export all of the constraints.""" 114 | print("Describing constraints...", file = sys.stderr) 115 | self.ExportObjects(Object.ObjectIterator(self.environment, 116 | "AllConstraints", Statements.CONSTRAINTS, 117 | "where o.owner %s" % self.schemasClause, Object.Constraint)) 118 | 119 | def ExportContexts(self): 120 | """Export all of the contexts.""" 121 | print("Describing contexts...", file = sys.stderr) 122 | whereClause = "where o.schema %s" % self.schemasClause 123 | self.ExportObjects(Object.ObjectIterator(self.environment, "Contexts", 124 | Statements.CONTEXTS, whereClause, Object.Context)) 125 | 126 | def ExportIndexes(self): 127 | """Export all of the indexes.""" 128 | print("Describing indexes...", file = sys.stderr) 129 | self.ExportObjects(Object.ObjectIterator(self.environment, 130 | "AllIndexes", Statements.INDEXES, 131 | "where o.owner %s" % self.schemasClause, Object.Index)) 132 | 133 | def ExportObject(self, object): 134 | """Exports the object to the output.""" 135 | self.SetOwner(object.owner, object.type) 136 | if isinstance(object, Object.Sequence): 137 | object.Export(self.outFile, self.wantSequenceValues) 138 | elif isinstance(object, (Object.ObjectWithStorage, Object.Constraint)): 139 | object.Export(self.outFile, self.wantTablespace, self.wantStorage) 140 | else: 141 | object.Export(self.outFile) 142 | if self.wantGrants and isinstance(object, Object.ObjectWithPrivileges): 143 | object.ExportPrivileges(self.outFile, self.mergeGrants) 144 | if self.wantComments and isinstance(object, Object.ObjectWithComments): 145 | object.ExportComments(self.outFile) 146 | if self.wantRelated: 147 | if isinstance(object, Object.StoredProcWithBody): 148 | body = object.Body() 149 | if body: 150 | self.ExportObject(body) 151 | if isinstance(object, Object.Table): 152 | for constraint in object.Constraints(): 153 | self.ExportObject(constraint) 154 | for index in object.Indexes(): 155 | self.ExportObject(index) 156 | if self.wantTriggers and isinstance(object, Object.ObjectWithTriggers): 157 | for trigger in object.Triggers(): 158 | self.ExportObject(trigger) 159 | if self.wantForeignKeys and isinstance(object, Object.Table): 160 | for constraint in object.ReferencedConstraints(): 161 | self.ExportObject(constraint) 162 | if self.wantSynonyms and object.supportsReferencedSynonyms: 163 | for synonym in object.ReferencedSynonyms(): 164 | self.ExportObject(synonym) 165 | 166 | def ExportObjects(self, sequence): 167 | """Export all the objects from the interator (or sequence).""" 168 | for obj in sequence: 169 | if not self.ObjectIncluded(obj): 170 | continue 171 | self.ExportObject(obj) 172 | 173 | def ExportRoles(self): 174 | """Export all roles granted with admin option to schemas exported.""" 175 | print("Describing roles...", file = sys.stderr) 176 | whereClause = "where o.role in (select granted_role " + \ 177 | "from dba_role_privs where admin_option = 'YES' " + \ 178 | "and grantee %s) or o.role %s" % \ 179 | (self.schemasClause, self.schemasClause) 180 | self.ExportObjects(Object.ObjectIterator(self.environment, "AllRoles", 181 | Statements.ROLES, whereClause, Object.Role)) 182 | 183 | def ExportSequences(self): 184 | """Export all of the sequences.""" 185 | print("Describing sequences...", file = sys.stderr) 186 | self.ExportObjects(Object.ObjectIterator(self.environment, 187 | "AllSequences", Statements.SEQUENCES, 188 | "where o.sequence_owner %s" % self.schemasClause, 189 | Object.Sequence)) 190 | 191 | def ExportSource(self): 192 | """Exports all source objects (in correct dependency order).""" 193 | print("Retrieving interdependent objects...", file = sys.stderr) 194 | objects = self.RetrieveSourceObjects() 195 | print("Retrieving dependencies...", file = sys.stderr) 196 | dependencies = self.RetrieveDependencies() 197 | print(len(objects), end=' ', file = sys.stderr) 198 | print("interdependent objects to describe...", file = sys.stderr) 199 | for owner, name, type in Utils.OrderObjects(objects, dependencies): 200 | self.RetrieveAndExportObject(owner, name, type) 201 | 202 | def ExportSynonyms(self): 203 | """Export all of the synonyms.""" 204 | print("Describing synonyms...", file = sys.stderr) 205 | whereClause = "where o.owner %s or o.owner = 'PUBLIC' " + \ 206 | "and o.table_owner %s" 207 | self.ExportObjects(Object.ObjectIterator(self.environment, 208 | "AllSynonyms", Statements.SYNONYMS, 209 | whereClause % (self.schemasClause, self.schemasClause), 210 | Object.Synonym)) 211 | 212 | def ExportTables(self): 213 | """Export all of the tables.""" 214 | print("Describing tables...", file = sys.stderr) 215 | self.ExportObjects(Object.ObjectIterator(self.environment, 216 | "AllTables", Statements.TABLES, 217 | "where o.owner %s" % self.schemasClause, Object.Table)) 218 | 219 | def ExportTriggers(self): 220 | """Export all of the triggers.""" 221 | print("Describing triggers...", file = sys.stderr) 222 | self.ExportObjects(Object.ObjectIterator(self.environment, 223 | "AllTriggers", Statements.TRIGGERS, 224 | "where o.owner %s" % self.schemasClause, Object.Trigger)) 225 | 226 | def ExportUsers(self): 227 | """Export all of the users.""" 228 | print("Describing users...", file = sys.stderr) 229 | whereClause = "where o.username %s" % self.schemasClause 230 | self.ExportObjects(Object.ObjectIterator(self.environment, "AllUsers", 231 | Statements.USERS, whereClause, Object.User)) 232 | 233 | def ExportViews(self): 234 | """Export all of the views (done in source by default).""" 235 | pass 236 | 237 | def ObjectIncluded(self, obj): 238 | """Return true if the object should be included in the export.""" 239 | if self.includedObjects is None: 240 | return True 241 | if isinstance(obj, (Object.Constraint, Object.Index)): 242 | name = obj.tableName 243 | elif isinstance(obj, Object.Synonym): 244 | name = obj.objectName 245 | else: 246 | name = obj.name 247 | return (obj.owner, name) in self.includedObjects 248 | 249 | def RetrieveAndExportObject(self, objectOwner, objectName, objectType): 250 | """Retrieve and export the object.""" 251 | if self.includedObjects is not None \ 252 | and (objectOwner, objectName) not in self.includedObjects: 253 | return 254 | self.SetOwner(objectOwner, objectType) 255 | if self.nameOnly: 256 | if objectOwner == "PUBLIC": 257 | objectType = "%s %s" % (objectOwner, objectType) 258 | print(objectName, "(%s)" % objectType, file = self.outFile) 259 | else: 260 | object = self.environment.ObjectByType(objectOwner, objectName, 261 | objectType) 262 | self.ExportObject(object) 263 | 264 | def RetrieveDependencies(self): 265 | """Retrieve the list of dependencies for source objects.""" 266 | typesArg = self.SourceTypesClause() 267 | cursor, isPrepared = self.environment.Cursor() 268 | cursor.execute(""" 269 | select 270 | owner, 271 | name, 272 | type, 273 | referenced_owner, 274 | referenced_name, 275 | referenced_type 276 | from %s_dependencies 277 | where referenced_owner %s 278 | and owner %s 279 | and referenced_link_name is null 280 | and type in (%s) 281 | and referenced_type in (%s)""" % \ 282 | (self.environment.ViewPrefix(), self.schemasClause, 283 | self.schemasClause, typesArg, typesArg)) 284 | return cursor.fetchall() 285 | 286 | def RetrieveSourceObjects(self): 287 | """Retrieve the list of source objects to be exported.""" 288 | cursor, isPrepared = self.environment.Cursor() 289 | cursor.execute(""" 290 | select 291 | owner, 292 | object_name, 293 | object_type 294 | from %s_objects 295 | where owner %s 296 | and object_type in (%s)""" % \ 297 | (self.environment.ViewPrefix(), self.schemasClause, 298 | self.SourceTypesClause())) 299 | return cursor.fetchall() 300 | 301 | def SetOwner(self, objectOwner, objectType): 302 | """Set the current owner being exported.""" 303 | if objectOwner != self.currentOwner and objectType != "PUBLIC SYNONYM": 304 | self.currentOwner = objectOwner 305 | if self.nameOnly: 306 | print(file = self.outFile) 307 | nameForOutput = self.environment.NameForOutput(self.currentOwner) 308 | print("connect", nameForOutput, file = self.outFile) 309 | print(file = self.outFile) 310 | 311 | def SourceTypes(self): 312 | """Return the list of source types to be included in the output.""" 313 | return [t for t in self.environment.sourceTypes \ 314 | if self.TypeIncluded(t)] 315 | 316 | def SourceTypesClause(self): 317 | """Return the clause suitable for inclusion in a SQL statement to 318 | restrict the source types to be included in the output.""" 319 | return ",".join(["'%s'" % t for t in self.SourceTypes()]) 320 | 321 | def TypeIncluded(self, objectType): 322 | """Return true if the type is to be included in the output.""" 323 | return not self.__objectTypes or objectType in self.__objectTypes 324 | 325 | 326 | class Exporter(Describer): 327 | """Exports objects into one file per object.""" 328 | 329 | def __init__(self, environment, options, baseDir): 330 | self.baseDir = baseDir 331 | if not os.path.exists(baseDir): 332 | os.makedirs(baseDir) 333 | self.splitRelated = False 334 | self.suppressOwnerDir = False 335 | self.exportLevel = 0 336 | self.dirs = {} 337 | Describer.__init__(self, environment, options) 338 | self.currentOwner = None 339 | 340 | def ExportObject(self, object): 341 | """Export the object into a new file.""" 342 | self.SetOwner(object.owner, object.type) 343 | if not self.exportLevel or self.splitRelated: 344 | ownerDir = self.currentOwner 345 | if self.suppressOwnerDir: 346 | ownerDir = "" 347 | fileName = FileNameForObject(self.baseDir, ownerDir, object.name, 348 | object.type) 349 | dirName = os.path.dirname(fileName) 350 | if dirName not in self.dirs: 351 | if not os.path.exists(dirName): 352 | os.makedirs(dirName) 353 | self.dirs[dirName] = None 354 | self.outFile = open(fileName, "w") 355 | self.exportLevel += 1 356 | Describer.ExportObject(self, object) 357 | self.exportLevel -= 1 358 | 359 | def ExportSource(self): 360 | """Export all of the source objects.""" 361 | print("Describing interdependent objects...", file = sys.stderr) 362 | for owner, name, type in self.RetrieveSourceObjects(): 363 | if type == "VIEW": 364 | continue 365 | if self.wantRelated and type in ("PACKAGE BODY", "TYPE BODY"): 366 | continue 367 | self.RetrieveAndExportObject(owner, name, type) 368 | 369 | def ExportViews(self): 370 | """Export all of the views.""" 371 | print("Describing views...", file = sys.stderr) 372 | self.ExportObjects(Object.ObjectIterator(self.environment, "AllViews", 373 | Statements.VIEWS, "where o.owner %s" % self.schemasClause, 374 | Object.View)) 375 | 376 | def SetOwner(self, owner, type): 377 | """Set the current owner being exported.""" 378 | if owner != self.currentOwner: 379 | self.currentOwner = owner 380 | 381 | 382 | def FileNameForObject(baseDir, owner, name, type): 383 | """Return the file name for the object.""" 384 | return os.path.join(baseDir, owner.lower(), type.lower().replace(" ", "_"), 385 | name.lower() + ".sql") 386 | 387 | -------------------------------------------------------------------------------- /cx_OracleParser/full/Processor.py: -------------------------------------------------------------------------------- 1 | """Module for processing dependencies in SQL.""" 2 | 3 | import cx_Exceptions 4 | import cx_Parser 5 | 6 | from . import Statements 7 | 8 | __all__ = [ "Processor" ] 9 | 10 | class Processor(cx_Parser.DispatchProcessor): 11 | systemIdentifiers = """ 12 | ascii binary_integer boolean ceil char chr count date decode dual 13 | floor greatest instr last_day least length lower lpad ltrim max min 14 | next_day null number nvl pls_integer raise_application_error 15 | replace round rpad rtrim sql substr sum to_char to_number to_date 16 | trim trunc upper varchar2""" 17 | 18 | def __init__(self): 19 | self.owner = None 20 | self.__directory = {} 21 | self.__identifiers = [] 22 | self.__AddScope() 23 | self.__ClearExternalReferences() 24 | for identifier in self.systemIdentifiers.split(): 25 | self.__AddIdentifier(identifier.upper()) 26 | 27 | def __AddIdentifier(self, identifier): 28 | """Adds an identifier to the current scope.""" 29 | self.__AddQualifiedIdentifier((identifier,)) 30 | 31 | def __AddQualifiedIdentifier(self, identifier): 32 | """Adds a qualified identifier to the current scope.""" 33 | identifier = tuple(identifier) 34 | self.__identifiers[-1][identifier] = None 35 | if identifier in self.__externalReferences: 36 | del self.__externalReferences[identifier] 37 | 38 | def __AddReference(self, identifier, ignoreUnqualified = False): 39 | """Add a reference to an identifier.""" 40 | if ignoreUnqualified and len(identifier) == 1: 41 | return 42 | if identifier in self.__externalReferences: 43 | return 44 | identifiers = [identifier[:n + 1] for n in range(len(identifier))] 45 | for scope in self.__identifiers: 46 | for identifier in identifiers: 47 | if identifier in scope: 48 | return 49 | self.__externalReferences[identifier] = None 50 | 51 | def __AddScope(self): 52 | """Add a level of scope.""" 53 | self.__identifiers.append({}) 54 | 55 | def __ClearExternalReferences(self): 56 | """Clear the external references defined.""" 57 | self.__externalReferences = {} 58 | 59 | def __ExternalReferences(self): 60 | """Return a sorted list of external references.""" 61 | childValues = list(self.__externalReferences.keys()) 62 | childValues.sort() 63 | return childValues 64 | 65 | def __IsIdentifier(self, tag): 66 | """Return true if the tag is for an identifier.""" 67 | return tag in ("unquoted_identifier", "quoted_identifier") 68 | 69 | def __LocalIdentifiers(self): 70 | """Return a sorted list of local identifiers.""" 71 | childValues = list(self.__identifiers[-1].keys()) 72 | childValues.sort() 73 | return childValues 74 | 75 | def __RemoveScope(self): 76 | """Remove a level of scope.""" 77 | self.__identifiers.pop() 78 | 79 | def argument(self, sql, tag, start, end, children): 80 | childTag, childValue = self.DispatchList(sql, children)[0] 81 | self.__AddIdentifier(childValue) 82 | 83 | def array_declaration(self, sql, tag, start, end, children): 84 | results = self.DispatchList(sql, children) 85 | childTag, childValue = results[1] 86 | self.__AddIdentifier(childValue) 87 | 88 | def check_constraint(self, sql, tag, start, end, children): 89 | results = self.DispatchList(sql, children) 90 | childTag, (constraintName, tableName) = results[0] 91 | return Statements.CheckConstraint(sql[start:end], self.__directory, 92 | self.owner, constraintName, tableName) 93 | 94 | def common_definition(self, sql, tag, start, end, children): 95 | childTag, childValue = self.Dispatch(sql, children[0]) 96 | self.__AddIdentifier(childValue) 97 | self.__AddScope() 98 | self.DispatchList(sql, children)[1:] 99 | 100 | def constraint_common_clause(self, sql, tag, start, end, children): 101 | results = self.DispatchList(sql, children) 102 | childTag, tableName = results[2] 103 | childTag, constraintName = results[5] 104 | return tag, (constraintName, tableName) 105 | 106 | def create_index_statement(self, sql, tag, start, end, children): 107 | indexName, tableName = [v for t, v in self.DispatchList(sql, children) 108 | if self.__IsIdentifier(t)] 109 | return Statements.Index(sql[start:end], self.__directory, self.owner, 110 | indexName, tableName) 111 | 112 | def create_package_statement(self, sql, tag, start, end, children): 113 | self.__ClearExternalReferences() 114 | self.__AddScope() 115 | isPackage = True 116 | for childTag, childValue in self.DispatchList(sql, children): 117 | if self.__IsIdentifier(childTag): 118 | objectName = childValue 119 | self.__AddIdentifier(childValue) 120 | if childTag == "KW_body": 121 | isPackage = False 122 | if isPackage: 123 | obj = Statements.Package(sql[start:end], self.__directory, 124 | self.owner, objectName, self.__ExternalReferences(), 125 | self.__LocalIdentifiers()) 126 | else: 127 | package = self.__directory.get((self.owner, objectName)) 128 | if package is None: 129 | raise MissingPackageHeader(objectName = objectName) 130 | for identifier in package.identifiers: 131 | self.__AddQualifiedIdentifier(identifier) 132 | obj = Statements.PackageBody(sql[start:end], self.__directory, 133 | self.owner, objectName, self.__ExternalReferences()) 134 | self.__RemoveScope() 135 | return obj 136 | 137 | def create_role_statement(self, sql, tag, start, end, children): 138 | tag, name = self.DispatchList(sql, children)[2] 139 | return Statements.Role(sql[start:end], self.__directory, name) 140 | 141 | def create_sequence_statement(self, sql, tag, start, end, children): 142 | childTag, name = self.DispatchList(sql, children)[2] 143 | return Statements.Sequence(sql[start:end], self.__directory, 144 | self.owner, name) 145 | 146 | def create_synonym_statement(self, sql, tag, start, end, children): 147 | results = self.DispatchList(sql, children) 148 | childTag, childValue = results[1] 149 | if childTag == "KW_public": 150 | childTag, name = results[3] 151 | childTag, refName = results[5] 152 | return Statements.PublicSynonym(sql[start:end], self.__directory, 153 | name, refName) 154 | else: 155 | childTag, name = results[2] 156 | childTag, refName = results[4] 157 | return Statements.Synonym(sql[start:end], self.__directory, 158 | self.owner, name, refName) 159 | 160 | def create_table_statement(self, sql, tag, start, end, children): 161 | name, = [v for t, v in self.DispatchList(sql, children) 162 | if self.__IsIdentifier(t)] 163 | return Statements.Table(sql[start:end], self.__directory, self.owner, 164 | name) 165 | 166 | def create_trigger_statement(self, sql, tag, start, end, children): 167 | name, tableName = [v for t, v in self.DispatchList(sql, children) 168 | if self.__IsIdentifier(t)] 169 | return Statements.Trigger(sql[start:end], self.__directory, self.owner, 170 | name) 171 | 172 | def create_type_statement(self, sql, tag, start, end, children): 173 | self.__ClearExternalReferences() 174 | self.__AddScope() 175 | for childTag, childValue in self.DispatchList(sql, children): 176 | if self.__IsIdentifier(childTag): 177 | self.__AddIdentifier(childValue) 178 | name, = self.__LocalIdentifiers()[0] 179 | references = self.__ExternalReferences() 180 | self.__RemoveScope() 181 | return Statements.Type(sql[start:end], self.__directory, self.owner, 182 | name, references) 183 | 184 | def create_user_statement(self, sql, tag, start, end, children): 185 | tag, name = self.DispatchList(sql, children)[2] 186 | return Statements.User(sql[start:end], self.__directory, name) 187 | 188 | def create_view_statement(self, sql, tag, start, end, children): 189 | self.__ClearExternalReferences() 190 | self.__AddScope() 191 | for childTag, childValue in self.DispatchList(sql, children): 192 | if self.__IsIdentifier(childTag): 193 | self.__AddIdentifier(childValue) 194 | viewName, = self.__LocalIdentifiers()[0] 195 | references = self.__ExternalReferences() 196 | self.__RemoveScope() 197 | return Statements.View(sql[start:end], self.__directory, self.owner, 198 | viewName, references) 199 | 200 | def cursor_definition(self, sql, tag, start, end, children): 201 | self.DispatchList(sql, children) 202 | self.__RemoveScope() 203 | return tag, [] 204 | 205 | def data_type(self, sql, tag, start, end, children): 206 | for childTag, childValue in self.DispatchList(sql, children): 207 | if childTag == "qualified_identifier": 208 | self.__AddReference(childValue) 209 | 210 | def delete_statement(self, sql, tag, start, end, children): 211 | for childTag, childValue in self.DispatchList(sql, children): 212 | if childTag == "qualified_identifier": 213 | self.__AddReference(childValue) 214 | 215 | def for_clause(self, sql, tag, start, end, children): 216 | childTag, childValue = self.DispatchList(sql, children)[0] 217 | self.__AddIdentifier(childValue) 218 | 219 | def foreign_key_constraint(self, sql, tag, start, end, children): 220 | results = self.DispatchList(sql, children) 221 | childTag, (constraintName, tableName) = results[0] 222 | childTag, referencedTable = results[5] 223 | return Statements.ForeignKey(sql[start:end], self.__directory, 224 | self.owner, constraintName, tableName, referencedTable) 225 | 226 | def from_clause(self, sql, tag, start, end, children): 227 | for childTag, childValue in self.DispatchList(sql, children): 228 | if childTag == "qualified_identifier": 229 | self.__AddReference(childValue) 230 | elif self.__IsIdentifier(childTag): 231 | self.__AddIdentifier(childValue) 232 | 233 | def function_declaration(self, sql, tag, start, end, children): 234 | self.DispatchList(sql, children) 235 | self.__RemoveScope() 236 | return tag, [] 237 | 238 | def function_expression(self, sql, tag, start, end, children): 239 | childTag, childValue = self.DispatchList(sql, children)[0] 240 | self.__AddReference(childValue) 241 | 242 | def grant_statement(self, sql, tag, start, end, children): 243 | items = self.DispatchList(sql, children) 244 | if items[2][0] == "KW_to": 245 | privileges = items[1][1] 246 | grantees = items[3][1] 247 | return Statements.Grant(sql[start:end], privileges, grantees) 248 | else: 249 | privileges = items[1][1] 250 | objectTuple = items[3][1] 251 | grantees = items[5][1] 252 | if len(objectTuple) == 1: 253 | objectOwner = self.owner 254 | objectName, = objectTuple 255 | else: 256 | objectOwner, objectName = objectTuple 257 | return Statements.Grant(sql[start:end], privileges, grantees, 258 | objectOwner, objectName) 259 | 260 | def identifier_list(self, sql, tag, start, end, children): 261 | return tag, [v for t, v in self.DispatchList(sql, children)] 262 | 263 | def identifier_modifier(self, sql, tag, start, end, children): 264 | return tag, None 265 | 266 | def insert_statement(self, sql, tag, start, end, children): 267 | childTag, childValue = self.DispatchList(sql, children)[2] 268 | self.__AddReference(childValue) 269 | 270 | def primary_key_constraint(self, sql, tag, start, end, children): 271 | results = self.DispatchList(sql, children) 272 | childTag, (constraintName, tableName) = results[0] 273 | return Statements.PrimaryKey(sql[start:end], self.__directory, 274 | self.owner, constraintName, tableName) 275 | 276 | def prior_expression(self, sql, tag, start, end, children): 277 | childTag, childValue = self.DispatchList(sql, children)[-1] 278 | self.__AddReference(childValue, ignoreUnqualified = True) 279 | 280 | def privilege(self, sql, tag, start, end, children): 281 | return tag, " ".join(sql[start:end].split()) 282 | 283 | def privilege_list(self, sql, tag, start, end, children): 284 | return tag, [v for t, v in self.DispatchList(sql, children)] 285 | 286 | def procedure_declaration(self, sql, tag, start, end, children): 287 | results = self.DispatchList(sql, children) 288 | self.__RemoveScope() 289 | return tag, [] 290 | 291 | def qualified_identifier(self, sql, tag, start, end, children): 292 | identifiers = [v for t, v in self.DispatchList(sql, children) if v] 293 | return tag, tuple(identifiers) 294 | 295 | def quoted_identifier(self, sql, tag, start, end, children): 296 | return tag, sql[start+1:end-1] 297 | 298 | def record_declaration(self, sql, tag, start, end, children): 299 | results = self.DispatchList(sql, children) 300 | childTag, childValue = results[1] 301 | self.__AddIdentifier(childValue) 302 | 303 | def return_clause(self, sql, tag, start, end, children): 304 | results = self.DispatchList(sql, children) 305 | childTag, childValue = results[1] 306 | self.__AddReference(childValue) 307 | 308 | def revoke_statement(self, sql, tag, start, end, children): 309 | items = self.DispatchList(sql, children) 310 | if items[2][0] == "KW_from": 311 | privileges = items[1][1] 312 | grantees = items[3][1] 313 | return Statements.Revoke(sql[start:end], privileges, grantees) 314 | else: 315 | privileges = items[1][1] 316 | objectTuple = items[3][1] 317 | grantees = items[5][1] 318 | if len(objectTuple) == 1: 319 | objectOwner = self.owner 320 | objectName, = objectTuple 321 | else: 322 | objectOwner, objectName = objectTuple 323 | return Statements.Revoke(sql[start:end], privileges, grantees, 324 | objectOwner, objectName) 325 | 326 | def select_into_statement(self, sql, tag, start, end, children): 327 | self.select_statement(sql, tag, start, end, children) 328 | 329 | def select_statement(self, sql, tag, start, end, children): 330 | self.__AddScope() 331 | for child in children: 332 | childTag, start, end, subChildren = child 333 | if childTag == "from_clause_list": 334 | self.Dispatch(sql, child) 335 | for child in children: 336 | childTag, start, end, subChildren = child 337 | if childTag != "from_clause_list": 338 | self.Dispatch(sql, child) 339 | self.__RemoveScope() 340 | return tag, None 341 | 342 | def simple_declaration(self, sql, tag, start, end, children): 343 | childTag, childValue = self.DispatchList(sql, children)[0] 344 | self.__AddIdentifier(childValue) 345 | 346 | def standalone_select_statement(self, sql, tag, start, end, children): 347 | self.__ClearExternalReferences() 348 | self.__AddScope() 349 | for childTag, childValue in self.DispatchList(sql, children): 350 | if self.__IsIdentifier(childTag): 351 | self.__AddIdentifier(childValue) 352 | references = self.__ExternalReferences() 353 | self.__RemoveScope() 354 | return Statements.StandaloneSelect(sql[start:end], self.__directory, 355 | references) 356 | 357 | def subtype_declaration(self, sql, tag, start, end, children): 358 | results = self.DispatchList(sql, children) 359 | childTag, childValue = results[1] 360 | self.__AddIdentifier(childValue) 361 | childTag, childValue = results[-1] 362 | if childTag == "range_clause": 363 | childTag, childValue = results[-2] 364 | self.__AddReference(childValue) 365 | 366 | def unique_constraint(self, sql, tag, start, end, children): 367 | results = self.DispatchList(sql, children) 368 | childTag, (constraintName, tableName) = results[0] 369 | return Statements.UniqueConstraint(sql[start:end], self.__directory, 370 | self.owner, constraintName, tableName) 371 | 372 | def unquoted_identifier(self, sql, tag, start, end, children): 373 | return tag, sql[start:end].upper() 374 | 375 | def update_statement(self, sql, tag, start, end, children): 376 | childTag, childValue = self.DispatchList(sql, children)[1] 377 | self.__AddReference(childValue) 378 | 379 | 380 | class MissingPackageHeader(cx_Exceptions.BaseException): 381 | message = "Missing header for package '%(objectName)s'" 382 | 383 | -------------------------------------------------------------------------------- /cx_OracleParser/full/Grammar.py: -------------------------------------------------------------------------------- 1 | """Grammar for parsing SQL as defined by Oracle.""" 2 | 3 | GRAMMAR = """ 4 | 5 | # tokens 6 | := '--' 7 | := [ \t\n] / comment 8 | := "'" 9 | := '"' 10 | := '/*' 11 | := '*/' 12 | := '\n' 13 | := ';' 14 | := [+-] 15 | := [0-9] 16 | := [Ee] 17 | := [A-Za-z] 18 | := '.' 19 | := ':' 20 | := ',' 21 | := '/' 22 | := '..' 23 | := ((KW_not, WS+)?, (KW_in / KW_between)) / KW_and / 24 | KW_or / KW_like / KW_escape / KW_mod 25 | := [+*/-] / '||' / '=' / '!=' / '<=' / '>=' / '<>' / '<' / 26 | '>' / ('(+)', WS*, '=') / (OPERATOR_KEYWORDS, WS+) 27 | := '(' 28 | := ')' 29 | := ':=' 30 | := '=>' 31 | 32 | # keywords 33 | KW_access := c"access" 34 | KW_accessed := c"accessed" 35 | KW_add := c"add" 36 | KW_admin := c"admin" 37 | KW_administer := c"administer" 38 | KW_all := c"all" 39 | KW_alter := c"alter" 40 | KW_analyze := c"analyze" 41 | KW_and := c"and" 42 | KW_any := c"any" 43 | KW_as := c"as" 44 | KW_audit := c"audit" 45 | KW_authid := c"authid" 46 | KW_backup := c"backup" 47 | KW_become := c"become" 48 | KW_begin := c"begin" 49 | KW_between := c"between" 50 | KW_body := c"body" 51 | KW_bulk := c"bulk" 52 | KW_by := c"by" 53 | KW_byte := c"byte" 54 | KW_cache := c"cache" 55 | KW_cascade := c"cascade" 56 | KW_case := c"case" 57 | KW_cast := c"cast" 58 | KW_char := c"char" 59 | KW_check := c"check" 60 | KW_close := c"close" 61 | KW_cluster := c"cluster" 62 | KW_collect := c"collect" 63 | KW_comment := c"comment" 64 | KW_commit := c"commit" 65 | KW_connect := c"connect" 66 | KW_constant := c"constant" 67 | KW_constraint := c"constraint" 68 | KW_context := c"context" 69 | KW_cost := c"cost" 70 | KW_count := c"count" 71 | KW_create := c"create" 72 | KW_cross := c"cross" 73 | KW_current_user := c"current_user" 74 | KW_cursor := c"cursor" 75 | KW_database := c"database" 76 | KW_debug := c"debug" 77 | KW_declare := c"declare" 78 | KW_default := c"default" 79 | KW_deferred := c"deferred" 80 | KW_definer := c"definer" 81 | KW_delete := c"delete" 82 | KW_dictionary := c"dictionary" 83 | KW_dimension := c"dimension" 84 | KW_directory := c"directory" 85 | KW_disable := c"disable" 86 | KW_distinct := c"distinct" 87 | KW_drop := c"drop" 88 | KW_else := c"else" 89 | KW_elsif := c"elsif" 90 | KW_end := c"end" 91 | KW_errors := c"errors" 92 | KW_escape := c"escape" 93 | KW_exception := c"exception" 94 | KW_execute := c"execute" 95 | KW_exempt := c"exempt" 96 | KW_exists := c"exists" 97 | KW_exit := c"exit" 98 | KW_externally := c"externally" 99 | KW_fetch := c"fetch" 100 | KW_flashback := c"flashback" 101 | KW_for := c"for" 102 | KW_forall := c"forall" 103 | KW_force := c"force" 104 | KW_foreign := c"foreign" 105 | KW_from := c"from" 106 | KW_function := c"function" 107 | KW_global := c"global" 108 | KW_globally := c"globally" 109 | KW_grant := c"grant" 110 | KW_group := c"group" 111 | KW_having := c"having" 112 | KW_identified := c"identified" 113 | KW_if := c"if" 114 | KW_immediate := c"immediate" 115 | KW_in := c"in" 116 | KW_index := c"index" 117 | KW_indextype := c"indextype" 118 | KW_initialized := c"initialized" 119 | KW_initially := c"initially" 120 | KW_insert := c"insert" 121 | KW_instead := c"instead" 122 | KW_intersect := c"intersect" 123 | KW_into := c"into" 124 | KW_is := c"is" 125 | KW_join := c"join" 126 | KW_key := c"key" 127 | KW_left := c"left" 128 | KW_library := c"library" 129 | KW_like := c"like" 130 | KW_limit := c"limit" 131 | KW_link := c"link" 132 | KW_lob := c"lob" 133 | KW_lock := c"lock" 134 | KW_log := c"log" 135 | KW_loop := c"loop" 136 | KW_manage := c"manage" 137 | KW_matched := c"matched" 138 | KW_materialized := c"materialized" 139 | KW_merge := c"merge" 140 | KW_minus := c"minus" 141 | KW_mod := c"mod" 142 | KW_nocache := c"nocache" 143 | KW_nocopy := c"nocopy" 144 | KW_not := c"not" 145 | KW_null := c"null" 146 | KW_object := c"object" 147 | KW_of := c"of" 148 | KW_on := c"on" 149 | KW_open := c"open" 150 | KW_operator := c"operator" 151 | KW_option := c"option" 152 | KW_or := c"or" 153 | KW_order := c"order" 154 | KW_organization := c"organization" 155 | KW_out := c"out" 156 | KW_outer := c"outer" 157 | KW_outline := c"outline" 158 | KW_package := c"package" 159 | KW_pipe := c"pipe" 160 | KW_pipelined := c"pipelined" 161 | KW_policy := c"policy" 162 | KW_preserve := c"preserve" 163 | KW_pragma := c"pragma" 164 | KW_primary := c"primary" 165 | KW_prior := c"prior" 166 | KW_privilege := c"privilege" 167 | KW_privileges := c"privileges" 168 | KW_procedure := c"procedure" 169 | KW_profile := c"profile" 170 | KW_public := c"public" 171 | KW_query := c"query" 172 | KW_raise := c"raise" 173 | KW_range := c"range" 174 | KW_read := c"read" 175 | KW_record := c"record" 176 | KW_ref := c"ref" 177 | KW_references := c"references" 178 | KW_refresh := c"refresh" 179 | KW_reject := c"reject" 180 | KW_replace := c"replace" 181 | KW_resource := c"resource" 182 | KW_restricted := c"restricted" 183 | KW_resumable := c"resumable" 184 | KW_return := c"return" 185 | KW_returning := c"returning" 186 | KW_reverse := c"reverse" 187 | KW_revoke := c"revoke" 188 | KW_rewrite := c"rewrite" 189 | KW_role := c"role" 190 | KW_rollback := c"rollback" 191 | KW_row := c"row" 192 | KW_rows := c"rows" 193 | KW_segment := c"segment" 194 | KW_select := c"select" 195 | KW_sequence := c"sequence" 196 | KW_session := c"session" 197 | KW_set := c"set" 198 | KW_snapshot := c"snapshot" 199 | KW_start := c"start" 200 | KW_storage := c"storage" 201 | KW_store := c"store" 202 | KW_subtype := c"subtype" 203 | KW_synonym := c"synonym" 204 | KW_sysdba := c"sysdba" 205 | KW_sysoper := c"sysoper" 206 | KW_system := c"system" 207 | KW_table := c"table" 208 | KW_tablespace := c"tablespace" 209 | KW_temporary := c"temporary" 210 | KW_then := c"then" 211 | KW_to := c"to" 212 | KW_transaction := c"transaction" 213 | KW_trigger := c"trigger" 214 | KW_type := c"type" 215 | KW_under := c"under" 216 | KW_union := c"union" 217 | KW_unique := c"unique" 218 | KW_unlimited := c"unlimited" 219 | KW_update := c"update" 220 | KW_user := c"user" 221 | KW_using := c"using" 222 | KW_values := c"values" 223 | KW_view := c"view" 224 | KW_when := c"when" 225 | KW_where := c"where" 226 | KW_while := c"while" 227 | KW_with := c"with" 228 | KW_write := c"write" 229 | 230 | # comments 231 | dash_comment := DASHES, -CR*, CR 232 | slash_comment := COMMENT_START, -COMMENT_END*, COMMENT_END 233 | comment := (dash_comment / slash_comment) 234 | 235 | # literals 236 | string_literal := (STRING_DELIM, -STRING_DELIM*, STRING_DELIM)+ 237 | integer_literal := SIGN?, DIGIT+ 238 | float_literal := SIGN?, DIGIT*, PERIOD?, DIGIT+, (EXPONENT, SIGN?, DIGIT+)? 239 | literal := (string_literal / float_literal / integer_literal) 240 | 241 | # identifiers 242 | unquoted_identifier := COLON?, LETTER, [a-zA-Z0-9_$#]* 243 | quoted_identifier := NAME_DELIM, [a-zA-Z0-9_$#.]+, NAME_DELIM 244 | >identifier< := quoted_identifier / unquoted_identifier 245 | identifier_list := identifier, (WS*, COMMA, WS*, identifier)* 246 | identifier_modifier := ('%' / '@'), identifier 247 | qualified_identifier := identifier, (PERIOD, identifier)*, 248 | identifier_modifier? 249 | qualified_identifier_list := qualified_identifier, 250 | (WS*, COMMA, WS*, qualified_identifier)* 251 | 252 | # expressions 253 | prior_expression := (KW_prior, WS+)?, qualified_identifier 254 | count_expression := KW_count, WS*, LPAREN, WS*, '*', WS*, RPAREN 255 | function_expression := qualified_identifier, WS*, 256 | (LPAREN, WS*, expression_list?, WS*, RPAREN)+, (PERIOD, identifier)* 257 | case_clause := KW_when, WS+, expression, WS+, KW_then, WS+, expression 258 | case_header := KW_case, (WS+, ?-KW_when, expression)? 259 | case_expression := case_header, WS+, case_clause, (WS+, case_clause)*, WS+, 260 | (KW_else, WS+, expression, WS+)?, KW_end 261 | cast_expression := KW_cast, WS*, LPAREN, WS*, expression, WS+, KW_as, WS+, 262 | data_type, WS*, RPAREN 263 | paren_expression := LPAREN, WS*, expression, WS*, RPAREN 264 | exists_expression := KW_exists, WS+, subquery 265 | >simple_expression< := literal / count_expression / cast_expression / 266 | function_expression / paren_expression_list / paren_expression / 267 | case_expression / subquery / exists_expression / prior_expression 268 | unary_operator := (KW_not / SIGN), WS* 269 | post_operator := WS+, KW_is, WS+, (KW_not, WS+)?, KW_null 270 | expression := (identifier, WS*, PLSQL_KW, WS*)?, unary_operator?, 271 | simple_expression, post_operator?, 272 | (WS*, OPERATOR, WS*, unary_operator?, simple_expression, post_operator?)* 273 | expression_list := expression, (WS*, COMMA, WS*, expression)* 274 | paren_expression_list := LPAREN, WS*, expression_list, WS*, RPAREN 275 | 276 | # statement enders 277 | := WS*, SEMICOLON 278 | := simple_statement_ender, WS+, SLASH 279 | 280 | # select statement 281 | select_keyword := (KW_from / KW_bulk / KW_cross / KW_into / KW_union / 282 | KW_minus / KW_intersect / KW_left / KW_join / KW_where / KW_start / 283 | KW_on / KW_for / KW_group), WS+ 284 | select_clause := expression, (WS+, ?-select_keyword, WS*, identifier)? 285 | select_clause_list := select_clause, 286 | (WS*, COMMA, WS*, select_clause)* 287 | >subquery< := LPAREN, WS*, select_statement, WS*, RPAREN 288 | table_cast_clause := KW_table, WS*, LPAREN, WS*, KW_cast, WS*, LPAREN, WS*, 289 | expression, WS+, KW_as, WS+, data_type, WS*, RPAREN, WS*, RPAREN 290 | table_clause := KW_table, WS*, LPAREN, WS*, expression, WS*, RPAREN 291 | from_clause := (table_cast_clause / table_clause / qualified_identifier / 292 | subquery), (WS+, ?-select_keyword, identifier)? 293 | join_type := (KW_left, WS+, KW_outer, WS+) / (KW_cross, WS+) 294 | join_clause := join_type?, KW_join, WS+, from_clause, (WS+, KW_on, WS+, 295 | expression)? 296 | table_ref := from_clause, (WS+, join_clause)* 297 | from_clause_list := table_ref, (WS*, COMMA, WS*, table_ref)* 298 | where_clause := KW_where, WS+, expression 299 | start_with_clause := KW_start, WS+, KW_with, WS+, expression, 300 | (WS+, KW_connect, WS+, KW_by, WS+, expression)? 301 | order_by_clause := KW_order, WS+, KW_by, WS+, select_clause_list 302 | group_by_clause := KW_group, WS+, KW_by, WS+, expression_list 303 | having_clause := KW_having, WS+, expression 304 | for_update_clause := KW_for, WS+, KW_update, 305 | (WS+, KW_of, WS+, identifier_list)? 306 | >select_modifier_clause< := start_with_clause / where_clause / 307 | for_update_clause / order_by_clause / group_by_clause / having_clause 308 | >simple_select_statement< := KW_select, WS+, (KW_distinct, WS+)?, 309 | ('*' / select_clause_list), WS+, KW_from, WS+, from_clause_list, 310 | (WS+, select_modifier_clause)* 311 | set_operation := (KW_union, (WS+, KW_all)?) / KW_minus / KW_intersect 312 | paren_select_statement := LPAREN, WS*, select_statement, WS*, RPAREN 313 | select_statement := simple_select_statement, 314 | ( WS+, set_operation, WS+, 315 | (simple_select_statement / paren_select_statement) )* 316 | 317 | # PL/SQL definitions 318 | >data_size_modifier< := WS+, (KW_char / KW_byte) 319 | >data_scale< := WS*, COMMA, WS*, integer_literal 320 | >data_size< := integer_literal, (data_scale / data_size_modifier)? 321 | data_type := qualified_identifier, (LPAREN, WS*, data_size, WS*, RPAREN)? 322 | argument := identifier, WS+, (KW_in, WS+)?, (KW_out, WS+)?, 323 | (KW_nocopy, WS+)?, data_type, (WS+, KW_default, WS+, expression)?, 324 | (WS*, PLSQL_EQ, WS*, expression)? 325 | argument_list := argument, (WS*, COMMA, WS*, argument)* 326 | common_definition := identifier, 327 | (WS*, LPAREN, WS*, argument_list, WS*, RPAREN)? 328 | procedure_definition := KW_procedure, WS+, common_definition 329 | procedure_body := WS+, (KW_is / KW_as), WS+, !, 330 | declaration_list?, WS*, basic_compound_statement, (WS+, identifier)? 331 | return_clause := KW_return, WS+, qualified_identifier 332 | function_definition := KW_function, WS+, common_definition, WS+, 333 | return_clause, (WS+, KW_pipelined)? 334 | cursor_definition := KW_cursor, WS+, common_definition, WS+, 335 | (KW_is / KW_as), WS+, select_statement 336 | range_clause := KW_range, WS+, integer_literal, PERIOD, PERIOD, 337 | integer_literal 338 | 339 | # PL/SQL declarations 340 | simple_declaration := ?-KW_begin, identifier, WS+, (KW_constant, WS+)?, 341 | data_type, (WS*, PLSQL_EQ, WS*, expression)? 342 | subtype_declaration := KW_subtype, WS+, identifier, WS+, KW_is, WS+, 343 | qualified_identifier, (WS+, range_clause)? 344 | record_item_list := simple_declaration, 345 | (WS*, COMMA, WS*, simple_declaration)* 346 | record_declaration := KW_type, WS+, identifier, WS+, KW_is, WS+, 347 | KW_record, WS*, LPAREN, WS*, record_item_list, WS*, RPAREN 348 | ref_cursor_declaration := KW_type, WS+, identifier, WS+, KW_is, WS+, 349 | KW_ref, WS+, KW_cursor, (WS+, KW_return, WS+, data_type)? 350 | index_by_clause := KW_index, WS+, KW_by, WS+, data_type 351 | array_declaration := KW_type, WS+, identifier, WS+, KW_is, WS+, 352 | KW_table, WS+, KW_of, WS+, data_type, (WS+, index_by_clause)? 353 | procedure_declaration := procedure_definition, procedure_body? 354 | function_declaration := function_definition, procedure_body? 355 | pragma_declaration := KW_pragma, WS+, identifier, 356 | (LPAREN, WS*, expression_list?, WS*, RPAREN)? 357 | declaration := (procedure_declaration / function_declaration / 358 | cursor_definition / subtype_declaration / record_declaration / 359 | array_declaration / ref_cursor_declaration / pragma_declaration / 360 | simple_declaration), WS*, SEMICOLON 361 | declaration_list := declaration, (WS+, ?-KW_end, declaration)* 362 | 363 | # PL/SQL control statements 364 | range_expression := expression, (WS*, RANGE, WS*, expression)? 365 | elsif_clause := KW_elsif, WS+, expression, WS+, KW_then, WS+, 366 | plsql_statement_list, WS+ 367 | if_statement := KW_if, WS+, expression, WS+, KW_then, WS+, 368 | plsql_statement_list, WS+, elsif_clause*, 369 | (KW_else, WS+, plsql_statement_list, WS+)?, KW_end, WS+, KW_if, 370 | simple_statement_ender 371 | for_clause := identifier, WS+, KW_in, WS+, (KW_reverse, WS+)?, 372 | (subquery / range_expression) 373 | loop_statement := KW_loop, WS+, plsql_statement_list, WS+, KW_end, WS+, 374 | KW_loop, simple_statement_ender 375 | for_statement := KW_for, WS+, for_clause, WS+, KW_loop, WS+, 376 | plsql_statement_list, WS+, KW_end, WS+, KW_loop, simple_statement_ender 377 | forall_statement := KW_forall, WS+, identifier, WS+, KW_in, WS+, 378 | expression, WS*, RANGE, WS*, expression, WS+, 379 | (insert_statement / update_statement / delete_statement) 380 | while_statement := KW_while, WS+, expression, WS+, KW_loop, WS+, 381 | plsql_statement_list, WS+, KW_end, WS+, KW_loop, simple_statement_ender 382 | 383 | # PL/SQL assignment statements 384 | assignment_statement := expression, WS*, PLSQL_EQ, WS*, expression, 385 | simple_statement_ender 386 | into_clause := (KW_bulk, WS+, KW_collect, WS+)?, KW_into, WS+, 387 | expression_list 388 | select_into_statement := KW_select, WS+, (KW_distinct, WS+)?, 389 | ('*' / select_clause_list), WS+, into_clause, WS+, KW_from, WS+, 390 | from_clause_list, (WS+, select_modifier_clause)* 391 | plsql_select_statement := select_into_statement, 392 | (WS+, set_operation, WS+, select_statement)*, simple_statement_ender 393 | 394 | # PL/SQL compound statements 395 | declare_section := KW_declare, WS+, (declaration_list, WS+)? 396 | exception_clause := KW_when, WS+, expression, WS+, KW_then, WS+, 397 | plsql_statement_list 398 | exception_clause_list := exception_clause, (WS+, exception_clause)* 399 | exception_section := KW_exception, WS+, exception_clause_list, WS+ 400 | basic_compound_statement := KW_begin, WS+, plsql_statement_list, WS+, 401 | exception_section?, KW_end 402 | compound_statement := declare_section?, basic_compound_statement 403 | 404 | # PL/SQL statements 405 | return_statement := KW_return, (WS+, expression)?, simple_statement_ender 406 | using_clause := (KW_out, WS+)?, expression 407 | using_clause_list := using_clause, (WS*, COMMA, WS*, using_clause)* 408 | execute_immediate_statement := KW_execute, WS+, KW_immediate, WS+, 409 | expression, (WS+, KW_using, WS+, using_clause_list)?, 410 | simple_statement_ender 411 | null_statement := KW_null, simple_statement_ender 412 | raise_statement := KW_raise, (WS+, qualified_identifier)?, 413 | simple_statement_ender 414 | close_statement := KW_close, WS+, identifier, simple_statement_ender 415 | fetch_statement := KW_fetch, WS+, identifier, WS+, into_clause, 416 | simple_statement_ender 417 | open_statement := KW_open, WS+, identifier, WS+, KW_for, WS+, 418 | select_statement, simple_statement_ender 419 | function_call := function_expression, simple_statement_ender 420 | procedure_call := ?-KW_end, qualified_identifier, simple_statement_ender 421 | block_statement := compound_statement, simple_statement_ender 422 | plsql_case_clause := KW_when, WS+, expression, WS+, KW_then, WS+, 423 | plsql_statement_list 424 | case_statement := case_header, WS+, plsql_case_clause, 425 | (WS+, plsql_case_clause)*, WS+, 426 | (KW_else, WS+, plsql_statement_list, WS+)?, KW_end, WS+, KW_case, 427 | simple_statement_ender 428 | pipe_statement := KW_pipe, WS+, expression, simple_statement_ender 429 | merge_update_clause := KW_when, WS+, KW_matched, WS+, KW_then, WS+, 430 | KW_update, WS+, KW_set, WS+, update_columns_clause_list, 431 | (WS+, where_clause)?, (WS+, KW_delete, WS+, where_clause)? 432 | merge_insert_clause := KW_when, WS+, KW_not, WS+, KW_matched, WS+, 433 | KW_then, WS+, KW_insert, WS+, columns_list?, values_clause, 434 | (WS*, where_clause)? 435 | merge_error_logging_clause := KW_log, WS+, KW_errors, 436 | (WS+, KW_into, WS+, qualified_identifier)?, 437 | (WS*, LPAREN, WS*, expression, WS*, RPAREN)?, 438 | (WS*, KW_reject, WS+, KW_limit, WS+, (KW_unlimited / integer_literal))? 439 | merge_header := KW_merge, WS+, KW_into, WS+, qualified_identifier, WS+, 440 | (?-KW_using, identifier, WS+)?, KW_using, WS+, 441 | (qualified_identifier / subquery), WS+, (?-KW_on, identifier, WS+)?, 442 | KW_on, WS*, LPAREN, WS*, expression, WS*, RPAREN 443 | merge_statement := merge_header, (WS*, merge_update_clause)?, 444 | (WS*, merge_insert_clause)?, 445 | (WS*, merge_error_logging_clause)?, simple_statement_ender 446 | plsql_statement := if_statement / for_statement / forall_statement / 447 | while_statement / loop_statement / plsql_select_statement / 448 | block_statement / return_statement / execute_immediate_statement / 449 | case_statement / pipe_statement / assignment_statement / 450 | insert_statement / update_statement / delete_statement / 451 | merge_statement / function_call / commit_statement / rollback_statement / 452 | exit_statement / raise_statement / null_statement / close_statement / 453 | fetch_statement / open_statement / procedure_call 454 | plsql_statement_list := plsql_statement, (WS+, plsql_statement)* 455 | 456 | # DML statements 457 | columns_list := LPAREN, WS*, qualified_identifier_list, WS*, RPAREN, WS* 458 | expression_columns_list := LPAREN, WS*, expression_list, WS*, RPAREN, WS* 459 | values_clause := KW_values, WS*, 460 | (paren_expression_list / qualified_identifier) 461 | update_columns_clause := qualified_identifier, WS*, '=', WS*, expression 462 | update_columns_clause_list := update_columns_clause, 463 | (WS*, COMMA, WS*, update_columns_clause)* 464 | returning_clause := WS+, KW_returning, WS+, expression_list, WS+, into_clause 465 | insert_statement := KW_insert, WS+, KW_into, WS+, qualified_identifier, WS+, 466 | columns_list?, (values_clause / select_statement), returning_clause?, 467 | simple_statement_ender 468 | update_statement := KW_update, WS+, qualified_identifier, WS+, 469 | (?-KW_set, identifier, WS+)?, KW_set, WS+, update_columns_clause_list, 470 | (WS+, where_clause)?, returning_clause?, simple_statement_ender 471 | delete_statement := KW_delete, WS+, (KW_from, WS+)?, qualified_identifier, 472 | (WS+, ?-KW_where, identifier)?, (WS+, where_clause)?, 473 | returning_clause?, simple_statement_ender 474 | standalone_select_statement := select_statement, simple_statement_ender 475 | 476 | # privilege manipulation statements 477 | privilege := 478 | (KW_administer, WS+, KW_database, WS+, KW_trigger) / 479 | (KW_analyze, WS+, KW_any) / 480 | (KW_alter, WS+, KW_any, WS+, KW_cluster) / 481 | (KW_alter, WS+, KW_any, WS+, KW_dimension) / 482 | (KW_alter, WS+, KW_any, WS+, KW_index) / 483 | (KW_alter, WS+, KW_any, WS+, KW_indextype) / 484 | (KW_alter, WS+, KW_any, WS+, KW_materialized, WS+, KW_view) / 485 | (KW_alter, WS+, KW_any, WS+, KW_outline) / 486 | (KW_alter, WS+, KW_any, WS+, KW_procedure) / 487 | (KW_alter, WS+, KW_any, WS+, KW_role) / 488 | (KW_alter, WS+, KW_any, WS+, KW_sequence) / 489 | (KW_alter, WS+, KW_any, WS+, KW_table) / 490 | (KW_alter, WS+, KW_any, WS+, KW_trigger) / 491 | (KW_alter, WS+, KW_any, WS+, KW_type) / 492 | (KW_alter, WS+, KW_database) / 493 | (KW_alter, WS+, KW_profile) / 494 | (KW_alter, WS+, KW_resource, WS+, KW_cost) / 495 | (KW_alter, WS+, KW_rollback, WS+, KW_segment) / 496 | (KW_alter, WS+, KW_session) / 497 | (KW_alter, WS+, KW_system) / 498 | (KW_alter, WS+, KW_tablespace) / 499 | (KW_alter, WS+, KW_user) / 500 | (KW_audit, WS+, KW_any) / 501 | (KW_audit, WS+, KW_system) / 502 | (KW_backup, WS+, KW_any, WS+, KW_table) / 503 | (KW_become, WS+, KW_user) / 504 | (KW_comment, WS+, KW_any, WS+, KW_table) / 505 | (KW_create, WS+, KW_any, WS+, KW_cluster) / 506 | (KW_create, WS+, KW_any, WS+, KW_context) / 507 | (KW_create, WS+, KW_any, WS+, KW_dimension) / 508 | (KW_create, WS+, KW_any, WS+, KW_directory) / 509 | (KW_create, WS+, KW_any, WS+, KW_index) / 510 | (KW_create, WS+, KW_any, WS+, KW_indextype) / 511 | (KW_create, WS+, KW_any, WS+, KW_library) / 512 | (KW_create, WS+, KW_any, WS+, KW_materialized, WS+, KW_view) / 513 | (KW_create, WS+, KW_any, WS+, KW_operator) / 514 | (KW_create, WS+, KW_any, WS+, KW_outline) / 515 | (KW_create, WS+, KW_any, WS+, KW_procedure) / 516 | (KW_create, WS+, KW_any, WS+, KW_sequence) / 517 | (KW_create, WS+, KW_any, WS+, KW_synonym) / 518 | (KW_create, WS+, KW_any, WS+, KW_table) / 519 | (KW_create, WS+, KW_any, WS+, KW_trigger) / 520 | (KW_create, WS+, KW_any, WS+, KW_type) / 521 | (KW_create, WS+, KW_any, WS+, KW_view) / 522 | (KW_create, WS+, KW_cluster) / 523 | (KW_create, WS+, KW_database, WS+, KW_link) / 524 | (KW_create, WS+, KW_dimension) / 525 | (KW_create, WS+, KW_indextype) / 526 | (KW_create, WS+, KW_library) / 527 | (KW_create, WS+, KW_materialized, WS+, KW_view) / 528 | (KW_create, WS+, KW_operator) / 529 | (KW_create, WS+, KW_procedure) / 530 | (KW_create, WS+, KW_profile) / 531 | (KW_create, WS+, KW_public, WS+, KW_database, WS+, KW_link) / 532 | (KW_create, WS+, KW_public, WS+, KW_synonym) / 533 | (KW_create, WS+, KW_role) / 534 | (KW_create, WS+, KW_rollback, WS+, KW_segment) / 535 | (KW_create, WS+, KW_sequence) / 536 | (KW_create, WS+, KW_session) / 537 | (KW_create, WS+, KW_snapshot) / 538 | (KW_create, WS+, KW_synonym) / 539 | (KW_create, WS+, KW_tablespace) / 540 | (KW_create, WS+, KW_table) / 541 | (KW_create, WS+, KW_trigger) / 542 | (KW_create, WS+, KW_type) / 543 | (KW_create, WS+, KW_user) / 544 | (KW_create, WS+, KW_view) / 545 | (KW_debug, WS+, KW_any, WS+, KW_procedure) / 546 | (KW_debug, WS+, KW_connect, WS+, KW_session) / 547 | (KW_delete, WS+, KW_any, WS+, KW_table) / 548 | (KW_drop, WS+, KW_any, WS+, KW_cluster) / 549 | (KW_drop, WS+, KW_any, WS+, KW_context) / 550 | (KW_drop, WS+, KW_any, WS+, KW_dimension) / 551 | (KW_drop, WS+, KW_any, WS+, KW_directory) / 552 | (KW_drop, WS+, KW_any, WS+, KW_index) / 553 | (KW_drop, WS+, KW_any, WS+, KW_indextype) / 554 | (KW_drop, WS+, KW_any, WS+, KW_library) / 555 | (KW_drop, WS+, KW_any, WS+, KW_materialized, WS+, KW_view) / 556 | (KW_drop, WS+, KW_any, WS+, KW_operator) / 557 | (KW_drop, WS+, KW_any, WS+, KW_outline) / 558 | (KW_drop, WS+, KW_any, WS+, KW_procedure) / 559 | (KW_drop, WS+, KW_any, WS+, KW_role) / 560 | (KW_drop, WS+, KW_any, WS+, KW_sequence) / 561 | (KW_drop, WS+, KW_any, WS+, KW_synonym) / 562 | (KW_drop, WS+, KW_any, WS+, KW_table) / 563 | (KW_drop, WS+, KW_any, WS+, KW_trigger) / 564 | (KW_drop, WS+, KW_any, WS+, KW_type) / 565 | (KW_drop, WS+, KW_any, WS+, KW_view) / 566 | (KW_drop, WS+, KW_public, WS+, KW_database, WS+, KW_link) / 567 | (KW_drop, WS+, KW_public, WS+, KW_synonym) / 568 | (KW_drop, WS+, KW_profile) / 569 | (KW_drop, WS+, KW_rollback, WS+, KW_segment) / 570 | (KW_drop, WS+, KW_tablespace) / 571 | (KW_drop, WS+, KW_user) / 572 | (KW_execute, WS+, KW_any, WS+, KW_indextype) / 573 | (KW_execute, WS+, KW_any, WS+, KW_operator) / 574 | (KW_execute, WS+, KW_any, WS+, KW_procedure) / 575 | (KW_execute, WS+, KW_any, WS+, KW_type) / 576 | (KW_exempt, WS+, KW_access, WS+, KW_policy) / 577 | (KW_flashback, WS+, KW_any, WS+, KW_table) / 578 | (KW_force, WS+, KW_any, WS+, KW_transaction) / 579 | (KW_force, WS+, KW_transaction) / 580 | (KW_grant, WS+, KW_any, WS+, KW_object, WS+, KW_privileges) / 581 | (KW_grant, WS+, KW_any, WS+, KW_object, WS+, KW_privilege) / 582 | (KW_grant, WS+, KW_any, WS+, KW_privilege) / 583 | (KW_grant, WS+, KW_any, WS+, KW_role) / 584 | (KW_insert, WS+, KW_any, WS+, KW_table) / 585 | (KW_lock, WS+, KW_any, WS+, KW_table) / 586 | (KW_manage, WS+, KW_tablespace) / 587 | (KW_query, WS+, KW_rewrite) / 588 | (KW_global, WS+, KW_query, WS+, KW_rewrite) / 589 | (KW_on, WS+, KW_commit, WS+, KW_refresh) / 590 | (KW_restricted, WS+, KW_session) / 591 | KW_resumable / 592 | (KW_select, WS+, KW_any, WS+, KW_dictionary) / 593 | (KW_select, WS+, KW_any, WS+, KW_sequence) / 594 | (KW_select, WS+, KW_any, WS+, KW_table) / 595 | KW_sysdba / 596 | KW_sysoper / 597 | (KW_unlimited, WS+, KW_tablespace) / 598 | (KW_under, WS+, KW_any, WS+, KW_type) / 599 | (KW_under, WS+, KW_any, WS+, KW_view) / 600 | (KW_update, WS+, KW_any, WS+, KW_table) / 601 | KW_select / KW_insert / KW_update / KW_delete / 602 | KW_all / KW_alter / KW_debug / KW_execute / KW_flashback / KW_index / 603 | (KW_on, WS+, KW_commit, WS+, KW_refresh) / 604 | (KW_query, WS+, KW_rewrite) / KW_read / KW_references / KW_under / 605 | KW_write / identifier 606 | privilege_list := privilege, (WS*, COMMA, WS*, privilege)* 607 | grant_statement := KW_grant, WS+, privilege_list, WS+, 608 | (KW_on, WS+, qualified_identifier, WS+)?, KW_to, WS+, identifier_list, 609 | (WS*, KW_with, WS+, (KW_grant / KW_admin), WS+, KW_option, WS*)?, 610 | simple_statement_ender 611 | revoke_statement := KW_revoke, WS+, privilege_list, WS+, 612 | (KW_on, WS+, qualified_identifier, WS+)?, KW_from, WS+, identifier_list, 613 | simple_statement_ender 614 | 615 | # DDL statements 616 | create_or_replace_clause := KW_create, WS+, (KW_or, WS+, KW_replace, WS+)? 617 | create_view_statement := create_or_replace_clause, KW_view, WS+, !, 618 | identifier, WS+, KW_as, WS+, select_statement, simple_statement_ender 619 | commit_statement := KW_commit, simple_statement_ender 620 | rollback_statement := KW_rollback, simple_statement_ender 621 | exit_statement := KW_exit, (WS+, KW_when, WS+, expression)?, 622 | simple_statement_ender 623 | package_init_section := KW_begin, WS+, plsql_statement_list, WS+, 624 | exception_section? 625 | create_package_statement := create_or_replace_clause, KW_package, WS+, !, 626 | (KW_body, WS+)?, identifier, WS+, 627 | (KW_authid, WS+, (KW_current_user / KW_definer), WS+)?, (KW_is / KW_as), 628 | WS+, declaration_list, WS+, package_init_section?, KW_end, 629 | (WS+, identifier)?, complex_statement_ender 630 | column_clause := identifier, WS+, data_type, 631 | (WS+, KW_default, WS+, expression)?, (WS+, KW_not, WS+, KW_null)? 632 | column_clause_list := column_clause, 633 | (WS*, COMMA, WS*, (embedded_primary_key_constraint / column_clause))* 634 | global_temp_clause := KW_on, WS+, KW_commit, WS+, (KW_delete / KW_preserve), 635 | WS+, KW_rows 636 | storage_clause := (KW_tablespace, WS+, identifier) / 637 | (KW_disable, WS+, KW_storage, WS+, KW_in, WS+, KW_row) / 638 | (KW_organization, WS+, KW_index) 639 | lob_clause := KW_lob, WS*, LPAREN, WS*, identifier, WS*, RPAREN, WS*, 640 | KW_store, WS+, KW_as, WS*, LPAREN, (WS*, storage_clause)+, WS*, RPAREN 641 | initially_deferred_option := WS*, KW_initially, WS+, KW_deferred 642 | indexed_constraint_option := initially_deferred_option / 643 | (WS*, KW_using, WS+, KW_index, WS+, storage_clause) 644 | create_table_statement := KW_create, WS+, 645 | (KW_global, WS+, KW_temporary, WS+)?, KW_table, WS+, identifier, WS*, 646 | LPAREN, WS*, column_clause_list, WS*, RPAREN, 647 | (WS*, global_temp_clause / storage_clause / lob_clause)*, 648 | simple_statement_ender 649 | constraint_common_clause := KW_alter, WS+, KW_table, WS+, identifier, WS+, 650 | KW_add, WS+, KW_constraint, WS+, identifier, WS+ 651 | primary_key_constraint := constraint_common_clause, KW_primary, WS+, KW_key, 652 | WS*, columns_list, indexed_constraint_option*, simple_statement_ender 653 | embedded_primary_key_constraint := KW_constraint, WS+, identifier, WS+, 654 | KW_primary, WS+, KW_key, WS+, columns_list, indexed_constraint_option* 655 | unique_constraint := constraint_common_clause, KW_unique, WS*, columns_list, 656 | indexed_constraint_option*, simple_statement_ender 657 | cascade_clause := KW_on, WS+, KW_delete, WS+, 658 | ( KW_cascade / (KW_set, WS+, KW_null)) 659 | foreign_key_constraint := constraint_common_clause, KW_foreign, WS+, KW_key, 660 | WS*, columns_list, KW_references, WS+, qualified_identifier, WS*, 661 | columns_list, cascade_clause?, initially_deferred_option?, 662 | simple_statement_ender 663 | check_constraint := constraint_common_clause, KW_check, WS*, LPAREN, WS*, 664 | expression, WS*, RPAREN, initially_deferred_option?, 665 | simple_statement_ender 666 | create_index_statement := KW_create, WS+, (KW_unique, WS+)?, KW_index, WS+, 667 | identifier, WS+, KW_on, WS+, identifier, WS*, expression_columns_list, 668 | storage_clause*, simple_statement_ender 669 | create_sequence_statement := KW_create, WS+, KW_sequence, WS+, identifier, 670 | ((WS+, KW_cache, WS+, DIGIT+) / (WS+, KW_nocache))?, 671 | simple_statement_ender 672 | create_synonym_statement := KW_create, WS+, (KW_public, WS+)?, KW_synonym, 673 | WS+, identifier, WS+, KW_for, WS+, qualified_identifier, 674 | simple_statement_ender 675 | type_object := KW_object, WS*, LPAREN, WS*, column_clause_list, WS*, RPAREN 676 | type_list := KW_table, WS+, KW_of, WS+, data_type 677 | create_type_statement := create_or_replace_clause, KW_type, WS+, !, 678 | identifier, WS+, KW_as, WS+, (type_object / type_list), 679 | complex_statement_ender 680 | triggering_op := KW_insert / KW_update / KW_delete 681 | triggering_op_list := triggering_op, (WS+, KW_or, WS+, triggering_op)* 682 | create_trigger_statement := create_or_replace_clause, KW_trigger, WS+, !, 683 | identifier, WS+, KW_instead, WS+, KW_of, WS+, triggering_op_list, WS+, 684 | KW_on, WS+, identifier, WS+, compound_statement, complex_statement_ender 685 | default_tablespace_clause := KW_default, WS+, KW_tablespace, WS+, identifier 686 | temp_tablespace_clause := KW_temporary, WS+, KW_tablespace, WS+, identifier 687 | create_role_statement := KW_create, WS+, KW_role, WS+, identifier, 688 | (WS+, KW_identified, WS+, KW_by, WS+, identifier)?, 689 | simple_statement_ender 690 | create_user_statement := KW_create, WS+, KW_user, WS+, identifier, WS+, 691 | KW_identified, WS+, KW_by, WS+, identifier, 692 | (WS+, default_tablespace_clause)?, (WS+, temp_tablespace_clause)?, 693 | simple_statement_ender 694 | context_statement_accessed_option := KW_accessed, WS+, KW_globally 695 | context_statement_initialized_option := KW_initialized, WS+, 696 | (KW_externally / KW_globally) 697 | context_statement_option := context_statement_accessed_option / 698 | context_statement_initialized_option 699 | create_context_statement := create_or_replace_clause, KW_context, WS+, 700 | identifier, WS+, KW_using, WS+, qualified_identifier, 701 | (WS+, context_statement_option)?, simple_statement_ender 702 | create_procedure_statement := create_or_replace_clause, procedure_definition, 703 | procedure_body, complex_statement_ender 704 | create_function_statement := create_or_replace_clause, function_definition, 705 | procedure_body, complex_statement_ender 706 | 707 | # SQL statements 708 | >sql_statement< := insert_statement / update_statement / delete_statement / 709 | standalone_select_statement / create_table_statement / 710 | create_view_statement / primary_key_constraint / unique_constraint / 711 | foreign_key_constraint / check_constraint / create_index_statement / 712 | create_sequence_statement / revoke_statement / create_synonym_statement / 713 | grant_statement / commit_statement / rollback_statement / 714 | create_package_statement / create_user_statement / 715 | create_role_statement / create_type_statement / 716 | create_trigger_statement / create_context_statement / 717 | create_procedure_statement / create_function_statement 718 | 719 | # file 720 | file := (WS*, sql_statement)*, WS* 721 | 722 | """ 723 | 724 | -------------------------------------------------------------------------------- /cx_OracleObject/Object.py: -------------------------------------------------------------------------------- 1 | """Defines classes used for describing Oracle objects.""" 2 | 3 | 4 | import cx_Exceptions 5 | import cx_OracleUtils 6 | import sys 7 | 8 | from . import Statements 9 | from . import Utils 10 | 11 | class Object(object): 12 | """Base class for describing Oracle objects.""" 13 | supportsReferencedSynonyms = True 14 | 15 | def __init__(self, environment, owner, name, type): 16 | self.environment = environment 17 | self.owner = owner 18 | self.ownerForOutput = environment.NameForOutput(owner) 19 | self.name = name 20 | self.nameForOutput = environment.NameForOutput(name) 21 | self.type = type 22 | 23 | def ReferencedSynonyms(self): 24 | """Return an iterator for the referencing synonyms for the object.""" 25 | clause = "where table_owner = :owner and table_name = :name" 26 | return ObjectIterator(self.environment, "ReferencedSynonyms", 27 | Statements.SYNONYMS, clause, Synonym, 28 | owner = self.owner, name = self.name) 29 | 30 | 31 | class ObjectWithComments(Object): 32 | """Base class for describing Oracle objects which have comments.""" 33 | 34 | def ExportComments(self, outFile): 35 | cursor, isPrepared = self.environment.Cursor("TableComments") 36 | if not isPrepared: 37 | cursor.prepare(""" 38 | select comments 39 | from %s_tab_comments 40 | where owner = :owner 41 | and table_name = :name 42 | and comments is not null 43 | order by comments""" % \ 44 | self.environment.ViewPrefix()) 45 | cursor.execute(None, 46 | owner = self.owner, 47 | name = self.name) 48 | for comments, in cursor: 49 | print("comment on table", self.nameForOutput, "is", end=' ', 50 | file = outFile) 51 | print("%s;" % cx_OracleUtils.QuotedString(comments), 52 | file = outFile) 53 | print(file = outFile) 54 | cursor, isPrepared = self.environment.Cursor("ColumnComments") 55 | if not isPrepared: 56 | cursor.prepare(""" 57 | select 58 | column_name, 59 | comments 60 | from %s_col_comments 61 | where owner = :owner 62 | and table_name = :name 63 | and comments is not null 64 | order by column_name, comments""" % \ 65 | self.environment.ViewPrefix()) 66 | cursor.execute(None, 67 | owner = self.owner, 68 | name = self.name) 69 | for name, comments in cursor: 70 | nameForOutput = "%s.%s" % \ 71 | (self.nameForOutput, self.environment.NameForOutput(name)) 72 | print("comment on column", nameForOutput, "is", end=' ', 73 | file = outFile) 74 | print("%s;" % cx_OracleUtils.QuotedString(comments), 75 | file = outFile) 76 | print(file = outFile) 77 | 78 | 79 | class ObjectWithPrivileges(Object): 80 | """Base class for describing Oracle objects which have privileges.""" 81 | 82 | def __MergedPrivilegeSets(self): 83 | """Return a list of merged privilege sets for export.""" 84 | 85 | # sort privileges by privilege and grantable 86 | granteesByPrivilege = {} 87 | for privilege, grantee, grantable in self.__RetrievePrivileges(): 88 | key = (privilege, grantable) 89 | grantees = granteesByPrivilege.setdefault(key, []) 90 | grantees.append(grantee) 91 | 92 | # create privilege sets 93 | keys = list(granteesByPrivilege.keys()) 94 | keys.sort() 95 | privilegeSets = [] 96 | for key in keys: 97 | foundSet = False 98 | privilege, grantable = key 99 | grantees = granteesByPrivilege[key] 100 | grantees.sort() 101 | for setGrantable, setPrivileges, setGrantees in privilegeSets: 102 | if grantable == setGrantable and grantees == setGrantees: 103 | foundSet = True 104 | setPrivileges.append(privilege) 105 | break 106 | if not foundSet: 107 | privilegeSets.append((grantable, [privilege], grantees)) 108 | 109 | return privilegeSets 110 | 111 | def __RetrievePrivileges(self): 112 | """Retrieve the privileges from the database.""" 113 | cursor, isPrepared = self.environment.Cursor("Privileges") 114 | if not isPrepared: 115 | if self.environment.useDbaViews: 116 | tableName = "dba_tab_privs" 117 | else: 118 | tableName = "all_tab_privs_made" 119 | cursor.prepare(""" 120 | select distinct 121 | lower(privilege), 122 | lower(grantee), 123 | grantable 124 | from %s 125 | where owner = :owner 126 | and table_name = :name""" % tableName) 127 | cursor.execute(None, 128 | owner = self.owner, 129 | name = self.name) 130 | return cursor.fetchall() 131 | 132 | def __UnmergedPrivilegeSets(self): 133 | """Return a list of unmerged privilege sets for export.""" 134 | privileges = self.__RetrievePrivileges() 135 | privileges.sort() 136 | return [(grantable, [privilege], [grantee]) 137 | for privilege, grantee, grantable in privileges] 138 | 139 | def _OutputGrantees(self, outFile, grantees, grantable): 140 | """Output grantees and grantable clause as needed.""" 141 | if grantable == "YES": 142 | finalClause = "\nwith grant option;" 143 | else: 144 | finalClause = ";" 145 | if len(grantees) == 1: 146 | print("to", grantees[0] + finalClause, file = outFile) 147 | else: 148 | clauses = Utils.ClausesForOutput(grantees, " ", " ", ",") 149 | print("to", file = outFile) 150 | print(clauses + finalClause, file = outFile) 151 | print(file = outFile) 152 | 153 | def ExportPrivileges(self, outFile, mergeGrants): 154 | """Export privileges for the object to the file as SQL statements.""" 155 | if mergeGrants: 156 | privilegeSets = self.__MergedPrivilegeSets() 157 | else: 158 | privilegeSets = self.__UnmergedPrivilegeSets() 159 | for grantable, privileges, grantees in privilegeSets: 160 | if len(privileges) == 1: 161 | print("grant", privileges[0], file = outFile) 162 | else: 163 | clauses = Utils.ClausesForOutput(privileges, " ", " ", ",") 164 | print("grant", file = outFile) 165 | print(clauses, file = outFile) 166 | print("on", self.nameForOutput, file = outFile) 167 | self._OutputGrantees(outFile, grantees, grantable) 168 | 169 | 170 | class ObjectWithColumnPrivileges(ObjectWithPrivileges): 171 | """Base class for describing objects which have column privileges.""" 172 | 173 | def __MergedPrivilegeSets(self): 174 | """Return a list of merged privilege sets for export.""" 175 | 176 | # sort privileges by privilege, column and grantable 177 | granteesByPrivilege = {} 178 | for privilege, column, grantee, \ 179 | grantable in self.__RetrievePrivileges(): 180 | key = (privilege, column, grantable) 181 | grantees = granteesByPrivilege.setdefault(key, []) 182 | grantees.append(grantee) 183 | 184 | # create privilege sets 185 | keys = list(granteesByPrivilege.keys()) 186 | keys.sort() 187 | privilegeSets = [] 188 | for key in keys: 189 | foundSet = False 190 | privilege, column, grantable = key 191 | grantees = granteesByPrivilege[key] 192 | grantees.sort() 193 | for setGrantable, setPrivilege, setGrantees, \ 194 | setColumns in privilegeSets: 195 | if grantable == setGrantable and grantees == setGrantees \ 196 | and privilege == setPrivilege: 197 | foundSet = True 198 | setColumns.append(column) 199 | break 200 | if not foundSet: 201 | privilegeSet = (grantable, privilege, grantees, [column]) 202 | privilegeSets.append(privilegeSet) 203 | 204 | return privilegeSets 205 | 206 | def __RetrievePrivileges(self): 207 | """Retrieve the privileges from the database.""" 208 | cursor, isPrepared = self.environment.Cursor("ColumnPrivileges") 209 | if not isPrepared: 210 | if self.environment.useDbaViews: 211 | tableName = "dba_col_privs" 212 | else: 213 | tableName = "all_col_privs_made" 214 | cursor.prepare(""" 215 | select distinct 216 | lower(privilege), 217 | lower(column_name), 218 | lower(grantee), 219 | grantable 220 | from %s 221 | where owner = :owner 222 | and table_name = :name""" % tableName) 223 | cursor.execute(None, 224 | owner = self.owner, 225 | name = self.name) 226 | return cursor.fetchall() 227 | 228 | def __UnmergedPrivilegeSets(self): 229 | """Return a list of unmerged privilege sets for export.""" 230 | privileges = self.__RetrievePrivileges() 231 | privileges.sort() 232 | return [(grantable, privilege, [grantee], [column]) 233 | for privilege, column, grantee, grantable in privileges] 234 | 235 | def ExportPrivileges(self, outFile, mergeGrants): 236 | """Export privileges for the object to the file as SQL statements.""" 237 | super(ObjectWithColumnPrivileges, self).ExportPrivileges(outFile, 238 | mergeGrants) 239 | if mergeGrants: 240 | privilegeSets = self.__MergedPrivilegeSets() 241 | else: 242 | privilegeSets = self.__UnmergedPrivilegeSets() 243 | for grantable, privilege, grantees, columns in privilegeSets: 244 | privilegeForOutput = "%s(%s)" % (privilege, ", ".join(columns)) 245 | print("grant", privilegeForOutput, file = outFile) 246 | print("on", self.nameForOutput, file = outFile) 247 | self._OutputGrantees(outFile, grantees, grantable) 248 | 249 | 250 | class UserOrRole(ObjectWithPrivileges): 251 | """Base class for objects which have system privileges.""" 252 | supportsReferencedSynonyms = False 253 | 254 | def __GetRolePrivileges(self): 255 | """Retrieve the role privileges from the database.""" 256 | cursor, isPrepared = self.environment.Cursor("RolePrivileges") 257 | if not isPrepared: 258 | cursor.prepare(""" 259 | select 260 | admin_option, 261 | lower(granted_role) 262 | from dba_role_privs 263 | where grantee = :grantee""") 264 | cursor.execute(None, grantee = self.name) 265 | return cursor.fetchall() 266 | 267 | def __GetSysPrivileges(self): 268 | """Retrieve the system privileges from the database.""" 269 | cursor, isPrepared = self.environment.Cursor("SysPrivileges") 270 | if not isPrepared: 271 | cursor.prepare(""" 272 | select 273 | admin_option, 274 | lower(privilege) 275 | from dba_sys_privs 276 | where grantee = :grantee""") 277 | cursor.execute(None, grantee = self.name) 278 | return cursor.fetchall() 279 | 280 | def __MergedPrivilegeSets(self, allPrivileges): 281 | """Merge the privileges, if possible.""" 282 | privilegeDict = {} 283 | allPrivileges.sort() 284 | for adminOption, privilege in allPrivileges: 285 | privileges = privilegeDict.get(adminOption) 286 | if privileges is None: 287 | privileges = privilegeDict[adminOption] = [] 288 | privileges.append(privilege) 289 | return list(privilegeDict.items()) 290 | 291 | def __UnmergedPrivilegeSets(self, allPrivileges): 292 | """Return a list of unmerged privilege sets for export.""" 293 | allPrivileges.sort() 294 | return [(adminOption, [privilege]) 295 | for adminOption, privilege in allPrivileges] 296 | 297 | def ExportPrivileges(self, outFile, mergeGrants): 298 | """Export privileges for the object to the file as SQL statements.""" 299 | sysPrivs = self.__GetSysPrivileges() 300 | rolePrivs = self.__GetRolePrivileges() 301 | if mergeGrants: 302 | privilegeSets = self.__MergedPrivilegeSets(sysPrivs) + \ 303 | self.__MergedPrivilegeSets(rolePrivs) 304 | else: 305 | privilegeSets = self.__UnmergedPrivilegeSets(sysPrivs) + \ 306 | self.__UnmergedPrivilegeSets(rolePrivs) 307 | for adminOption, privileges in privilegeSets: 308 | finalClause = ";" 309 | if adminOption == "YES": 310 | finalClause = "\nwith admin option;" 311 | if len(privileges) == 1: 312 | print("grant", privileges[0], file = outFile) 313 | else: 314 | clauses = Utils.ClausesForOutput(privileges, " ", " ", ",") 315 | print("grant", file = outFile) 316 | print(clauses, file = outFile) 317 | print("to", self.nameForOutput + finalClause, file = outFile) 318 | print(file = outFile) 319 | 320 | 321 | class ObjectWithStorage(Object): 322 | """Base class for objects which have storage.""" 323 | 324 | def AddStorageClauses(self, clauses, wantTablespace, wantStorage): 325 | """Add storage clauses to the list of clauses to be exported.""" 326 | if wantTablespace and self.tablespaceName is not None: 327 | clauses.append("tablespace %s" % self.tablespaceName.lower()) 328 | if wantStorage and self.initialExtent is not None: 329 | clauses.append("storage (") 330 | clauses.append(" initial %s next %s" % \ 331 | (Utils.SizeForOutput(self.initialExtent), 332 | Utils.SizeForOutput(self.nextExtent))) 333 | if self.maxExtents == 2147483645: 334 | clauses.append(" minextents %d maxextents unlimited" % \ 335 | self.minExtents) 336 | else: 337 | clauses.append(" minextents %d maxextents %d" % \ 338 | (self.minExtents, self.maxExtents)) 339 | if self.percentIncrease is not None: 340 | clauses.append(" pctincrease %d" % self.percentIncrease) 341 | clauses.append(")") 342 | 343 | def RetrievePartitionColumns(self): 344 | """Retrieve partition columns for the object.""" 345 | cursor, isPrepared = self.environment.Cursor("PartitionColumns") 346 | if not isPrepared: 347 | cursor.prepare(""" 348 | select column_name 349 | from %s_part_key_columns 350 | where owner = :owner 351 | and name = :name 352 | order by column_position""" % \ 353 | self.environment.ViewPrefix()) 354 | cursor.execute(None, 355 | owner = self.owner, 356 | name = self.name) 357 | self.partitionColumns = [n for n, in cursor] 358 | 359 | 360 | class ObjectWithTriggers(Object): 361 | """Base class for objects which have triggers.""" 362 | 363 | def Triggers(self): 364 | """Return an interator for the triggers associated with the object.""" 365 | return ObjectIterator(self.environment, "AssocTriggers", 366 | Statements.TRIGGERS, 367 | "where o.table_owner = :owner and o.table_name = :name", 368 | Trigger, owner = self.owner, name = self.name) 369 | 370 | 371 | class Context(Object): 372 | """Class for describing contexts.""" 373 | 374 | def __init__(self, environment, row): 375 | name, self.packageOwner, self.packageName, self.contextType = row 376 | Object.__init__(self, environment, "SYSTEM", name, "CONTEXT") 377 | 378 | def Export(self, outFile): 379 | """Export the object as a SQL statement.""" 380 | packageName = "%s.%s" % \ 381 | (self.environment.NameForOutput(self.packageOwner), 382 | self.environment.NameForOutput(self.packageName)) 383 | print("create or replace context", self.nameForOutput, file = outFile) 384 | if self.contextType == "ACCESSED LOCALLY": 385 | print("using", packageName + ";", file = outFile) 386 | else: 387 | print("using", packageName, file = outFile) 388 | print(self.contextType.lower() + ";", file = outFile) 389 | print(file = outFile) 390 | 391 | 392 | class Constraint(Object): 393 | """Class for describing constraints.""" 394 | supportsReferencedSynonyms = False 395 | 396 | def __init__(self, environment, row): 397 | owner, name, self.constraintType, self.tableName, searchCondition, \ 398 | refOwner, refConstraintName, deleteRule, deferred, \ 399 | deferrable = row 400 | Object.__init__(self, environment, owner, name, "CONSTRAINT") 401 | self.deferrable = (deferrable == "DEFERRABLE") 402 | self.initiallyDeferred = (deferred == "DEFERRED") 403 | self.relatedIndex = None 404 | if self.constraintType == "C": 405 | self.condition = searchCondition.strip() 406 | elif self.constraintType in ("P", "U"): 407 | self.__RetrieveColumns() 408 | for index in ObjectIterator(self.environment, "Index", 409 | Statements.INDEXES_ANY, 410 | "where o.owner = :owner and o.index_name = :name", 411 | Index, owner = self.owner, name = self.name): 412 | self.relatedIndex = index 413 | environment.CacheObject(self) 414 | elif self.constraintType == "R": 415 | self.__RetrieveColumns() 416 | self.deleteRule = None 417 | if deleteRule != "NO ACTION": 418 | self.deleteRule = deleteRule.lower() 419 | self.refConstraint = environment.CachedObject(refOwner, 420 | refConstraintName) 421 | if not self.refConstraint: 422 | refConstraints = list(ObjectIterator(self.environment, 423 | "Constraint", Statements.CONSTRAINTS, 424 | "where o.owner = :owner and o.constraint_name = :name", 425 | Constraint, owner = refOwner, name = refConstraintName)) 426 | if not refConstraints: 427 | raise CannotFetchReferencedConstraintInfo( 428 | owner = refOwner, name = refConstraintName) 429 | self.refConstraint, = refConstraints 430 | 431 | def __FinalClauses(self, wantTablespace, wantStorage): 432 | """Return a string containing the final clauses for the constraint.""" 433 | clause = "" 434 | if self.initiallyDeferred: 435 | clause += " initially deferred" 436 | elif self.deferrable: 437 | clause += " deferrable" 438 | if self.relatedIndex: 439 | clauses = [] 440 | self.relatedIndex.AddStorageClauses(clauses, wantTablespace, 441 | wantStorage) 442 | clause += Utils.ClausesForOutput(clauses, " using index ", " ", 443 | "") 444 | return clause 445 | 446 | def __RetrieveColumns(self): 447 | """Retrieve the columns for the constraint.""" 448 | cursor, isPrepared = self.environment.Cursor("ConsColumns") 449 | if not isPrepared: 450 | cursor.prepare(""" 451 | select column_name 452 | from %s_cons_columns 453 | where owner = :owner 454 | and constraint_name = :name 455 | order by position""" % self.environment.ViewPrefix()) 456 | cursor.execute(None, 457 | owner = self.owner, 458 | name = self.name) 459 | self.columns = [self.environment.NameForOutput(c) for c, in cursor] 460 | 461 | def Export(self, outFile, wantTablespace, wantStorage): 462 | """Export the object as a SQL statement.""" 463 | nameForOutput = self.environment.NameForOutput(self.tableName) 464 | print("alter table", nameForOutput, file = outFile) 465 | print("add constraint %s" % self.nameForOutput, file = outFile) 466 | finalClauses = self.__FinalClauses(wantTablespace, wantStorage) 467 | if self.constraintType == "C": 468 | print("check (%s)%s;" % (self.condition, finalClauses), 469 | file = outFile) 470 | elif self.constraintType == "P": 471 | clauses = Utils.ClausesForOutput(self.columns, " ", " ", ",") 472 | print("primary key (", file = outFile) 473 | print(clauses, file = outFile) 474 | print(")%s;" % finalClauses, file = outFile) 475 | elif self.constraintType == "U": 476 | clauses = Utils.ClausesForOutput(self.columns, " ", " ", ",") 477 | print("unique (", file = outFile) 478 | print(clauses, file = outFile) 479 | print(")%s;" % finalClauses, file = outFile) 480 | elif self.constraintType == "R": 481 | clauses = Utils.ClausesForOutput(self.columns, " ", " ", ",") 482 | print("foreign key (", file = outFile) 483 | print(clauses, file = outFile) 484 | tableName = self.refConstraint.tableName 485 | refName = self.environment.NameForOutput(tableName) 486 | if self.refConstraint.owner != self.owner: 487 | refName = "%s.%s" % \ 488 | (self.refConstraint.ownerForOutput, refName) 489 | print(") references %s (" % refName, file = outFile) 490 | clauses = Utils.ClausesForOutput(self.refConstraint.columns, " ", 491 | " ", ",") 492 | print(clauses, file = outFile) 493 | if self.deleteRule: 494 | finalClauses = " on delete %s%s" % \ 495 | (self.deleteRule, finalClauses) 496 | print(")%s;" % finalClauses, file = outFile) 497 | print(file = outFile) 498 | 499 | 500 | class Index(ObjectWithStorage): 501 | """Class for describing indexes.""" 502 | supportsReferencedSynonyms = False 503 | 504 | def __init__(self, environment, row): 505 | owner, name, self.tableName, self.tablespaceName, uniqueness, \ 506 | self.initialExtent, self.nextExtent, self.minExtents, \ 507 | self.maxExtents, self.percentIncrease, indexType, \ 508 | partitioned, temporary, compressed, self.prefixLength, \ 509 | self.indexTypeOwner, self.indexTypeName, self.parameters = row 510 | Object.__init__(self, environment, owner, name, "INDEX") 511 | self.typeModifier = None 512 | if uniqueness == "UNIQUE": 513 | self.typeModifier = "UNIQUE" 514 | elif indexType == "BITMAP": 515 | self.typeModifier = "BITMAP" 516 | self.temporary = (temporary == "Y") 517 | self.partitioned = (partitioned == "YES") 518 | self.compressed = (compressed == "ENABLED") 519 | self.reversed = indexType.endswith("NORMAL/REV") 520 | self.functionBased = indexType.startswith("FUNCTION-BASED") 521 | self.__RetrieveColumns() 522 | if self.partitioned: 523 | self.__RetrievePartitionInfo() 524 | 525 | def __AddPartitionClauses(self, clauses, wantTablespace, wantStorage): 526 | """Add clauses for the partitions to the list of clauses.""" 527 | for partition in self.partitions: 528 | if clauses: 529 | clauses[-1] += "," 530 | partition.AddClauses(clauses, wantTablespace, wantStorage) 531 | columns = [n.lower() for n in self.partitionColumns] 532 | if self.locality == "LOCAL": 533 | clauses.insert(0, "local") 534 | else: 535 | clauses.insert(0, "global partition by %s (%s)" % \ 536 | (self.partitionType.lower(), ", ".join(columns))) 537 | clauses[1] = "( " + clauses[1].lstrip() 538 | clauses.append(")") 539 | 540 | def __RetrieveColumns(self): 541 | """Retrieve the columns for the index.""" 542 | expressions = {} 543 | if self.functionBased: 544 | cursor, isPrepared = self.environment.Cursor("IndexExpressions") 545 | if not isPrepared: 546 | cursor.prepare(""" 547 | select 548 | column_position, 549 | column_expression 550 | from %s_ind_expressions 551 | where index_owner = :owner 552 | and index_name = :name""" % \ 553 | self.environment.ViewPrefix()) 554 | cursor.execute(None, 555 | owner = self.owner, 556 | name = self.name) 557 | expressions = dict(cursor) 558 | cursor, isPrepared = self.environment.Cursor("IndexColumns") 559 | if not isPrepared: 560 | cursor.prepare(""" 561 | select 562 | column_name, 563 | descend 564 | from %s_ind_columns 565 | where index_owner = :owner 566 | and index_name = :name 567 | order by column_position""" % \ 568 | self.environment.ViewPrefix()) 569 | cursor.execute(None, 570 | owner = self.owner, 571 | name = self.name) 572 | self.columns = [] 573 | for name, descending in cursor: 574 | expression = expressions.get(cursor.rowcount) 575 | if expression is not None: 576 | nameForOutput = expression 577 | else: 578 | nameForOutput = self.environment.NameForOutput(name) 579 | if descending == "DESC": 580 | nameForOutput += " desc" 581 | self.columns.append(nameForOutput) 582 | 583 | def __RetrievePartitionInfo(self): 584 | """Retrieve partition info for the table.""" 585 | cursor, isPrepared = self.environment.Cursor("IndexPartitionInfo") 586 | if not isPrepared: 587 | cursor.prepare(""" 588 | select 589 | partitioning_type, 590 | locality 591 | from %s_part_indexes 592 | where owner = :owner 593 | and index_name = :name""" % \ 594 | self.environment.ViewPrefix()) 595 | cursor.execute(None, 596 | owner = self.owner, 597 | name = self.name) 598 | row = cursor.fetchone() 599 | if row is None: 600 | raise CannotLocatePartitionedIndexInfo(owner = self.owner, 601 | viewPrefix = self.environment.ViewPrefix(), 602 | name = self.name) 603 | self.partitionType, self.locality = row 604 | if self.locality == "LOCAL": 605 | partitionKeywords = None 606 | elif self.partitionType == "RANGE": 607 | partitionKeywords = "values less than" 608 | else: 609 | partitionKeywords = "values" 610 | self.RetrievePartitionColumns() 611 | self.partitions = list(ObjectIterator(self.environment, 612 | "IndexPartitions", Statements.INDEX_PARTITIONS, 613 | "where o.index_owner = :owner and o.index_name = :name", 614 | Partition, partitionKeywords, owner = self.owner, 615 | name = self.name)) 616 | 617 | def AddStorageClauses(self, clauses, wantTablespace, wantStorage): 618 | """Add storage clauses to the list of clauses to be exported.""" 619 | if self.compressed: 620 | clauses.append("compress %d" % self.prefixLength) 621 | ObjectWithStorage.AddStorageClauses(self, clauses, wantTablespace, 622 | wantStorage) 623 | 624 | def Export(self, outFile, wantTablespace, wantStorage): 625 | """Export the object as a SQL statement.""" 626 | print("create", end=' ', file = outFile) 627 | if self.typeModifier: 628 | print(self.typeModifier.lower(), end=' ', file = outFile) 629 | print(self.type.lower(), self.nameForOutput, file = outFile) 630 | tableName = self.environment.NameForOutput(self.tableName) 631 | print("on", tableName, "(", file = outFile) 632 | print(" " + ",\n ".join(self.columns), file = outFile) 633 | clauses = [] 634 | if self.reversed: 635 | clauses.append("reverse") 636 | self.AddStorageClauses(clauses, wantTablespace, wantStorage) 637 | if self.partitioned: 638 | self.__AddPartitionClauses(clauses, wantTablespace, wantStorage) 639 | if self.indexTypeOwner is not None: 640 | owner = self.environment.NameForOutput(self.indexTypeOwner) 641 | name = self.environment.NameForOutput(self.indexTypeName) 642 | clauses.append("indextype is %s.%s" % (owner, name)) 643 | if self.parameters is not None: 644 | clauses.append("parameters ('%s')" % self.parameters) 645 | clauses = Utils.ClausesForOutput(clauses, " ", " ", "") 646 | print(")%s;" % clauses, file = outFile) 647 | print(file = outFile) 648 | 649 | 650 | class Library(Object): 651 | """Class for describing libraries.""" 652 | 653 | def __init__(self, environment, row): 654 | owner, name, self.filespec = row 655 | Object.__init__(self, environment, owner, name, "LIBRARY") 656 | 657 | def Export(self, outFile): 658 | """Export the object as a SQL statement.""" 659 | print("create or replace library", self.nameForOutput, "as", 660 | file = outFile) 661 | print("'%s';" % self.filespec.strip(), file = outFile) 662 | print("/", file = outFile) 663 | print(file = outFile) 664 | 665 | 666 | class Lob(ObjectWithStorage): 667 | """Class for describing LOB segments.""" 668 | supportsReferencedSynonyms = False 669 | 670 | def __init__(self, environment, row): 671 | owner, name, self.tableName, self.segmentName, self.inRow = row 672 | Object.__init__(self, environment, owner, name, "LOB") 673 | self.__RetrieveSegment() 674 | 675 | def __RetrieveSegment(self): 676 | """Retrieve the segment information from the database.""" 677 | cursor, isPrepared = self.environment.Cursor("LobSegments") 678 | if not isPrepared: 679 | statement = """ 680 | select 681 | tablespace_name, 682 | initial_extent, 683 | next_extent, 684 | min_extents, 685 | max_extents, 686 | pct_increase\n""" 687 | if self.environment.useDbaViews: 688 | statement += "from dba_segments\n" 689 | else: 690 | statement += "from user_segments\n" 691 | statement += "where segment_type = 'LOBSEGMENT' " + \ 692 | "and segment_name = :segmentName" 693 | if self.environment.useDbaViews: 694 | statement += " and owner = :owner" 695 | cursor.prepare(statement) 696 | if self.environment.useDbaViews: 697 | cursor.execute(None, 698 | segmentName = self.segmentName, 699 | owner = self.owner) 700 | else: 701 | cursor.execute(None, 702 | segmentName = self.segmentName) 703 | row = cursor.fetchone() 704 | if not row: 705 | raise CannotLocateLOBSegment(owner = self.owner, name = self.name) 706 | self.tablespaceName, self.initialExtent, self.nextExtent, \ 707 | self.minExtents, self.maxExtents, self.percentIncrease = row 708 | 709 | def AddClauses(self, clauses, wantTablespace, wantStorage): 710 | """Add clauses for exporting as a SQL statement to the list.""" 711 | subClauses = [] 712 | self.AddStorageClauses(subClauses, wantTablespace, wantStorage) 713 | if self.inRow != "YES": 714 | subClauses.append("disable storage in row") 715 | if subClauses: 716 | clauses.append("lob (%s)" % self.nameForOutput) 717 | clauses.append(" store as (") 718 | for clause in subClauses: 719 | clauses.append(" " + clause) 720 | clauses.append(" )") 721 | 722 | 723 | class Partition(ObjectWithStorage): 724 | """Class for describing partitions.""" 725 | supportsReferencedSynonyms = False 726 | 727 | def __init__(self, environment, row, partitionKeywords): 728 | owner, name, self.highValue, self.tablespaceName, self.initialExtent, \ 729 | self.nextExtent, self.minExtents, self.maxExtents, \ 730 | self.percentIncrease = row 731 | self.partitionKeywords = partitionKeywords 732 | Object.__init__(self, environment, owner, name, "PARTITION") 733 | 734 | def AddClauses(self, clauses, wantTablespace, wantStorage): 735 | """Add clauses for exporting as a SQL statement to the list.""" 736 | subClauses = [] 737 | self.AddStorageClauses(subClauses, wantTablespace, wantStorage) 738 | if self.partitionKeywords is not None: 739 | clauses.append(" partition %s %s (%s)" % \ 740 | (self.nameForOutput, self.partitionKeywords, \ 741 | self.highValue)) 742 | else: 743 | clauses.append(" partition %s" % self.nameForOutput) 744 | for clause in subClauses: 745 | clauses.append(" " + clause) 746 | 747 | 748 | class Role(UserOrRole): 749 | """Class for describing roles.""" 750 | supportsReferencedSynonyms = False 751 | 752 | def __init__(self, environment, row): 753 | name, passwordRequired = row 754 | super(Role, self).__init__(environment, "SYSTEM", name, "ROLE") 755 | self.passwordRequired = (passwordRequired == "YES") 756 | 757 | def Export(self, outFile): 758 | """Export the object as a SQL statement.""" 759 | if self.passwordRequired: 760 | clause = " identified by password;" 761 | else: 762 | clause = ";" 763 | print("create role", self.nameForOutput + clause, file = outFile) 764 | print(file = outFile) 765 | 766 | 767 | class Sequence(ObjectWithPrivileges): 768 | """Class for describing roles.""" 769 | 770 | def __init__(self, environment, row): 771 | owner, name, self.minValue, self.maxValue, self.incrementBy, \ 772 | self.cycleFlag, self.orderFlag, self.cacheSize, \ 773 | self.lastNumber = row 774 | if environment.ServerVersion() < (11, 2): 775 | numDigits = 27 776 | else: 777 | numDigits = 28 778 | self.maxMaxValue = "9" * numDigits 779 | Object.__init__(self, environment, owner, name, "SEQUENCE") 780 | 781 | def Export(self, outFile, includeValue): 782 | """Export the object as a SQL statement.""" 783 | options = [] 784 | if includeValue and self.lastNumber != "1": 785 | options.append("start with %s" % self.lastNumber) 786 | if self.minValue != "1": 787 | options.append("minvalue %s" % self.minValue) 788 | if self.maxValue != self.maxMaxValue: 789 | options.append("maxvalue %s" % self.maxValue) 790 | if self.incrementBy != "1": 791 | options.append("increment by %s" % self.incrementBy) 792 | if self.cycleFlag != "N": 793 | options.append("cycle") 794 | if self.cacheSize == "0": 795 | options.append("nocache") 796 | elif self.cacheSize != "20": 797 | options.append("cache %s" % self.cacheSize) 798 | if self.orderFlag != "N": 799 | options.append("order") 800 | optionsString = "".join(["\n " + o for o in options]) 801 | print("create sequence", end=' ', file = outFile) 802 | print(self.nameForOutput + optionsString + ";", file = outFile) 803 | print(file = outFile) 804 | 805 | 806 | class StoredProc(Object): 807 | """Class for describing stored procedures, functions, packages, types.""" 808 | 809 | def __init__(self, environment, owner, name, type): 810 | Object.__init__(self, environment, owner, name, type) 811 | self.__Retrieve() 812 | 813 | def __Retrieve(self): 814 | """Retrieve the information from the database.""" 815 | cursor, isPrepared = self.environment.Cursor("Source") 816 | if not isPrepared: 817 | cursor.prepare(""" 818 | select text 819 | from %s_source 820 | where owner = :owner 821 | and name = :name 822 | and type = :type 823 | order by line""" % self.environment.ViewPrefix()) 824 | cursor.execute(None, 825 | owner = self.owner, 826 | name = self.name, 827 | type = self.type) 828 | self.source = "".join([t for t, in cursor]) 829 | 830 | def Export(self, outFile): 831 | """Export the object as a SQL statement.""" 832 | print("create or replace", self.source, file = outFile) 833 | print("/", file = outFile) 834 | print(file = outFile) 835 | 836 | 837 | class StoredProcWithPrivileges(StoredProc, ObjectWithPrivileges): 838 | """Base class for stored procedures with privileges.""" 839 | pass 840 | 841 | 842 | class StoredProcWithBody(StoredProcWithPrivileges): 843 | """Base class for stored procedures with bodies.""" 844 | 845 | def Body(self): 846 | """Return the body for the package or type, if it exists.""" 847 | type = self.type + " BODY" 848 | if self.environment.ObjectExists(self.owner, self.name, type): 849 | return self.environment.ObjectByType(self.owner, self.name, type) 850 | 851 | 852 | class Synonym(Object): 853 | """Class for describing synonyms.""" 854 | supportsReferencedSynonyms = False 855 | 856 | def __init__(self, environment, row): 857 | owner, name, self.objectOwner, self.objectName, self.dbLink = row 858 | if owner == "PUBLIC": 859 | if self.objectOwner: 860 | owner = self.objectOwner 861 | Object.__init__(self, environment, owner, name, "PUBLIC SYNONYM") 862 | else: 863 | Object.__init__(self, environment, owner, name, "SYNONYM") 864 | 865 | def Export(self, outFile): 866 | """Export the object as a SQL statement.""" 867 | name = self.environment.NameForOutput(self.objectName) 868 | if self.objectOwner: 869 | owner = self.environment.NameForOutput(self.objectOwner) 870 | name = "%s.%s" % (owner, name) 871 | if self.dbLink: 872 | name += "@%s" % self.dbLink.lower() 873 | print("create", self.type.lower(), self.nameForOutput, end=' ', 874 | file = outFile) 875 | print("for", file = outFile) 876 | print(name + ";", file = outFile) 877 | print(file = outFile) 878 | 879 | 880 | class Table(ObjectWithStorage, ObjectWithTriggers, \ 881 | ObjectWithColumnPrivileges, ObjectWithComments): 882 | """Class for describing tables.""" 883 | 884 | def __init__(self, environment, row): 885 | owner, name, self.tablespaceName, self.initialExtent, \ 886 | self.nextExtent, self.minExtents, self.maxExtents, \ 887 | self.percentIncrease, temporary, partitioned, duration, \ 888 | iotType = row 889 | Object.__init__(self, environment, owner, name, "TABLE") 890 | self.temporary = (temporary == "Y") 891 | self.partitioned = (partitioned == "YES") 892 | self.indexOrganized = (iotType == "IOT") 893 | if self.temporary: 894 | if duration == "SYS$TRANSACTION": 895 | self.onCommitAction = "delete" 896 | else: 897 | self.onCommitAction = "preserve" 898 | self.__RetrieveColumns() 899 | if self.partitioned: 900 | self.__RetrievePartitionInfo() 901 | 902 | def __AddLobClauses(self, clauses, wantTablespace, wantStorage): 903 | """Add clauses for the LOBS to the list of clauses.""" 904 | for lob in self.lobs: 905 | lob.AddClauses(clauses, wantTablespace, wantStorage) 906 | 907 | def __AddPartitionClauses(self, clauses, wantTablespace, wantStorage): 908 | """Add clauses for the partitions to the list of clauses.""" 909 | for partition in self.partitions: 910 | if clauses: 911 | clauses[-1] += "," 912 | partition.AddClauses(clauses, wantTablespace, wantStorage) 913 | columns = [self.environment.NameForOutput(n) \ 914 | for n in self.partitionColumns] 915 | clauses.insert(0, "partition by %s (%s)" % \ 916 | (self.partitionType.lower(), ", ".join(columns))) 917 | clauses[1] = "( " + clauses[1].lstrip() 918 | clauses.append(")") 919 | 920 | def __ColumnClause(self, row): 921 | """Return a clause for exporting a column as a SQL statement.""" 922 | name, dataType, nullable, precision, scale, length, charLength, \ 923 | charType, defaultValue = row 924 | if dataType == "NUMBER" and precision is None and scale is not None: 925 | dataType = "integer" 926 | elif dataType.startswith("INTERVAL"): 927 | precision = None 928 | nameForOutput = self.environment.NameForOutput(name) 929 | clause = nameForOutput.ljust(32) + dataType.lower() 930 | if precision: 931 | clause += "(%d" % int(precision) 932 | if scale: 933 | clause += ", %d" % int(scale) 934 | clause += ")" 935 | elif charLength: 936 | lengthClause = "%s char" % charLength \ 937 | if charType == "C" and dataType in ("CHAR", "VARCHAR2") \ 938 | else charLength 939 | clause += "(%s)" % lengthClause 940 | elif dataType == "RAW": 941 | clause += "(%s)" % length 942 | if defaultValue: 943 | clause += "\n default %s" % defaultValue.strip() 944 | if nullable == "N": 945 | clause += " not null" 946 | return clause 947 | 948 | def __RetrieveColumns(self): 949 | """Retrieve the columns for the table from the database.""" 950 | cursor, isPrepared = self.environment.Cursor("TableColumns") 951 | if not isPrepared: 952 | cursor.prepare(""" 953 | select 954 | column_name, 955 | data_type, 956 | nullable, 957 | data_precision, 958 | data_scale, 959 | data_length, 960 | char_length, 961 | char_used, 962 | data_default 963 | from %s_tab_columns 964 | where owner = :owner 965 | and table_name = :name 966 | order by column_id""" % self.environment.ViewPrefix()) 967 | cursor.execute(None, 968 | owner = self.owner, 969 | name = self.name) 970 | self.columns = cursor.fetchall() 971 | self.lobs = [] 972 | lobColumns = [c[0] for c in self.columns \ 973 | if c[1] in ("CLOB", "BLOB", "NCLOB")] 974 | if lobColumns: 975 | self.lobs = list(ObjectIterator(self.environment, "Lobs", 976 | Statements.LOBS, 977 | "where o.owner = :owner and o.table_name = :name", Lob, 978 | owner = self.owner, name = self.name)) 979 | 980 | def __RetrievePartitionInfo(self): 981 | """Retrieve partition info for the table.""" 982 | cursor, isPrepared = self.environment.Cursor("PartitionInfo") 983 | if not isPrepared: 984 | cursor.prepare(""" 985 | select partitioning_type 986 | from %s_part_tables 987 | where owner = :owner 988 | and table_name = :name""" % \ 989 | self.environment.ViewPrefix()) 990 | cursor.execute(None, 991 | owner = self.owner, 992 | name = self.name) 993 | row = cursor.fetchone() 994 | if row is None: 995 | raise CannotLocatePartitionedTableInfo(owner = self.owner, 996 | viewPrefix = self.environment.ViewPrefix(), 997 | name = self.name) 998 | self.partitionType, = row 999 | partitionKeywords = "values" 1000 | if self.partitionType == "RANGE": 1001 | partitionKeywords = "values less than" 1002 | self.RetrievePartitionColumns() 1003 | self.partitions = list(ObjectIterator(self.environment, 1004 | "TablePartitions", Statements.TABLE_PARTITIONS, 1005 | "where o.table_owner = :owner and o.table_name = :name", 1006 | Partition, partitionKeywords, owner = self.owner, 1007 | name = self.name)) 1008 | 1009 | def Constraints(self, constraintType = None): 1010 | """Return an interator for the constraints for the table.""" 1011 | cursorName = "TableConstraints" 1012 | whereClause = "where o.owner = :owner and o.table_name = :name" 1013 | if constraintType is not None: 1014 | cursorName = "TablePrimaryKey" 1015 | whereClause += " and o.constraint_type = '%s'" % constraintType 1016 | elif self.indexOrganized: 1017 | cursorName = "TableConstraintsNoPrimaryKey" 1018 | whereClause += " and o.constraint_type != 'P'" 1019 | return ObjectIterator(self.environment, cursorName, 1020 | Statements.CONSTRAINTS, whereClause, Constraint, 1021 | owner = self.owner, name = self.name) 1022 | 1023 | def Export(self, outFile, wantTablespace, wantStorage): 1024 | """Export the object as a SQL statement.""" 1025 | clauses = [self.__ColumnClause(c) for c in self.columns] 1026 | if self.indexOrganized: 1027 | primaryKey, = self.Constraints('P') 1028 | colClauses = Utils.ClausesForOutput(primaryKey.columns, " ", 1029 | " ", ",") 1030 | clauses.append("constraint %s primary key (\n%s\n )" % \ 1031 | (primaryKey.nameForOutput, colClauses)) 1032 | clauses = Utils.ClausesForOutput(clauses, " ", " ", ",") 1033 | print("create", end=' ', file = outFile) 1034 | if self.temporary: 1035 | print("global temporary", end=' ', file = outFile) 1036 | print(self.type.lower(), self.nameForOutput, "(", file = outFile) 1037 | print(clauses, file = outFile) 1038 | clauses = [] 1039 | if self.temporary: 1040 | clauses.append("on commit %s rows" % self.onCommitAction) 1041 | if self.indexOrganized: 1042 | clauses.append("organization index") 1043 | primaryKey.relatedIndex.AddStorageClauses(clauses, wantTablespace, 1044 | wantStorage) 1045 | else: 1046 | self.AddStorageClauses(clauses, wantTablespace, wantStorage) 1047 | if self.lobs: 1048 | self.__AddLobClauses(clauses, wantTablespace, wantStorage) 1049 | if self.partitioned: 1050 | self.__AddPartitionClauses(clauses, wantTablespace, wantStorage) 1051 | clauses = Utils.ClausesForOutput(clauses, " ", " ", "") 1052 | print(")%s;" % clauses, file = outFile) 1053 | print(file = outFile) 1054 | 1055 | def Indexes(self): 1056 | """Return an iterator for the indexes for the table.""" 1057 | return ObjectIterator(self.environment, "TableIndexes", 1058 | Statements.INDEXES, 1059 | "where o.owner = :owner and o.table_name = :name", 1060 | Index, owner = self.owner, name = self.name) 1061 | 1062 | def ReferencedConstraints(self): 1063 | """Return an iterator for the referencing constraints for the table.""" 1064 | clause = """, 1065 | %s_constraints p 1066 | where p.owner = :owner 1067 | and p.table_name = :name 1068 | and p.constraint_type in ('P', 'U') 1069 | and o.r_owner = p.owner 1070 | and o.r_constraint_name = p.constraint_name""" % \ 1071 | self.environment.ViewPrefix() 1072 | return ObjectIterator(self.environment, "ReferencedConstraints", 1073 | Statements.CONSTRAINTS, clause, Constraint, 1074 | owner = self.owner, name = self.name) 1075 | 1076 | 1077 | class Trigger(Object): 1078 | """Class for describing tables.""" 1079 | supportsReferencedSynonyms = False 1080 | 1081 | def __init__(self, environment, row): 1082 | owner, name, self.tableName, self.description, self.whenClause, \ 1083 | actionType, body = row 1084 | Object.__init__(self, environment, owner, name, "TRIGGER") 1085 | if self.whenClause: 1086 | self.whenClause = self.whenClause.replace("\0", "").strip() 1087 | self.body = body.replace("\0", "").strip() 1088 | if actionType == "CALL": 1089 | self.body = "call %s" % self.body 1090 | if self.body.endswith(";"): 1091 | self.body = self.body[:-1] 1092 | 1093 | def Export(self, outFile): 1094 | """Export the object as a SQL statement.""" 1095 | print("create or replace trigger", self.description.strip(), 1096 | file = outFile) 1097 | if self.whenClause: 1098 | print("when (%s)" % self.whenClause, file = outFile) 1099 | print(self.body, file = outFile) 1100 | print("/", file = outFile) 1101 | print(file = outFile) 1102 | 1103 | 1104 | class User(UserOrRole): 1105 | """Class for describing users.""" 1106 | supportsReferencedSynonyms = False 1107 | 1108 | def __init__(self, environment, row): 1109 | name, defaultTablespace, temporaryTablespace = row 1110 | Object.__init__(self, environment, "SYSTEM", name, "USER") 1111 | self.defaultTablespace = environment.NameForOutput(defaultTablespace) 1112 | self.temporaryTablespace = \ 1113 | environment.NameForOutput(temporaryTablespace) 1114 | self.quotas = [] 1115 | if environment.wantQuotas: 1116 | self.__RetrieveTablespaceQuotas() 1117 | 1118 | def __RetrieveTablespaceQuotas(self): 1119 | """Retrieve the tablespace quotas for the user from the database.""" 1120 | cursor, isPrepared = self.environment.Cursor("TablespaceQuotas") 1121 | if not isPrepared: 1122 | cursor.prepare(""" 1123 | select 1124 | tablespace_name, 1125 | max_bytes 1126 | from dba_ts_quotas 1127 | where username = :name""") 1128 | cursor.execute(None, name = self.name) 1129 | self.quotas = cursor.fetchall() 1130 | self.quotas.sort() 1131 | 1132 | def Export(self, outFile): 1133 | """Export the object as a SQL statement.""" 1134 | quotaClauses = ["\n quota %s on %s" % \ 1135 | (Utils.SizeForOutput(s), self.environment.NameForOutput(t)) \ 1136 | for t, s in self.quotas] 1137 | finalClause = "".join(quotaClauses) + ";" 1138 | print("create user", self.nameForOutput, end=' ', file = outFile) 1139 | print("identified by password", file = outFile) 1140 | print(" default tablespace", self.defaultTablespace, file = outFile) 1141 | print(" temporary tablespace", end=' ', file = outFile) 1142 | print(self.temporaryTablespace + finalClause, file = outFile) 1143 | print(file = outFile) 1144 | 1145 | 1146 | class View(ObjectWithTriggers, ObjectWithColumnPrivileges, ObjectWithComments): 1147 | """Class for describing views.""" 1148 | 1149 | def __init__(self, environment, row): 1150 | owner, name, self.text = row 1151 | Object.__init__(self, environment, owner, name, "VIEW") 1152 | if environment.wantViewColumns: 1153 | self.__RetrieveColumns() 1154 | else: 1155 | self.columns = [] 1156 | 1157 | def __RetrieveColumns(self): 1158 | """Retrieve the columns for the view from the database.""" 1159 | cursor, isPrepared = self.environment.Cursor("ViewColumns") 1160 | if not isPrepared: 1161 | cursor.prepare(""" 1162 | select column_name 1163 | from %s_tab_columns 1164 | where owner = :owner 1165 | and table_name = :name 1166 | order by column_id""" % self.environment.ViewPrefix()) 1167 | cursor.execute(None, 1168 | owner = self.owner, 1169 | name = self.name) 1170 | self.columns = [n for n, in cursor] 1171 | 1172 | def Export(self, outFile): 1173 | """Export the object as a SQL statement.""" 1174 | print("create or replace view", self.nameForOutput, end=' ', 1175 | file = outFile) 1176 | if self.columns: 1177 | print("(", file = outFile) 1178 | print(" " + ",\n ".join(self.columns), file = outFile) 1179 | print(")", end=' ', file = outFile) 1180 | print("as", file = outFile) 1181 | print(self.text.strip() + ";", file = outFile) 1182 | print(file = outFile) 1183 | 1184 | 1185 | class ViewNoRetrieve(View): 1186 | """Class for describing views without having described it first.""" 1187 | 1188 | def __init__(self, environment, owner, name, type): 1189 | cursor = PreparedCursor(environment, "View", Statements.VIEWS, 1190 | "where o.owner = :owner and o.view_name = :name") 1191 | cursor.execute(None, owner = owner, name = name) 1192 | row = cursor.fetchone() 1193 | View.__init__(self, environment, row) 1194 | 1195 | 1196 | def ObjectIterator(environment, tag, statement, whereClause, classFactory, 1197 | *args, **keywordArgs): 1198 | """Return an iterator for iterating through a list of objects one at a 1199 | time without requiring all of the objects to be in memory at once.""" 1200 | cursor = PreparedCursor(environment, tag, statement, whereClause) 1201 | cursor.execute(None, **keywordArgs) 1202 | for row in cursor: 1203 | yield classFactory(environment, row, *args) 1204 | 1205 | def PreparedCursor(environment, tag, statement, whereClause): 1206 | """Return a prepared cursor with the given statement.""" 1207 | cursor, isPrepared = environment.Cursor(tag) 1208 | if not isPrepared: 1209 | args = { 1210 | "p_ViewPrefix" : environment.ViewPrefix(), 1211 | "p_WhereClause" : whereClause 1212 | } 1213 | statement = statement % args 1214 | if environment.maxLongSize is not None: 1215 | cursor.setoutputsize(environment.maxLongSize) 1216 | cursor.prepare(statement) 1217 | return cursor 1218 | 1219 | 1220 | class CannotFetchReferencedConstraintInfo(cx_Exceptions.BaseException): 1221 | message = "Cannot fetch information about referenced constraint named\n" \ 1222 | "%(owner)s.%(name)s. Grant privileges or use --use-dba-views." 1223 | 1224 | 1225 | class CannotLocateLOBSegment(cx_Exceptions.BaseException): 1226 | message = "Unable to locate LOB segment %(owner)s.%(name)s" 1227 | 1228 | 1229 | class CannotLocatePartitionedIndexInfo(cx_Exceptions.BaseException): 1230 | message = "Index %(owner)s.%(name)s is partitioned but no row found in " \ 1231 | "%(viewPrefix)s_part_indexes." 1232 | 1233 | 1234 | class CannotLocatePartitionedTableInfo(cx_Exceptions.BaseException): 1235 | message = "Table %(owner)s.%(name)s is partitioned but no row found in " \ 1236 | "%(viewPrefix)s_part_tables." 1237 | 1238 | --------------------------------------------------------------------------------