Skip to main content

eloquent 4.3.0 copy "eloquent: ^4.3.0" to clipboard
eloquent: ^4.3.0 copied to clipboard

Laravel-style (Eloquent 5.2) query builder, schema builder and migrations for Dart, with PostgreSQL and MySQL drivers.

4.3.0 #

Security: an operator or a JSON selector that came from outside could carry SQL.

  • where, orWhere, whereNot, whereAny, whereAll, having, join, joinWhere, whereDate and the other date clauses, whereJsonLength and the subquery wheres put the operator in the SQL as it came: where('id', '= 0 or 1=1 or 1 =', 1) returned every row. They now accept only known operators and throw ArgumentError for any other. If your code builds a query from an operator it received, as a search filter does, update.
  • where('data->x', ...) with a quote in the key on PostgreSQL and on MySQL, and a backslash in the path of whereJsonContains on MySQL and MariaDB, ended the string and could carry SQL. They are escaped now, and in the JSON key of an update on MySQL too.
  • whereColumn('a', 'unknown', 'b') was silently a = unknown, dropping b. It throws now. whereColumn('a', 'b') is still a = b.

Operators:

  • custom_operators in the connection config, and Connection.addCustomOperators, add the operators of an extension or of a CREATE OPERATOR, on any database: ['<=>', 'OPERATOR(geo.<->)']. They also apply to whereSqlOperator.
  • On PostgreSQL every operator of pg_operator is accepted, and a test compares the list with the server's catalog. whereSqlOperator accepts all the ones that return a boolean.
  • ?, ?|, ?& and @? work on PostgreSQL, compiled to jsonb_exists, jsonb_exists_any, jsonb_exists_all and jsonb_path_exists_opr, as ? is the bind placeholder. They failed with a wrong number of parameters.
  • SqlOperator.postgresRegexAll and postgresNotRegexAll were ~/// and !~///. They are ~* and !~*, so whereFlex('a', '~*', 'x') works.
  • On MySQL # is no longer an operator: it starts a comment.

4.2.1 #

  • postgres_v3: a date DateTime cannot represent (infinity, -infinity, a year past its range) is now null, as on the other adapters, with any pg_types, and in arrays. 'infinity'::date came back as the wrong date 36070-01-11, and 'infinity'::timestamp failed with a RangeError no caller could catch, which ended the process. Dates before Christ keep their value.
  • postgres_v3: postgres 3.5.7 has errors no caller can catch, which by default end the process: a write to a socket the server reset, a script with two statements that return rows, a malformed message. The adapter now opens its sockets in a zone that drops them. The call they belong to still fails, or succeeds, by itself, and your own errors reach you as before.
  • Requires dargres 4.1.1, dpgsql 1.3.1 and postgres_fork 3.1.2: an error writing to a socket the server reset no longer ends the process there either.

4.2.0 #

  • The dargres and dpgsql adapters no longer send the password in clear text when the server asks for it with no verified TLS: whoever asks gets the password itself, so with sslmode require, or without TLS, that could be anyone between the application and the server. The connection fails instead. With a server that authenticates with LDAP, PAM or password, use sslmode verify-full, or driver_options allowCleartextPassword: true (dargres) or 'Allow Cleartext Password': 'true' (dpgsql). postgres already refused it, and its allowClearTextPassword now also works with pool. On postgres_v3 the postgres package has no such option: it still sends it.
  • The dpgsql adapter requires dpgsql 1.3.0: opening a connection is now bounded by Timeout (15 s by default; it had no limit, so a server that stopped answering left it waiting forever), and bytes it cannot read on an idle connection lose that connection instead of ending the process.
  • The dargres adapter requires dargres 4.1.0, and the postgres adapter postgres_fork 3.1.1.
  • README: what each sslmode does, the host name a certificate must name, what require does not protect from, and a "What's new in 4.1" section.

4.1.0 #

  • TLS certificates on every adapter: the sslrootcert, sslcert, sslkey (the names Laravel uses for PostgreSQL) and sslpassword config keys, and security_context for a SecurityContext with certificates in memory. A private CA can verify the server with sslmode verify-ca or verify-full (only the system roots could), and the connection can present a client certificate. Settings that cannot be honored throw ArgumentError instead of connecting with less security: sslrootcert in a mode that does not verify, any certificate with sslmode disable or absent, a certificate without its key, a file that cannot be loaded.
  • The mysql adapter requires mysql_dart 4.0.1. sslmode keeps its meaning: require, allow and prefer use TLS without verifying the certificate, verify-ca and verify-full verify it.
  • The dpgsql adapter requires dpgsql 1.2.0, and the postgres adapter postgres_fork 3.1.0.

