Code Monkey home page Code Monkey logo

haskell-opaleye's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

haskell-opaleye's Issues

Rename pg..., PG... to sql..., Sql...

In anticipation of opaleye-sqlite change pg and PG in names to sql and Sql instead. Examples

  • PGTypes module becomes SqlTypes
  • PGText type becomes SqlText
  • pgText function becomes SqlText

Prefer non-PG specific types

Generally, people should be using Column Bool, Column Int etc, and not Column PGBool. In SQLite columns for types such as Bool don't really exist, so must be implemented as Column SLInt. That's not enough type specificity, so should not be the preferred format.

Streaming interface for queries?

I've already asked this for the groundhog library (see lykahb/groundhog#41), and now I'm wondering whether opaleye has support or plans to support a streaming-like interface for querying many tuples from a database in constant-space.

Tests fail

Preprocessing test suite 'test' for opaleye-0.2...
[1 of 1] Compiling Main             ( Test/Test.hs, dist/build/test/test-tmp/Main.dyn_o )
Linking dist/build/test/test ...
Preprocessing test suite 'tutorial' for opaleye-0.2...

Doc/Tutorial/Main.hs:1:8:
    Could not find module ‘TutorialBasic’
    Use -v to see a list of the files searched for.

Doc/Tutorial/Main.hs:2:8:
    Could not find module ‘TutorialManipulation’
    Use -v to see a list of the files searched for.

Aggregate min on Bool column generates invalid query

Running Aggregate.min function on boolean column generates an invalid query that will fail when ran.

select id, min(closed) from
No function matches the given name and argument types. You might need to add explicit type casts.

Some other functions from the same module might have the same problem, like avg for string fields etc.


Idea: It could be solved by restricting the a type of min with a methodless typeclass. All the datatypes that can be applied to the function would be instances of the typeclass.

Implementing 'with recursive'

Have you thought about implementing with recursive? I'd like to perhaps give it a go, and was thinking it would look something like:

recursive :: Query a -> QueryArr a a -> Query a

This would then generate something like:

with recursive temp (cols) (
  <query1>
  union all
  <query2 with references to the input bound to 'temp'>
  )
select cols from temp

Do you think that makes sense? Do you have any pointers for the implementation?

Remove all/most of the Internal modules

They confuse things, bloat the interface, and should never be used anyway. Take feature requests for the ones that are in use, or Google for the module names.

Simplify creation of constants

It might be useful to simplify the creation of constants, currently done something like this:

data Foo a b c = Foo a b c

fooColumn :: Foo (Column PGInt4) (Column PGBool) (Column PGText)
fooColumn = Foo (pgInt4 1) (pgBool True) (pgStrictText "foo")

We could use a product profunctor and a Default instance to simplify this to

fooColumn = constant (Foo 1 True "foo")

The downside is that this would probably require some explicit type signatures.

Allow subselects in inserts/updates

I'd like to be able to write queries like this:

update users set last_post = (select id from posts where foo) where bar

And similarly for inserts. This was already filed as karamaan/karamaan-opaleye#66 where it was noted that this can fail at runtime if the subselect gives something other than 1 result. But I don't think there's anything that can be done about that.

arrangeInsertSql PGText ?

Hello,
following manipulation tutorial. Int4 insertion works fine but what about inserting PGText ?
let be this:
import Opaleye as O
table' :: O.Table (Maybe (Column O.PGInt4), Column O.PGText, Column O.PGText)
(Column O.PGInt4, Column O.PGText, Column O.PGText)
O.arrangeInsertSql table' (Nothing, "ABC", "TEST")
gives:
Couldn't match expected type ‘Column PGText’
with actual type ‘[Char]’
In the expression: "ABC"
and:
Couldn't match expected type ‘Column PGText’
with actual type ‘[Char]’
In the expression: "TEST"

So, I have to change type of "ABC" into 'Column PGText'

I suppose there is an easy way to do it already.
If not tell me, maybe I could patch it.

Cheers,
anoe

Subquery optimization for MySQL

I had a look at the queries being generated, but I'm afraid I think that subquery optmization for MySQL is not very effective.

E.g. looking at...

EXPLAIN SELECT `createdAt0_1` as result1_2,
       `updatedAt1_1` as result2_2,
       `originID2_1` as result3_2,
       `originType3_1` as result4_2,
       `partID4_1` as result5_2,
       `dpn5_1` as result6_2,
       `url6_1` as result7_2,
       `provider7_1` as result8_2,
       `markup8_1` as result9_2
