Example #1
0
 /**
  * Creates a unique cache prefix to help prevent cache conflicts
  * 
  * @return void
  */
 private function makeCachePrefix()
 {
     $prefix = 'fSchema::' . $this->database->getType() . '::';
     if ($this->database->getHost()) {
         $prefix .= $this->database->getHost() . '::';
     }
     if ($this->database->getPort()) {
         $prefix .= $this->database->getPort() . '::';
     }
     $prefix .= $this->database->getDatabase() . '::';
     if ($this->database->getUsername()) {
         $prefix .= $this->database->getUsername() . '::';
     }
     return $prefix;
 }
 /**
  * Translates Flourish SQL `ALTER TABLE` statements to the appropriate
  * statements for Oracle
  *
  * @param string $sql                The SQL statements that will be executed against the database
  * @param array  &$extra_statements  Any extra SQL statements required for Oracle
  * @param array  $data               Data parsed from the `ALTER TABLE` statement
  * @return string  The modified SQL statement
  */
 private function translateOracleAlterTableStatements($sql, &$extra_statements, $data)
 {
     $data['schema'] = strtolower($this->database->getUsername());
     $data['table_without_schema'] = $data['table'];
     if (strpos($data['table'], '.') !== FALSE) {
         list($data['schema'], $data['table_without_schema']) = explode('.', $data['table']);
     }
     if (in_array($data['type'], array('drop_check_constraint', 'drop_primary_key', 'drop_foreign_key', 'drop_unique'))) {
         $column_info = $this->database->query("SELECT\n\t\t\t\t\tATC.COLUMN_NAME\n\t\t\t\tFROM\n\t\t\t\t\tALL_TABLES AT LEFT JOIN\n\t\t\t\t\tALL_TAB_COLUMNS ATC ON\n\t\t\t\t\t\tATC.OWNER = AT.OWNER AND\n\t\t\t\t\t\tATC.TABLE_NAME = AT.TABLE_NAME AND\n\t\t\t\t\t\tLOWER(ATC.COLUMN_NAME) = %s\n\t\t\t\tWHERE\n\t\t\t\t\tLOWER(AT.OWNER) = %s AND\n\t\t\t\t\tLOWER(AT.TABLE_NAME) = %s", isset($data['column_name']) ? $data['column_name'] : '', $data['schema'], $data['table_without_schema']);
         if (!$column_info->countReturnedRows()) {
             $this->throwException(self::compose('The table "%1$s" does not exist', $data['table']), $sql);
         }
         if (isset($data['column_name'])) {
             $row = $column_info->fetchRow();
             if (!strlen($row['column_name'])) {
                 $this->throwException(self::compose('The column "%1$s" does not exist in the table "%2$s"', $data['column_name'], $data['table']), $sql);
             }
         }
     }
     if ($data['type'] == 'drop_check_constraint' || $data['type'] == 'set_check_constraint') {
         $constraints = $this->database->query("SELECT\n\t\t\t\t\tAC.CONSTRAINT_NAME,\n\t\t\t\t\tAC.SEARCH_CONDITION\n\t\t\t\tFROM\n\t\t\t\t\tALL_TAB_COLUMNS ATC INNER JOIN\n\t\t\t\t\tALL_CONS_COLUMNS ACC ON\n\t\t\t\t\t\tATC.OWNER = ACC.OWNER AND\n\t\t\t\t\t\tATC.COLUMN_NAME = ACC.COLUMN_NAME AND\n\t\t\t\t\t\tATC.TABLE_NAME = ACC.TABLE_NAME AND\n\t\t\t\t\t\tACC.POSITION IS NULL INNER JOIN\n\t\t\t\t\tALL_CONSTRAINTS AC ON\n\t\t\t\t\t\tAC.OWNER = ACC.OWNER AND\n\t\t\t\t\t\tAC.CONSTRAINT_NAME = ACC.CONSTRAINT_NAME AND\n\t\t\t\t\t\tAC.CONSTRAINT_TYPE = 'C' AND\n\t\t\t\t\t\tAC.STATUS = 'ENABLED'\n\t\t\t\tWHERE\n\t\t\t\t\tLOWER(ATC.OWNER) = %s AND\n\t\t\t\t\tLOWER(ATC.TABLE_NAME) = %s AND\n\t\t\t\t\tLOWER(ATC.COLUMN_NAME) = %s", $data['schema'], $data['table_without_schema'], $data['column_name']);
         $constraint_name = NULL;
         foreach ($constraints as $row) {
             if (preg_match("#^\\s*\"?\\w+\"?\\s+IN\\s+(\\(\\s*(?:(?<!')'(?:''|[^']+)*'|%\\d+\\\$s)(?:\\s*,\\s*(?:(?<!')'(?:''|[^']+)*'|%\\d+\\\$s))*\\s*\\))\\s*\$#i", $row['search_condition'])) {
                 $constraint_name = $row['constraint_name'];
                 break;
             } elseif (preg_match('/^\\s*"?' . preg_quote($data['column_name'], '/') . '"?\\s*=\\s*\'((\'\'|[^\']+)*)\'(\\s+OR\\s+"?' . preg_quote($data['column_name'], '/') . '"?\\s*=\\s*\'((\'\'|[^\']+)*)\')*\\s*$/i', $row['search_condition'])) {
                 $constraint_name = $row['constraint_name'];
                 break;
             }
         }
     }
     if ($data['type'] == 'set_not_null' || $data['type'] == 'drop_not_null') {
         $not_null_result = $this->database->query("SELECT\n\t\t\t\t\tATC.NULLABLE\n\t\t\t\tFROM\n\t\t\t\t\tALL_TAB_COLUMNS ATC\n\t\t\t\tWHERE\n\t\t\t\t\tLOWER(ATC.OWNER) = %s AND\n\t\t\t\t\tLOWER(ATC.TABLE_NAME) = %s AND\n\t\t\t\t\tLOWER(ATC.COLUMN_NAME) = %s", $data['schema'], $data['table_without_schema'], $data['column_name']);
         $not_null = $not_null_result->countReturnedRows() && $not_null_result->fetchScalar() == 'N';
     }
     if ($data['type'] == 'set_not_null' && isset($data['default']) && $data['default'] == "''") {
         $data['type'] = 'drop_not_null';
         $data['default'] = ' NULL';
     }
     if ($data['type'] == 'column_comment') {
         // Oracle handles the normalized syntax
     } elseif ($data['type'] == 'rename_table') {
         // Oracle handles the normalized syntax
     } elseif ($data['type'] == 'rename_column') {
         // When renaming a primary key column we need to check
         // for triggers that implement autoincrement
         $res = $this->database->query("SELECT\n\t\t\t\t\tTRIGGER_NAME,\n\t\t\t\t\tTRIGGER_BODY\n\t\t\t\tFROM\n\t\t\t\t\tALL_TRIGGERS\n\t\t\t\tWHERE\n\t\t\t\t\tTRIGGERING_EVENT LIKE 'INSERT%' AND\n\t\t\t\t\tSTATUS = 'ENABLED' AND\n\t\t\t\t\tTRIGGER_NAME NOT LIKE 'BIN\$%' AND\n\t\t\t\t\tLOWER(TABLE_NAME) = %s AND\n\t\t\t\t\tLOWER(OWNER) = %s", $data['table_without_schema'], $data['schema']);
         foreach ($res as $row) {
             if (!preg_match('#\\s+:new\\."?' . preg_quote($data['column_name'], '#') . '"?\\s+FROM\\s+DUAL#i', $row['trigger_body'])) {
                 continue;
             }
             $trigger = 'CREATE OR REPLACE TRIGGER ' . $row['trigger_name'] . "\n";
             $trigger .= "BEFORE INSERT ON " . $data['table_without_schema'] . "\n";
             $trigger .= "FOR EACH ROW\n";
             $trigger .= preg_replace('#( :new\\.)' . preg_quote($data['column_name'], '#') . '( )#i', '\\1' . $data['new_column_name'] . '\\2', $row['trigger_body']);
             $extra_statements[] = $trigger;
         }
     } elseif ($data['type'] == 'add_column') {
         // If NOT NULL DEFAULT '' is present, both are removed since Oracle converts '' to NULL
         $data['column_definition'] = preg_replace('#(\\bNOT\\s+NULL\\s+DEFAULT\\s+\'\'|\\bDEFAULT\\s+\'\'\\s+NOT\\s+NULL)#', '', $data['column_definition']);
         // Oracle does not support ON UPDATE clauses
         $data['column_definition'] = preg_replace('#(\\sON\\s+UPDATE\\s+(CASCADE|SET\\s+NULL|NO\\s+ACTION|RESTRICT))#i', '', $data['column_definition']);
         // Oracle requires NOT NULL to come after the DEFAULT value or UNIQUE constraint
         $data['column_definition'] = preg_replace('#(\\s+NOT\\s+NULL)((?:\\s+UNIQUE|\\s+DEFAULT\\s+(?:[^, \'\\n]*|\'(?:\'\'|[^\']+)*\'))+)#i', '\\2\\1', $data['column_definition']);
         $sql = $this->database->escape('ALTER TABLE %r ADD ', $data['table']) . $data['column_definition'];
         $sql = $this->translateDataTypes($sql);
         // Create sequences and triggers for Oracle
         if (stripos($sql, 'autoincrement') !== FALSE && preg_match('#^\\s*(?:[a-z]+)(?:\\((?:\\d+)\\))?.*?\\bAUTOINCREMENT\\b.*$#iD', $sql, $matches)) {
             // If we are creating a new autoincrementing primary key on an existing
             // table we have to take some extra steps to pre-populate the
             // auto-incrementing column, otherwise Oracle will error out
             if (preg_match('#\\bPRIMARY\\s+KEY\\b#i', $sql)) {
                 $sql = preg_replace('# PRIMARY\\s+KEY\\b#i', '', $sql);
                 $extra_statements[] = $this->database->escape("UPDATE %r SET %r = ROWNUM", $data['table'], $data['column_name']);
                 $extra_statements[] = $this->database->escape("ALTER TABLE %r ADD CONSTRAINT %r PRIMARY KEY (%r)", $data['table'], $this->generateConstraintName($sql, 'pk'), $data['column_name']);
             }
             $table_column = substr(str_replace('"', '', $data['table']) . '_' . str_replace('"', '', $data['column_name']), 0, 26);
             $sequence_name = $table_column . '_seq';
             $trigger_name = $table_column . '_trg';
             $sequence = "DECLARE\n\t\t\t\t\tcreate_seq_sql VARCHAR2(200);\n    \t\t\tBEGIN\n\t\t\t\t\tSELECT\n\t\t\t\t\t\t'CREATE SEQUENCE " . $sequence_name . " START WITH ' || (SQ.TOTAL_ROWS + 1)\n\t\t\t\t\tINTO\n\t\t\t\t\t\tcreate_seq_sql\n\t\t\t\t\tFROM\n\t\t\t\t\t\t(SELECT COUNT(*) TOTAL_ROWS FROM " . $data['table'] . ") SQ \\;\n\t\t\t\t\tEXECUTE IMMEDIATE create_seq_sql\\;\n\t\t\t\tEND\\;";
             $trigger = 'CREATE OR REPLACE TRIGGER ' . $trigger_name . "\n";
             $trigger .= "BEFORE INSERT ON " . $data['table'] . "\n";
             $trigger .= "FOR EACH ROW\n";
             $trigger .= "BEGIN\n";
             $trigger .= "  IF :new." . $data['column_name'] . " IS NULL THEN\n";
             $trigger .= "\tSELECT " . $sequence_name . ".nextval INTO :new." . $data['column_name'] . " FROM dual;\n";
             $trigger .= "  END IF;\n";
             $trigger .= "END;";
             $extra_statements[] = $sequence;
             $extra_statements[] = $trigger;
             $sql = preg_replace('#\\s+autoincrement\\b#i', '', $sql);
         }
     } elseif ($data['type'] == 'drop_column') {
         $sql .= ' CASCADE CONSTRAINTS';
         // Drop any triggers and sequences that implement autoincrement for the column
         $res = $this->database->query("SELECT\n\t\t\t\t\tTRIGGER_NAME,\n\t\t\t\t\tTRIGGER_BODY\n\t\t\t\tFROM\n\t\t\t\t\tALL_TRIGGERS\n\t\t\t\tWHERE\n\t\t\t\t\tTRIGGERING_EVENT LIKE 'INSERT%' AND\n\t\t\t\t\tSTATUS = 'ENABLED' AND\n\t\t\t\t\tTRIGGER_NAME NOT LIKE 'BIN\$%' AND\n\t\t\t\t\tLOWER(TABLE_NAME) = %s AND\n\t\t\t\t\tLOWER(OWNER) = %s", $data['table_without_schema'], $data['schema']);
         foreach ($res as $row) {
             if (!preg_match('#SELECT\\s+"?(\\w+)"?\\.nextval\\s+INTO\\s+:new\\."?' . preg_quote($data['column_name'], '#') . '"?\\s+FROM\\s+dual#i', $row['trigger_body'], $match)) {
                 continue;
             }
             $extra_statements[] = $this->database->escape("DROP SEQUENCE %r", $match[1]);
             $extra_statements[] = $this->database->escape("DROP TRIGGER %r", $row['trigger_name']);
         }
     } elseif ($data['type'] == 'alter_type') {
         $data['data_type'] = $this->translateDataTypes($data['data_type']);
         $sql = $this->database->escape("ALTER TABLE %r MODIFY (%r ", $data['table'], $data['column_name']) . $data['data_type'] . ')';
     } elseif ($data['type'] == 'set_default') {
         $sql = $this->database->escape("ALTER TABLE %r MODIFY (%r DEFAULT ", $data['table'], $data['column_name']) . $data['default_value'] . ')';
     } elseif ($data['type'] == 'drop_default') {
         $sql = $this->database->escape("ALTER TABLE %r MODIFY (%r DEFAULT NULL)", $data['table'], $data['column_name']);
     } elseif ($data['type'] == 'set_not_null') {
         // If it is already NOT NULL, don't set it again since it will cause an error
         if ($not_null_result->countReturnedRows() && $not_null) {
             if (isset($data['default'])) {
                 $sql = $this->database->escape("ALTER TABLE %r MODIFY (%r DEFAULT ", $data['table'], $data['column_name']) . $data['default'] . ')';
             } else {
                 $sql = "SELECT 'noop - no NOT NULL to set' FROM dual";
             }
         } else {
             $sql = $this->database->escape("ALTER TABLE %r MODIFY (%r", $data['table'], $data['column_name']);
             if (isset($data['default'])) {
                 $sql .= ' DEFAULT ' . $data['default'];
             }
             $sql .= ' NOT NULL)';
         }
     } elseif ($data['type'] == 'drop_not_null') {
         if (!$not_null_result->countReturnedRows() || $not_null) {
             $sql = $this->database->escape("ALTER TABLE %r MODIFY (%r", $data['table'], $data['column_name']);
             if (isset($data['default'])) {
                 $sql .= ' DEFAULT ' . $data['default'];
             }
             $sql .= ' NULL)';
             // If it is already NULL, don't set it again since it will cause an error
         } else {
             if (isset($data['default'])) {
                 $sql = $this->database->escape("ALTER TABLE %r MODIFY (%r DEFAULT", $data['table'], $data['column_name']) . $data['default'] . ')';
             } else {
                 $sql = "SELECT 'noop - no NOT NULL to drop' FROM dual";
             }
         }
     } elseif ($data['type'] == 'drop_check_constraint') {
         if (!$constraint_name) {
             $this->throwException(self::compose('The column "%1$s" in the table "%2$s" does not have a check constraint', $data['column_name'], $data['table']), $sql);
         }
         $sql = $this->database->escape("ALTER TABLE %r DROP CONSTRAINT %r", $data['table'], $constraint_name);
     } elseif ($data['type'] == 'set_check_constraint') {
         if ($constraint_name) {
             $extra_statements[] = $this->database->escape("ALTER TABLE %r DROP CONSTRAINT %r", $data['table'], $constraint_name);
         }
         $sql = $this->database->escape("ALTER TABLE %r ADD CONSTRAINT %r", $data['table'], $this->generateConstraintName($sql, 'ck')) . $data['constraint'];
         $extra_statements[] = $sql;
         $sql = array_shift($extra_statements);
     } elseif ($data['type'] == 'drop_primary_key') {
         $constraint_columns = $this->database->query("SELECT\n\t\t\t\t\tAC.CONSTRAINT_NAME CONSTRAINT_NAME,\n\t\t\t\t\tLOWER(ACC.COLUMN_NAME) \"COLUMN\",\n\t\t\t\t\tANC.SEARCH_CONDITION\n\t\t\t\tFROM\n\t\t\t\t\tALL_CONSTRAINTS AC INNER JOIN\n\t\t\t\t\tALL_CONS_COLUMNS ACC ON\n\t\t\t\t\t\tAC.CONSTRAINT_NAME = ACC.CONSTRAINT_NAME AND\n\t\t\t\t\t\tAC.OWNER = ACC.OWNER LEFT JOIN\n\t\t\t\t\tALL_CONS_COLUMNS ANCC ON\n\t\t\t\t\t\tACC.OWNER = ANCC.OWNER AND\n\t\t\t\t\t\tACC.TABLE_NAME = ANCC.TABLE_NAME AND\n\t\t\t\t\t\tACC.COLUMN_NAME = ANCC.COLUMN_NAME LEFT JOIN\n\t\t\t\t\tALL_CONSTRAINTS ANC ON\n\t\t\t\t\t\tANC.CONSTRAINT_NAME = ANCC.CONSTRAINT_NAME AND\n\t\t\t\t\t\tANC.OWNER = ANCC.OWNER AND\n\t\t\t\t\t\tANC.CONSTRAINT_TYPE = 'C' AND\n\t\t\t\t\t\tANC.STATUS = 'ENABLED'\n\t\t\t\tWHERE\n\t\t\t\t\tAC.CONSTRAINT_TYPE = 'P' AND\n\t\t\t\t\tLOWER(AC.OWNER) = %s AND\n\t\t\t\t\tLOWER(AC.TABLE_NAME) = %s", $data['schema'], $data['table_without_schema']);
         $constraint_name = NULL;
         $primary_key_columns = array();
         $nullable = FALSE;
         foreach ($constraint_columns as $row) {
             $constraint_name = $row['constraint_name'];
             $primary_key_columns[] = $row['column'];
             $nullable = strtolower($row['search_condition']) != '"' . $row['column'] . '" is not null';
         }
         if (!$constraint_name) {
             $this->throwException(self::compose('The table "%1$s" does not have a primary key constraint', $data['table']), $sql);
         }
         $sql = $this->database->escape("ALTER TABLE %r DROP CONSTRAINT %r CASCADE", $data['table'], $constraint_name);
         // When dropping a primary key we want to drop any trigger
         // and sequence used to implement autoincrement
         $res = $this->database->query("SELECT\n\t\t\t\t\tTRIGGER_NAME,\n\t\t\t\t\tTRIGGER_BODY\n\t\t\t\tFROM\n\t\t\t\t\tALL_TRIGGERS\n\t\t\t\tWHERE\n\t\t\t\t\tTRIGGERING_EVENT LIKE 'INSERT%' AND\n\t\t\t\t\tSTATUS = 'ENABLED' AND\n\t\t\t\t\tTRIGGER_NAME NOT LIKE 'BIN\$%' AND\n\t\t\t\t\tLOWER(TABLE_NAME) = %s AND\n\t\t\t\t\tLOWER(OWNER) = %s", $data['table_without_schema'], $data['schema']);
         foreach ($res as $row) {
             if (!preg_match('#SELECT\\s+"?(\\w+)"?\\.nextval\\s+INTO\\s+:new\\.("?\\w+"?)\\s+FROM\\s+DUAL#i', $row['trigger_body'], $match)) {
                 continue;
             }
             $extra_statements[] = $this->database->escape("DROP TRIGGER %r", $data['schema'] . '.' . $row['trigger_name']);
             $extra_statements[] = $this->database->escape("DROP SEQUENCE %r", $data['schema'] . '.' . $match[1]);
         }
         if (count($primary_key_columns) == 1 && $nullable) {
             $extra_statements[] = $this->database->escape("ALTER TABLE %r MODIFY (%r NOT NULL)", $data['table'], reset($primary_key_columns));
         }
     } elseif ($data['type'] == 'add_primary_key') {
         $sql = $this->database->escape("ALTER TABLE %r ADD CONSTRAINT %r PRIMARY KEY (%r)", $data['table'], $this->generateConstraintName($sql, 'pk'), $data['column_names']);
         if ($data['autoincrement']) {
             $extra_statements[] = $sql;
             $sql = $this->database->escape("UPDATE %r SET %r = ROWNUM", $data['table'], $data['column_name']);
             $table_column = substr(str_replace('"', '', $data['table']) . '_' . str_replace('"', '', $data['column_name']), 0, 26);
             $sequence_name = $table_column . '_seq';
             $trigger_name = $table_column . '_trg';
             $sequence = "DECLARE\n\t\t\t\t\tcreate_seq_sql VARCHAR2(200);\n    \t\t\tBEGIN\n\t\t\t\t\tSELECT\n\t\t\t\t\t\t'CREATE SEQUENCE " . $sequence_name . " START WITH ' || (SQ.TOTAL_ROWS + 1)\n\t\t\t\t\tINTO\n\t\t\t\t\t\tcreate_seq_sql\n\t\t\t\t\tFROM\n\t\t\t\t\t\t(SELECT COUNT(*) TOTAL_ROWS FROM " . $data['table'] . ") SQ \\;\n\t\t\t\t\tEXECUTE IMMEDIATE create_seq_sql\\;\n\t\t\t\tEND\\;";
             $trigger = 'CREATE OR REPLACE TRIGGER ' . $trigger_name . "\n";
             $trigger .= "BEFORE INSERT ON " . $data['table'] . "\n";
             $trigger .= "FOR EACH ROW\n";
             $trigger .= "BEGIN\n";
             $trigger .= "  IF :new." . $data['column_name'] . " IS NULL THEN\n";
             $trigger .= "\tSELECT " . $sequence_name . ".nextval INTO :new." . $data['column_name'] . " FROM dual;\n";
             $trigger .= "  END IF;\n";
             $trigger .= "END;";
             $extra_statements[] = $sequence;
             $extra_statements[] = $trigger;
         }
     } elseif ($data['type'] == 'drop_foreign_key') {
         $constraint = $this->database->query("SELECT\n\t\t\t\t\tAC.CONSTRAINT_NAME CONSTRAINT_NAME\n\t\t\t\tFROM\n\t\t\t\t\tALL_CONSTRAINTS AC INNER JOIN\n\t\t\t\t\tALL_CONS_COLUMNS ACC ON\n\t\t\t\t\t\tAC.CONSTRAINT_NAME = ACC.CONSTRAINT_NAME AND\n\t\t\t\t\t\tAC.OWNER = ACC.OWNER\n\t\t\t\tWHERE\n\t\t\t\t\tAC.CONSTRAINT_TYPE = 'R' AND\n\t\t\t\t\tLOWER(AC.OWNER) = %s AND\n\t\t\t\t\tLOWER(AC.TABLE_NAME) = %s AND\n\t\t\t\t\tLOWER(ACC.COLUMN_NAME) = %s", $data['schema'], $data['table_without_schema'], $data['column_name']);
         if (!$constraint->countReturnedRows()) {
             $this->throwException(self::compose('The column "%1$s" in the table "%2$s" does not have a foreign key constraint', $data['column_name'], $data['table']), $sql);
         }
         $sql = $this->database->escape("ALTER TABLE %r DROP CONSTRAINT %r", $data['table'], $constraint->fetchScalar());
     } elseif ($data['type'] == 'add_foreign_key') {
         // Oracle does not support ON UPDATE clauses
         $data['references'] = preg_replace('#(\\sON\\s+UPDATE\\s+(CASCADE|SET\\s+NULL|NO\\s+ACTION|RESTRICT))#i', '', $data['references']);
         $sql = $this->database->escape("ALTER TABLE %r ADD CONSTRAINT %r FOREIGN KEY (%r) REFERENCES " . $data['references'], $data['table'], $this->generateConstraintName($sql, 'fk'), $data['column_name']);
     } elseif ($data['type'] == 'drop_unique') {
         $constraint_rows = $this->database->query("SELECT\n\t\t\t\t\tAC.CONSTRAINT_NAME,\n\t\t\t\t\tLOWER(ACC.COLUMN_NAME) \"COLUMN\"\n\t\t\t\tFROM\n\t\t\t\t\tALL_CONSTRAINTS AC INNER JOIN\n\t\t\t\t\tALL_CONS_COLUMNS ACC ON\n\t\t\t\t\t\tAC.CONSTRAINT_NAME = ACC.CONSTRAINT_NAME AND\n\t\t\t\t\t\tAC.OWNER = ACC.OWNER\n\t\t\t\tWHERE\n\t\t\t\t\tAC.CONSTRAINT_TYPE = 'U' AND\n\t\t\t\t\tLOWER(AC.OWNER) = %s AND\n\t\t\t\t\tLOWER(AC.TABLE_NAME) = %s", $data['schema'], $data['table_without_schema']);
         $constraints = array();
         foreach ($constraint_rows as $row) {
             if (!isset($constraints[$row['constraint_name']])) {
                 $constraints[$row['constraint_name']] = array();
             }
             $constraints[$row['constraint_name']][] = $row['column'];
         }
         $constraint_name = NULL;
         sort($data['column_names']);
         foreach ($constraints as $name => $columns) {
             sort($columns);
             if ($columns == $data['column_names']) {
                 $constraint_name = $name;
                 break;
             }
         }
         if (!$constraint_name) {
             if (count($data['column_names']) > 1) {
                 $message = self::compose('The columns "%1$s" in the table "%2$s" do not have a unique constraint', join('", "', $data['column_names']), $data['table']);
             } else {
                 $message = self::compose('The column "%1$s" in the table "%2$s" does not have a unique constraint', reset($data['column_names']), $data['table']);
             }
             $this->throwException($message, $sql);
         }
         $sql = $this->database->escape("ALTER TABLE %r DROP CONSTRAINT %r CASCADE", $data['table'], $constraint_name);
     } elseif ($data['type'] == 'add_unique') {
         $sql = $this->database->escape("ALTER TABLE %r ADD CONSTRAINT %r UNIQUE (%r)", $data['table'], $this->generateConstraintName($sql, 'uc'), $data['column_names']);
     }
     return $sql;
 }
 /**
  * Creates a unique cache prefix to help prevent cache conflicts
  * 
  * @return string  The cache prefix to use
  */
 private function makeCachePrefix()
 {
     if (!$this->cache_prefix) {
         $prefix = 'fSQLTranslation::' . $this->database->getType() . '::';
         if ($this->database->getHost()) {
             $prefix .= $this->database->getHost() . '::';
         }
         if ($this->database->getPort()) {
             $prefix .= $this->database->getPort() . '::';
         }
         $prefix .= $this->database->getDatabase() . '::';
         if ($this->database->getUsername()) {
             $prefix .= $this->database->getUsername() . '::';
         }
         $this->cache_prefix = $prefix;
     }
     return $this->cache_prefix;
 }