{"id":13656798,"url":"https://github.com/marcobambini/sqlite-createtable-parser","last_synced_at":"2025-08-20T08:31:45.101Z","repository":{"id":38325863,"uuid":"52945021","full_name":"marcobambini/sqlite-createtable-parser","owner":"marcobambini","description":"A parser for SQLite create table sql statements.","archived":false,"fork":false,"pushed_at":"2024-05-21T13:37:33.000Z","size":151,"stargazers_count":136,"open_issues_count":0,"forks_count":13,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-07T12:22:27.436Z","etag":null,"topics":["c","parse","parser","sqlite","sqlite3"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcobambini.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-03-02T08:03:03.000Z","updated_at":"2025-02-23T17:21:06.000Z","dependencies_parsed_at":"2024-05-11T06:25:00.440Z","dependency_job_id":"a8fd1b94-51c1-4435-a051-cc793c111695","html_url":"https://github.com/marcobambini/sqlite-createtable-parser","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/marcobambini/sqlite-createtable-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcobambini%2Fsqlite-createtable-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcobambini%2Fsqlite-createtable-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcobambini%2Fsqlite-createtable-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcobambini%2Fsqlite-createtable-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcobambini","download_url":"https://codeload.github.com/marcobambini/sqlite-createtable-parser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcobambini%2Fsqlite-createtable-parser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271287619,"owners_count":24733424,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-20T02:00:09.606Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["c","parse","parser","sqlite","sqlite3"],"created_at":"2024-08-02T05:00:32.399Z","updated_at":"2025-08-20T08:31:44.834Z","avatar_url":"https://github.com/marcobambini.png","language":"C","readme":"## SQLite CREATE and ALTER TABLE Parser\nA parser for SQLite [create table](https://www.sqlite.org/lang_createtable.html) and [alter table](https://www.sqlite.org/lang_altertable.html) sql statements.\n\n* Extremely fast parser with no memory copy overhead\n* MIT licensed with no dependencies (just drop the C file into your project)\n* Never recurses or allocates more memory than it needs\n* Very simple API \n\n## Motivation\n[SQLite](https://www.sqlite.org/) is a very powerful software but it lacks an easy way to extract complete information about tables and columns constraints. This drawback in addition to the lack of full ALTER TABLE support makes alterring a table a very hard task. The built-in sqlite pragmas provide incomplete information and a manual parsing is required in order to extract all the metadata from a table.\n```c\nPRAGMA table_info(table-name);  \nPRAGMA foreign_key_list(table-name);\n```\nCREATE TABLE syntax diagrams can be found on the official [sqlite website](https://www.sqlite.org/lang_createtable.html).\n\n## Pre-requisites\n- A C99 compiler.\n- SQL statement must be successfully compiled by SQLite.\n\n## Usage\nIn order to extract the original CREATE TABLE sql statement you need to query the sqlite_master table from within an SQLite database:\n```sql\nSELECT sql FROM sqlite_master WHERE name = 'myTable';\n```\n\nthen just include sql3parse_table.h and sql3parse_table.c in your project and invoke:\n```c\n// sql is the CREATE TABLE sql statement\n// length is the length of sql (if 0 then strlen will be used)\n// error is the returned error code (can be NULL)\n// return value: NULL in case of error or an opaque pointer in case of success\n\nsql3table *sql3parse_table (const char *sql, size_t length, sql3error_code *error);\n```\n**sql3table** is an opaque struct that you can introspect using the sql3table* public functions.\n\n## API\n```c\n// Main Entrypoint\nsql3table *sql3parse_table (const char *sql, size_t length, sql3error_code *error);\n\n// Table information\nsql3string          *sql3table_schema (sql3table *table);\nsql3string          *sql3table_name (sql3table *table);\nsql3string          *sql3table_comment (sql3table *table);\nbool                sql3table_is_temporary (sql3table *table);\nbool                sql3table_is_ifnotexists (sql3table *table);\nbool                sql3table_is_withoutrowid (sql3table *table);\nsize_t              sql3table_num_columns (sql3table *table);\nsql3column          *sql3table_get_column (sql3table *table, size_t index);\nsize_t              sql3table_num_constraints (sql3table *table);\nsql3tableconstraint *sql3table_get_constraint (sql3table *table, size_t index);\nvoid                sql3table_free (sql3table *table);\nsql3statement_type  sql3table_type (sql3table *table);\nsql3string          *sql3table_current_name (sql3table *table);\nsql3string          *sql3table_new_name (sql3table *table);\n\n\t\n// Table constraints\nsql3string          *sql3table_constraint_name (sql3tableconstraint *tconstraint);\nsql3constraint_type sql3table_constraint_type (sql3tableconstraint *tconstraint);\nsize_t              sql3table_constraint_num_idxcolumns (sql3tableconstraint *tconstraint);\nsql3idxcolumn       *sql3table_constraint_get_idxcolumn (sql3tableconstraint *tconstraint, size_t index);\nsql3conflict_clause sql3table_constraint_conflict_clause (sql3tableconstraint *tconstraint);\nsql3string          *sql3table_constraint_check_expr (sql3tableconstraint *tconstraint);\nsize_t              sql3table_constraint_num_fkcolumns (sql3tableconstraint *tconstraint);\nsql3string          *sql3table_constraint_get_fkcolumn (sql3tableconstraint *tconstraint, size_t index);\nsql3foreignkey      *sql3table_constraint_foreignkey_clause (sql3tableconstraint *tconstraint);\n\n// Column constraints\nsql3string          *sql3column_name (sql3column *column);\nsql3string          *sql3column_type (sql3column *column);\nsql3string          *sql3column_length (sql3column *column);\nsql3string          *sql3column_constraint_name (sql3column *column);\nsql3string          *sql3column_comment (sql3column *column);\nbool                sql3column_is_primarykey (sql3column *column);\nbool                sql3column_is_autoincrement (sql3column *column);\nbool                sql3column_is_notnull (sql3column *column);\nbool                sql3column_is_unique (sql3column *column);\nsql3order_clause    sql3column_pk_order (sql3column *column);\nsql3conflict_clause sql3column_pk_conflictclause (sql3column *column);\nsql3conflict_clause sql3column_notnull_conflictclause (sql3column *column);\nsql3conflict_clause sql3column_unique_conflictclause (sql3column *column);\nsql3string          *sql3column_check_expr (sql3column *column);\nsql3string          *sql3column_default_expr (sql3column *column);\nsql3string          *sql3column_collate_name (sql3column *column);\nsql3foreignkey      *sql3column_foreignkey_clause (sql3column *column);\n\t\n// Foreign key\nsql3string          *sql3foreignkey_table (sql3foreignkey *fk);\nsize_t              sql3foreignkey_num_columns (sql3foreignkey *fk);\nsql3string          *sql3foreignkey_get_column (sql3foreignkey *fk, size_t index);\nsql3fk_action       sql3foreignkey_ondelete_action (sql3foreignkey *fk);\nsql3fk_action       sql3foreignkey_onupdate_action (sql3foreignkey *fk);\nsql3string          *sql3foreignkey_match (sql3foreignkey *fk);\nsql3fk_deftype      sql3foreignkey_deferrable (sql3foreignkey *fk);\n\n// Indexed column\nsql3string          *sql3idxcolumn_name (sql3idxcolumn *idxcolumn);\nsql3string          *sql3idxcolumn_collate (sql3idxcolumn *idxcolumn);\nsql3order_clause    sql3idxcolumn_order (sql3idxcolumn *idxcolumn);\n\t\n// String Utils\nconst char          *sql3string_ptr (sql3string *s, size_t *length);\nconst char          *sql3string_cstring (sql3string *s);\n```\n\n## Example\nDump to stdout complete table information:\n```c\n// all the necessary code is in sql3parse_debug.h/.c\nvoid table_dump (sql3table *table) {\n    if (!table) return;\n    \n    // schema name\n    sql3string *ptr = sql3table_schema(table);\n    sql3string_dump(ptr, \"Schema Name\");\n    \n    // table name\n    ptr = sql3table_name(table);\n    sql3string_dump(ptr, \"Table Name\");\n\n    // table comment\n    ptr = sql3table_comment(table);\n    if (ptr) sql3string_dump(ptr, \"Table Comment\");\n    \n    // table flags\n    printf(\"Temporary: %d\\n\", sql3table_is_temporary(table));\n    printf(\"If Not Exists: %d\\n\", sql3table_is_ifnotexists(table));\n    printf(\"Without RowID: %d\\n\", sql3table_is_withoutrowid(table));\n    \n    // loop to print complete columns info\n    size_t num_columns = sql3table_num_columns(table);\n    printf(\"Num Columns: %zu\\n\", num_columns);\n    for (size_t i=0; i\u003cnum_columns; ++i) {\n        sql3column *column = sql3table_get_column(table, i);\n        printf(\"\\n== COLUMN %zu ==\\n\", i);\n        sql3column_dump(column);\n    }\n    \n    // loop to print complete table constraints\n    size_t num_constraint = sql3table_num_constraints(table);\n    printf(\"\\nNum Table Constraint: %zu\\n\", num_constraint);\n    for (size_t i=0; i\u003cnum_constraint; ++i) {\n        sql3tableconstraint *constraint = sql3table_get_constraint(table, i);\n        printf(\"\\n== TABLE CONSTRAINT %zu ==\\n\", i);\n        sql3tableconstraint_dump(constraint);\n    }\n    printf(\"\\n\");\n}\n```\n\n## IMPLEMENT a COMPLETE ALTER TABLE in SQLite\n\nSQLite supports a limited subset of [ALTER TABLE](https://www.sqlite.org/lang_altertable.html). The ALTER TABLE command in SQLite allows these alterations of an existing table: it can be renamed; a column can be renamed; a column can be added to it; or a column can be dropped from it.\n\nMost SQL database engines store the schema already parsed into various system tables. On those database engines, ALTER TABLE merely has to make modifications to the corresponding system tables. SQLite is different in that it stores the schema in the sqlite_schema table as the original text of the CREATE statements that define the schema. Hence ALTER TABLE needs to revise the text of the CREATE statement. Doing so can be tricky for certain \"creative\" schema designs.\n\nThe only schema altering commands directly supported by SQLite are the \"rename table\", \"rename column\", \"add column\", \"drop column\" commands shown above. However, applications can make other arbitrary changes to the format of a table using a simple sequence of operations. The steps to make arbitrary changes to the schema design of some table are as follows (to create the new table starting from the old one you need a way to extract complete information from a SQLite table and that's the main reason why I created this parser):\n\n```c\n// The following algorithm needs to be revised based on new notes added to https://www.sqlite.org/lang_altertable.html\n```\n\nALTER TABLE algorithm looks like:\n```sql\nPRAGMA foreign_keys=off;\n \nBEGIN TRANSACTION;\n \nALTER TABLE old_table RENAME TO temp_table;\n\n/* new_table can be recreated by parsing the old_table and extracting all relevant information using this repo */\nCREATE TABLE new_table(\n   column_definition,\n   ...\n);\n \nINSERT INTO new_table (column_list)\n  SELECT column_list\n  FROM temp_table;\n \nDROP TABLE temp_table;\n \nCOMMIT;\n \nPRAGMA foreign_keys=on;\n```\n\n\n## Speed and memory considerations\nThe parser is blazing fast, mainly because very few memory allocations are performed and no copy operations are used between the sql string and the internal sql3string structs. Memory requirement is linearly proportional to the number of columns in the table.\n```\nYou can estimate memory usage (on a 64bit system) using the following formula:\nN1: number of columns WITHOUT a foreign key constraint\nN2: number of columns WITH a foreign key constraint\nN3: number of indexed columns in table constraint\nK: 0 if no foreign key yable constraint is used or 64\nMemory usage (in bytes): 144 + (N1 * 144) + (N2 * 208) + (N3 * 40) + K\n```\n\n## Other information\nThis code is actually used by [Creo](https://creolabs.com).\n\nIf you are interested in my others GitHub projects then take a look at the [Gravity](https://github.com/marcobambini/gravity) programming language.\n","funding_links":[],"categories":["C (61)"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcobambini%2Fsqlite-createtable-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcobambini%2Fsqlite-createtable-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcobambini%2Fsqlite-createtable-parser/lists"}