FROM (SELECT *
      FROM (SELECT `createdAt` as createdAt0_1,
                   `updatedAt` as updatedAt1_1,
                   `originID` as originID2_1,
                   `originType` as originType3_1,
                   `partID` as partID4_1,
                   `dpn` as dpn5_1,
                   `url` as url6_1,
                   `provider` as provider7_1,
                   `markup` as markup8_1
            FROM source as T1) as T1) as T1;

...produces...

# id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, PRIMARY, <derived2>, ALL, , , , , 53385, 
2, DERIVED, <derived3>, ALL, , , , , 53385, 
3, DERIVED, T1, ALL, , , , , 63367, 

...so it seems temporary tables are not optimized away.

queryTable fails with thirteen columns

The following code does not compile in ghci:

thirteenTable :: Table (Column PGText, Column PGFloat8, Column PGText
                   ,Column PGText, Column PGFloat8, Column PGText
                   ,Column PGText, Column PGFloat8, Column PGText
                   ,Column PGText, Column [PGFloat8], Column PGText
                   ,Column PGText)
                   (Column PGText, Column PGFloat8, Column PGText
                   ,Column PGText, Column PGFloat8, Column PGText
                   ,Column PGText, Column PGFloat8, Column PGText
                   ,Column PGText, Column [PGFloat8], Column PGText
                   ,Column PGText)

 thirteenTable = Table "thirteenTable" (p13 ( required "a1"
                                       , required "a2"
                                       , required "a3"
                                       , required "a4"
                                       , required "a5"
                                       , required "a6"
                                       , required "a7"
                                       , required "a8"
                                       , required "a9"
                                       , required "a10"
                                       , required "a11"
                                       , required "a12"
                                       , required "a13"))

thirteenQuery :: Query(Column PGText, Column PGFloat8, Column PGText
                  ,Column PGText, Column PGFloat8, Column PGText
                  ,Column PGText, Column PGFloat8, Column PGText
                  ,Column PGText, Column [PGFloat8], Column PGText
                  ,Column PGText)
thirteenQuery = queryTable thirteenTable

It give the error:

No instance for (Default
                   Opaleye.Internal.TableMaker.ColumnMaker
                   (Column PGText,
                    Column PGFloat8,
                    Column PGText,
                    Column PGText,
                    Column PGFloat8,
                    Column PGText,
                    Column PGText,
                    Column PGFloat8,
                    Column PGText,
                    Column PGText,
                    Column [PGFloat8],
                    Column PGText,
                    Column PGText)
                   (Column PGText,
                    Column PGFloat8,
                    Column PGText,
                    Column PGText,
                    Column PGFloat8,
                    Column PGText,
                    Column PGText,
                    Column PGFloat8,
                    Column PGText,
                    Column PGText,
                    Column [PGFloat8],
                    Column PGText,
                    Column PGText))
  arising from a use of ‘queryTable’
In the expression: queryTable thirteenTable
In an equation for ‘thirteenQuery’:
    thirteenQuery = queryTable thirteenTable

However removing one of the columns or adding another column causes the example above to work fine.

Add more property tests

The following remain to be tested

  • union
  • aggregate
  • left join
  • nullability
  • all the operators (numeric, logical, etc.)
  • creating tables

Move "unpack columns" functionality to `Unpackspec` module

Currently the functionality for unpacking columns is trapped inside runQueryArrUnpack. Move it to the Unpackspec module.

runQueryArrUnpack :: U.Unpackspec a b
                  -> Query a -> ([HPQ.PrimExpr], PQ.PrimQuery, Tag)
runQueryArrUnpack unpackspec q = (primExprs, primQ, endTag)
  where (columns, primQ, endTag) = runSimpleQueryArrStart q ()
        f pe = ([pe], pe)
        primExprs :: [HPQ.PrimExpr]
        (primExprs, _) = U.runUnpackspec unpackspec f columns

So Many imports

Is it possible to add a convenience import to OpalEye? It's a bit intimidating to (as is done in the basic tutorial) have to import 18 modules before writing a single like of functional code, especially if I'm only meaning to take OpalEye for a test drive.

Only escape keywords

At the moment opaleye (in reality, not in the tutorial) escapes all column names. It should only do that for ones that conflict, to keep the default SQL simpler for human consumption.

Leaking into GROUP BY

Not a bug per se, but is there anything can be done about it?