4.0.0 #

Details and migration notes: doc/release_notes_4.0.0.md.

Breaking changes #

  • ConnectionInterface: transaction<T>() is generic, and implementations must add transactionIfNeeded<T>(), isInTransaction and supportsSavepoints.
  • transaction() inside a transaction now creates a SAVEPOINT instead of a new transaction, and timeoutInSeconds is passed to the driver.
  • InvalidArgumentException replaced by ArgumentError.
  • Time columns (timestamp, dateTime, time and the Tz forms, timestamps(), softDeletes()) have no precision by default (timestamp without time zone, microseconds on PostgreSQL); they were timestamp(0), which rounds the fractions that 4.0 writes to the next second. SchemaBuilder.defaultTimePrecision = 0 restores Laravel's default; precision: sets one column.
  • Blueprint.toSql throws for a command that does not compile or is unknown (it printed a warning and skipped the DDL).
  • Manager.bootEloquent() (empty) and Manager.setFetchMode() (wrote a database.fetch setting nothing read) removed.
  • Container stubs that returned null (getMethodDependencies, getCallReflector, getDependencies, resolveNonClass, resolveClass, keyParametersByArgument, getFunctionHint) and the static Container.object removed.
  • The dargres adapter requires dargres 4.0.1.
  • The dpgsql adapter requires dpgsql 1.1.1 (DpgsqlCommand.simpleQuery; the session settings no longer time out after 100 ms, which failed connections and, on a pool, handed them out without the settings under load).
  • The mysql adapter requires mysql_dart 3.0.3. A DATE/DATETIME that DateTime cannot represent (the zero date 0000-00-00, a zero month or day, 2024-02-30 under ALLOW_INVALID_DATES) reads as null; it came back as another day (0000-00-00 as -0001-11-30). driver_options zeroDateBehavior: 'exception' throws instead.
  • where(col, op, null) with an operator other than =, <> or != throws (it silently became is not null); where(col, value) means = value.
  • value(col) returns the column value instead of the row; update() and ConnectionInterface.update return Future<int>.
  • count() on grouped/distinct/union queries counts their rows (it returned the first group's count); count('*', CountMode.laravel) keeps Laravel's semantics.
  • PostgreSQL update/delete with a left join and recursionLimit() on PostgreSQL/MySQL throw instead of producing wrong or ignored SQL.
  • PostgreSQL values are the same Dart types on the four adapters (pg_types: 'uniform', the default): the types without a natural Dart type (time, timetz, interval, inet, cidr, macaddr, geometric types, bit, tsvector, ranges) are the server's text (postgres_fork returned PgTime, PgInterval, PgInet, PgPoint; postgres_v3 Time, Interval, IntRange...; dpgsql DpgsqlInterval, DpgsqlRange...), money the decimal amount, arrays List<T?>, arrays of user-defined types (enum[]) the array literal. pg_types: 'driver' keeps each driver's types. See the release notes.
  • A unique violation (PostgreSQL 23505, MySQL 1062/1586) throws UniqueConstraintViolationException (a QueryException with the driver's exception in previous and its text in toString()) instead of the driver's exception; other errors are not wrapped.
  • A statement that loses its connection while it runs is not run again: the server may have committed a write (running it twice duplicated the row), and a select can have side effects too (nextval, a function that writes). The connection is reopened for the next statement and the error is thrown. A statement the driver refused before sending it (a connection the server had already closed, issue #2) is still retried. Concurrent reconnections of a connection share one (each made its own and left the others' PDOs open).
  • With 'pool': true, a lost connection no longer closes and remakes the whole pool, which failed the statements other requests were running on it: the pool discards the dead connection (pool_reconnect: 'keep', the default). pool_reconnect: 'replace' keeps the old behavior. A TLS handshake or certificate failure is not taken for a lost connection (it was retried 10 times, about 40 s).
  • sslmode is honored by every adapter (disable, allow, prefer, require, verify-ca, verify-full, and the MySQL names): postgres and dargres ignored it and connected in plain text, postgres_v3 only knew require, dpgsql trusted any certificate in verify-full. An unknown value, or a mode the adapter cannot honor (verify-full on a postgres pool), throws ArgumentError instead of connecting with less security. The dargres adapter requires dargres 4.0.2 (its TLS handshake always failed).
  • An unknown driver_implementation throws ArgumentError (it fell back to postgres); port is optional (5432, 3306 for mysql); pool, poolsize and port accept strings.
  • min/max (QueryBuilder and QueryBuilder2) are generic: without a type they return the driver's value (a DateTime for a date column, text for a PostgreSQL numeric); final num? m = await q.max('x') still gets a number. They threw on date columns.
  • pretend() covers only the code of its callback (a Zone): another request on the same connection was pretended too, and a query started in the callback but run after it is not pretended. pretendingProp is deprecated.
  • A query listener that throws no longer turns a successful statement into an error: its error goes to listen(callback, onError: ...), or is printed.
  • outsideTransaction() inside a transactionScope needs poolsize >= 2 (it waited forever on a pool of one).
  • Connection.tryReconnectCount and reconnectIfMissingConnection() were removed; getElapsedTime returns milliseconds (it returned nanoseconds).
  • DatabaseManager keeps name::read/name::write apart from name, and concurrent connection(name) calls share one connection (the second one leaked).
  • MySQL: strict: false turns strict mode off (it turned it on), the strict modes no longer include NO_AUTO_CREATE_USER (removed in MySQL 8), charset/collation work as in Laravel (charset: 'utf8mb4' failed with "Unknown collation").
  • QueryBuilder: setBindings takes a List; whereBetween needs two bounds; insertMany returns PDOResults, uses the keys of every row and inlines QueryExpression values; inRandomOrder takes an integer seed only.
  • Utils/Arr/Fluent follow their PHP namesakes: explode with a limit, arrayGet with dot notation, arrayPop, arrayShift, arrayRand, mapExceptSd, ltrim, substr, empty, strval, endsWith, Fluent.isset, Str.parseCallback.
  • Utils members renamed to lowerCamelCase (no aliases): array_map → arrayMap, array_filter → arrayFilter, in_array → inArray, str_replace → strReplace, array_pluck → arrayPluck, map_change_key_case_sd → mapChangeKeyCaseSd, array_get → arrayGet, data_get → dataGet, array_accessible → arrayAccessible, is_object → isObject, array_collapse → arrayCollapse, array_shift → arrayShift, array_exists → arrayExists, string_contains → stringContains, array_merge → arrayMerge, array_merge_ss → arrayMergeSs, array_merge_ms → arrayMergeMs, array_merge_sa → arrayMergeSa, array_flatten → arrayFlatten, map_merge_sd → mapMergeSd, map_add_sd → mapAddSd, map_except_sd → mapExceptSd, map_unset_deep_sd → mapUnsetDeepSd, array_unshift → arrayUnshift, array_rand → arrayRand, is_array → isArray, is_map → isMap, is_bool → isBool, is_string → isString, array_is_empty → arrayIsEmpty, is_null → isNull, is_null_or_empty → isNullOrEmpty, map_key_exists → mapKeyExists, map_first_key → mapFirstKey, array_end → arrayEnd, var_dump → varDump, str_length → strLength, array_pop → arrayPop, map_values → mapValues, int_max → intMax, array_fill → arrayFill. Utils.explode returns List<String>.
  • Stricter types: QueryBuilder.bindings is Map<String, List<dynamic>>; prepareBindings returns a List and pretend a Future<List<Map<String, dynamic>>>; Connection.setPdo/setReadPdo return Connection, getPostProcessor a Processor? and getDefaultSchemaGrammar a SchemaGrammar?; DatabaseManager.connectionsProp holds Connections; some untyped QueryBuilder parameters (boolean, not, type, operator, column) are typed.

Added #

  • pg_types ('uniform' | 'driver') and driver_options (settings passed to the driver; an unknown key throws with the list of known ones) config keys; PDOConfig.fromMap reads snake_case and camelCase spellings (application_name/applicationName, poolsize/poolSize, allowreconnect/allowReconnect, is_unix_socket/isUnixSocket), so PDOConfig.fromMap(config.toMap()) round-trips.
  • MySQL driver_options zeroDateBehavior ('convertToNull', the default, or 'exception'; also the ZeroDateBehavior value) for dates DateTime cannot represent.
  • test/pg_type_parity_integration_test.dart: ~55 PostgreSQL types read cold, warm, in one row and inside a transaction must give the same Dart type and value on the four adapters.
  • QueryBuilder2 (db.table2()): Dart 3 API with fixed-meaning arguments (where(column, value, {operator})), CompOperator/SortDirection enums, typed group/join builders, whereSqlOperator validated per dialect and typed results, paginate/simplePaginate (PageResult), OnConflict, insertReturning/updateReturning/deleteReturning, upsert, insertOrIgnore, union and (recursive) CTEs; compiled by the existing grammars.
  • datetime_bindings: 'native' sends DateTime bindings to the driver unchanged (microseconds kept, correct instant for timestamptz); the default 'string' keeps Laravel's yyyy-MM-dd HH:mm:ss formatting.
  • PostgreSQL, datetime_bindings: 'string' (default): a DateTime with fractional seconds keeps them (2024-03-10 12:34:56.789123; 10:20:30.123 was stored as 10:20:30). Whole seconds are formatted as before. MySQL keeps whole seconds (its DATETIME would round a fraction instead of truncating it). New Grammar.formatDateTime, also used by toRawSql().
  • PostgreSQL LISTEN/NOTIFY on every PostgreSQL adapter: db.notify() (transactional, via pg_notify), db.notifications(channel) and PostgresNotificationListener (one dedicated connection, reconnection, heartbeat, ready(), onReconnect).
  • Nested transactions via SAVEPOINT, transactionIfNeeded(), isInTransaction and a real transactionLevel() (issue #6).
  • Row locks: lockForUpdate({skipLocked, noWait, of}), sharedLock(...), skipLocked(), noWait().
  • groupByRaw(sql, bindings); groupBy accepts db.raw(...) (it was silently ignored) and rejects other values.
  • upsert(), insertOrIgnore(), onConflict()/doUpdate()/doNothing() and returning().
  • PostgreSQL JSON, full-text, distinctOn() and array operators (whereArrayContains, whereArrayContainedBy, whereArrayOverlaps).
  • cursor()/lazy() streaming and driver() access (COPY, LISTEN/NOTIFY) for dpgsql.
  • Migrations, seeders and CLI (make:migration, make:seeder, schema:diff); transactional migrations.
  • Working schema introspection and diff → ALTER TABLE for PostgreSQL.
  • Doctrine schema port completed: Schema.toSql()/toDropSql(), PostgreSQLPlatform, MySQLPlatform, MariaDBPlatform (db.getDatabasePlatform()), Comparator.compareSchemas() + getAlterSchemaSQL(), introspectSchema(), namespaces, MATCH/DEFERRABLE foreign keys, UNLOGGED tables.
  • ORM without code generation or reflection, in two styles: Active Record (Model<T> with plain fields, toMap()/fill() and modelDefinition; or AttributeModel<T> with the columns in a map and typed getters/setters; ModelDefinition with fillable (assign()), hidden (toJson()) and insertNulls, ModelQuery, casts, save() of the changed columns, HasOne/HasMany/BelongsTo/BelongsToMany, latestOfMany/ofMany, eager loading in batches, global and local scopes, soft deletes, observers) and Data Mapper (Repository<T>, EntityMapper<T>).
  • DargresPDO applies search_path, encoding, time zone, application name and timeouts like the postgres_fork adapter; numeric as exact String.
  • pubspec repository, issue_tracker, documentation and topics.
  • From Laravel 12: db.listen(), onSlowQuery(), totalQueryDuration() and the QueryExecuted event; toRawSql()/dumpRawSql(); when(), unless(), tap(); whereAny/whereAll/whereNone/whereNot; whereLike (case-insensitive by default); orderByDesc, reorder and orderBy with a subquery; select/addSelect with subqueries, fromSub, leftJoinSub/rightJoinSub/crossJoinSub; lazyById, eachById, chunkByIdDesc, chunkMap; sole, soleValue, firstOrFail, findOr, doesntExist, existsOr; havingBetween, havingNull, nested having; whereIntegerInRaw (no 65535 parameter limit); incrementEach/decrementEach; MySQL whereFullText.
  • db.transactionScope((ctx) ...): an ambient transaction; inside it, code that uses db (a repository called from the callback) runs in the same transaction instead of another pooled connection (or waiting forever on a single connection), db.transaction becomes a SAVEPOINT and db.isInTransaction is true. db.outsideTransaction(...) leaves the scope on purpose (needs a pool); isInTransactionScope.
  • Search and filters: whereSearch(columns, term, {match, unaccent, everyWord}) (the term matched literally: %/_ escaped; a null or blank term adds nothing), whereAnyLike, unaccent: true on whereLike (PostgreSQL unaccent(col::text) ilike unaccent(?), QueryPostgresGrammar.unaccentFunction), lower: true (lower(col) like lower(?), for expression indexes and case-sensitive MySQL collations), QueryBuilder.escapeLike and likePattern; orderByInput(field, direction, allowed: {...}, fallback:) orders by a client parameter through an allow-list.
  • whereInTuple/whereNotInTuple ((a, b) in ((?, ?), ...)), whereRowValues ((a, b) > (?, ?)), whereAnyArray/whereNotAnyArray (col = any(?) with one array parameter, in (...) on MySQL), whereBetweenColumns, whereValueBetween.
  • Migrations: Migrator.runPending() (the registered migrations not run yet, from the registry alone, so it works in an AOT build) and pending(); markAsRan() (baseline of an existing database); Migrator.isolated (default on PostgreSQL: each migration takes an advisory lock and checks the log again, so concurrent deploys apply it once); the log is written in the migration's transaction (transactionScope), so a failure rolls both back; Migration.connection and Migration.statement(sql) for raw SQL; the migration table gets an id primary key.
  • PostgreSQL column comments (comment(...)) become COMMENT ON COLUMN (they were ignored); MySQL time columns take precision: (datetime(3)).
  • cursorPaginate() (keyset pagination with opaque cursors, CursorPage), paginateWithWindowCount() (opt-in one-query pagination with count(*) over ()), explain({analyze}), afterQuery(callback), updateFrom().
  • Bulk: copyInto(rows) (COPY on dpgsql, multi-row INSERTs elsewhere), insertUnnest(rows, types:) (insert ... select * from unnest(?::int[], ...), PostgreSQL); nextValue(column) (max + 1 per scope under pg_advisory_xact_lock).
  • cursor() on every PostgreSQL adapter: the adapters without an incremental reader use an SQL cursor (DECLARE ... FETCH in a transaction); PDOExecutionContext.supportsQueryStream.
  • QueryBuilder2: whereAny/whereAll/whereNone, whereDate/Time/ Day/Month/Year, JSON (whereJsonContains, whereJsonLength), arrays (whereArray), whereFullText, distinctOn, joinSub, joinLateral/leftJoinLateral, latest/oldest/inRandomOrder,
  • db.unprepared(sql) and db.execute(sql) (Laravel's DB::unprepared, PHP's PDO::exec) use the simple query protocol on the four PostgreSQL adapters, so a script with several statements, dollar-quoted bodies and its own BEGIN/COMMIT runs the same everywhere; postgres_v3 and dpgsql sent it through the extended protocol, which refuses more than one statement (42601). Queries with bindings keep the extended protocol.
  • UniqueConstraintViolationException (Laravel 10+) with the violated constraint, on the five adapters, also when it ends a transaction on postgres/postgres_v3. createOrFirst()/firstOrCreate() (insert or return the row a unique index already has; on conflict do nothing returning * on PostgreSQL, so a surrounding transaction stays usable).
  • PostgreSQL locks: db.advisoryLock(key)/db.tryAdvisoryLock(key) (pg_advisory_xact_lock, held until the transaction ends) and lockTable(TableLockMode, {noWait}) (LOCK TABLE, mode from an enum). cursor, chunk, chunkById, insertUsing, firstOrFail, createOrFirst, firstOrCreate, lockTable, and every item above.

Fixed #

  • A PostgreSQL transaction whose callback caught an SQL error (without a savepoint) reported success on dargres and dpgsql, although the server answered its COMMIT with a ROLLBACK and nothing was saved. It now fails with an error on every PostgreSQL adapter. Requires dargres 4.0.3 and dpgsql 1.1.3.

  • postgres_v3: an SQL error inside a nested transaction (savepoint) rolled back the whole transaction, because postgres 3.x runTx fails the transaction after any error. The adapter sends BEGIN/COMMIT itself on a reserved connection (from the pool, or the single connection under its lock) and tracks whether the server aborted the transaction, so ROLLBACK TO SAVEPOINT recovers as on the other adapters. A COMMIT or ROLLBACK that fails without an answer from the server closes that connection, so it is never reused with the transaction in an unknown state.

  • A connection the server closed for being idle was not always seen as lost: PostgreSQL 14+ idle_session_timeout (SQLSTATE 57P05, "terminating connection due to idle-session timeout") made every later query fail on dargres; it reconnects, as MySQL/MariaDB wait_timeout already did (issue #2). Tested on every adapter, with and without a pool.

  • postgres (postgres_fork): an SQL error inside a nested transaction() (SAVEPOINT) rolled back the whole transaction, as the driver did not know a savepoint was active; nested transactions use the driver's own savepoint() (also through the pool), so the error rolls back to the savepoint and the outer transaction goes on, as on dargres, dpgsql and MySQL. New PDOExecutionContext.runInSavepoint for such drivers.

  • unprepared() in pretend() threw a TypeError (it returned true as the affected rows); it returns 0.

  • MySQL increments() in a create or add failed with error 1075 (the auto_increment column was not declared a key); it is primary key inline, as in Laravel.

  • Manager.connection(), table() and schema() used the global instance (instance!), so they failed with a null check without setAsGlobal(); they use the manager they are called on.

  • IoC Container: factories were called with the wrong arguments (every closure binding failed); they are called with (), (container) or (container, parameters). extend, rebinding, contextual bindings (when().needs().give(), which also passed a method instead of the abstract) and []= with a value crashed; share cached one value for all closures; make of an unknown type crashed with NoSuchMethodError (BindingResolutionException now); a binding to another name threw UnimplementedError; resolving/afterResolving did nothing; empty list/map instances were not bound; call/wrap passed null instead of the parameters; refresh and Class@method throw UnsupportedError (they need reflection).

  • dpgsql: EXPLAIN, VALUES, TABLE, FETCH and parenthesized SELECTs returned no rows (they were run as statements without a result).

  • postgres_v3: inet, cidr, macaddr and oid came back as garbage strings (binary bytes decoded as text) and money, timetz, bit, regclass, enum[] and arrays of numeric, money and interval threw FormatException.

  • postgres_fork: driver_options queryTimeoutInSeconds (or the pool's queryTimeout) is the default per-query timeout; each query passed its own 30 s, so the connection's default never applied. Same for postgres_v3 queryTimeout and dargres commandTimeout (pool).

  • dpgsql: a statement on a connection the server closed is detected as a lost connection and retried on a new one (dpgsql 1.0.3 reports it as "the connection to the server was lost").

  • Query builder: groupBy(raw) ignored, whereIn([]), exists() read connection, union(Function), updateOrInsert, clone(), paginate side effects, builder left without orders/limit after a failed count.

  • PostgreSQL: delete with joins (USING), update/delete with limit (ctid), CTEs in update/delete, update binding order.

  • MySQL: CTEs in select/update/delete; session settings (set names, time_zone, sql_mode) on every pool connection.

  • Timeouts: forwarded by get/insert/update/delete/selectOne; session timeouts on postgres_v3 and MySQL; snake_case timeout keys; per-call timeouts on dargres without pool; dpgsql cancels timed-out commands and replaces the connection.

  • dpgsql: affected rows of WITH ... UPDATE/DELETE.

  • MySQL: exists() was always false (1 == true), so updateOrInsert always inserted.

  • whereColumn([...]) bound the second column as a value.

  • MySQL insertGetId could return another request's id (a separate SELECT LAST_INSERT_ID() ran on another pooled connection, or after another caller's insert); the id now comes with the INSERT result (PDOResults.insertId).

  • postgres_v3 and dargres without pool: a request that arrived during another one's transaction failed; callers of the single connection are serialized, like dpgsql and MySQL.

  • dpgsql: numeric comes back as the exact server text ('150.50'), as on postgres, postgres_v3 and dargres; it was a double (150.5, digits lost beyond ~15). Uses dpgsql 1.0.3's Decode Numeric As String.

  • dpgsql single connection: transactions run through DpgsqlConnection.runInTransaction (dpgsql 1.0.3), which keeps other callers' commands out of them even without the adapter's lock.

  • union + orderByRaw with bindings emitted them before the union parts' bindings; pagination counts of a union kept its limit.

  • MySQL onConflict() emitted PostgreSQL's on conflict; it is compiled as on duplicate key update.

  • Lost connections: dpgsql and MySQL without pool were not detected (every later query failed); detection now uses the error codes and the messages of each adapter, without matching data such as a CEP.

  • Migrations: foreign() produced no SQL (PostgreSQL and MySQL), schema.create() ran an empty query on MySQL, and dropColumn(), dropForeign() and rename() produced no SQL on PostgreSQL; deferrable()/initiallyDeferred() are compiled on PostgreSQL.

  • Doctrine Table.addForeignKey() always threw; renameColumn() updates indexes, unique constraints and foreign keys.

  • where/having with a List value (Uint8List, PostgreSQL array, JSON) spread it into several bindings for one placeholder.

  • pretend() returned before an async callback ran (empty log) and ran selects for real; it now awaits the callback and restores the log.

  • chunkById reused the same builder between pages and did not detect a missing id column.

  • PostgreSQL whereFullText validates the language (it is inlined in SQL).

  • dpgsql and MySQL without pool serialize concurrent callers of the single connection, like the other adapters (they failed with "connection is busy").

  • postgres_fork: same duplicate-column mapping inside transactions.

  • prepareBindings no longer modifies the caller's list; lost-connection retry loop; getConfig(); driver extensions lookup.

  • Empty insert SQL, List schema config, migration batches and rollback, PostgreSQL and MySQL introspection, ColumnDiff comparison.

  • Connections: the read connection (readPdo) is closed by disconnect and used by the SQL cursor fallback; cancelling a cursor waits for it to close; purge(null) and reconnect close the old connection; setTablePrefix reaches the schema grammar; ConnectionFactory closes the write connection when the read one fails, rejects read/write configs with different drivers or no read host, accepts read without write (and the reverse), several read hosts, a null prefix, and no longer changes the caller's config map.

  • PostgreSQL session: schema names with quotes or commas, an empty schema (no SET search_path TO with nothing), application_name and timezone with quotes on postgres and postgres_v3.

  • SQL: whereTime on PostgreSQL (::time, it produced invalid SQL), whereIn inside a join, insertUsing with a CTE on PostgreSQL, whereJsonContains on MySQL, JSON updates on MySQL, onRaw in a PostgreSQL update with joins, leading AND/OR of raw wheres, whereFlex with a list, get() restores the columns after a failure.

  • Migrator: pretend mode without the pretend key threw.

  • db.raw(value) with a value that is not a String (db.raw(1)) threw a TypeError when the query was compiled; it is written with toString().

  • DSNParser.getDSN() always threw (the user, password, port and params were used as bool conditions); it builds the URL again.

  • DSNParser.port threw for a heroku-style URL, whose port is parsed as an int.

  • DSNParser: user, password, host, driver and database threw when the DSN has no such part (always for user and password of a PDO DSN); they are nullable now.

  • DSNParser.params threw for a PDO DSN (its default {} was not a Map<String, dynamic>).

  • DSNParser.options threw when the DSN has options=: it is the String written there, not a Map.

Performance #

  • About 17% faster toSql() (direct dispatch and wrap() fast paths) and no per-query column copy in get().

3.4.3 #

  • add Postgres whereExists integration test, expand whereExists docs, and include README example for QueryBuilder subquery usage

3.4.2 #

  • implemented insertMany to execute an optimized "batch insert", generating a single SQL statement

3.4.0 #

  • upgrade mysql_dart to 1.2.0 for fix textual BLOB/TEXT columns are decoded as UTF‑8 strings consistently

3.3.2 #

Added #

  • JoinClause.onRaw(String sql, [String boolean = 'and', List bindings = const []]) — Allows raw SQL fragments inside JOIN ... ON conditions. Use the optional boolean to chain with AND/OR and bindings to safely pass parameters that will be appended to the join bindings.

    Examples

    // Simple raw predicate (defaults to boolean = 'and' and no bindings)
    db.table('processes')
      .join('listings as l', (JoinClause jc) {
        jc.on('l.process_id', '=', 'processes.id');
        jc.onRaw("l.state IN ('open','running')");
      })
      .get();
    
    // Raw predicate with OR and parameter bindings
    db.table('tickets as t')
      .join('labels as l', (JoinClause jc) {
        jc.on('l.ticket_id', '=', 't.id');
        jc.onRaw('l.tags @> ARRAY[?]::text[]', 'or', ['urgent']); // adds to join bindings
      })
      .get();
    
  • QueryBuilder.clone() — Creates a deep, independent copy of a query builder (including selects, joins, wheres, orders, and bindings). Modifying the clone does not affect the original, which is handy for deriving variations from a base query.

    Example

    // 1) Base query for active users
    final baseQuery = db.table('users').where('status', '=', 'active');
    
    // 2) Independent clones
    final usersCountQuery = baseQuery.clone();
    final firstUserQuery = baseQuery.clone();
    
    // 3) Different uses
    final totalActiveUsers = await usersCountQuery.count();
    final firstUser = await firstUserQuery.orderBy('created_at').first();
    
    // Original remains reusable
    final allActiveUsers = await baseQuery.get();
    
  • QueryBuilder.joinRaw(String tableExpression, [List bindings = const [], String type = 'inner', Function(JoinClause)? on]) — Adds a JOIN using a raw table/Expression (kept unwrapped), optional parameter bindings for that expression, an optional join type (default inner), and an optional on callback to configure ON conditions (including onRaw).

    Examples

    // 1) Join a subquery with bindings and configure ON via callback
    final since = DateTime.now().subtract(const Duration(days: 30));
    db.table('orders')
      .joinRaw(
        '(select * from invoices where created_at >= ?) as i',
        [since],                         // bindings for the tableExpression
        'left',
        (JoinClause jc) {                // ON configuration
          jc.on('i.order_id', '=', 'orders.id');
          jc.onRaw('i.status IN (?, ?)', 'and', ['paid', 'settled']);
        },
      )
      .get();
    
    // 2) Join a raw expression without ON (e.g., NATURAL or ON TRUE)
    db.table('metrics')
      .joinRaw('generate_series(1, 10) as g')
      .get();
    

Tests #

  • Expanded Postgres pool v2 coverage: concurrency limits (server‑measured), SET LOCAL scoping, lock_timeout behavior, connection reuse, and PID churn after purge().

3.3.1 #

  • fix bug on PgPool for PostgresV2PDO

3.3.0 #

  • add option to decode timestamp without timezone and date as local DateTime and decode timestamp with timezone respecting the timezone defined in the connection for driver_implementation postgres

  • update postgres_v3 to ^3.5.4 With that dependency, upgraded minimum SDK to 3.4

  • change mysql_client to mysql_dart ^1.0.0

  • change postgres_fork ^2.8.4 to postgres_fork ^2.8.5

 final manager = Manager();
  manager.addConnection({
    'driver': 'pgsql',
    'driver_implementation': 'postgres_v3', // postgres | dargres | postgres_v3
    'timezone': 'America/Sao_Paulo',   
    // If true, decodes the timestamp with timezone (timestamptz) as UTC = default
    // If false, decodes the timestamp with timezone using the timezone defined in the connection.
    'forceDecodeTimestamptzAsUTC': false,
    // If true, decodes the timestamp without timezone (timestamp) as UTC.
    // If false, decodes the timestamp without timezone as local datetime.
    'forceDecodeTimestampAsUTC': false,
    // If true, decodes the date as UTC.
    // If false, decodes the date as local datetime.
    'forceDecodeDateAsUTC': false,
    'pool': true,
    'poolsize': 2,
    'host': 'localhost',
    'port': '5435',
    'database': 'siamweb',
    'username': 'dart',
    'password': 'dart',
    'charset': 'win1252',
    'prefix': '',
    'schema': ['public'],
    // require | disable
    //'sslmode' : 'require',
  });

3.2.1 #

  • fix bug on format Schema

3.2.0 #

  • fix bugs in onOpen callback to configure connection settings
  • improvements to README
  • implemented connection pool for 'postgres' (v2) driver_implementation

3.1.2 #

  • fix bugs in lost connection detection to automatically reconnect. Updated postgres to 3.1.2 to be able to use the onOpen callback to configure connection settings like setting search path

3.0.1 #

  • fix bug on query builder count()

3.0.0 #

  • implemented support for mysql through the 'mysql_client' package and also implemented support for posgress with the postgres v3 package, now you can choose the driver implementation through 'driver_implementation': 'postgres_v3', in addConnection method

2.2.0 #

  • add fromRaw (sub-query as from), joinSub (method to join a query to a sub-query)

2.1.2 #

  • remove print from disconnect() in Connection class

2.1.1 #

  • small improvement and optimization in postgres PDO implementation

2.1.0 #

  • change dependencie postgres to my fork postgres_fork
  • add more tests

2.0.2 #

2.0.2 #

  • fix bug on set application_name to postgresql < 8.2

2.0.1 #

  • fixes critical bug in version 2.0.0 that caused stack overflow, timeout parameters were removed from query execution methods

2.0.0 #

  • implemented Connection pool with option to automatically reconnect in case of connection drop
final manager = Manager();
  manager.addConnection({
    'driver': 'pgsql',
    'host': 'localhost',
    'port': '5432',
    'database': 'database',
    'username': 'username',
    'password': 'password',
    'charset': 'latin1',  
    'schema': ['public'],
    'pool': true, //new
    'poolsize': 2, //new
    'allowreconnect': true, //new   
  });

1.2.1 #

  • fix parameter type of column orWhere

1.2.0 #

  • fix bugs and update dargres to 2.2.4

1.1.1 #

  • fix bugs on detects_lost_connections

1.1.0 #

  • implemented tryAgainIfCausedByLostConnection

1.0.0 #

  • Initial version
16
likes
150
points
979
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Laravel-style (Eloquent 5.2) query builder, schema builder and migrations for Dart, with PostgreSQL and MySQL drivers.

Repository (GitHub)
View/report issues

Topics

#database #orm #query-builder #postgresql #mysql

License

MIT (license)

Dependencies

collection, dargres, dpgsql, intl, meta, mysql_dart, path, postgres, postgres_fork

More

Packages that depend on eloquent