{"id":37064607,"url":"https://github.com/kajitiluna/twinsqla","last_synced_at":"2026-01-14T07:33:06.219Z","repository":{"id":52808982,"uuid":"325442394","full_name":"kajitiluna/twinsqla","owner":"kajitiluna","description":"TWinSQLA is a light framework for mapping SQL statements to python functions and methods.","archived":false,"fork":false,"pushed_at":"2021-04-18T17:01:48.000Z","size":181,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-25T21:26:31.957Z","etag":null,"topics":["python","sql","two-way-sql"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kajitiluna.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-30T03:13:09.000Z","updated_at":"2021-04-18T16:59:52.000Z","dependencies_parsed_at":"2022-08-22T21:10:29.431Z","dependency_job_id":null,"html_url":"https://github.com/kajitiluna/twinsqla","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/kajitiluna/twinsqla","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kajitiluna%2Ftwinsqla","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kajitiluna%2Ftwinsqla/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kajitiluna%2Ftwinsqla/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kajitiluna%2Ftwinsqla/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kajitiluna","download_url":"https://codeload.github.com/kajitiluna/twinsqla/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kajitiluna%2Ftwinsqla/sbom","scorecard":{"id":547965,"data":{"date":"2025-08-11","repo":{"name":"github.com/kajitiluna/twinsqla","commit":"8478a1fa9ee5c0475b7063f5cbbc4524b1c675d8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.6,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/14 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: containerImage not pinned by hash: Dockerfile:3","Warn: containerImage not pinned by hash: Dockerfile:12","Warn: pipCommand not pinned by hash: Dockerfile:6","Warn: pipCommand not pinned by hash: Dockerfile:16-18","Info:   0 out of   2 containerImage dependencies pinned","Info:   0 out of   2 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"14 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2022-42986 / GHSA-43fp-rhv2-5gv8","Warn: Project is vulnerable to: PYSEC-2023-135 / GHSA-xqr8-7jwr-rhp7","Warn: Project is vulnerable to: PYSEC-2024-60 / GHSA-jjg7-2v4v-x38h","Warn: Project is vulnerable to: PYSEC-2021-112 / GHSA-hwfp-hg2m-9vr2","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-9wx4-h78v-vm56","Warn: Project is vulnerable to: PYSEC-2023-74 / GHSA-j8r2-6x86-q33q","Warn: Project is vulnerable to: GHSA-34jh-p97f-mpxf","Warn: Project is vulnerable to: PYSEC-2021-59 / GHSA-5phf-pp7p-vc2r","Warn: Project is vulnerable to: PYSEC-2023-212 / GHSA-g4mx-q9vg-27p4","Warn: Project is vulnerable to: GHSA-pq67-6m6q-mj2v","Warn: Project is vulnerable to: PYSEC-2021-108 / GHSA-q2q7-5pp4-w6pg","Warn: Project is vulnerable to: PYSEC-2023-192 / GHSA-v845-jxx5-vc9f","Warn: Project is vulnerable to: GHSA-jfmj-5v4g-7637"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 19 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T10:01:13.657Z","repository_id":52808982,"created_at":"2025-08-20T10:01:13.657Z","updated_at":"2025-08-20T10:01:13.657Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","sql","two-way-sql"],"created_at":"2026-01-14T07:33:05.456Z","updated_at":"2026-01-14T07:33:06.201Z","avatar_url":"https://github.com/kajitiluna.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TWinSQLA\n\nTWinSQLA is a light framework for mapping SQL statements to python functions and methods.\n\n## Features\n- Available in Python 3.6+\n    - We recommends Python 3.7+ since available to use `@dataclasses.dataclass` decorator in entity classes.\n- This framework concept is avoid ORM features!\n    Coding with almost-raw SQL query (with prepared parameters) simply.\n    - If you can use SQL query with coding simply, it make you to skipping the times of converting python coding\n        with ORM features and checking result.\n    - TWinSQLA support you to checking only SQL query without coding with ORM features.\n- Support \"two-way SQL\" template.\n    - \"Two-way SQL\" templates can be executed SQL statements with dynamic parameter written by python expression.\n    - In \"two-way SQL\", dynamic parameters and conditional expressions are surrounded by '/\\*' and '\\*/'.\n        So, \"two-way SQL\" templates are available to execute in SQL tools as they are.\n    - TWinSQLa is inspired by [Doma](https://github.com/domaframework/doma),\n        which is Java framework for accessing databases.\n- Since [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) core is used for accessing databases,\n    SQLAlchemy core features can be utilized. (such as connection pool)\n\n\n## How to install\nYou can install from PyPI by the follow command.\n```bash\npip install twinsqla\n```\n\n## Usage\n\n### First step (In case that TWinSQLA object is available in global scope)\n\n```python\nfrom typing import Optional\nfrom collections import OrderedDict\nimport sqlalchemy\nfrom twinsqla import TWinSQLA\n\nengine: sqlalchemy.engine.base.Engine = sqlalchemy.create_engine(...)\nsqla: TWinSQLA = TWinSQLA(engine)\n\nclass StaffDao():\n    @sqla.select(\"SELECT * FROM staff WHERE staff_id = /* :staff_id */1\")\n    def find_by_id(self, staff_id: int) -\u003e Tuple[OrderDict, ...]:\n        pass\n```\n\nAt first, create instance of sqlalchemy engine, and create TWinSQLA instance with sqlalchemy engine.\n```python\nengine: sqlalchemy.engine.base.Engine = sqlalchemy.create_engine(...)\nsqla: TWinSQLA = TWinSQLA(engine)\n```\n\nTo execute select query, use `sqla.select` decorator. In this case, `sqla` is TWinSQLA instance.\n```python\n@sqla.select(\"SELECT * FROM staff WHERE staff_id = /* :staff_id */1\")\ndef find_by_id(self, staff_id: int) -\u003e Tuple[OrderDict, ...]:\n    pass\n```\n\nThe above example, select query in decorator's argument is written as \"two-way SQL.\"\nWhen called `dao.find_by_id(staff_id=10)`, then the like following code will be executed.\n```python\n\u003e query = sqlalchemy.sql.text(\"SELECT * FROM staff WHERE staff_id = :staff_id\")\n\u003e engine.execute(query, {staff_id:10})\n```\nThe execution results will be converted to sequence of OrderedDict, and returned from the above method.\n\nThe `sqla.select` decorator can return object for your custom class, or return the results iterable.  For more details, see the other section.\n\n### In production usage\n\nFor about production usage, you may separate source codes as dao classes, entity classes, and handling transaction classes.\n\n```python\nfrom dataclasses import dataclass, field\nimport twinsqla\nfrom twinsqla import TWinSQLA, table, autopk\n\n# Entity class\n@dataclass(frozen=True)\n@table(\"staff\", pk=autopk(\"staff_id\"))\nclass Staff:\n    staff_id: int = field(default=None)\n    staff_name: str = field(default=None)\n    age: int = field(default=None)\n\n\n# Dao class\nclass StaffDao:\n    def __init__(self, sqla: TWinSQLA):\n        self.sqla: TWinSQLA = sqla\n\n    @twinsqla.select(\n        \"SELECT * FROM staff WHERE staff_id \u003e= /* :more_than_id */2\",\n        result_type=List[Staff]\n    )\n    def fetch(self, more_than_id: int) -\u003e List[Staff]:\n        pass\n\n    @twinsqla.insert()\n    def insert(self, staff: Staff):\n        pass\n\n\n# Service class (Handling database transaction)\nclass StaffService:\n    def __init__(self, sqla: TWinSQLA):\n        self.sqla: TWinSQLA = sqla\n        self.staff_dao: StaffDao = StaffDao(sqla)\n\n    def find_staff(self, more_than_id: int) -\u003e List[Staff]:\n        return self.staff_dao.fetch(more_than_id)\n\n    def register(self, staff_name: str, age: int):\n        new_staff: Staff = Staff(staff_name=staff_name, age=age)\n\n        # DB transaction scope\n        with self.sqla.transaction():\n            self.staff_dao.insert(new_staff)\n```\n\n#### Dao class\n\n##### Initializing\n\nIn this cases, the TWinSQLA object is not existed in global scope but only in dao instance scope. So, you cannot use TWinSQLA instance decorators (for example : `@sqla.select()`) at the dao methods.\nInstead of using instance decorators, you can use TWinSQLA function decorators. (for example : `@twinsqla.select()`)\n\nWhen executing, the TWinSQLA function decorators search TWinSQLA object. By this search, TWinSQLA instance can be found specified by one of the follow ways.\n\n- By configured with instance parameter with named 'sqla'. (above the sample code)\n    ```python\n    def __init__(self, sqla: TWinSQLA):\n        self.sqla: TWinSQLA = sqla\n    ```\n\n- Or, the other way, by specified with method arguments with named 'sqla'.\n    ```python\n    @twinsqla.select(...)\n    def fetch(self, sqla: TWinSQLA, more_than_id: int) -\u003e List[Staff]:\n        pass\n    ```\n\n##### Select\n\nTo executing select query, you need to use `twinsqla.select()` function decorator instead of `sqla.select()` instance decorator.\n```python\n@twinsqla.select(\n    \"SELECT * FROM staff WHERE staff_id \u003e= /* :more_than_id */2\", ...\n)\ndef fetch(self, more_than_id: int) -\u003e ... :\n    pass\n```\n\nYou can customise the results of select query by the decorator's argument `result_type`.\nThe argument `result_type` needs to be specified a class or sequence of a class.\nIn the case that results of select query is more than one object, you need specify the `result_type` as sequence of a class. (for example, `List[...]`)\n```python\n@twinsqla.select(\n    ... , result_type=List[Staff]\n)\ndef fetch(...) -\u003e List[Staff]:\n    pass\n```\nIn the above code, each one result of select query is convert to Staff instance, and `fetch()` method returns list of Staff.\n\n##### Insert\n\nOther examples, to insert a record, you can use `twinsqla.insert()` function decorator.\n```python\n@twinsqla.insert()\ndef insert(self, staff: Staff):\n    pass\n```\nThe `insert()` decorator automatically build insert query with `Staff` instance which class decorated by `@table()` with table_name.\n```python\n\u003e query = sqlalchemy.sql.text(\"INSERT INTO staff(staff_name, age) VALUES (:staff_name, :age)\")\n\u003e engine.execute(query, {staff_name: staff.staff_name, age: staff.age})\n```\n\nBy other way, you can build insert query by your hand as following.\n```python\n@twinsqla.insert(\"INSERT INTO staff(staff_name, age) VALUES (:staff_name, :age)\")\ndef insert(self, staff_name: str, age: int):\n    pass\n```\n\n#### Entity class\n\n##### Result of select\n\nEntity class of select query needs to have the constructor with arguments of listed column names\n```python\nclass Staff:\n    def __init__(self, staff_id: int, staff_name: str, age: int):\n        self.staff_id: int = staff_id\n        self.staff_name: str = staff_name\n        self.age: int = age\n```\n\nThe above code can be replaced to the following with decorated by `@dataclass()`.\n```python\nfrom dataclasses import dataclass\n\n@dataclass(frozen=True)\nclass Staff:\n    staff_id: int\n    staff_name: str\n    age: int\n```\n\n##### Insert\n\nEntity class of insert query with automatically building needs to be decorated by `@table()` with argument of the table name and have attributes for inserting.\n\n```python\n@dataclass(frozen=True)\n@table(\"staff\", pk=autopk(\"staff_id\"))\nclass Staff:\n    staff_id: int = field(default=None)\n    staff_name: str = field(default=None)\n    age: int = field(default=None)\n```\n\nIn the above code, use the `Staff` instance can insert into 'staff' table with columns 'staff_name' and 'age'. The column 'staff_id' is removed in insert query by specified `autopk('staff_id')` in `@table()`'s `pk` argument.\n`autopk('column_name')` means that the value of primary column  `column_name` is auto-increment by database.\n\nThis entity class can be also available in return type of select query and argument of update / delete queries.\n\n##### Auto generating entity codes\n\nTWinSQLA supports that automatically generating entity codes for existing database.\n\n- Example\n    ```sh\n    $ twinsqlacodegen --to_file {path/to/output} {database url}\n    ```\n    For more details, run `twinsqlacodegen -h` in your terminal.\n\n\n### Transaction\nIn using TWinSQLA, `TWinSQLA.transaction()` can handle database transaction by context manager via sqlalchemy api.\n```python\nwith sqla.transaction():\n    # execute query\n```\nWhen any exceptions are not occured in context block, then database transaction are commited. Otherwise, if any exceptions are occured, database transaction will be rollbacked and sqlalchemy exception are raised over context bock.\n\n### Exceptions\nIn using TWinSQLA, two type base exceptions may be occured.\n- `twinsqla.exceptions.TWinSQLAException`\n- `sqlalchemy.exc.SQLAlchemyError`\n\n`TWinSQLAException` is occured when your queries or implementation are invalid.\nThe other hand, `SQLAlchemyError` is raised by sqlalchemy.\n\nIn implementation, you need to consider about handling [sqlalchemy.exc.DBAPIError](https://docs.sqlalchemy.org/en/13/core/exceptions.html#sqlalchemy.exc.DBAPIError), which raised in database operation failed.\n\n## API Reference\n\n### `twinsqla.TWinSQLA`\n```python\n    def __init__(self, engine: sqlalchemy.engine.base.Engine, *,\n                 available_dynamic_query: bool = True,\n                 sql_file_root: Optional[Union[Path, str]] = None,\n                 cache_size: Optional[int] = 128):\n        ...\n    \"\"\"\n    Args:\n        engine (sqlalchemy.engine.base.Engine): SQLAlchemy engine instance.\n        available_dynamic_query (bool, optional):\n            If True, then two-ways SQL is available.\n            If False, sql statements are not converted in executing\n            but executed as it is specified. Defaults to True.\n        sql_file_root (Optional[Union[Path, str]], optional):\n            Specify the root directory of sql files. Defaults to None.\n        cache_size (Optional[int], optional):\n            Cache size of loaded query function. Defaults to 128.\n    \"\"\"\n```\n\n### `TWinSQLA.transaction()`\n\n### `twinsqla.select()`, `TWinSQLA.select()`\n```python\ndef select(query: Optional[str] = None, *, sql_path: Optional[str] = None,\n           result_type: Type[Any] = Tuple[OrderedDict, ...],\n           iteratable: bool = False):\n    \"\"\"\n    Function decorator of select operation.\n    Only one argument `query` or `sql_path` must be specified.\n\n    In called decorated method, the processing implemented by the method\n    is not executed, but arguments of method are used for bind parameters.\n\n    Args:\n        query (Optional[str], optional):\n            select query (available TwoWay SQL). Defaults to None.\n        sql_path (Optional[str], optional):\n            file path with sql (available TwoWay SQL). Defaults to None.\n        result_type (Type[Any], optional):\n            return type. Defaults to Tuple[OrderedDict, ...].\n        iteratable (bool, optional):\n            When you want to fetching iterataly result, then True specified\n            and returned ResultIterator object. Defaults to False.\n\n    Returns:\n        Callable: Function decorator\n    \"\"\"\n```\n\n### `twinsqla.insert()`, `TWinSQLA.insert()`\n```python\ndef insert(query: Optional[str] = None, *, sql_path: Optional[str] = None,\n           table_name: Optional[str] = None, result_type: Type[Any] = None,\n           iteratable: bool = False):\n    \"\"\"\n    Function decorator of insert operation.\n    In constructing insert query by yourself, you need to specify either\n    one of the arguments `query` or `sql_path`.\n\n    In neither `query` nor `sql_path` are specified, this decorator creates\n    insert query with arguments of decorated method.\n    In this case, you need to specify inserted table name by decorator\n    argument 'table_name' or decorating '@twinsqla.table' to entity class.\n\n    Args:\n        query (Optional[str], optional):\n            insert query (available TwoWay SQL). Defaults to None.\n        sql_path (Optional[str], optional):\n            file path with sql (available TwoWay SQL). Defaults to None.\n        table_name (Optional[str], optional):\n            table name for inserting. Defaults to None.\n        result_type (Type[Any], optional):\n            When constructing \"INSERT RETURNING\" query, it is useful to\n            specify return type. Defaults to None.\n        iteratable (bool, optional):\n            In almost cases, this argument need not to specified.\n            The only useful case is in using \"INSERT RETURNING\" query.\n            Defaults to False.\n\n    Returns:\n        Callable: Function decorator for insert query\n    \"\"\"\n```\n\n### `twinsqla.update()`, `TWinSQLA.update()`\n```python\ndef update(query: Optional[str] = None, *, sql_path: Optional[str] = None,\n           table_name: Optional[str] = None,\n           condition_columns: Optional[Union[str, Tuple[str, ...]]] = None,\n           result_type: Type[Any] = None, iteratable: bool = False):\n    \"\"\"\n    Function decorator of update operation.\n    In constructing update query by yourself, you need to specify either\n    one of the arguments `query` or `sql_path`.\n\n    In neither `query` nor `sql_path` are specified, this decorator creates\n    update query with arguments of decorated method.\n    In this case, you need follows.\n        1. To specify updated table name\n            by decorating '@twinsqla.table' to entity class.\n            or by decorator argument 'table_name'\n        2. To specifry the column names for using WHERE conditions\n            by decorating '@twinsqla.table' with `pk` parameter\n            or by decorator argument 'condition_columns'.\n\n    Args:\n        query (Optional[str], optional):\n            update query (available TwoWay SQL). Defaults to None.\n        sql_path (Optional[str], optional):\n            file path with sql (available TwoWay SQL). Defaults to None.\n        table_name (Optional[str], optional):\n            table name for updating. Defaults to None.\n        condition_columns (Optional[Union[str, Tuple[str, ...]]], optional):\n            column names in WHERE condition. In almost cases, you are\n            recommended to specify primary key names of the table.\n            Defaults to None.\n        result_type (Type[Any], optional):\n            When constructing \"UPDATE RETURNING\" query, it is useful to\n            specify return type. Defaults to None.\n        iteratable (bool, optional):\n            In almost cases, this argument need not to specified.\n            The only useful case is in using \"UPDATE RETURNING\" query.\n            Defaults to False.\n\n    Returns:\n        Callable: Function decorator for update query\n    \"\"\"\n```\n\n### `twinsqla.delete()`, `TWinSQLA.delete()`\n```python\ndef delete(query: Optional[str] = None, *, sql_path: Optional[str] = None,\n           table_name: Optional[str] = None,\n           condition_columns: Optional[Union[str, Tuple[str, ...]]] = None,\n           result_type: Type[Any] = None, iteratable: bool = False):\n    \"\"\"\n    Function decorator of delete operation.\n    In constructing delete query by yourself, you need to specify either\n    one of the arguments `query` or `sql_path`.\n\n    In neither `query` nor `sql_path` are specified, this decorator creates\n    delete query with arguments of decorated method.\n    In this case, you need follows.\n        1. To specify updated table name\n            by decorating '@twinsqla.table' to entity class.\n            or by decorator argument 'table_name'\n        2. To specifry the column names for using WHERE conditions\n            by decorating '@twinsqla.table' with `pk` parameter\n            or by decorator argument 'condition_columns'.\n\n    Args:\n        query (Optional[str], optional):\n            delete query (available TwoWay SQL). Defaults to None.\n        sql_path (Optional[str], optional):\n            file path with sql (available TwoWay SQL). Defaults to None.\n        table_name (Optional[str], optional):\n            table name for deleting. Defaults to None.\n        condition_columns (Optional[Union[str, Tuple[str, ...]]], optional):\n            column names in WHERE condition. In almost cases, you are\n            recommended to specify primary key names of the table.\n            Defaults to None.\n        result_type (Type[Any], optional):\n            When constructing \"DELETE RETURNING\" query, it is useful to\n            specify return type. Defaults to None.\n        iteratable (bool, optional):\n            In almost cases, this argument need not to specified.\n            The only useful case is in using \"DELETE RETURNING\" query.\n            Defaults to False.\n\n    Returns:\n        Callable: Function decorator for delete query\n    \"\"\"\n```\n\n### `twinsqla.execute()`, `TWinSQLA.execute()`\n```python\ndef execute(query: Optional[str] = None, *, sql_path: Optional[str] = None,\n            result_type: Type[Any] = Tuple[OrderedDict, ...],\n            iteratable: bool = False):\n    \"\"\"\n    Function decorator of any operation.\n    Only one argument `query` or `sql_path` must be specified.\n\n    In called decorated method, the processing implemented by the method\n    is not executed, but arguments of method are used for bind parameters.\n\n    Args:\n        query (Optional[str], optional):\n            any query (available TwoWay SQL). Defaults to None.\n        sql_path (Optional[str], optional):\n            file path with sql (available TwoWay SQL). Defaults to None.\n        result_type (Type[Any], optional):\n            return type. Defaults to Tuple[OrderedDict, ...].\n        iteratable (bool, optional):\n            When you want to fetching iterataly result, then True specified\n            and returned ResultIterator object. Defaults to False.\n\n    Returns:\n        Callable: Function decorator\n    \"\"\"\n```\n\n## SQL Template\n### Bind variable\n\nTWinSQLA's two-way SQL can handle the bind parameter named *some_parameter* as follow.\n```sql\n/* :some_parameter */_dummy_value_\n```\nWhere, *_dummy_value_* is ignored in TWinSQLA dynamic query.\n\nImplementation.\n```python\n@twinsqla.select(\n    \"SELECT * FROM table_name WHERE key = /* :some_value */300\"\n)\ndef fetch_by_key(self, some_value: int) -\u003e dict:\n    pass\n```\n\nCalling methods.\n```python\ndao.fetch_by_key(10)\n```\n\nIn this case, the follow statement and codes are executed.\n```python\nquery = sqlalchemy.sql.text(\"SELECT * FROM table_name WHERE key = :some_value\")\nsqlalchemy_engin.execute(query, {\"some_value\": 10})\n```\n\n\n### ~~Bind variable with iterator~~ (Not yet implemented)\n\n(Not yet implemented handling iterable binding parameter)\n```sql\nSELECT * FROM table_name\nWHERE keys IN /* :some_values */(300, 305, 317)\n```\n\n\n### Python expression variable\n\nTWinSQLA's two-way SQL can embed a python expressions in sql statements as follow.\n```sql\n/* python_expression */_dummy_value_\n```\nWhere, *_dummy_value_* is ignored in TWinSQLA query.\n\nImplementation.\n```python\n@twinsqla.select(\n    \"SELECT * FROM table_name WHERE key = /* some_value * 100 */300\"\n)\ndef fetch_by_key(self, some_value: int) -\u003e dict:\n    pass\n```\nIn this case, `some_value * 100` is the python expression, and `some_value` must be specified in this method's arguments.\n\nCall methods.\n```python\ndao.fetch_by_key(10)\n```\n\nThen the follow statement and codes are executed.\n```python\nquery = sqlalchemy.sql.text(\"SELECT * FROM table_name WHERE key = :dynamic_param\")\nsqlalchemy_engin.execute(query, {\"dynamic_param\": 10 * 100})\n```\nThis bind parameter `:dynamic_param` is automatically generated by TWinSQLA to assign the python expression `some_value * 100` to this bind parameter.\n\n\n### IF block (Basic usage)\n\nDefinition of dynamic if-block\n```sql\n/*%if _python_expression_ */ sql_expression\n[ /*%elif _python_expression_ */ dummy_op sql_expression [...] ]\n[ /*%else*/ dummy_op sql_expression ]\n/*%end*/\n\ndummy_op := \"AND\" | \"OR\"\n```\n\nImplementation\n```python\n@twinsqla.select(r\"\"\"\n    SELECT * FROM table_name\n    WHERE\n        /*%if some_value == 'first' */\n        some_column1 \u003e 0\n        /*%elif some_value == 'second' */\n        OR some_column2 \u003e 0\n        /*%else*/\n        OR some_column1 = 0 AND some_column2 = 0\n        /*%end*/\n\"\"\")\ndef find(self, some_value: str) -\u003e List[dict]:\n    pass\n```\n\nCall sample1.\n```python\ndao.find(\"first\")\n```\nThen query1 is:\n```sql\n    SELECT * FROM table_name\n    WHERE\n        some_column1 \u003e 0\n```\nBy the first if-condition is satisfied, then the others expressions are ignored.\n\nCall sample2.\n```python\ndao.find(\"second\")\n```\nThen query2 is:\n```sql\n    SELECT * FROM table_name\n    WHERE\n        some_column2 \u003e 0\n```\nBy the first if-condition is not satisfied and second is, then the excepts for second expression are ignored. And, noticed that `OR` operation ahead of expression `some_column2 \u003e 0` is ignored.\n\nCall sample3.\n```python\ndao.find(\"other\")\n```\nThen query3 is:\n```sql\n    SELECT * FROM table_name\n    WHERE\n        some_column1 = 0 AND some_column2 = 0\n```\n\n\n### IF block (Advanced usage)\n\n- Nested IF block\n\n    IF block can be nested.\n\n    Example.\n    ```python\n    @twinsqla.select(r\"\"\"\n        SELECT * FROM table_name\n        WHERE\n            /*%if some_value1 == 'first' */\n            some_column1 \u003e 0\n            /*%elif some_value1 == 'second' */\n            OR some_column2 \u003e 0 AND\n                /*%if some_value2 \u003e 0 */\n                some_column3 = some_column4\n                /*%else*/\n                OR some_column3 IS NULL\n                /*%end*/\n            /*%else*/\n            OR some_column1 = 0 AND some_column2 = 0\n            /*%end*/\n    \"\"\")\n    def find(self, some_value1: str, some_value2: int) -\u003e List[dict]:\n        pass\n    ```\n\n- About python expression nested in if-blocks evaluation\n\n    Python expression variables nested in if-blocks are evaluated only when if-condition is satisfied.\n    Consider the follow example with if-block and python expression variable.\n\n    ```python\n    @twinsqla.select(r\"\"\"\n        SELECT * FROM table_name\n        WHERE\n            /*%if some_value1 != 0 */\n            some_column1 \u003e /* some_value2 / some_value1 */10\n            /*%else*/\n            OR some_column1 \u003e 0\n            /*%end*/\n    \"\"\")\n    def find(self, some_value1: int, some_value2: int) -\u003e List[dict]:\n        pass\n    ```\n\n    In the first case, the follow calling has no problem.\n    ```python\n    dao.find(10, 50)\n    ```\n    The above calling is the almost same the following execution.\n    ```python\n    query = sqlalchemy.sql.text(\"\"\"\n        SELECT * FROM table_name\n        WHERE\n            some_column1 \u003e :dynamic_param\n    \"\"\")\n    sqlalchemy_engin.execute(query, {\"dynamic_param\": (50 / 10)})\n    ```\n\n    In the next case, the follow calling.\n    ```python\n    dao.find(0, 7)\n    ```\n    In this case.\n    ```python\n    query = sqlalchemy.sql.text(\"\"\"\n        SELECT * FROM table_name\n        WHERE\n            some_column1 \u003e 0\n    \"\"\")\n    sqlalchemy_engin.execute(query, {\"dynamic_param\": None})\n    ```\n\n    Because for the first if-condition `some_value1 != 0` is not satisfied, the first python expression variable is not evaluated. (In detail, evaluated as `None` without evaluating dividing by zero `5 / 0`.)\n\n\n### ~~FOR block~~ (Not yet implemented)\n\nsample\n```sql\nSELECT * FROM table_name\nWHERE\n    /*%for item in iterator */\n    some_column = /* item */'dummy'\n    /*%or*/\n    /*%end*/\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkajitiluna%2Ftwinsqla","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkajitiluna%2Ftwinsqla","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkajitiluna%2Ftwinsqla/lists"}