{-# LANGUAGE Arrows #-}
import Opaleye
import qualified Database.PostgreSQL.Simple as PGS
import Control.Arrow

q2 :: Column PGInt4 -> Query (Column PGInt4)
q2 i = proc () -> do
          -- x <- queryTable tableX -< ()  -- one column for simplicity
          -- restrict -< x .== i
          returnA -< i

a2 :: Column PGInt4 -> Query (Column PGInt4)
a2 i = (aggregate groupBy) (q2 i)

main = do
  c <- PGS.connect connectInfo
  r <- runQuery c (a2 1) :: IO [Int]  -- it will “GROUP BY (1)” [sic]
  print r

connectInfo :: PGS.ConnectInfo

Expose/implement more aggregation operators

In the postgresql, there are more aggregation operators than the user of opaleye can use. Some are just not exposed in the public api, whereas some are just not implemented.

Insert a user defined record type?

Given these types:

data User' a b = User {uID :: a, uEmail :: b} deriving (Show)
type User = User' Int Text
type UserColumn = User' (Column PGInt4) (Column PGText)

I can run a query and get back IO [User] rather than IO [UserColumn] which is great. But is there any way to insert a User without first manually converting it to a UserColumn?

Rename pgInt4 and pgInt8

Most pg* functions are named after the haskell type e.g. pgTimeOfDay, but there's pgInt4 and pgInt8 which have postgres names, and "The type names int2, int4, and int8 are extensions, which are also used by some other SQL database systems."

Haskell's Int isn't guaranteed to fit in an int4, only on 32bit systems? For me Int and Int64 are the same.

So I think it would make more sense to have pgInt and pgInt64 instead since 64bit users can't safely map Int to int4.

Provide a single exported module

At the moment the docs are awful since you get a massive list of modules that you can't search through. Make the modules such as Opaleye.Operators hidden so that Opaleye documents everything.

Allow inserting multiple values at once

Postgres allows you to insert multiple values in a single insert statement:

silk=# create table t (id integer primary key not null);
CREATE TABLE
silk=# insert into t values (1), (2), (3);
INSERT 0 3

It would be nice if this could also be done from Opaleye, but it looks like currently you can only insert a single row at a time.

Lacking `in` operator

I had a stab at this with:

in_ :: Column a -> [Column a] -> Column PGBool
in_ x ys =
  Opaleye.binOp HPQ.OpIn
                x
                (Opaleye.Column (HPQ.ListExpr [y | Opaleye.Column y <- ys]))

However this produces:

WHERE ((id0_2) IN ((10025,10355)))) as T1

Which isn't correct, as there are one too many levels of parenthesis, which changes the semantics to be a list of records, rather than list of integers.

Composing a series of filters.

Hello! I've been very impressed with opaleye and I'm looking into using it on a project where I need to be able to compose a dynamic set of filters in order to build a query. It's going well but I have a couple of questions.

So, fist some setup. Given a query that returns users:
usersQuery :: Query UserColumn

I need to be able to compose an assortment of filters in order to get the final set of users. My filters all basically have this form: 1.) accept some users 2.) apply some filter to the users 3.) pass the filtered users along. Like this:

hasFeature featureID value = proc user -> do
    fv <- featureValuesQuery -< ()
    restrict -< (fvUserID .== uID user) 
            .&& (fvFeatureID .== featureID) 
            .&& (fvValue .== value)
    returnA -< user

And this all composes nicely like this:

usersQuery >>> hasFeature 1 (pgString "foo") >>> hasFeature 2 (pgString "bar")

So far so good. My questions are:

1.) Is this a reasonable approach to the problem? As in, will it generate terribly inefficient SQL? Looking at the output of printSql it actually looks pretty reasonable.
2.) I'm currently doing this to compose my list of filters: foldl' (>>>) usersQuery [f1, f2...]. Is there a more idiomatic way to fold over a bunch of arrows?

Thank you!

EDIT: Also, how would I use count to get the number of rows returned?
I'm currently doing this but it seems cumbersome and specific to the type of row I'm counting: aggregate count (usersQuery >>^ uID)

No longer possible to enforce foreign key compatibility on the type level(?)

This is coming late, but I didn't realize the impact of the PGTypes change that happened a while back. I'm not sure if this can be solved outside of opaleye?

Take this example:

create table foo (id serial primary key);
create table bar (id serial primary key, foo_id int references foo (id));

The opaleye mapping for this would be

newtype FooId = FooId Int
data Foo' a = Foo { fooId :: a }
type Foo = Foo' FooId
type FooColumn = Foo (Column PGInt)

newtype BarId = BarId Int
data Bar' a b = Foo { barId :: a,  fooBarId :: b }
type Bar = Bar' BarId FooId
type BarColumn = Foo (Column PGInt) (Column PGInt)

If we construct queries joining these tables we might by accident end up with

myQuery = proc () -> do
  f <- queryTable fooTable
  b <- queryTable barTable
  restrict -< fooId f .== barId b
  id -< (f,b)

