From: Tim Otten Date: Fri, 30 May 2014 22:51:43 +0000 (-0700) Subject: CRM_Utils_SQL_Insert X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=3e4ef32307441b4bdc6f6077ab97619ad8ba7224;p=civicrm-core.git CRM_Utils_SQL_Insert --- diff --git a/CRM/Utils/SQL/Insert.php b/CRM/Utils/SQL/Insert.php new file mode 100644 index 0000000000..e67457c787 --- /dev/null +++ b/CRM/Utils/SQL/Insert.php @@ -0,0 +1,120 @@ +row(array('col1' => '1', 'col2' => '2' )) + * ->row(array('col1' => '2b', 'col2' => '1b')); + * echo $insert->toSQL(); + * + * Note: In MySQL, numeric values may be escaped. Except for NULL values, + * it's reasonable for us to simply escape all values by default -- without + * any knowledge of the underlying schema. + * + * Design principles: + * - Portable + * - No knowledge of the underlying SQL API (except for escaping -- CRM_Core_DAO::escapeString) + * - No knowledge of the underlying data model + * - Single file + * - SQL clauses correspond to PHP functions ($select->where("foo_id=123")) + */ +class CRM_Utils_SQL_Insert { + + /** + * @var string + */ + private $table; + + /** + * @var array + */ + private $rows; + + /** + * array list of column names + */ + private $columns; + + /** + * Create a new INSERT query + * + * @param string $table table-name and optional alias + * @return CRM_Utils_SQL_Insert + */ + public static function into($table) { + return new self($table); + } + + /** + * Create a new SELECT query + * + * @param string $from table-name and optional alias + */ + public function __construct($table) { + $this->table = $table; + $this->rows = array(); + } + + /** + * @param array $rows + * @return CRM_Utils_SQL_Insert + */ + public function rows($rows) { + foreach ($rows as $row) { + $this->row($row); + } + return $this; + } + + /** + * @param array $row + * @return CRM_Utils_SQL_Insert + * @throws CRM_Core_Exception + */ + public function row($row) { + $columns = array_keys($row); + sort($columns); + + if ($this->columns === NULL) { + $this->columns = $columns; + } + elseif ($this->columns != $columns) { + throw new CRM_Core_Exception("Inconsistent column names"); + } + + $escapedRow = array(); + foreach ($columns as $column) { + $escapedRow[$column] = $this->escapeString($row[$column]); + } + $this->rows[] = $escapedRow; + + return $this; + } + + /** + * @param string|NULL $value + * @return string SQL expression, e.g. "it\'s great" (with-quotes) or NULL (without-quotes) + */ + protected function escapeString($value) { + return $value === NULL ? 'NULL' : '"' . CRM_Core_DAO::escapeString($value) . '"'; + } + + /** + * @return string SQL statement + */ + public function toSQL() { + $columns = "`" . implode('`,`', $this->columns) . "`"; + $sql = "INSERT INTO {$this->table} ({$columns}) VALUES"; + + $nextDelim = ''; + foreach ($this->rows as $row) { + $sql .= "{$nextDelim}\n(" . implode(',', $row) . ")"; + $nextDelim = ','; + } + $sql .= "\n"; + + return $sql; + } +} diff --git a/tests/phpunit/CRM/Utils/SQL/InsertTest.php b/tests/phpunit/CRM/Utils/SQL/InsertTest.php new file mode 100644 index 0000000000..2231fb2935 --- /dev/null +++ b/tests/phpunit/CRM/Utils/SQL/InsertTest.php @@ -0,0 +1,50 @@ +row(array('first' => '1', 'second' => '2' )) + ->row(array('second' => '2b', 'first' => '1b')) + ; + $expected = ' + INSERT INTO foo (`first`,`second`) VALUES + ("1","2"), + ("1b","2b") + '; + $this->assertLike($expected, $insert->toSQL()); + } + + function testRows() { + $insert = CRM_Utils_SQL_Insert::into('foo') + ->row(array('first' => '1', 'second' => '2' )) + ->rows(array( + array('second' => '2b', 'first' => '1b'), + array('first' => '1c', 'second' => '2c') + )) + ->row(array('second' => '2d', 'first' => '1d')) + ; + $expected = ' + INSERT INTO foo (`first`,`second`) VALUES + ("1","2"), + ("1b","2b"), + ("1c","2c"), + ("1d","2d") + '; + $this->assertLike($expected, $insert->toSQL()); + } + + /** + * @param $expected + * @param $actual + * @param string $message + */ + function assertLike($expected, $actual, $message = '') { + $expected = trim((preg_replace('/[ \r\n\t]+/', ' ', $expected))); + $actual = trim((preg_replace('/[ \r\n\t]+/', ' ', $actual))); + $this->assertEquals($expected, $actual, $message); + } +} \ No newline at end of file