summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/dialects/sqlite/base.py
Commit message (Collapse)AuthorAgeFilesLines
* Update Black's target-version to py37Hugo van Kemenade2022-01-051-2/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | <!-- Provide a general summary of your proposed changes in the Title field above --> ### Description <!-- Describe your changes in detail --> Black's `target-version` was still set to `['py27', 'py36']`. Set it to `[py37]` instead. Also update Black and other pre-commit hooks and re-format with Black. ### Checklist <!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once) --> This pull request is: - [ ] A documentation / typographical error fix - Good to go, no issue or tests are needed - [ ] A short code fix - please include the issue number, and create an issue if none exists, which must include a complete example of the issue. one line code fixes without an issue and demonstration will not be accepted. - Please include: `Fixes: #<issue number>` in the commit message - please include tests. one line code fixes without tests will not be accepted. - [ ] A new feature implementation - please include the issue number, and create an issue if none exists, which must include a complete example of how the feature would look. - Please include: `Fixes: #<issue number>` in the commit message - please include tests. **Have a nice day!** Closes: #7536 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/7536 Pull-request-sha: b3aedf5570d7e0ba6c354e5989835260d0591b08 Change-Id: I8be85636fd2c9449b07a8626050c8bd35bd119d5
* Merge "Replace raise_ with raise from" into mainFederico Caselli2021-12-271-6/+3
|\
| * Replace raise_ with raise fromFederico Caselli2021-12-271-6/+3
| | | | | | | | | | Change-Id: I7aaeb5bc130271624335b79cf586581d6c6c34c7 References: #4600
* | Merge "cursor tweaks" into mainmike bayer2021-12-271-0/+2
|\ \ | |/ |/|
| * cursor tweaksMike Bayer2021-12-271-0/+2
| | | | | | | | | | | | | | tighten up creation of dictionaries and conditional logic within the creation of CursorResultMetaData objects Change-Id: I5538ecc343ab0cabcf58d7c92ae0a552d5ac1d8a
* | consider truediv as truediv; support floordiv operatorMike Bayer2021-12-261-0/+7
|/ | | | | | | | | | | | | | | | | | | | | Implemented full support for "truediv" and "floordiv" using the "/" and "//" operators. A "truediv" operation between two expressions using :class:`_types.Integer` now considers the result to be :class:`_types.Numeric`, and the dialect-level compilation will cast the right operand to a numeric type on a dialect-specific basis to ensure truediv is achieved. For floordiv, conversion is also added for those databases that don't already do floordiv by default (MySQL, Oracle) and the ``FLOOR()`` function is rendered in this case, as well as for cases where the right operand is not an integer (needed for PostgreSQL, others). The change resolves issues both with inconsistent behavior of the division operator on different backends and also fixes an issue where integer division on Oracle would fail to be able to fetch a result due to inappropriate outputtypehandlers. Fixes: #4926 Change-Id: Id54cc018c1fb7a49dd3ce1216d68d40f43fe2659
* Replace c extension with cython versions.workflow_test_cythonFederico Caselli2021-12-171-1/+1
| | | | | | | | | | | | | | | Re-implement c version immutabledict / processors / resultproxy / utils with cython. Performance is in general in par or better than the c version Added a collection module that has cython version of OrderedSet and IdentitySet Added a new test/perf file to compare the implementations. Run ``python test/perf/compiled_extensions.py all`` to execute the comparison test. See results here: https://docs.google.com/document/d/1nOcDGojHRtXEkuy4vNXcW_XOJd9gqKhSeALGG3kYr6A/edit?usp=sharing Fixes: #7256 Change-Id: I2930ef1894b5048210384728118e586e813f6a76 Signed-off-by: Federico Caselli <cfederico87@gmail.com>
* provide connectionfairy on initializeMike Bayer2021-11-291-6/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is so that dialect methods that are called within init can assume the same argument structure as when they are called in other places; we can nail down the type of object as well. This change seems to mostly impact the isolation level routines in the dialects, as these are called during initialize() as well as on established connections. these methods can now assume a non-proxied DBAPI connection object in all cases, as it is commonly required that attributes like ".autocommit" are set on the object which don't work well in a proxied situation. Other changes: * adds an interface for the "connectionfairy" concept called PoolProxiedConnection. * Removes ``Connectable`` superclass of Connection. ``Connectable`` was originally meant to provide for the "method which accepts connection or engine" theme. As this pattern is greatly reduced in 2.0 and Engine no longer extends from it, the ``Connectable`` superclass doesnt serve any real purpose. Leading from that, to set this in I also applied pep 484 annotations to the Dialect base, and then in the interests of seeing some of the typing information show up in my IDE did a little bit for Engine, Connection and others. I hope that it's feasible that we can add annotations to specific classes and attributes ahead of when we actually try to mass-populate the whole library. This was the original spirit of pep-484 that we can apply annotations gradually. I do of course want to try to do a mass-populate although i think even in that case we will end up doing a lot of manual work anyway (in particular for the changes here which are distinct from what the stubs have). Fixes: #7122 Change-Id: I5dd7fbff8a7ae520a81c165091af12a6a68826db
* Clean up most py3k compatFederico Caselli2021-11-241-3/+3
| | | | Change-Id: I8172fdcc3103ff92aa049827728484c8779af6b7
* Remove object in class definitionFederico Caselli2021-11-221-1/+1
| | | | | References: #4600 Change-Id: I2a62ddfe00bc562720f0eae700a497495d7a987a
* fully support isolation_level parameter in base dialectMike Bayer2021-11-181-28/+5
| | | | | | | | | | | | | | | | | | | | Generalized the :paramref:`_sa.create_engine.isolation_level` parameter to the base dialect so that it is no longer dependent on individual dialects to be present. This parameter sets up the "isolation level" setting to occur for all new database connections as soon as they are created by the connection pool, where the value then stays set without being reset on every checkin. The :paramref:`_sa.create_engine.isolation_level` parameter is essentially equivalent in functionality to using the :paramref:`_engine.Engine.execution_options.isolation_level` parameter via :meth:`_engine.Engine.execution_options` for an engine-wide setting. The difference is in that the former setting assigns the isolation level just once when a connection is created, the latter sets and resets the given level on each connection checkout. Fixes: #6342 Change-Id: Id81d6b1c1a94371d901ada728a610696e09e9741
* removals: all unicode encoding / decodingMike Bayer2021-11-101-2/+0
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Removed here includes: * convert_unicode parameters * encoding create_engine() parameter * description encoding support * "non-unicode fallback" modes under Python 2 * String symbols regarding Python 2 non-unicode fallbacks * any concept of DBAPIs that don't accept unicode statements, unicode bound parameters, or that return bytes for strings anywhere except an explicit Binary / BLOB type * unicode processors in Python / C Risk factors: * Whether all DBAPIs do in fact return Unicode objects for all entries in cursor.description now * There was logic for mysql-connector trying to determine description encoding. A quick test shows Unicode coming back but it's not clear if there are still edge cases where they return bytes. if so, these are bugs in that driver, and at most we would only work around it in the mysql-connector DBAPI itself (but we won't do that either). * It seems like Oracle 8 was not expecting unicode bound parameters. I'm assuming this was all Python 2 stuff and does not apply for modern cx_Oracle under Python 3. * third party dialects relying upon built in unicode encoding/decoding but it's hard to imagine any non-SQLAlchemy database driver not dealing exclusively in Python unicode strings in Python 3 Change-Id: I97d762ef6d4dd836487b714d57d8136d0310f28a References: #7257
* Merge "Fixes: #5289"mike bayer2021-09-151-1/+2
|\
| * Fixes: #5289jonathan vanasco2021-09-081-1/+2
| | | | | | | | | | | | Clarify Foreign Key support on SQLite must be enabled before `MetaData.create_all` Change-Id: Ic41b86f736be21dd6fc890a915a2ffd572df73a4
* | ensure pysqlite dialect enumerates correct isolation levelsMike Bayer2021-08-301-2/+8
|/ | | | | | | | | Fixed bug where the error message for SQLite invalid isolation level on the pysqlite driver would fail to indicate that "AUTOCOMMIT" is one of the valid isolation levels. Change-Id: Icbceab9a28af6a560859761fa92320b5473269a9 References: #6959
* Replace all http:// links to https://Federico Caselli2021-07-041-16/+16
| | | | | | Also replace http://pypi.python.org/pypi with https://pypi.org/project Change-Id: I84b5005c39969a82140706472989f2a30b0c7685
* Use non-subquery form for empty INMike Bayer2021-04-291-0/+5
| | | | | | | | | | | | | | | | | | | | Revised the "EMPTY IN" expression to no longer rely upon using a subquery, as this was causing some compatibility and performance problems. The new approach for selected databases takes advantage of using a NULL-returning IN expression combined with the usual "1 != 1" or "1 = 1" expression appended by AND or OR. The expression is now the default for all backends other than SQLite, which still had some compatibility issues regarding tuple "IN" for older SQLite versions. Third party dialects can still override how the "empty set" expression renders by implementing a new compiler method ``def visit_empty_set_op_expr(self, type_, expand_op)``, which takes precedence over the existing ``def visit_empty_set_expr(self, element_types)`` which remains in place. Fixes: #6258 Fixes: #6397 Change-Id: I2df09eb00d2ad3b57039ae48128fdf94641b5e59
* Improved datetime documentation for sqliteFederico Caselli2021-04-181-1/+1
| | | | | | closes #6313 Change-Id: Ib8e988915afc65c95ae6d4c22b7802a1226a2913
* Merge "Support DEFAULT VALUES and VALUES(DEFAULT) individually"mike bayer2021-04-141-0/+5
|\
| * Support DEFAULT VALUES and VALUES(DEFAULT) individuallyMike Bayer2021-04-141-0/+5
| | | | | | | | | | | | | | | | | | | | | | | | | | Fixed regression where the introduction of the INSERT syntax "INSERT... VALUES (DEFAULT)" was not supported on some backends that do however support "INSERT..DEFAULT VALUES", including SQLite. The two syntaxes are now each individually supported or non-supported for each dialect, for example MySQL supports "VALUES (DEFAULT)" but not "DEFAULT VALUES". Support for Oracle is still not enabled as there are unresolved issues in using RETURNING at the same time. Fixes: #6254 Change-Id: I47959bc826e3d9d2396ccfa290eb084841b02e77
* | Explicitly test for Connection in dialect.has_table()Mike Bayer2021-04-141-0/+2
|/ | | | | | | | | | | | | | The :meth:`_engine.Dialect.has_table` method now raises an informative exception if a non-Connection is passed to it, as this incorrect behavior seems to be common. This method is not intended for external use outside of a dialect. Please use the :meth:`.Inspector.has_table` method or for cross-compatibility with older SQLAlchemy versions, the :meth:`_engine.Engine.has_table` method. Fixes: #5780 Fixes: #6062 Fixes: #6260 Change-Id: I9b2439675167019b68d682edee3dcdcfce836987
* Default caching to opt-out for 3rd party dialectsMike Bayer2021-04-011-0/+1
| | | | | | | | | | | | | | | | | | | Added a new flag to the :class:`_engine.Dialect` class called :attr:`_engine.Dialect.supports_statement_cache`. This flag now needs to be present directly on a dialect class in order for SQLAlchemy's :ref:`query cache <sql_caching>` to take effect for that dialect. The rationale is based on discovered issues such as :ticket:`6173` revealing that dialects which hardcode literal values from the compiled statement, often the numerical parameters used for LIMIT / OFFSET, will not be compatible with caching until these dialects are revised to use the parameters present in the statement only. For third party dialects where this flag is not applied, the SQL logging will show the message "dialect does not support caching", indicating the dialect should seek to apply this flag once they have verified that no per-statement literal values are being rendered within the compilation phase. Fixes: #6184 Change-Id: I6fd5b5d94200458d4cb0e14f2f556dbc25e27e22
* Add supported database version for internal dialectsFederico Caselli2021-03-071-0/+3
| | | | Change-Id: I08d150f1780a0f3a848c0edcd40013b5593d18f0
* Use schema._copy_expression() fully in column collection constraintsGord Thompson2021-01-291-3/+6
| | | | | | | | | | Fixed issue where using :meth:`_schema.Table.to_metadata` (called :meth:`_schema.Table.tometadata` in 1.3) in conjunction with a PostgreSQL :class:`_postgresql.ExcludeConstraint` that made use of ad-hoc column expressions would fail to copy correctly. Fixes: #5850 Change-Id: I062480afb23f6f60962b7b55bc93f5e4e6ff05e4
* Fix many spell glitches in docstrings and commentsLele Gaifax2021-01-241-3/+3
| | | | | | | | | | These were revealed by running `pylint --disable all --enable spelling --spelling-dict en_US` over all sources. Closes: #5868 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5868 Pull-request-sha: bb249195d92e3b806e81ecf1192d5a1b3cd5db48 Change-Id: I96080ec93a9fbd20ce21e9e16265b3c77f22bb14
* happy new yearMike Bayer2021-01-041-1/+1
| | | | Change-Id: Ic5bb19ca8be3cb47c95a0d3315d84cb484bac47c
* Merge "Support IF EXISTS/IF NOT EXISTS for DDL constructs"mike bayer2020-12-141-1/+7
|\
| * Support IF EXISTS/IF NOT EXISTS for DDL constructsRamonWill2020-12-141-1/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Added parameters :paramref:`_ddl.CreateTable.if_not_exists`, :paramref:`_ddl.CreateIndex.if_not_exists`, :paramref:`_ddl.DropTable.if_exists` and :paramref:`_ddl.DropIndex.if_exists` to the :class:`_ddl.CreateTable`, :class:`_ddl.DropTable`, :class:`_ddl.CreateIndex` and :class:`_ddl.DropIndex` constructs which result in "IF NOT EXISTS" / "IF EXISTS" DDL being added to the CREATE/DROP. These phrases are not accepted by all databases and the operation will fail on a database that does not support it as there is no similarly compatible fallback within the scope of a single DDL statement. Pull request courtesy Ramon Williams. Fixes: #2843 Closes: #5663 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5663 Pull-request-sha: 748b8472345d96efb446e2a444fbe020b313669f Change-Id: I6a2b1f697993ed49c31584f0a31887fb0a868ed3
* | fix non-ascii quotes in sqlite on update featureMike Bayer2020-12-071-3/+3
| | | | | | | | | | | | | | | | Fixed a SQLite source file that had non-ascii characters inside of its docstring without a source encoding, introduced within the "INSERT..ON CONFLICT" feature, which would cause failures under Python 2. Change-Id: Ie789d2ce554d24643a453b60bb11da9b69106c19
* | Support Column objects in the SET clause for upsertMike Bayer2020-11-231-13/+19
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Established support for :class:`_schema.Column` objects as well as ORM instrumented attributes as keys in the ``set_`` dictionary passed to the :meth:`_postgresql.Insert.on_conflict_do_update` and :meth:`_sqlite.Insert.on_conflict_do_update` methods, which match to the :class:`_schema.Column` objects in the ``.c`` collection of the target :class:`_schema.Table`. Previously, only string column names were expected; a column expression would be assumed to be an out-of-table expression that would render fully along with a warning. Fixes: #5722 Change-Id: Ice73b501d721c28d978a0277a83cedc6aff756a9
* | Merge "Support SQLite WITHOUT ROWID tables"mike bayer2020-11-091-1/+27
|\ \ | |/ |/|
| * Support SQLite WITHOUT ROWID tablesSean Anderson2020-11-081-1/+27
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This adds support for creating tables WITHOUT ROWID in the SQLite dialect. WITHOUT ROWID tables were introduced in SQLite version 3.8.2 (2013-12-06). They do not use an implicit rowid column as the primary key. This may result in space and performance savings for tables without INTEGER primary keys and tables with composite primary keys. For more information about this feature, see the sqlite documentation [1]. [1] https://www.sqlite.org/withoutrowid.html Fixes: #5685 ### Checklist This pull request is: - [x] A new feature implementation - please include the issue number, and create an issue if none exists, which must include a complete example of how the feature would look. - Please include: `Fixes: #<issue number>` in the commit message - please include tests. Closes: #5686 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5686 Pull-request-sha: 2b44782d1b3d858e31ce1ff8e08e197af37344d8 Change-Id: Ifcf727b0c07c90e267b79828a8e3fd7a8260a074
* | Implement upsert for SQLiteRamonWill2020-11-081-1/+305
|/ | | | | | | | | | | | Implemented INSERT... ON CONFLICT clause for SQLite. Pull request courtesy Ramon Williams. Fixes: #4010 Closes: #5580 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5580 Pull-request-sha: fb422e0749fac442a455cbce539ef662d9512bc0 Change-Id: Ibeea44f4c2cee8dab5dc22b7ec3ae1ab95c12b65
* Apply underscore naming to several more operatorsjonathan vanasco2020-10-301-1/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | The operator changes are: * `isfalse` is now `is_false` * `isnot_distinct_from` is now `is_not_distinct_from` * `istrue` is now `is_true` * `notbetween` is now `not_between` * `notcontains` is now `not_contains` * `notendswith` is now `not_endswith` * `notilike` is now `not_ilike` * `notlike` is now `not_like` * `notmatch` is now `not_match` * `notstartswith` is now `not_startswith` * `nullsfirst` is now `nulls_first` * `nullslast` is now `nulls_last` Because these are core operators, the internal migration strategy for this change is to support legacy terms for an extended period of time -- if not indefinitely -- but update all documentation, tutorials, and internal usage to the new terms. The new terms are used to define the functions, and the legacy terms have been deprecated into aliases of the new terms. Fixes: #5435 Change-Id: Ifbd7cb1cdda5981990243c4fc4b4ff467dc132ac
* Correct reflection for composite primary keysfulpm2020-10-211-0/+1
| | | | | | | | | | | | | | Fixes: #5661 ### Description Fixes reflection of composite primary keys to maintain the correct column order in the MSSQL and SQLite dialects. Closes: #5662 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5662 Pull-request-sha: b568dec7070b4f3ee46a528bdf16fb237baade2a Change-Id: I452b23cbf7f389c4a0a34cffce5c32498efe37d2
* Add support for regular expression on supported backend.Federico Caselli2020-08-271-0/+6
| | | | | | | | | | | | Two operations have been defined: * :meth:`~.ColumnOperators.regexp_match` implementing a regular expression match like function. * :meth:`~.ColumnOperators.regexp_replace` implementing a regular expression string replace function. Fixes: #1390 Change-Id: I44556846e4668ccf329023613bd26861d5c674e6
* Merge "test single and double quote inspection scenarios"2020_tutorialmike bayer2020-07-131-17/+14
|\
| * test single and double quote inspection scenariosMike Bayer2020-07-131-17/+14
| | | | | | | | | | | | | | | | | | | | | | Applied a sweep through all included dialects to ensure names that contain single or double quotes are properly escaped when querying system tables, for all :class:`.Inspector` methods that accept object names as an argument (e.g. table names, view names, etc). SQLite and MSSQL contained two quoting issues that were repaired. Fixes: #5456 Change-Id: I3bc98806f5166f3d82275650079ff561446f2aef
* | more docs for autocommit isolation levelMike Bayer2020-07-121-0/+4
|/ | | | | | | | this concept is not clear that we offer real DBAPI autocommit everywhere. backport 1.3 with edits as well Change-Id: I2e8328b7fb6e1cdc5453ab29c94276f60c7ca149
* Add 'exists' to SQLite's reserved keywordsThodoris Sotiropoulos2020-06-111-0/+1
| | | | | | | | | | | | | Added "exists" to the list of reserved words for SQLite so that this word will be quoted when used as a label or column name. Pull request courtesy Thodoris Sotiropoulos. Fixes: #5395 Closes: #5396 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5396 Pull-request-sha: 5608317a38db7b63115e3b467d182a3e5cc31580 Change-Id: Ia4769de2dec159dcf282eb4b30c11560da51a5c7
* SQLite 3.31 added support for computed column.Federico Caselli2020-05-191-14/+67
| | | | | | | This change enables their support in SQLAlchemy when targeting SQLite. Fixes: #5297 Change-Id: Ia9f21a49e58fc977e3c669b8176036c95d93b9c8
* Documentation updates for ResultProxy -> ResultMike Bayer2020-05-011-2/+2
| | | | | | | | | This is based off of I8091919d45421e3f53029b8660427f844fee0228 and includes all documentation-only changes as a separate merge, once the parent is merged. Change-Id: I711adea23df0f9f0b1fe7c76210bd2de6d31842d
* Set up absolute references for create_engine and relatedMike Bayer2020-04-141-3/+4
| | | | | | | includes more replacements for create_engine(), Connection, disambiguation of Result from future/baked Change-Id: Icb60a79ee7a6c45ea9056c211ffd1be110da3b5e
* Run search and replace of symbolic module namesMike Bayer2020-04-141-25/+27
| | | | | | | | Replaces a wide array of Sphinx-relative doc references with an abbreviated absolute form now supported by zzzeeksphinx. Change-Id: I94bffcc3f37885ffdde6238767224296339698a2
* Broaden is[not]_distinct_from supportGord Thompson2020-04-021-3/+0
| | | | | | | Added support for .is[not]_distinct_from to SQL Server, MySQL, and Oracle. Fixes: #5137 Change-Id: I3b4d3b199821a55687f83c9a5b63a95d07a64cd5
* Merge "Implement autocommit isolation level for pysqlite"mike bayer2020-03-241-2/+11
|\
| * Implement autocommit isolation level for pysqliteGord Thompson2020-03-241-2/+11
| | | | | | | | | | Fixes: #5164 Change-Id: I190b9de552dfed9f2a33babf82e42465ef09c82a
* | Convert schema_translate to a post compileMike Bayer2020-03-241-0/+3
|/ | | | | | | | | | | | | | | | Revised the :paramref:`.Connection.execution_options.schema_translate_map` feature such that the processing of the SQL statement to receive a specific schema name occurs within the execution phase of the statement, rather than at the compile phase. This is to support the statement being efficiently cached. Previously, the current schema being rendered into the statement for a particular run would be considered as part of the cache key itself, meaning that for a run against hundreds of schemas, there would be hundreds of cache keys, rendering the cache much less performant. The new behavior is that the rendering is done in a similar manner as the "post compile" rendering added in 1.4 as part of :ticket:`4645`, :ticket:`4808`. Fixes: #5004 Change-Id: Ia5c89eb27cc8dc2c5b8e76d6c07c46290a7901b6
* Deprecate plain string in execute and introduce `exec_driver_sql`Federico Caselli2020-03-211-17/+17
| | | | | | | | | | | | | | | Execution of literal sql string is deprecated in the :meth:`.Connection.execute` and a warning is raised when used stating that it will be coerced to :func:`.text` in a future release. To execute a raw sql string the new connection method :meth:`.Connection.exec_driver_sql` was added, that will retain the previous behavior, passing the string to the DBAPI driver unchanged. Usage of scalar or tuple positional parameters in :meth:`.Connection.execute` is also deprecated. Fixes: #4848 Fixes: #5178 Change-Id: I2830181054327996d594f7f0d59c157d477c3aa9
* Ensure all nested exception throws have a causeMike Bayer2020-03-021-8/+14
| | | | | | | | | | | | | | | Applied an explicit "cause" to most if not all internally raised exceptions that are raised from within an internal exception catch, to avoid misleading stacktraces that suggest an error within the handling of an exception. While it would be preferable to suppress the internally caught exception in the way that the ``__suppress_context__`` attribute would, there does not as yet seem to be a way to do this without suppressing an enclosing user constructed context, so for now it exposes the internally caught exception as the cause so that full information about the context of the error is maintained. Fixes: #4849 Change-Id: I55a86b29023675d9e5e49bc7edc5a2dc0bcd4751