CodeIgniter 4种子器向NOT NULL列插入NULL值的PostgreSQL适配问题
问题场景
使用CodeIgniter 4(CI4)迁移生成的表,所有列默认都是NOT NULL。在MySQL中执行种子器时,仅插入部分列也能正常运行,未指定的列会被自动处理;但切换到PostgreSQL后,迁移仅需将DOUBLE改为NUMERIC即可正常执行,种子器却报错:
Message: pg_query(): Query failed: ERROR: null value in column "created_at" violates not-null constraint
即使手动设置created_at和updated_at的填充规则,其他NOT NULL列仍会触发相同错误,而这些列在MySQL中会被留空处理。目前手动给缺失字段填充空值可正常执行,但需要明确问题原因,以及是否会影响后续表单提交等POST请求场景。
迁移代码示例
class Configuracoes extends Migration { public function up() { $this->forge->addField([ 'id_config' => [ 'type' => 'INT', 'constraint' => 9, 'usigned' => true, 'auto_increment' => true, ], 'nome_do_app' => [ 'type' => 'VARCHAR', 'constraint' => 128 ], 'tema' => [ 'type' => 'INT' ], 'xNome' => [ 'type' => 'VARCHAR', 'constraint' => 128 ], 'xFant' => [ 'type' => 'VARCHAR', 'constraint' => 128 ], 'CNPJ' => [ 'type' => 'VARCHAR', 'constraint' => 14 ], 'telefone' => [ 'type' => 'VARCHAR', 'constraint' => 11 ], 'endereco' => [ 'type' => 'VARCHAR', 'constraint' => 256 ], 'arquivo-imagem-de-fundo-login' => [ 'type' => 'VARCHAR', 'constraint' => 128 ], 'logomarca' => [ 'type' => 'VARCHAR', 'constraint' => 128 ], 'created_at' => [ 'type' => 'DATETIME' ], 'updated_at' => [ 'type' => 'DATETIME' ], 'deleted_at' => [ 'type' => 'DATETIME' ] ]); $this->forge->addKey('id_config', true); $this->forge->createTable('configuracoes'); } }
种子器代码示例
$this->db->table('configuracoes')->insert([ 'nome_do_app' => 'App', 'tema' => '4', 'xNome' => 'name', 'xFant' => 'name', 'CNPJ' => '0000000000000', 'telefone' => '(000) 0000-0000', 'endereco' => 'adress' ]);
问题原因
核心是MySQL与PostgreSQL对NOT NULL列的默认值处理逻辑不同:
- MySQL在宽松模式下,当列定义为
NOT NULL但未指定默认值时,会自动生成隐式默认值:字符串类型默认是空串'',数字类型默认是0,datetime类型默认是'0000-00-00 00:00:00'(这是MySQL的非标准行为)。 - PostgreSQL严格遵循SQL标准,
NOT NULL列必须有明确的赋值,或者列定义时指定了显式默认值。如果插入时未指定该列的值,数据库会认为要插入NULL,直接触发NOT NULL约束错误。
你的迁移代码中,除了自增的id_config,所有列都是NOT NULL但未设置默认值,所以PostgreSQL插入未指定的列时就会报错,而MySQL会自动补全隐式默认值。
解决方案
迁移时设置显式默认值
在迁移的字段定义中,给需要自动填充的列添加default参数:'created_at' => [ 'type' => 'DATETIME', 'default' => 'CURRENT_TIMESTAMP' ], 'arquivo-imagem-de-fundo-login' => [ 'type' => 'VARCHAR', 'constraint' => 128, 'default' => '' ], 'tema' => [ 'type' => 'INT', 'default' => 0 ]对于软删除的
deleted_at,可以设置为允许NULL:'deleted_at' => [ 'type' => 'DATETIME', 'null' => true ]启用CI4模型自动时间戳
创建对应的模型ConfiguracoesModel.php,开启自动时间戳功能,CI4会自动填充created_at和updated_at:class ConfiguracoesModel extends Model { protected $table = 'configuracoes'; protected $primaryKey = 'id_config'; protected $useTimestamps = true; protected $allowedFields = ['nome_do_app', 'tema', ...]; // 允许批量赋值的字段 }之后用模型插入数据,无需手动处理时间戳:
$model = model(ConfiguracoesModel::class); $model->insert([ 'nome_do_app' => 'App', 'tema' => '4', // 其他字段... ]);种子器中指定所有NOT NULL列的值
如果不想修改迁移,就在种子器中明确写出所有NOT NULL列的取值,比如空串、0或者合适的默认值。
对POST请求场景的影响
会直接影响POST请求。如果前端表单未提交某些NOT NULL且无默认值的字段,PostgreSQL会和种子器场景一样触发约束错误。
解决思路和种子器一致:
- 前端确保所有必填字段都有值(比如做表单校验);
- 后端接收请求时,给缺失的字段设置默认值;
- 数据库层面给列添加显式默认值。
内容的提问来源于stack exchange,提问作者Nathan Rodrigues