yielding something like

select f.* from foo f inner join bar b on f.id = b.id;

This used to be a type error because fooId f :: Column FooId and barId b :: Column BarId. Now they are both :: Column PGInt and there is no protection against doing nonsensical joins.

So in a lot of ways queries are no longer type safe as I'd like them to be, they are only type safe in the sense that the postgres types are the same. You can misplace any column that happens to have the same representation as another one.

Thoughts on some types

Hi, this issue is going to be a little rambly, but hopefully I can provide some useful information....

As I've mentioned before, I don't really like the way records interact in Opaleye. data Foo a b c d e is not idiomatic Haskell, and it makes adding Opaleye in later difficult. However, the benefit of having named accessors is an indispensable win - so whatever I suggest needs to be able to provide this.

The first observation is that we're doing things this way in order to "vary the idiom". Sometimes we are working with a table where everything is a Column, other times everything is in TableProperties, and other times everything is in Identity (you've executed the query).

This notion comes up a lot - we see it once in generics-sop (all products/sums are parametrized by a functor) and again in vinyl. I would wager that it's possible to achieve what we have at the moment using vinyl - we get named fields and thus order-independent access/construction. Along with this, the specification of tables becomes easier - all you need to do is list the types (it's unclear whether you would list PostgreSQL types or Haskell types), and you only need to do that once. We're then moving towards something like

someTable :: Table '[Int, String, String, UTCTime]

The next observation is that the type inference is pretty hairy - and I'd say that's almost entirely due to Default. With the above idea you now have an inductive structure that you can meaningfully fold over. Running a query simply swaps out the functor from Column to something like:

data PostgreSQLSimple :: * -> * where
  PostgreSQLSimple :: FromField a => PostgreSQLSimple a

(there are details here as to when you're able to capture this constraint, but I can't see any reason it can't be done, off hand).

Again, just by varying the functor you get more useful properties. A left join should be inferrable now, because a left join simply changes your functor from f to Compose Nullable f, perhaps joining Nullable (Nullable a) along the way.

This is bit of a sketch of ideas, but hopefully I'll find time to help contribute some of this work.

Has anyone explored any of these ideas?

Yesod Integration

Can someone give me hints how to replace Persistent with OpalEye in a scaffolded Yesod 1.4 application?

Test cases broken

I wanted to add tests for #58 and found that the test cases on current master are broken. Here is the output:

opaleye-0.3.1.2: test (suite: test)
NOTICE:  table "TABLE2" does not exist, skipping
*** Failed! Exception: 'SqlError {sqlState = "42601", sqlExecStatus = FatalError, sqlErrorMsg = "syntax error at or near \"FROM\"", sqlErrorDetail = "", sqlErrorHint = ""}' (after 1 test):
A permutation
SELECT "column10_1" as result1_2
FROM (SELECT *
      FROM (SELECT *
            FROM (SELECT *
                  FROM (SELECT "column1" as column10_1,
                               "column2" as column21_1
                        FROM "table1" as T1) as T1) as T1
            LIMIT 71) as T1) as T1
test: Failed
opaleye-0.3.1.2: test (suite: tutorial)
Test suite failure for package opaleye-0.3.1.2
    test:  exited with: ExitFailure 1
Logs printed to console

`PGLift` idea

At Fynder we have a lot of custom types. In fact, every table gives rise to (at least) one newtype wrapper around its surrogate id. The following type is useful to "lift" coercions in postgresql-simple to opaleye:

data PGLift a

instance PG.FromField a => Opaleye.QueryRunnerColumnDefault (PGLift a) a where
  queryRunnerColumnDefault = fieldQueryRunnerColumn

Maybe this is something that would fit nicely in opaleye itself?

Compare two newtype columns

When I have wrap an id in a newtype, then I need to unwrap it every time when I need to do something useful with it.

newtype CompanyId' id = CompanyId { getCompanyId :: id }
type CompanyId = CompanyId' Int
makeAdaptorAndInstance "pCompanyId" ''C.CompanyId'

Then I need to do the unwrapping everytime:

restrict -< C.getCompanyId companyPK .== (pgInt4 . C.getCompanyId) companyId

Whereas I'd like to write:

restrict -< companyPK .== companyId

Does anyone have some nice solution for this?

Simplify the generated SQL

The generated SQL is pretty verbose. I suggest you try and simplify all the ones in the tutorial with simple rewrite rules. For other databases like SQLite it is common to try and not rely on the database to do clever optimisations.

Table names should be escaped

Apparently table names with capital letters (and presumably other non-lowercase alphabeticals) are not handled properly: #48

Merge the pull request and add a test.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.