From 9e6b7dea243efe79d22027a250fad8244af919bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wa=C5=9B?= Date: Sat, 11 Apr 2015 01:31:20 +0200 Subject: [PATCH 1/5] add an unit test, which fails for postgresql and oracle --- tests/unit/data/oci.sql | 7 +++++++ tests/unit/data/postgres.sql | 2 +- tests/unit/data/sqlite.sql | 2 +- tests/unit/framework/db/ActiveRecordTest.php | 9 ++++++++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/unit/data/oci.sql b/tests/unit/data/oci.sql index cf8736f217..dab47f7cbf 100644 --- a/tests/unit/data/oci.sql +++ b/tests/unit/data/oci.sql @@ -28,6 +28,7 @@ BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "category_SEQ"'; EXCEPTION WHEN OTHERS TH BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "item_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "order_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "order_with_null_fk_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- +BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "null_values_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "bool_values_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- BEGIN EXECUTE IMMEDIATE 'DROP SEQUENCE "animal_SEQ"'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -2289 THEN RAISE; END IF; END;-- @@ -126,6 +127,7 @@ CREATE TABLE "null_values" ( "stringcol" varchar2(32) DEFAULT NULL, CONSTRAINT "null_values_PK" PRIMARY KEY ("id") ENABLE ); +CREATE SEQUENCE "null_values_SEQ"; CREATE TABLE "type" ( "int_col" integer NOT NULL, @@ -213,6 +215,11 @@ CREATE TRIGGER "order_with_null_fk_TRG" BEFORE INSERT ON "order_with_null_fk" FO END COLUMN_SEQUENCES; END; / +CREATE TRIGGER "null_values_TRG" BEFORE INSERT ON "null_values" FOR EACH ROW BEGIN <> BEGIN + IF INSERTING AND :NEW."id" IS NULL THEN SELECT "null_values_SEQ".NEXTVAL INTO :NEW."id" FROM SYS.DUAL; END IF; +END COLUMN_SEQUENCES; +END; +/ CREATE TRIGGER "bool_values_TRG" BEFORE INSERT ON "bool_values" FOR EACH ROW BEGIN <> BEGIN IF INSERTING AND :NEW."id" IS NULL THEN SELECT "bool_values_SEQ".NEXTVAL INTO :NEW."id" FROM SYS.DUAL; END IF; END COLUMN_SEQUENCES; diff --git a/tests/unit/data/postgres.sql b/tests/unit/data/postgres.sql index bbb8aba72f..c0b2d9079b 100644 --- a/tests/unit/data/postgres.sql +++ b/tests/unit/data/postgres.sql @@ -97,7 +97,7 @@ CREATE TABLE "composite_fk" ( ); CREATE TABLE "null_values" ( - id INT NOT NULL, + id serial NOT NULL, var1 INT NULL, var2 INT NULL, var3 INT DEFAULT NULL, diff --git a/tests/unit/data/sqlite.sql b/tests/unit/data/sqlite.sql index 2c2d5901a3..8f3433c57c 100644 --- a/tests/unit/data/sqlite.sql +++ b/tests/unit/data/sqlite.sql @@ -86,7 +86,7 @@ CREATE TABLE "composite_fk" ( ); CREATE TABLE "null_values" ( - id INTEGER UNSIGNED PRIMARY KEY NOT NULL, + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, var1 INTEGER UNSIGNED, var2 INTEGER, var3 INTEGER DEFAULT NULL, diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php index f9eef9727e..91aa52d265 100644 --- a/tests/unit/framework/db/ActiveRecordTest.php +++ b/tests/unit/framework/db/ActiveRecordTest.php @@ -679,7 +679,7 @@ class ActiveRecordTest extends DatabaseTestCase $this->assertEquals(3, count($orders[1]->orderItems)); $this->assertEquals(1, count($orders[2]->orderItems)); } - + public function testPopulateRecordCallWhenQueryingOnParentClass() { (new Cat())->save(false); @@ -691,4 +691,11 @@ class ActiveRecordTest extends DatabaseTestCase $animal = Animal::find()->where(['type' => Cat::className()])->one(); $this->assertEquals('meow', $animal->getDoes()); } + + public function testSaveEmpty() + { + $record = new NullValues; + $this->assertTrue($record->save(false)); + $this->assertEquals(1, $record->id); + } } From 6e48ed05c71128be5610377ec81e2fa0fa14ba9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wa=C5=9B?= Date: Sat, 11 Apr 2015 02:20:13 +0200 Subject: [PATCH 2/5] allow calling Command::insert() without any columns --- framework/db/ActiveRecord.php | 5 ---- framework/db/QueryBuilder.php | 4 +-- framework/db/mysql/QueryBuilder.php | 39 +++++++++++++++++++++++++++++ framework/db/oci/QueryBuilder.php | 39 +++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/framework/db/ActiveRecord.php b/framework/db/ActiveRecord.php index 92f742c6cc..27bb5eabd3 100644 --- a/framework/db/ActiveRecord.php +++ b/framework/db/ActiveRecord.php @@ -449,11 +449,6 @@ class ActiveRecord extends BaseActiveRecord return false; } $values = $this->getDirtyAttributes($attributes); - if (empty($values)) { - foreach ($this->getPrimaryKey(true) as $key => $value) { - $values[$key] = $value; - } - } $db = static::getDb(); $command = $db->createCommand()->insert($this->tableName(), $values); if (!$command->execute()) { diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index bc5df9a84d..6b58746909 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -153,8 +153,8 @@ class QueryBuilder extends \yii\base\Object } return 'INSERT INTO ' . $schema->quoteTableName($table) - . ' (' . implode(', ', $names) . ') VALUES (' - . implode(', ', $placeholders) . ')'; + . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '') + . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : 'DEFAULT VALUES'); } /** diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php index f69c5a4a2b..63e3630ec8 100644 --- a/framework/db/mysql/QueryBuilder.php +++ b/framework/db/mysql/QueryBuilder.php @@ -164,4 +164,43 @@ class QueryBuilder extends \yii\db\QueryBuilder return $sql; } + + /** + * @inheritdoc + */ + public function insert($table, $columns, &$params) + { + $schema = $this->db->getSchema(); + if (($tableSchema = $schema->getTableSchema($table)) !== null) { + $columnSchemas = $tableSchema->columns; + } else { + $columnSchemas = []; + } + $names = []; + $placeholders = []; + foreach ($columns as $name => $value) { + $names[] = $schema->quoteColumnName($name); + if ($value instanceof Expression) { + $placeholders[] = $value->expression; + foreach ($value->params as $n => $v) { + $params[$n] = $v; + } + } else { + $phName = self::PARAM_PREFIX . count($params); + $placeholders[] = $phName; + $params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value; + } + } + if (empty($names) && $tableSchema !== null) { + $columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : reset($tableSchema->columns)->name; + foreach ($columns as $name) { + $names[] = $schema->quoteColumnName($name); + $placeholders[] = 'DEAFULT'; + } + } + + return 'INSERT INTO ' . $schema->quoteTableName($table) + . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '') + . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : 'DEFAULT VALUES'); + } } diff --git a/framework/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php index cddd86fa43..c61543e005 100644 --- a/framework/db/oci/QueryBuilder.php +++ b/framework/db/oci/QueryBuilder.php @@ -161,6 +161,45 @@ EOD; return $sql; } + /** + * @inheritdoc + */ + public function insert($table, $columns, &$params) + { + $schema = $this->db->getSchema(); + if (($tableSchema = $schema->getTableSchema($table)) !== null) { + $columnSchemas = $tableSchema->columns; + } else { + $columnSchemas = []; + } + $names = []; + $placeholders = []; + foreach ($columns as $name => $value) { + $names[] = $schema->quoteColumnName($name); + if ($value instanceof Expression) { + $placeholders[] = $value->expression; + foreach ($value->params as $n => $v) { + $params[$n] = $v; + } + } else { + $phName = self::PARAM_PREFIX . count($params); + $placeholders[] = $phName; + $params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value; + } + } + if (empty($names) && $tableSchema !== null) { + $columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : reset($tableSchema->columns)->name; + foreach ($columns as $name) { + $names[] = $schema->quoteColumnName($name); + $placeholders[] = 'DEAFULT'; + } + } + + return 'INSERT INTO ' . $schema->quoteTableName($table) + . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '') + . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : 'DEFAULT VALUES'); + } + /** * Generates a batch INSERT SQL statement. * For example, From edf400df96ce6fe1304f8a1fb91a78f30cca4e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Wa=C5=9B?= Date: Sat, 11 Apr 2015 02:25:26 +0200 Subject: [PATCH 3/5] add missing space --- framework/db/QueryBuilder.php | 2 +- framework/db/mysql/QueryBuilder.php | 2 +- framework/db/oci/QueryBuilder.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index 6b58746909..9613555610 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -154,7 +154,7 @@ class QueryBuilder extends \yii\base\Object return 'INSERT INTO ' . $schema->quoteTableName($table) . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '') - . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : 'DEFAULT VALUES'); + . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : ' DEFAULT VALUES'); } /** diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php index 63e3630ec8..587f7bd668 100644 --- a/framework/db/mysql/QueryBuilder.php +++ b/framework/db/mysql/QueryBuilder.php @@ -201,6 +201,6 @@ class QueryBuilder extends \yii\db\QueryBuilder return 'INSERT INTO ' . $schema->quoteTableName($table) . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '') - . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : 'DEFAULT VALUES'); + . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : ' DEFAULT VALUES'); } } diff --git a/framework/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php index c61543e005..c0db023e3e 100644 --- a/framework/db/oci/QueryBuilder.php +++ b/framework/db/oci/QueryBuilder.php @@ -197,7 +197,7 @@ EOD; return 'INSERT INTO ' . $schema->quoteTableName($table) . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '') - . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : 'DEFAULT VALUES'); + . (!empty($placeholders) ? ' VALUES (' . implode(', ', $placeholders) . ')' : ' DEFAULT VALUES'); } /** From 67799ad09241e28141081171715344bef86e1326 Mon Sep 17 00:00:00 2001 From: John Was Date: Sat, 11 Apr 2015 16:41:33 +0200 Subject: [PATCH 4/5] fix typos --- framework/db/mysql/QueryBuilder.php | 2 +- framework/db/oci/QueryBuilder.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php index 587f7bd668..14f946d7a2 100644 --- a/framework/db/mysql/QueryBuilder.php +++ b/framework/db/mysql/QueryBuilder.php @@ -195,7 +195,7 @@ class QueryBuilder extends \yii\db\QueryBuilder $columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : reset($tableSchema->columns)->name; foreach ($columns as $name) { $names[] = $schema->quoteColumnName($name); - $placeholders[] = 'DEAFULT'; + $placeholders[] = 'DEFAULT'; } } diff --git a/framework/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php index c0db023e3e..ff1f1afdbd 100644 --- a/framework/db/oci/QueryBuilder.php +++ b/framework/db/oci/QueryBuilder.php @@ -191,7 +191,7 @@ EOD; $columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : reset($tableSchema->columns)->name; foreach ($columns as $name) { $names[] = $schema->quoteColumnName($name); - $placeholders[] = 'DEAFULT'; + $placeholders[] = 'DEFAULT'; } } From c73ad6573d3dc000d10c7620d46c56df91ad55e1 Mon Sep 17 00:00:00 2001 From: John Was Date: Sun, 24 May 2015 23:33:55 +0200 Subject: [PATCH 5/5] updated changelog [ci skip] --- framework/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index bece4b3ae1..6055f546cf 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -5,6 +5,7 @@ Yii Framework 2 Change Log ----------------------- - Bug #7305: Logging of Exception objects resulted in failure of the logger i.e. no logs being written (cebe) +- Bug #7374: Use proper INSERT syntax with default values when no values are specified (nineinchnick) - Bug #7707: client-side `trim` validator now passes the trimmed value to subsequent validators (nkovacs) - Bug #8322: `yii\behaviors\TimestampBehavior::touch()` now throws an exception if owner is new record (klimov-paul) - Bug #8451: `yii\i18n\Formatter` did not allow negative unix timestamps as input for date formatting (cebe)